See attached patch.

I incorporated Mikael's comments from the PR.

I regression tested this and also verified the original test case is fixed.

I also added a new test case.

OK for mainline?

Regards,

Jerry
---

    fortran: [PR125761] Unlimited-poly character array section base

    When a CLASS(*) array section carrying a CHARACTER payload is passed
    as the base of an array reference, build_array_ref and
    gfc_build_array_ref computed the element span from the dynamic type's
    vptr size alone, ignoring the unlimited-polymorphic object's _len
    field.  For a deferred-length character payload this gave the wrong
    element size, so the section's data pointer was offset incorrectly by
    the lower bound, producing wrong code (no ICE needed to reproduce: a
    section of a CLASS(*) character array passed to a procedure reads from
    the wrong address).

            PR fortran/125761

    gcc/fortran/ChangeLog:

            * trans-array.cc (build_array_ref): When the descriptor's
            canonical type is a class type, pass the class container as
            'decl' to gfc_build_array_ref for genuine array element
            references (rank > 0), so the element size can be corrected
            for an unlimited-polymorphic character payload.
            * trans-types.cc (gfc_typenode_for_spec): Move setting the
            GFC_CLASS_TYPE_P bit to..
            (gfc_get_derived_type): .. and use attr.is_class.
            (gfc_get_derived_type): Move setting the GFC_CLASS_TYPE_P bit
            as the last step before returning the derived->backend_decl
            if the derived->attr.is_class is true.
            * trans.cc (gfc_build_array_ref): Recognize a class container
            via its canonical type as well as GFC_CLASS_TYPE_P, and apply
            the _len-based span correction in that case too.

    gcc/testsuite/ChangeLog:

            * gfortran.dg/select_type_53.f90: New test.


commit 59ab0a7d5063c659bfc59f41d8a3f3ab26c68977
Author: Jerry DeLisle <[email protected]>
Date:   Tue Jun 23 12:56:24 2026 -0700

    fortran: [PR125761] Unlimited-poly character array section base
    
    When a CLASS(*) array section carrying a CHARACTER payload is passed
    as the base of an array reference, build_array_ref and
    gfc_build_array_ref computed the element span from the dynamic type's
    vptr size alone, ignoring the unlimited-polymorphic object's _len
    field.  For a deferred-length character payload this gave the wrong
    element size, so the section's data pointer was offset incorrectly by
    the lower bound, producing wrong code (no ICE needed to reproduce: a
    section of a CLASS(*) character array passed to a procedure reads from
    the wrong address).
    
            PR fortran/125761
    
    gcc/fortran/ChangeLog:
    
            * trans-array.cc (build_array_ref): When the descriptor's
            canonical type is a class type, pass the class container as
            'decl' to gfc_build_array_ref for genuine array element
            references (rank > 0), so the element size can be corrected
            for an unlimited-polymorphic character payload.
            * trans-types.cc (gfc_typenode_for_spec): Move setting the
            GFC_CLASS_TYPE_P bit to..
            (gfc_get_derived_type): .. and use attr.is_class.
            (gfc_get_derived_type): Move setting the GFC_CLASS_TYPE_P bit
            as the last step before returning the derived->backend_decl
            if the derived->attr.is_class is true.
            * trans.cc (gfc_build_array_ref): Recognize a class container
            via its canonical type as well as GFC_CLASS_TYPE_P, and apply
            the _len-based span correction in that case too.
    
    gcc/testsuite/ChangeLog:
    
            * gfortran.dg/select_type_53.f90: New test.

diff --git a/gcc/fortran/trans-array.cc b/gcc/fortran/trans-array.cc
index cde5d3e4b32..75780afccc6 100644
--- a/gcc/fortran/trans-array.cc
+++ b/gcc/fortran/trans-array.cc
@@ -4493,7 +4493,18 @@ build_array_ref (tree desc, tree offset, tree decl, tree vptr)
       type = TREE_TYPE (TREE_OPERAND (cdesc, 0));
       if (TYPE_CANONICAL (type)
 	  && GFC_CLASS_TYPE_P (TYPE_CANONICAL (type)))
-	vptr = gfc_class_vptr_get (TREE_OPERAND (cdesc, 0));
+	{
+	  vptr = gfc_class_vptr_get (TREE_OPERAND (cdesc, 0));
+	  /* Pass the class container as decl so that gfc_build_array_ref can
+	     correct the element size for an unlimited polymorphic character
+	     payload (the _len field), which the vptr size alone omits.  Only do
+	     this for a genuine array element reference; a scalar coarray has
+	     nothing to span-correct and gfc_build_array_ref asserts decl is null
+	     for it.  */
+	  if (decl == NULL_TREE
+	      && GFC_TYPE_ARRAY_RANK (TREE_TYPE (cdesc)) > 0)
+	    decl = TREE_OPERAND (cdesc, 0);
+	}
     }
 
   tmp = gfc_conv_array_data (desc);
