Re: [PATCH] fortran: [PR53800] Wrong copy-in/out with CLASS array to assumed-shape TYPE

Mikael Morin <[email protected]> Wed, 5 Aug 2026 12:25:49 +0200
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.fortran
Message-ID <[email protected]>
Hello,

Le 30/07/2026 à 19:21, Jerry D a écrit :
> See the attached patch.
> 
> As stated in the PR we were missing the stride multiplier (ie span)
> 
> I will note as a useful tool: I had an initial patch for this but wanted 
> to exercise it. I prompted Claude Pro Opus 5 to create numerous 
> variations on the original test case provided by Tobias Burnus in the 
> original report. The result was 25 to 30 variations which were helpful 
> in identifying code paths I would not have otherwise found.  I am not 
> using those test cases here.
> 
> I flagged out this one to work on as it has been around a long time and 
> we were giving wrong results without any idea it was happening.
> 
> I also wanted to mention that the fortran standardese is difficult to 
> interpret but I think I got this right.
> 
> Regression tested on x86_64.
> 
> OK for mainline?
> 
> Comments appreciated.
> 
> Regards,
> 
> Jerry
> ---
>      fortran: [PR53800] Wrong copy-in/out with CLASS array to assumed- 
> shape TYPE
> 
>      A CLASS array actual passed to a plain assumed-shape derived-type 
> dummy
>      was always copied in and out, so pointers associated with the dummy 
> went
>      stale on return.  F2023 15.5.2.5 paragraph 9 requires the dummy to be
>      associated with the actual's storage when it has the TARGET 
> attribute and
>      lacks CONTIGUOUS and VALUE; paragraph 8 leaves every other case 
> free to
>      use a copy.  Pass such a dummy by reference and address its 
> elements with
>      the descriptor's runtime span.
> 

