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

Mikael Morin <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.