Dear All,

The first attempts at fixing this bug were posted to the PR in
February of this year. Since then, real life has intervened and I have
not been able to get back to it until now.

The first patch used the address of the vtable to perform the
switching in SELECT_TYPE. Unfortunately, it failed in submodule_6.f90
and I have not been able to find a way to fix this without breaking
the ABI and having to bump up the module version number.

The second patch uses a string for the switching, which comprises a
concatenation of the type name and the module or procedure name.
Clearly, there is a performance penalty associated with this. My
recent efforts have been focussed on making this version detect
incoming selectors and associates that are use associated with
libraries that were compiled before this patch was applied and the
result is this submission. By the way, I was unable to find a way of
testing this feature as part of the testsuite but have done so 'by
hand'.

If the performance penalty is considered to be a show stopper, I could
develop further the version based on the vtable addresses but will
have to postpone any further work on this for a few weeks.

Otherwise, this patch does bootstrap and regtest on FC21/x86_64 - OK for trunk?

Cheers

Paul

2016-09-27  Paul Thomas  <pa...@gcc.gnu.org>

    PR fortran/69834
    * class.c (get_unique_type_string): Add an extra argument
    'icase' that defaults to false but, when true, switches the
    order of type name and module or procedure name.
    (get_unique_hashed_string): New argument 'icase' switches
    bewteen the old form and a new one in which the string length
    is limited to GFC_MAX_SYMBOL_LEN and, in case of this limit
    being exceeded, the hash string is followed by as much of the
    composite name as possible.
    (gfc_case_name): New function.
    (gfc_find_derived_vtab): Add '_name' field to vtable. This is
    initialized by 'get_unique_type_string' with 'icase' true.
    (find_intrinsic_vtab): Ditto with initialization performed by a
    call to 'gfc_case_name'.
    * gfortran.h : Add macro 'gfc_add_name_component' and prototype
    for 'gfc_case_name'.
    * resolve.c (vtable_old_style): New function to determine if a
    use associated vtable is missing the '_name' field.
    (resolve_select_type): Call 'vtable_old_style' to determine if
    any of the derived types or vtables come from a library that
    was compiled before this patch. If this is the case, the old
    form of SELECT TYPE is activated, in which the cases are set by
    the hash value. Otherwise, the 'unique_type_string' is used.

2016-09-27  Paul Thomas  <pa...@gcc.gnu.org>

    PR fortran/69834
    * gfortran.dg/finalize_21.f90: Remove semi colon from the tree
    scan.
    * gfortran.dg/select_type_36.f03: New test.
    * gfortran.dg/select_type_37.f03: New test.