> diff --git a/gcc/fortran/trans.h b/gcc/fortran/trans.h
> index 7b28ecfce47..4063bbe70c4 100644
> --- a/gcc/fortran/trans.h
> +++ b/gcc/fortran/trans.h
> @@ -1070,6 +1070,7 @@ struct GTY(()) lang_decl {
>    unsigned int scalar_pointer : 1;
>    unsigned int scalar_target : 1;
>    unsigned int optional_arg : 1;
> +  unsigned int span_array : 1;
>  };
>
>
> @@ -1087,12 +1088,16 @@ struct GTY(()) lang_decl {
>    (DECL_LANG_SPECIFIC (node)->scalar_target)
>  #define GFC_DECL_OPTIONAL_ARGUMENT(node) \
>    (DECL_LANG_SPECIFIC (node)->optional_arg)
> +#define GFC_DECL_SPAN_ARRAY_P(node) \
> +  (DECL_LANG_SPECIFIC (node)->span_array)
>  #define GFC_DECL_GET_SCALAR_ALLOCATABLE(node) \
>    (DECL_LANG_SPECIFIC (node) ? GFC_DECL_SCALAR_ALLOCATABLE (node) : 0)
>  #define GFC_DECL_GET_SCALAR_POINTER(node) \
>    (DECL_LANG_SPECIFIC (node) ? GFC_DECL_SCALAR_POINTER (node) : 0)
>  #define GFC_DECL_GET_SCALAR_TARGET(node) \
>    (DECL_LANG_SPECIFIC (node) ? GFC_DECL_SCALAR_TARGET (node) : 0)
> +#define GFC_DECL_GET_SPAN_ARRAY_P(node) \
> +  (DECL_LANG_SPECIFIC (node) ? GFC_DECL_SPAN_ARRAY_P (node) : 0)
>  #define GFC_DECL_PACKED_ARRAY(node) DECL_LANG_FLAG_0(node)
>  #define GFC_DECL_PARTIAL_PACKED_ARRAY(node) DECL_LANG_FLAG_1(node)
>  #define GFC_DECL_ASSIGN(node) DECL_LANG_FLAG_2(node)

I have the impression that the information provided by the new flag is 
somehow redundant with the existing flag GFC_DECL_PTR_ARRAY_P, and 
indeed, need_span is used everywhere side by side with is_pointer_array.
Could the flags be merged together, or maybe just reuse 
GFC_DECL_PTR_ARRAY_P?

> diff --git a/gcc/fortran/trans-array.cc b/gcc/fortran/trans-array.cc
> index cf7eddf6e1d..0daa3f2ddad 100644
> --- a/gcc/fortran/trans-array.cc
> +++ b/gcc/fortran/trans-array.cc
> diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
> index 585d27fa5a2..c9e1f66edb1 100644
> --- a/gcc/fortran/trans-expr.cc
> +++ b/gcc/fortran/trans-expr.cc
> @@ -7222,6 +7224,26 @@ conv_null_actual (gfc_se * parmse, gfc_expr * e, gfc_symbol * fsym)
>  }
>  
>  
> +/* Return true if a CLASS array actual may alias the derived-type dummy
> +   FSYM rather than be passed through a copy-in/copy-out temporary.  F2023
> +   15.5.2.5 paragraph 9 requires the association only for a TARGET
> +   assumed-shape dummy without CONTIGUOUS or VALUE.  */
> +
> +static bool
> +class_actual_aliases_type_dummy (gfc_symbol *fsym, bool nodesc_arg)
> +{
> +  return fsym != NULL
> +	 && fsym->ts.type == BT_DERIVED

The check on type is probably unneeded.
Here is a (failing) variation of the testcase with integer type:

diff --git a/gcc/testsuite/gfortran.dg/class_to_type_5.f90 
b/gcc/testsuite/gfortran.dg/class_to_type_5.f90
index ad299db514d..50753bdf922 100644
--- a/gcc/testsuite/gfortran.dg/class_to_type_5.f90
+++ b/gcc/testsuite/gfortran.dg/class_to_type_5.f90
@@ -16,20 +16,20 @@ program class_to_type
      integer :: j
    end type t2
    class(t), target, allocatable :: a(:,:)
-  type(t), pointer :: ptr
+  integer, pointer :: ptr

    allocate (t2 :: a(5,5))
    a(:,:)%i = 53
    a(3,3)%i = 42
    a(4,4)%i = 74

-  call f (a)
-  if (ptr%i /= 42) stop 1
+  call f (a%i)
+  if (ptr /= 42) stop 1
    a(3,3)%i = 999
-  if (ptr%i /= 999) stop 2
+  if (ptr /= 999) stop 2
  contains
    subroutine f(x)
-    type(t), target :: x(:,:)
+    integer, target :: x(:,:)
      ptr => x(3,3)
    end subroutine f
  end program class_to_type


> +	 && fsym->attr.target
> +	 && !fsym->attr.pointer
Same for the check for pointer with the following variant:

diff --git a/gcc/testsuite/gfortran.dg/class_to_type_5.f90 
b/gcc/testsuite/gfortran.dg/class_to_type_5.f90
index ad299db514d..3fcec17ec11 100644
--- a/gcc/testsuite/gfortran.dg/class_to_type_5.f90
+++ b/gcc/testsuite/gfortran.dg/class_to_type_5.f90
@@ -15,7 +15,7 @@ program class_to_type
    type, extends(t) :: t2
      integer :: j
    end type t2
-  class(t), target, allocatable :: a(:,:)
+  class(t), pointer :: a(:,:)
    type(t), pointer :: ptr

    allocate (t2 :: a(5,5))
@@ -27,9 +27,10 @@ program class_to_type
    if (ptr%i /= 42) stop 1
    a(3,3)%i = 999
    if (ptr%i /= 999) stop 2
+  deallocate(a)
  contains
    subroutine f(x)
-    type(t), target :: x(:,:)
+    type(t), pointer :: x(:,:)
      ptr => x(3,3)
    end subroutine f
  end program class_to_type


> +	 && !fsym->attr.value
> +	 && !fsym->attr.contiguous
> +	 && !nodesc_arg
> +	 && fsym->as != NULL
> +	 && fsym->as->type == AS_ASSUMED_SHAPE;
There is also AS_DEFERRED for the pointer case above, and also 
AS_ASSUMED_RANK with this variant:

diff --git a/gcc/testsuite/gfortran.dg/class_to_type_5.f90 
b/gcc/testsuite/gfortran.dg/class_to_type_5.f90
index ad299db514d..84a8ef4aa7e 100644
--- a/gcc/testsuite/gfortran.dg/class_to_type_5.f90
+++ b/gcc/testsuite/gfortran.dg/class_to_type_5.f90
@@ -29,7 +29,12 @@ program class_to_type
    if (ptr%i /= 999) stop 2
  contains
    subroutine f(x)
-    type(t), target :: x(:,:)
-    ptr => x(3,3)
+    type(t), target :: x(..)
+    select rank(x)
+      rank(2)
+        ptr => x(3,3)
+      rank default
+        error stop 3
+    end select
    end subroutine f
  end program class_to_type


For the rest, the two get_array_span would need to be reworked to have 
the flags merged, but I think that's doable, and the rest looks good.

Mikael