Hello,

this patch, extracted with some modifications from PR50981 comment #28
[http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50981#c28],
(which has accumulated a lot of things) fixes an ICE noticed in several
PRs with an error like:

internal compiler error: in gfc_conv_descriptor_data_get,
at fortran/trans-array.c:147

internal compiler error: in gfc_conv_descriptor_offset, at
fortran/trans-array.c:210

the problem is a missing "_data" reference (to escape the class container) when trying to access a subobject of a class object.

The solution proposed is to replace the call to gfc_add_component_ref(expr, "_data") with a call to a new, more general, function gfc_fix_class_refs which takes care of adding the "_data" component in all references (not only the last one) where it is missing.
Thus, it works
    - in the scalar case: class%array_comp(1), class%scalar_comp
    - with multiple level of components: class%comp%subclass%sub_comp
    - in the array case (but this was working before): class%array_comp(:)
    - in any mix of the above cases.

I have chosen to make it a separate function instead of fixing gfc_add_component_ref, so that it can be reused later (maybe...) even if we don't want to add a "_data", or "_vptr" or ... component.


W.R.T. the code itself, I think it is rather straightforward. There is an odd thing to prevent a regression in class_41.f03. See the big comment in class_data_ref_missing.


Regression tested on x86_64-unknown-freebsd9.0.  OK for trunk?

Mikael



2012-02-02  Mikael Morin  <mik...@gcc.gnu.org>

        PR fortran/41587
        PR fortran/46356
        PR fortran/51754
        PR fortran/50981
        * class.c (insert_component_ref, class_data_ref_missing,
        gfc_fix_class_refs): New functions.
        * gfortran.h (gfc_fix_class_refs): New prototype.
        * trans-expr.c (gfc_conv_expr): Remove special case handling and call
        gfc_fix_class_refs instead.

diff --git a/class.c b/class.c
index 52c5a61..24e06d2 100644
--- a/class.c
+++ b/class.c
@@ -52,6 +52,129 @@ along with GCC; see the file COPYING3.  If not see
 #include "constructor.h"
 
 
+/* Inserts a derived type component reference in a data reference chain.
+    TS: base type of the ref chain so far, in which we will pick the component
+    REF: the address of the GFC_REF pointer to update
+    NAME: name of the component to insert
+   Note that component insertion makes sense only if we are at the end of
+   the chain (*REF == NULL) or if we are adding a missing "_data" component
+   to access the actual contents of a class object.  */
+
+static void
+insert_component_ref (gfc_typespec *ts, gfc_ref **ref, const char * const name)
+{
+  gfc_symbol *type_sym;
+  gfc_ref *new_ref;
+
+  gcc_assert (ts->type == BT_DERIVED || ts->type == BT_CLASS);
+  type_sym = ts->u.derived;
+
+  new_ref = gfc_get_ref ();
+  new_ref->type = REF_COMPONENT;
+  new_ref->next = *ref;
+  new_ref->u.c.sym = type_sym;
+  new_ref->u.c.component = gfc_find_component (type_sym, name, true, true);
+  gcc_assert (new_ref->u.c.component);
+
+  if (new_ref->next)
+    {
+      gfc_ref *next = NULL;
+
+      /* We need to update the base type in the trailing reference chain to
+        that of the new component.  */
+
+      gcc_assert (strcmp (name, "_data") == 0);
+
+      if (new_ref->next->type == REF_COMPONENT)
+       next = new_ref->next;
+      else if (new_ref->next->type == REF_ARRAY
+              && new_ref->next->next
+              && new_ref->next->next->type == REF_COMPONENT)
+       next = new_ref->next->next;
+
+      if (next != NULL)
+       {
+         gcc_assert (new_ref->u.c.component->ts.type == BT_CLASS
+                     || new_ref->u.c.component->ts.type == BT_DERIVED);
+         next->u.c.sym = new_ref->u.c.component->ts.u.derived;
+       }
+    }
+
+  *ref = new_ref;
+}
+
+
+/* Tells whether we need to add a "_data" reference to access REF subobject
+   from an object of type TS.  If FIRST_REF_IN_CHAIN is set, then the base
+   object accessed by REF is a variable; in other words it is a full object,
+   not a subobject.  */
+
+static bool
+class_data_ref_missing (gfc_typespec *ts, gfc_ref *ref, bool 
first_ref_in_chain)
+{
+  /* Only class containers may need the "_data" reference.  */
+  if (ts->type != BT_CLASS)
+    return false;
+
+  /* Accessing a class container with an array reference is certainly wrong.  
*/
+  if (ref->type != REF_COMPONENT)
+    return true;
+
+  /* Accessing the class container's fields is fine.  */
+  if (ref->u.c.component->name[0] == '_')
+    return false;
+
+  /* At this point we have a class container with a non class container's field
+     component reference.  We don't want to add the "_data" component if we are
+     at the first reference and the symbol's type is an extended derived type.
+     In that case, conv_parent_component_references will do the right thing so
+     it is not absolutely necessary.  Omitting it prevents a regression (see
+     class_41.f03) in the interface mapping mechanism.  When evaluating string
+     lengths depending on dummy arguments, we create a fake symbol with a type
+     equal to that of the dummy type.  However, because of type extension,
+     the backend type (corresponding to the actual argument) can have a
+     different (extended) type.  Adding the "_data" component explicitly, using
+     the base type, confuses the gfc_conv_component_ref code which deals with
+     the extended type.  */
+  if (first_ref_in_chain && ts->u.derived->attr.extension)
+    return false;
+
+  /* We have a class container with a non class container's field component
+     reference that doesn't fall into the above.  */
+  return true;
+}
+
+
+/* Browse through a data reference chain and add the missing "_data" references
+   when a subobject of a class object is accessed without it.
+   Note that it doesn't add the "_data" reference when the class container
+   is the last element in the reference chain.  */
+
+void
+gfc_fix_class_refs (gfc_expr *e)
+{
+  gfc_typespec *ts;
+  gfc_ref **ref;
+
+  if ((e->expr_type != EXPR_VARIABLE
+       && e->expr_type != EXPR_FUNCTION)
+      || (e->expr_type == EXPR_FUNCTION
+         && e->value.function.isym != NULL))
+    return;
+
+  ts = &e->symtree->n.sym->ts;
+
+  for (ref = &e->ref; *ref != NULL; ref = &(*ref)->next)
+    {
+      if (class_data_ref_missing (ts, *ref, ref == &e->ref))
+       insert_component_ref (ts, ref, "_data");
+
+      if ((*ref)->type == REF_COMPONENT)
+       ts = &(*ref)->u.c.component->ts;
+    }
+}
+
+
 /* Insert a reference to the component of the given name.
    Only to be used with CLASS containers and vtables.  */
 
diff --git a/gfortran.h b/gfortran.h
index 23c16ba..6989eb1 100644
--- a/gfortran.h
+++ b/gfortran.h
@@ -2919,6 +2919,7 @@ gfc_try gfc_calculate_transfer_sizes (gfc_expr*, 
gfc_expr*, gfc_expr*,
                                      size_t*, size_t*, size_t*);
 
 /* class.c */
+void gfc_fix_class_refs (gfc_expr *e);
 void gfc_add_component_ref (gfc_expr *, const char *);
 void gfc_add_class_array_ref (gfc_expr *);
 #define gfc_add_data_component(e)     gfc_add_component_ref(e,"_data")
diff --git a/trans-expr.c b/trans-expr.c
index 7543149..ea6a993 100644
--- a/trans-expr.c
+++ b/trans-expr.c
@@ -5486,10 +5486,7 @@ gfc_conv_expr (gfc_se * se, gfc_expr * expr)
         }
     }
 