diff --git a/gcc/fortran/trans-types.cc b/gcc/fortran/trans-types.cc
index 5bccea960aa..6798f54beb2 100644
--- a/gcc/fortran/trans-types.cc
+++ b/gcc/fortran/trans-types.cc
@@ -1380,9 +1380,6 @@ gfc_typenode_for_spec (gfc_typespec * spec, int codim)
     case BT_CLASS:
       basetype = gfc_get_derived_type (spec->u.derived, codim);
 
-      if (spec->type == BT_CLASS)
-	GFC_CLASS_TYPE_P (basetype) = 1;
-
       /* If we're dealing with either C_PTR or C_FUNPTR, we modified the
          type and kind to fit a (void *) and the basetype returned was a
          ptr_type_node.  We need to pass up this new information to the
@@ -2970,6 +2967,8 @@ gfc_get_derived_type (gfc_symbol * derived, int codimen)
       TYPE_NAME (typenode) = get_identifier (derived->name);
       TYPE_PACKED (typenode) = flag_pack_derived;
       derived->backend_decl = typenode;
+      if (derived->attr.is_class)
+	GFC_CLASS_TYPE_P (typenode) = 1;
     }
 
   if (derived->components
@@ -3132,15 +3131,6 @@ gfc_get_derived_type (gfc_symbol * derived, int codimen)
 	  field_type = build_pointer_type_for_mode (TREE_TYPE (field_type),
 						    ptr_mode, true);
 
-      /* Ensure that the CLASS language specific flag is set.  */
-      if (c->ts.type == BT_CLASS)
-	{
-	  if (POINTER_TYPE_P (field_type))
-	    GFC_CLASS_TYPE_P (TREE_TYPE (field_type)) = 1;
-	  else
-	    GFC_CLASS_TYPE_P (field_type) = 1;
-	}
-
       field = gfc_add_field_to_struct (typenode,
 				       get_identifier (c->name),
 				       field_type, &chain);
@@ -3213,6 +3203,9 @@ copy_derived_types:
 	break;
     }
 
+  if (derived->attr.is_class)
+    GFC_CLASS_TYPE_P (derived->backend_decl) = 1;
+
   return derived->backend_decl;
 }
 
diff --git a/gcc/fortran/trans.cc b/gcc/fortran/trans.cc
index adf392cec6f..c10484ff636 100644
--- a/gcc/fortran/trans.cc
+++ b/gcc/fortran/trans.cc
@@ -524,8 +524,13 @@ gfc_build_array_ref (tree base, tree offset, tree decl,
       span = gfc_vptr_size_get (vptr);
 
       /* Check if this is an unlimited polymorphic object carrying a character
-	 payload. In this case, the 'len' field is non-zero.  */
-      if (decl && GFC_CLASS_TYPE_P (TREE_TYPE (decl)))
+	 payload. In this case, the 'len' field is non-zero.  The class
+	 container type does not always carry GFC_CLASS_TYPE_P, but its
+	 canonical type does.  */
+      if (decl
+	  && (GFC_CLASS_TYPE_P (TREE_TYPE (decl))
+	      || (TYPE_CANONICAL (TREE_TYPE (decl))
+		  && GFC_CLASS_TYPE_P (TYPE_CANONICAL (TREE_TYPE (decl))))))
 	span = gfc_resize_class_size_with_len (NULL, decl, span);
     }
   else if (decl)
diff --git a/gcc/testsuite/gfortran.dg/select_type_53.f90 b/gcc/testsuite/gfortran.dg/select_type_53.f90
new file mode 100644
index 00000000000..98f16dccfb1
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/select_type_53.f90
@@ -0,0 +1,41 @@
+! { dg-do run }
+!
+! PR fortran/125761
+!
+! Passing an array section of an unlimited-polymorphic array with a CHARACTER
+! payload addressed the section data with the wrong element size: the section
+! base data pointer was offset using the vptr size (the kind size, 1) rather
+! than the _len-corrected element size, so every element but the first was
+! wrong.
+
+program p
+  character(4), target :: arr(5) = ["aaaa", "bbbb", "cccc", "dddd", "eeee"]
+  character(2), target :: arr2(4) = ["xx", "yy", "zz", "ww"]
+  class(*), pointer :: ptr(:)
+
+  ptr => arr
+  call check (ptr(2:4), ["bbbb", "cccc", "dddd"])   ! contiguous
+  call check (ptr(1:5:2), ["aaaa", "cccc", "eeee"]) ! strided
+
+  ptr => arr2
+  call check (ptr(2:3), ["yy", "zz"])
+
+contains
+
+  subroutine check (x, expect)
+    class(*) :: x(:)
+    character(*), intent(in) :: expect(:)
+    integer :: i
+    select type (x)
+     type is (character(*))
+       if (len (x) /= len (expect)) stop 1
+       if (size (x) /= size (expect)) stop 2
+       do i = 1, size (x)
+          if (x(i) /= expect(i)) stop 3
+       end do
+     class default
+       stop 4
+    end select
+  end subroutine
+
+end program p

Reply via email to