I plan to commit the attached patch posted by Christopher in the PR and verified
by the reporter, Andrew Benson.
Thanks to both for the report and the timely fix!
Regression tested by myself on x86_64.
Regards,
Jerry
When copying derived types with allocatable array components where the
array element type also has allocatable components, the condition at
line 11071 was incorrectly triggering a call to gfc_duplicate_allocatable.
However, for allocatable arrays with nested allocatables (where
cmp_has_alloc_comps && c->as is true), the add_when_allocated code
already includes a gfc_duplicate_allocatable call (generated by the
recursive structure_alloc_comps call at lines 10290-10293).
This caused the outer array to be allocated twice: first by the explicit
gfc_duplicate_allocatable call at line 11099, and then again by the
gfc_duplicate_allocatable embedded in add_when_allocated. The first
allocation was leaked when the second allocation overwrote the data
pointer.
PR121628 added "add_when_allocated != NULL_TREE ||" to the condition,
which was redundant for scalars (already handled by !c->as) and wrong
for arrays (caused double allocation). Simply removing this clause
restores the correct pre-PR121628 behavior.
PR fortran/123868
gcc/fortran/ChangeLog:
* trans-array.cc (structure_alloc_comps): For COPY_ALLOC_COMP,
remove the add_when_allocated != NULL_TREE clause that PR121628
added. This clause was redundant for scalars and caused double
allocation for arrays with nested allocatable components.
gcc/testsuite/ChangeLog:
* gfortran.dg/array_memcpy_2.f90: Update expected memcpy count
from 4 to 3, as the double allocation bug is now fixed.
* gfortran.dg/pr123868.f90: New test.
Signed-off-by: Christopher Albert <[email protected]>
commit b1774c9f007ff646006bb2a7d5b8c18538f4a34f
Author: Jerry DeLisle <[email protected]>
Date: Fri Jan 30 13:25:23 2026 -0800
Fortran: Fix PR123868
When copying derived types with allocatable array components where the
array element type also has allocatable components, the condition at
line 11071 was incorrectly triggering a call to gfc_duplicate_allocatable.
However, for allocatable arrays with nested allocatables (where
cmp_has_alloc_comps && c->as is true), the add_when_allocated code
already includes a gfc_duplicate_allocatable call (generated by the
recursive structure_alloc_comps call at lines 10290-10293).
This caused the outer array to be allocated twice: first by the explicit
gfc_duplicate_allocatable call at line 11099, and then again by the
gfc_duplicate_allocatable embedded in add_when_allocated. The first
allocation was leaked when the second allocation overwrote the data
pointer.
PR121628 added "add_when_allocated != NULL_TREE ||" to the condition,
which was redundant for scalars (already handled by !c->as) and wrong
for arrays (caused double allocation). Simply removing this clause
restores the correct pre-PR121628 behavior.
PR fortran/123868
gcc/fortran/ChangeLog:
* trans-array.cc (structure_alloc_comps): For COPY_ALLOC_COMP,
remove the add_when_allocated != NULL_TREE clause that PR121628
added. This clause was redundant for scalars and caused double
allocation for arrays with nested allocatable components.
gcc/testsuite/ChangeLog:
* gfortran.dg/array_memcpy_2.f90: Update expected memcpy count
from 4 to 3, as the double allocation bug is now fixed.
* gfortran.dg/pr123868.f90: New test.
Signed-off-by: Christopher Albert <[email protected]>
diff --git a/gcc/fortran/trans-array.cc b/gcc/fortran/trans-array.cc
index e207b0c06d3..ca2bff22ba3 100644
--- a/gcc/fortran/trans-array.cc
+++ b/gcc/fortran/trans-array.cc
@@ -11063,9 +11063,14 @@ structure_alloc_comps (gfc_symbol * der_type, tree decl, tree dest,
copy_wrapper);
gfc_add_expr_to_block (&fnblock, call);
}
+ /* For allocatable arrays with nested allocatable components,
+ add_when_allocated already includes gfc_duplicate_allocatable
+ (from the recursive structure_alloc_comps call at line 10290-10293),
+ so we must not call it again here. PR121628 added an
+ add_when_allocated != NULL clause that was redundant for scalars
+ (already handled by !c->as) and wrong for arrays (double alloc). */
else if (c->attr.allocatable && !c->attr.proc_pointer
- && (add_when_allocated != NULL_TREE
- || !cmp_has_alloc_comps
+ && (!cmp_has_alloc_comps
|| !c->as
|| c->attr.codimension
|| caf_in_coarray (caf_mode)))
diff --git a/gcc/testsuite/gfortran.dg/array_memcpy_2.f90 b/gcc/testsuite/gfortran.dg/array_memcpy_2.f90
index a95908c2928..8fcac0e63f1 100644
--- a/gcc/testsuite/gfortran.dg/array_memcpy_2.f90
+++ b/gcc/testsuite/gfortran.dg/array_memcpy_2.f90
@@ -1,11 +1,12 @@
! This checks that the "z = y" assignment is not considered copyable, as the
! array is of a derived type containing allocatable components. Hence, we
-! we should expand the scalarized loop, which contains *two* memcpy calls
+! should expand the scalarized loop, which contains *two* memcpy calls
! for the assignment itself, plus one for initialization.
! { dg-do compile }
! { dg-options "-O2 -fdump-tree-original" }
!
! PR 121628
+! PR 123868 - fixed double allocation that caused 4 memcpy instead of 3
!
type :: a
integer, allocatable :: i(:)
@@ -26,4 +27,4 @@
z = y
end
-! { dg-final { scan-tree-dump-times "__builtin_memcpy" 4 "original" } }
+! { dg-final { scan-tree-dump-times "__builtin_memcpy" 3 "original" } }
diff --git a/gcc/testsuite/gfortran.dg/pr123868.f90 b/gcc/testsuite/gfortran.dg/pr123868.f90
new file mode 100644
index 00000000000..e21501ddacc
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/pr123868.f90
@@ -0,0 +1,34 @@
+! { dg-do run }
+! PR fortran/123868 - Memory leak on assignment with nested allocatable
+! components. Regression introduced by PR121628 commit which caused
+! gfc_duplicate_allocatable to be called twice for allocatable array
+! components with nested allocatable components.
+
+module bugMod
+
+ type :: vs
+ character(len=1), allocatable :: s
+ end type vs
+
+ type :: ih
+ type(vs), allocatable, dimension(:) :: hk
+ end type ih
+
+end module bugMod
+
+program bugProg
+ use bugMod
+
+ block
+ type(ih) :: c, d
+
+ allocate(d%hk(1))
+ allocate(d%hk(1)%s)
+ d%hk(1)%s='z'
+ c=d
+ if (c%hk(1)%s /= 'z') stop 1
+ if (d%hk(1)%s /= 'z') stop 2
+
+ end block
+
+end program bugProg