-  /* TODO: make this work for general class array expressions.  */
-  if (expr->ts.type == BT_CLASS
-       && expr->ref && expr->ref->type == REF_ARRAY)
-    gfc_add_component_ref (expr, "_data");
+  gfc_fix_class_refs (expr);
 
   switch (expr->expr_type)
     {
2012-02-02  Mikael Morin  <mik...@gcc.gnu.org>

        PR fortran/41587
        * gfortran.dg/class_array_10.f03: New test.

        PR fortran/46356
        * gfortran.dg/class_array_11.f03: New test.

        PR fortran/51754
        * gfortran.dg/class_array_12.f03: New test.

! { dg-do compile}
!
! PR fortran/41587
! This program was leading to an ICE related to class allocatable arrays
!
! Contributed by Dominique D'Humieres <domi...@lps.ens.fr>

type t0
  integer :: j = 42
end type t0
type t
  integer :: i
  class(t0), allocatable :: foo(:)
end type t
type(t) :: k
allocate(t0 :: k%foo(3))
print *, k%foo%j
end
! { dg-do compile }
!
! PR fortran/46356
! This program was leading to an ICE related to class arrays
!
! Original testcase by Ian Harvey <ian_har...@bigpond.com>
! Reduced by Janus Weil <ja...@gcc.gnu.org>

  IMPLICIT NONE

  TYPE :: ParentVector
    INTEGER :: a
  END TYPE ParentVector  

CONTAINS       

  SUBROUTINE vector_operation(pvec)     
    CLASS(ParentVector), INTENT(INOUT) :: pvec(:)
    print *,pvec(1)%a
  END SUBROUTINE

END

! { dg-do compile }
!
! PR fortran/51754
! This program was leading to an ICE related to class arrays
!
! Contributed by Andrew Benson <aben...@caltech.edu>

module test
  private

  type :: componentB
  end type componentB

  type :: treeNode
     class(componentB), allocatable, dimension(:) :: componentB
  end type treeNode

contains

  function BGet(self)
    implicit none
    class(componentB), pointer :: BGet
    class(treeNode), target, intent(in) :: self
    select type (self)
    class is (treeNode)
       BGet => self%componentB(1)
    end select
    return
  end function BGet

end module test

! { dg-final { cleanup-modules "test" } }

Reply via email to