Index: gcc/fortran/class.c
===================================================================
*** gcc/fortran/class.c (revision 240492)
--- gcc/fortran/class.c (working copy)
*************** gfc_class_initializer (gfc_typespec *ts,
*** 472,492 ****
     containers and vtab symbols.  */
  
  static void
! get_unique_type_string (char *string, gfc_symbol *derived)
  {
    char dt_name[GFC_MAX_SYMBOL_LEN+1];
    if (derived->attr.unlimited_polymorphic)
      strcpy (dt_name, "STAR");
    else
      strcpy (dt_name, gfc_dt_upper_string (derived->name));
!   if (derived->attr.unlimited_polymorphic)
!     sprintf (string, "_%s", dt_name);
!   else if (derived->module)
!     sprintf (string, "%s_%s", derived->module, dt_name);
!   else if (derived->ns->proc_name)
!     sprintf (string, "%s_%s", derived->ns->proc_name->name, dt_name);
    else
!     sprintf (string, "_%s", dt_name);
  }
  
  
--- 472,508 ----
     containers and vtab symbols.  */
  
  static void
! get_unique_type_string (char *string, gfc_symbol *derived, bool iscase = 
false)
  {
    char dt_name[GFC_MAX_SYMBOL_LEN+1];
    if (derived->attr.unlimited_polymorphic)
      strcpy (dt_name, "STAR");
    else
      strcpy (dt_name, gfc_dt_upper_string (derived->name));
! 
!   /* The new style SELECT TYPE requires the type name to appear first.  */
!   if (iscase)
!     {
!       if (derived->attr.unlimited_polymorphic)
!       sprintf (string, "_%s", dt_name);
!       else if (derived->module)
!       sprintf (string, "%s_%s", dt_name, derived->module);
!       else if (derived->ns->proc_name)
!       sprintf (string, "%s_%s", dt_name, derived->ns->proc_name->name);
!       else
!       sprintf (string, "_%s", dt_name);
!     }
    else
!     {
!       if (derived->attr.unlimited_polymorphic)
!       sprintf (string, "_%s", dt_name);
!       else if (derived->module)
!       sprintf (string, "%s_%s", derived->module, dt_name);
!       else if (derived->ns->proc_name)
!       sprintf (string, "%s_%s", derived->ns->proc_name->name, dt_name);
!       else
!       sprintf (string, "_%s", dt_name);
!     }
  }
  
  
*************** get_unique_type_string (char *string, gf
*** 494,512 ****
     string will not be too long (replacing it by a hash string if needed).  */
  
  static void
! get_unique_hashed_string (char *string, gfc_symbol *derived)
  {
    char tmp[2*GFC_MAX_SYMBOL_LEN+2];
!   get_unique_type_string (&tmp[0], derived);
!   /* If string is too long, use hash value in hex representation (allow for
!      extra decoration, cf. gfc_build_class_symbol & gfc_find_derived_vtab).
!      We need space to for 15 characters "__class_" + symbol name + "_%d_%da",
!      where %d is the (co)rank which can be up to n = 15.  */
!   if (strlen (tmp) > GFC_MAX_SYMBOL_LEN - 15)
!     {
!       int h = gfc_hash_value (derived);
        sprintf (string, "%X", h);
      }
    else
      strcpy (string, tmp);
  }
--- 510,543 ----
     string will not be too long (replacing it by a hash string if needed).  */
  
  static void
! get_unique_hashed_string (char *string, gfc_symbol *derived, bool iscase = 
false)
  {
    char tmp[2*GFC_MAX_SYMBOL_LEN+2];
!   int h;
! 
!   get_unique_type_string (&tmp[0], derived, iscase);
! 
!   /* Whether this function is called by 'gfc_case_name' or
!      'gfc_find_derived_vtab' makes a big difference as to what is written to
!      'string' in the event that the unique type string is over long.  */
!   if (!iscase && strlen (tmp) > GFC_MAX_SYMBOL_LEN - 15)
!     {
!       /* If string is too long, use hash value in hex representation (allow 
for
!        extra decoration, cf. gfc_build_class_symbol & gfc_find_derived_vtab).
!        We need space for 15 characters "__class_" + symbol name + "_%d_%da",
!        where %d is the (co)rank which can be up to n = 15.  */
!       h = gfc_hash_value (derived);
        sprintf (string, "%X", h);
      }
+   else if (iscase && strlen (tmp) > GFC_MAX_SYMBOL_LEN)
+     {
+       /* If string is too long, use hash value in hex representation followed
+        by as much of the unique name as possible.  */
+       char str[GFC_MAX_SYMBOL_LEN-8];
+       h = gfc_hash_value (derived);
+       strncpy (str, tmp, (size_t)(GFC_MAX_SYMBOL_LEN - 8));
+       sprintf (string, "%X%s", h, str);
+     }
    else
      strcpy (string, tmp);
  }
*************** gfc_intrinsic_hash_value (gfc_typespec *
*** 552,557 ****
--- 583,596 ----
    return (hash % 100000000);
  }
  
+ void
+ gfc_case_name (char *name, gfc_typespec *ts)
+ {
+   if (ts->type == BT_DERIVED || ts->type == BT_CLASS)
+     get_unique_hashed_string (name, ts->u.derived, true);
+   else
+     sprintf (name, "%s_%d_", gfc_basic_typename (ts->type), ts->kind);
+ }
  
  /* Get the _len component from a class/derived object storing a string.
     For unlimited polymorphic entities a ref to the _data component is 
available
*************** gfc_find_derived_vtab (gfc_symbol *deriv
*** 2405,2410 ****
--- 2444,2460 ----
              c->tb->ppc = 1;
              generate_finalization_wrapper (derived, ns, tname, c);
  
+             if (!gfc_add_component (vtype, "_name", &c))
+               goto cleanup;
+             c->ts.type = BT_CHARACTER;
+             c->ts.kind = gfc_default_character_kind;
+             c->attr.access = ACCESS_PRIVATE;
+             c->ts.u.cl = gfc_get_charlen();
+             get_unique_hashed_string (tname, derived, true);
+             c->ts.u.cl->length = gfc_get_int_expr (4, &derived->declared_at,
+                                                   GFC_MAX_SYMBOL_LEN+1);
+             c->initializer = gfc_get_character_expr (c->ts.kind, NULL,
+                                                      tname, strlen (tname));
              /* Add procedure pointers for type-bound procedures.  */
              if (!derived->attr.unlimited_polymorphic)
                add_procs_to_declared_vtab (derived, vtype);
*************** find_intrinsic_vtab (gfc_typespec *ts)
*** 2678,2683 ****
--- 2728,2746 ----
              c->tb = XCNEW (gfc_typebound_proc);
              c->tb->ppc = 1;
              c->initializer = gfc_get_null_expr (NULL);
+ 
+             if (!gfc_add_component (vtype, "_name", &c))
+               goto cleanup;
+             c->ts.type = BT_CHARACTER;
+             c->ts.kind = gfc_default_character_kind;
+             c->attr.access = ACCESS_PRIVATE;
+             c->ts.u.cl = gfc_get_charlen();
+             gfc_case_name (tname, ts);
+             c->ts.u.cl->length = gfc_get_int_expr (gfc_index_integer_kind,
+                                                    &gfc_current_locus,
+                                                    GFC_MAX_SYMBOL_LEN+1);
+             c->initializer = gfc_get_character_expr 
(gfc_default_character_kind, NULL,
+                                                      tname, strlen (tname));
            }
          vtab->ts.u.derived = vtype;
          vtab->value = gfc_default_initializer (&vtab->ts);
Index: gcc/fortran/gfortran.h
===================================================================
*** gcc/fortran/gfortran.h      (revision 240492)
--- gcc/fortran/gfortran.h      (working copy)
*************** void gfc_add_class_array_ref (gfc_expr *
*** 3266,3276 ****
--- 3266,3278 ----
  #define gfc_add_size_component(e)     gfc_add_component_ref(e,"_size")
  #define gfc_add_def_init_component(e) gfc_add_component_ref(e,"_def_init")
  #define gfc_add_final_component(e)    gfc_add_component_ref(e,"_final")
+ #define gfc_add_name_component(e)    gfc_add_component_ref(e,"_name")
  bool gfc_is_class_array_ref (gfc_expr *, bool *);
  bool gfc_is_class_scalar_expr (gfc_expr *);
  bool gfc_is_class_container_ref (gfc_expr *e);
  gfc_expr *gfc_class_initializer (gfc_typespec *, gfc_expr *);
  unsigned int gfc_hash_value (gfc_symbol *);
+ void gfc_case_name (char *, gfc_typespec *);
  gfc_expr *gfc_get_len_component (gfc_expr *e);
  bool gfc_build_class_symbol (gfc_typespec *, symbol_attribute *,
                             gfc_array_spec **);
Index: gcc/fortran/resolve.c
===================================================================
*** gcc/fortran/resolve.c       (revision 240492)
--- gcc/fortran/resolve.c       (working copy)
*************** resolve_assoc_var (gfc_symbol* sym, bool
*** 8310,8315 ****
--- 8310,8348 ----
  }
  
  
+ /* See if the 'name' field appears in the vtable. If so, SELECT TYPE can
+    proceed with the comparison of composite names. Otherwise, the hash
+    values are used.  */
+ 
+ static bool
+ vtable_old_style (gfc_typespec ts)
+ {
+   gfc_symbol *vtab;
+ 
+   if (ts.u.derived == NULL
+       || !ts.u.derived->attr.use_assoc
+       || ts.u.derived->components == NULL)
+     return false;
+ 
+   if (ts.u.derived->attr.vtype)
+      return gfc_find_component (ts.u.derived, "_name", true, true, NULL)
+                               ? false : true;
+ 
+   if (ts.type == BT_CLASS
+       && (ts.u.derived->components == NULL
+         || ts.u.derived->components->ts.u.derived == NULL
+         || !ts.u.derived->components->ts.u.derived->attr.use_assoc))
+     return false;
+ 
+   vtab = gfc_find_vtab (&ts);
+   if (gfc_find_component (vtab->ts.u.derived, "_name", true, true, NULL))
+     return false;
+ 
+   /* This is an old style vtable.  */
+   return true;
+ }
+ 
+ 
  /* Resolve a SELECT TYPE statement.  */
  
  static void
*************** resolve_select_type (gfc_code *code, gfc
*** 8324,8329 ****
--- 8357,8363 ----
    gfc_namespace *ns;
    int error = 0;
    int charlen = 0;
+   bool old_style_vtable = false;
  
    ns = code->ext.block.ns;
    gfc_resolve (ns);
*************** resolve_select_type (gfc_code *code, gfc
*** 8372,8377 ****
--- 8406,8414 ----
      {
        c = body->ext.block.case_list;
  
+       if (c->ts.type == BT_DERIVED || c->ts.type == BT_CLASS)
+       old_style_vtable = vtable_old_style (c->ts);
+ 
        /* Check F03:C815.  */
        if ((c->ts.type == BT_DERIVED || c->ts.type == BT_CLASS)
          && !selector_type->attr.unlimited_polymorphic
*************** resolve_select_type (gfc_code *code, gfc
*** 8465,8480 ****
    code = new_st;
    code->op = EXEC_SELECT;
  
    gfc_add_vptr_component (code->expr1);
    gfc_add_hash_component (code->expr1);
  
    /* Loop over TYPE IS / CLASS IS cases.  */
    for (body = code->block; body; body = body->block)
      {
        c = body->ext.block.case_list;
  
        if (c->ts.type == BT_DERIVED)
!       c->low = c->high = gfc_get_int_expr (gfc_default_integer_kind, NULL,
                                             c->ts.u.derived->hash_value);
        else if (c->ts.type != BT_CLASS && c->ts.type != BT_UNKNOWN)
        {
--- 8502,8531 ----
    code = new_st;
    code->op = EXEC_SELECT;
  
+ 
    gfc_add_vptr_component (code->expr1);
+   old_style_vtable = vtable_old_style (code->expr1->ts);
+ 
+   if (old_style_vtable)
    gfc_add_hash_component (code->expr1);
+   else
+     gfc_add_name_component (code->expr1);
  
    /* Loop over TYPE IS / CLASS IS cases.  */
    for (body = code->block; body; body = body->block)
      {
+       char tname[GFC_MAX_SYMBOL_LEN+1];
+ 
        c = body->ext.block.case_list;
  
+       if (old_style_vtable)
+       {
+         /* At least one old style vtable has been detected. Use the
+            hash value for the SELECT CASE. Note that this will remain
+            prone to clashes as in PR69834.  */
        if (c->ts.type == BT_DERIVED)
!           c->low = c->high = gfc_get_int_expr (gfc_default_integer_kind,
!                                                NULL,
                                             c->ts.u.derived->hash_value);
        else if (c->ts.type != BT_CLASS && c->ts.type != BT_UNKNOWN)
        {
*************** resolve_select_type (gfc_code *code, gfc
*** 8486,8494 ****
          e = CLASS_DATA (ivtab)->initializer;
          c->low = c->high = gfc_copy_expr (e);
        }
- 
        else if (c->ts.type == BT_UNKNOWN)
        continue;
  
        /* Associate temporary to selector.  This should only be done
         when this case is actually true, so build a new ASSOCIATE
--- 8537,8562 ----
          e = CLASS_DATA (ivtab)->initializer;
          c->low = c->high = gfc_copy_expr (e);
        }
        else if (c->ts.type == BT_UNKNOWN)
        continue;
+       }
+       else
+       {
+         /* New style selection using a composite name generated in
+            class.c (gfc_case_name).  */
+         if (c->ts.type != BT_UNKNOWN)
+           gfc_case_name (&tname[0], &c->ts);
+         else if (c->ts.type == BT_UNKNOWN)
+           continue;
+ 
+         c->low = gfc_get_character_expr (gfc_default_character_kind, NULL,
+                                          tname, strlen (tname));
+         c->low->ts.u.cl = gfc_get_charlen();
+         c->low->ts.u.cl->length = gfc_get_int_expr (gfc_index_integer_kind,
+                                                     &code->expr1->where,
+                                                     GFC_MAX_SYMBOL_LEN+1);
+         c->high = c->low;
+       }
  
        /* Associate temporary to selector.  This should only be done
         when this case is actually true, so build a new ASSOCIATE
Index: gcc/testsuite/gfortran.dg/finalize_21.f90
===================================================================
*** gcc/testsuite/gfortran.dg/finalize_21.f90   (revision 240492)
--- gcc/testsuite/gfortran.dg/finalize_21.f90   (working copy)
***************
*** 8,11 ****
  class(*), allocatable :: var
  end
  
! ! { dg-final { scan-tree-dump "static struct __vtype__STAR __vtab__STAR = 
{._hash=0, ._size=., ._extends=0B, ._def_init=0B, ._copy=0B, ._final=0B};" 
"original" } }
--- 8,11 ----
  class(*), allocatable :: var
  end
  
! ! { dg-final { scan-tree-dump "static struct __vtype__STAR __vtab__STAR = 
{._hash=0, ._size=., ._extends=0B, ._def_init=0B, ._copy=0B, ._final=0B," 
"original" } }
Index: gcc/testsuite/gfortran.dg/select_type_36.f03
===================================================================
*** gcc/testsuite/gfortran.dg/select_type_36.f03        (revision 0)
--- gcc/testsuite/gfortran.dg/select_type_36.f03        (working copy)
***************
*** 0 ****
--- 1,44 ----
+ ! { dg-do run }
+ !
+ ! Test the fix for PR69834 in which the two derived types below
+ ! had the same hash value and so generated an error in the resolution
+ ! of SELECT TYPE.
+ !
+ ! Reported by James van Buskirk on clf:
+ ! https://groups.google.com/forum/#!topic/comp.lang.fortran/0bm3E5xJpkM
+ !
+ module types
+    implicit none
+    type CS5SS
+       integer x
+       real y
+    end type CS5SS
+    type SQS3C
+       logical u
+       character(7) v
+    end type SQS3C
+    contains
+       subroutine sub(x, switch)
+          class(*), allocatable :: x
+          integer :: switch
+          select type(x)
+             type is(CS5SS)
+                if (switch .ne. 1) call abort
+             type is(SQS3C)
+                if (switch .ne. 2) call abort
+             class default
+                call abort
+          end select
+       end subroutine sub
+ end module types
+ 
+ program test
+    use types
+    implicit none
+    class(*), allocatable :: u1, u2
+ 
+    allocate(u1,source = CS5SS(2,1.414))
+    allocate(u2,source = SQS3C(.TRUE.,'Message'))
+    call sub(u1, 1)
+    call sub(u2, 2)
+ end program test
Index: gcc/testsuite/gfortran.dg/select_type_37.f03
===================================================================
*** gcc/testsuite/gfortran.dg/select_type_37.f03        (revision 0)
--- gcc/testsuite/gfortran.dg/select_type_37.f03        (working copy)
***************
*** 0 ****
--- 1,30 ----
+ ! { dg-do run }
+ !
+ ! Test the fix for PR69834 in which the hash value was insufficient to
+ ! prevent type clashes. This test exercises that cases where the combined
+ ! name is longer than GFC_MAX_SYMBOL_LEN, so that the hash is rolled into
+ ! the composite name used in SELECT TYPE.
+ !
+ module extreme_and_very_silly_module_named_brian
+   type :: daft_type_name_that_sounds_like_blue_parrot
+     integer :: i
+   end type
+   type, extends(daft_type_name_that_sounds_like_blue_parrot) :: &
+        daft_type_name_that_sounds_that_is_spam_spam
+     real :: r
+   end type
+ end module
+ 
+   use extreme_and_very_silly_module_named_brian
+ 
+   class (daft_type_name_that_sounds_like_blue_parrot), allocatable ::c
+ 
+   allocate (c, source = daft_type_name_that_sounds_that_is_spam_spam (22, 
3.0))
+ 
+   select type (c)
+     type is (daft_type_name_that_sounds_like_blue_parrot)
+       call abort
+     type is (daft_type_name_that_sounds_that_is_spam_spam)
+       print *, c%i, c%r
+   end select
+ end

Reply via email to