[Patch, fortran] PR121683 - Data corruption with sourced allocation from constructor

Paul Richard Thomas <[email protected]>
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.fortran
Message-ID <CAGkQGi+Tz=sCj6rtQrv5gWCSqYc9SD51nbaWhBF5bCyfjapvtA@mail.gmail.com>
This one is straightforward, going on obvious. Allocatable components
were not being copied in the assignment of class or derived type
component. Three places in gfc_trans_subcomponent_assign were touched.

The patch was reviewed and tested in January but was not pushed
because of the release of 16.1.1 and then forgotten.

Regression tests OK on FC44/x86_64. OK for mainline and then, after an
interval, 16-branch?

Paul
submit.patch (text/x-patch, 7.3 KB)
From 3b8aca292f08b11f7776ae08f954f2c546616f95 Mon Sep 17 00:00:00 2001
From: Paul Thomas <[email protected]>
Date: Thu, 13 Aug 2026 16:00:15 +0100
Subject: [PATCH] ortran: Fix data corruption with allocation from ctr source
 [P121683]

2026-08-13  Paul Thomas  <[email protected]>

gcc/fortran
	PR fortran/110626
	* trans-expr.cc (gfc_trans_subcomponent_assign): For derived
	type expressions assigned to a class or derived type component
	copy allocatable components if necessary. Likewise, for a class
	expression assigned to a scalar class component, allocate the
	component and use the vptr copy.

gcc/testsuite/
	PR fortran/110626
	* gfortran.dg/pr11026.f90: structure_constructor_18.f90.
---
 gcc/fortran/trans-expr.cc                     |  69 +++++++++++-
 .../gfortran.dg/structure_constructor_18.f90  | 102 ++++++++++++++++++
 2 files changed, 169 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/structure_constructor_18.f90

diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index 33b7838f74a..a5fcd26aff4 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -10184,6 +10184,7 @@ gfc_trans_subcomponent_assign (tree dest, gfc_component * cm,
 	       && expr->ts.type != BT_CLASS)))
     {
       tree size;
+      tree tmp2;
 
       gfc_init_se (&se, NULL);
       gfc_conv_expr (&se, expr);
@@ -10245,6 +10246,17 @@ gfc_trans_subcomponent_assign (tree dest, gfc_component * cm,
 	  gfc_add_expr_to_block (&block,
 				 gfc_build_memcpy_call (tmp, se.expr, size));
 
+	  if (expr->ts.type == BT_DERIVED
+	      && expr->ts.u.derived->attr.alloc_comp
+	      && expr->expr_type != EXPR_NULL)
+	    {
+	      tmp2 = gfc_class_data_get (dest);
+	      tmp2 = gfc_copy_alloc_comp (expr->ts.u.derived, tmp2,
+					  gfc_class_data_get (dest),
+					  expr->rank, 0);
+	      gfc_add_expr_to_block (&block, tmp2);
+	    }
+
 	  /* Fill the unlimited polymorphic _len field.  */
 	  if (UNLIMITED_POLY (cm) && expr->ts.type == BT_CHARACTER)
 	    {
@@ -10255,8 +10267,20 @@ gfc_trans_subcomponent_assign (tree dest, gfc_component * cm,
 	    }
 	}
       else
-	gfc_add_modify (&block, tmp,
-			fold_convert (TREE_TYPE (tmp), se.expr));
+	{
+	  gfc_add_modify (&block, tmp,
+			  fold_convert (TREE_TYPE (tmp), se.expr));
+	  if (expr->ts.type == BT_DERIVED
+	      && expr->ts.u.derived->attr.alloc_comp
+	      && expr->expr_type != EXPR_NULL)
+	    {
+	      tmp2 = build_fold_indirect_ref_loc (input_location, dest);
+	      tmp2 = gfc_copy_alloc_comp (cm->ts.u.derived, tmp2,
+					  se.expr, expr->rank, 0);
+	      gfc_add_expr_to_block (&block, tmp2);
+	    }
+	}
+
       gfc_add_block_to_block (&block, &se.post);
     }
   else if (expr->ts.type == BT_UNION)
@@ -10356,6 +10380,47 @@ gfc_trans_subcomponent_assign (tree dest, gfc_component * cm,
 	  gfc_add_expr_to_block (&block, tmp);
 	}
     }
