See attached patch.

This one is fairly simple. This is also the last of the meta-bug PR125515.

Regression tested on x86_64.

I will probably simplify the commit message. Thought I should comments before I do that.

OK for mainline and later backport to 16.

Regards,

Jerry

---


When a nested implied-do array constructor called a transformational
intrinsic (e.g. RESHAPE) whose result type has allocatable components, the
argument temporaries were freed before the result's allocatable components
were deep-copied, causing a heap-use-after-free and wrong runtime values.

The root cause: in gfc_conv_procedure_call, the call to
gfc_add_block_to_block (&se->pre, &post) — which frees the allocatable
components of argument temporaries — was issued before the deep-copy loop
for the result's allocatable components.  Transformational library functions
such as RESHAPE do a shallow byte-copy of the source array into the result,
so the result's component pointers alias those of the argument temporaries.
Freeing the temporaries first and then copying yielded use-after-free.

Fix: move gfc_add_block_to_block (&se->pre, &post) to after the deep-copy
loop, so that the result's allocatable components are copied while the
source storage is still live.

PR fortran/125535

Assisted by: Claude Sonnet 4.6

gcc/fortran/ChangeLog:

        PR fortran/125535
        * trans-expr.cc (gfc_conv_procedure_call): Move post block append
        to after the deep copy of allocatable components for transformational
        intrinsics, so that argument temporaries are not freed before the
        result components are copied.

gcc/testsuite/ChangeLog:

        PR fortran/125535
        * gfortran.dg/implied_do_alloc_comp_1.f90: New test.
---
From 7b0e7473fc53127f70276cdd3e33268e3b867c7b Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <[email protected]>
Date: Fri, 5 Jun 2026 13:20:05 -0700
Subject: [PATCH] fortran: wrong-code for implied-do with allocatable-component
 derived type and transformational intrinsic
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

When a nested implied-do array constructor called a transformational
intrinsic (e.g. RESHAPE) whose result type has allocatable components, the
argument temporaries were freed before the result's allocatable components
were deep-copied, causing a heap-use-after-free and wrong runtime values.

The root cause: in gfc_conv_procedure_call, the call to
gfc_add_block_to_block (&se->pre, &post) — which frees the allocatable
components of argument temporaries — was issued before the deep-copy loop
for the result's allocatable components.  Transformational library functions
such as RESHAPE do a shallow byte-copy of the source array into the result,
so the result's component pointers alias those of the argument temporaries.
Freeing the temporaries first and then copying yielded use-after-free.

Fix: move gfc_add_block_to_block (&se->pre, &post) to after the deep-copy
loop, so that the result's allocatable components are copied while the
source storage is still live.

PR fortran/125535

Assisted by: Claude Sonnet 4.6

gcc/fortran/ChangeLog:

	PR fortran/125535
	* trans-expr.cc (gfc_conv_procedure_call): Move post block append
	to after the deep copy of allocatable components for transformational
	intrinsics, so that argument temporaries are not freed before the
	result components are copied.

gcc/testsuite/ChangeLog:

	PR fortran/125535
	* gfortran.dg/implied_do_alloc_comp_1.f90: New test.
---
 gcc/fortran/trans-expr.cc                     | 10 ++---
 .../gfortran.dg/implied_do_alloc_comp_1.f90   | 39 +++++++++++++++++++
 2 files changed, 44 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/implied_do_alloc_comp_1.f90

diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index c808c1b3ef3..00925f2ef5a 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -9069,12 +9069,10 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
   /* Follow the function call with the argument post block.  */
   if (byref)
     {
-      gfc_add_block_to_block (&se->pre, &post);
-
       /* Transformational functions of derived types with allocatable
-	 components must have the result allocatable components copied when the
-	 argument is actually given.  This is unnecessry for REDUCE because the
-	 wrapper for the OPERATION function takes care of this.  */
+	 components must have the result allocatable components copied
+	 BEFORE the argument post block is appended.  Copying the result
+	 first, then freeing the argument, gives the correct order.  */
       arg = expr->value.function.actual;
       if (result && arg && expr->rank
 	  && isym && isym->transformational
@@ -9102,6 +9100,8 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
 					    NULL, GFC_CAF_COARRAY_NOCOARRAY);
 	  gfc_add_expr_to_block (&se->pre, tmp);
 	}
+
+      gfc_add_block_to_block (&se->pre, &post);
     }
   else
     {
diff --git a/gcc/testsuite/gfortran.dg/implied_do_alloc_comp_1.f90 b/gcc/testsuite/gfortran.dg/implied_do_alloc_comp_1.f90
new file mode 100644
index 00000000000..8f0ae4976d8
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/implied_do_alloc_comp_1.f90
@@ -0,0 +1,39 @@
+! { dg-do run }
+! Test that a nested implied-do array constructor with a derived-type
+! constructor whose argument uses an outer loop variable produces correct
+! results when the derived type has an allocatable component.
+!
+! PR fortran/125535
+
+module m
+  implicit none
+  type :: t
+    real, allocatable :: v(:)
+  end type
+contains
+  pure function make(x) result(r)
+    real, intent(in) :: x(:)
+    type(t) :: r
+    r%v = x
+  end function
+end module
+
+program implied_do_alloc_comp
+  use m
+  implicit none
+  integer, parameter :: n=3, k=2
+  type(t), allocatable :: a(:,:)
+  real :: h(k, n)
+  integer :: i, j
+
+  h(1,:) = [10., 20., 30.]
+  h(2,:) = [11., 21., 31.]
+
+  ! Nested implied-do where inner DT constructor uses outer variable i.
+  ! Without the fix this produces heap-use-after-free and wrong values.
+  a = reshape([([(make(h(:,i)), j=1,1)], i=1,n)], [1,n])
+
+  if (any(a(1,1)%v /= h(:,1))) stop 1
+  if (any(a(1,2)%v /= h(:,2))) stop 2
+  if (any(a(1,3)%v /= h(:,3))) stop 3
+end program
-- 
2.54.0

Reply via email to