+  else if (cm->ts.type == BT_CLASS
+	   && !CLASS_DATA (cm)->as
+	   && expr->ts.type == BT_CLASS)
+    {
+      tree vptr1, vptr2;
+      tree data1, data2;
+      tree size, fcn;
+
+      gfc_init_se (&se, NULL);
+
+      gfc_conv_expr (&se, expr);
+
+      /* Copy the _vptr to the destination....  */
+      vptr1 = gfc_class_vptr_get (dest);
+      vptr2 = gfc_class_vptr_get (se.expr);
+      gfc_add_modify (&block, vptr1,
+		      fold_convert (TREE_TYPE (vptr1), vptr2));
+
+      /* ....and the _len field if necessary.  */
+      size = gfc_vptr_size_get (vptr2);
+      if (UNLIMITED_POLY (cm) && UNLIMITED_POLY (expr))
+	{
+	  gfc_add_modify (&block, gfc_class_len_get (dest),
+			  gfc_class_len_get (se.expr));
+	  size = gfc_resize_class_size_with_len (&block, se.expr, size);
+	}
+
+      /* Allocate the destination data.  */
+      data1 = gfc_class_data_get (dest);
+      data2 = gfc_class_data_get (se.expr);
+      tmp = gfc_call_malloc (&block, TREE_TYPE (data1), size);
+      gfc_add_modify (&block, data1, tmp);
+
+      /* Now call the copy function. */
+      fcn = gfc_vptr_copy_get (vptr2);
+      if (POINTER_TYPE_P (TREE_TYPE (fcn)))
+	fcn = build_fold_indirect_ref_loc (input_location, fcn);
+      tmp = build_call_expr_loc (input_location, fcn, 2,
+				 data2, data1);
+      gfc_add_expr_to_block (&block, tmp);
+    }
   else if (!cm->attr.artificial)
     {
       /* Scalar component (excluding deferred parameters).  */
diff --git a/gcc/testsuite/gfortran.dg/structure_constructor_18.f90 b/gcc/testsuite/gfortran.dg/structure_constructor_18.f90
new file mode 100644
index 00000000000..08b12c83ab6
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/structure_constructor_18.f90
@@ -0,0 +1,102 @@
+! { dg-do run }
+!
+! Test the fix for PR121683 and a variant from Jerry Delisle. Explicit
+! deallocations were added, when both original and variant were stand
+! alone programs.
+!
+! Contributed by Neil Carlson  <[email protected]>
+!            and Jerry Delisle  <[email protected]>
+!
+module mod
+  implicit none
+  type, abstract :: func
+  end type
+  type, extends(func) :: poly
+    real, allocatable :: array(:)
+  end type
+  type, extends(func) :: func_deriv
+    class(func), allocatable :: f
+  end type
+
+contains
+
+  subroutine alloc_deriv(f, df)
+    class(func), intent(in) :: f
+    class(func), allocatable, intent(out) :: df
+    allocate(df, source=func_deriv(f)) ! THIS CORRUPTS F
+  end subroutine
+
+end module
+
+program fubar
+  call original
+  call variant
+contains
+
+  subroutine original
+    use mod
+    type(poly), allocatable :: p
+    class(func), allocatable :: f, df
+    real :: array(2) = [1, 2]
+    allocate(p)
+    p%array = array
+    call move_alloc(p, f)
+    call alloc_deriv(f, df)
+    if (.not.allocated(f)) stop 1
+    select type (f)
+      type is (poly)
+        if (.not.allocated(f%array)) stop 2 ! SANITIZER: HEAP-USE-AFTER-FREE
+        if (size(f%array) /= size(array)) stop 3
+        if (any(f%array /= array)) stop 4   ! SEGFAULTS HERE WITHOUT SANITIZER
+      class default
+        stop 5
+    end select
+    deallocate (f)
+    select type (df)                        ! Verify that df is OK
+      type is (func_deriv)
+      if (.not.allocated(df%f)) stop 6
+      select type (f => df%f)
+        type is (poly)
+          if (.not.allocated(f%array)) stop 7
+          if (size(f%array) /= size(array)) stop 8
+          if (any(f%array /= array)) stop 9
+        class default
+          stop 10
+        end select
+      class default
+        stop 11
+    end select
+    deallocate (df)
+  end
+
+  subroutine variant
+    type :: base
+      integer :: i = 1
+    end type
+    type, extends(base) :: t1
+      integer, allocatable :: array(:)
+    end type
+    type(t1) :: x
+    type :: t2
+      class(base), allocatable :: b
+    end type
+    type(t2), allocatable :: y
+    type :: t3
+      type(t1), allocatable :: b
+    end type
+    type(t3), allocatable :: z
+    type :: t4
+      type(t1) :: b
+    end type
+    type(t4), allocatable :: z2
+
+    x%array = [1,2]
+    allocate(y, source = t2(x))             ! This was the original problem...
+    if (any (x%array /= [1,2])) stop 12     ! ... x%array was overwritten
+    allocate(z, source = t3(x))
+    if (any (x%array /= [1,2])) stop 13     !    -ditto-
+    allocate(z2, source = t4(x))
+    if (any (x%array /= [1,2])) stop 14     ! Was already OK
+    deallocate (x%array, y, z, z2%b%array)
+  end
+end ! Valgrind shows "     in use at exit: 0 bytes in 0 blocks"
-- 
2.55.0
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.