Hi all,

on this regression I had to chew a longer time. Assume this Fortran:

type T
   integer, allocatable:: a
end type T

result(type T) function bar()
  allocate(bar%a)
end function

call foo([bar()])

That Fortran fragment was translated to something like (pseudo code):

T temp;
T arr[];
temp = bar();
arr[0]= temp;
foo(arr);
if (temp.a) { free(temp.a); temp.a= NULL;}
for (i in size(arr))
  if (arr[i].a) { free(arr[i].a]; <-- double free here
    arr[i].a = NULL;
}

I.e., when the derived type result of a function was used in an array
constructor that was used a function argument, then the temporary used to
evaluate the function only ones was declared to be of value. When the derived
type now had allocatable components, freeing those would be done on the value
typed temporary (here temp). But later on the array would also be freed. Now a
doulbe free occured, because the temporary variable was already freed. The
patch fixes this, by preventing the temporary when not necessary, or using a
temporary that is reference into the array, i.e., the memory freed (and marked
as such) is stored at the same location.

So after the patch this looks like this:

T *temp; // Now a pointer!
T arr[];
arr[0] = bar();
temp = &arr[0];
... Now we're safe, because freeing temp->a sets arr[0].a to NULL and the
following loop is safe.

Regtests ok on x86_64-pc-linux-gnu / F41. Ok for mainline?

Regards,
        Andre
--
Andre Vehreschild * Email: vehre ad gmx dot de
From e9fd144ed6b72ddeb37c629a710bebbfba918e19 Mon Sep 17 00:00:00 2001
From: Andre Vehreschild <ve...@gcc.gnu.org>
Date: Wed, 26 Feb 2025 14:30:13 +0100
Subject: [PATCH] Fortran: Fix regression on double free on elemental function
 [PR118747]

Fix a regression were adding a temporary variable inserted a copy of the
argument to the elemental function.  That copy was then later used to
free allocated memory, but the freeing was not tracked in the source
array correctly.

	PR fortran/118747

gcc/fortran/ChangeLog:

	* trans-array.cc (gfc_trans_array_ctor_element): Remove copy to
	temporary variable.
	* trans-expr.cc (gfc_conv_procedure_call): Use references to
	array members instead of copies when freeing after use.
	Formatting fix.

gcc/testsuite/ChangeLog:

	* gfortran.dg/alloc_comp_auto_array_4.f90: New test.
---
 gcc/fortran/trans-array.cc                    | 11 +++-----
 gcc/fortran/trans-expr.cc                     | 13 ++++++---
 .../gfortran.dg/alloc_comp_auto_array_4.f90   | 27 +++++++++++++++++++
 3 files changed, 41 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/alloc_comp_auto_array_4.f90

diff --git a/gcc/fortran/trans-array.cc b/gcc/fortran/trans-array.cc
index 8f76870b286..6a00d26cb2f 100644
--- a/gcc/fortran/trans-array.cc
+++ b/gcc/fortran/trans-array.cc
@@ -2002,13 +2002,10 @@ gfc_trans_array_ctor_element (stmtblock_t * pblock, tree desc,

   if (expr->expr_type == EXPR_FUNCTION && expr->ts.type == BT_DERIVED
       && expr->ts.u.derived->attr.alloc_comp)
-    {
-      if (!VAR_P (se->expr))
-	se->expr = gfc_evaluate_now (se->expr, &se->pre);
-      gfc_add_expr_to_block (&se->finalblock,
-			     gfc_deallocate_alloc_comp_no_caf (
-			       expr->ts.u.derived, se->expr, expr->rank, true));
-    }
+    gfc_add_expr_to_block (&se->finalblock,
+			   gfc_deallocate_alloc_comp_no_caf (expr->ts.u.derived,
+							     tmp, expr->rank,
+							     true));

   if (expr->ts.type == BT_CHARACTER)
     {
diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index ab55940638e..e619013f261 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -6999,6 +6999,12 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
 	  if ((fsym && fsym->attr.value)
 	      || (ulim_copy && (argc == 2 || argc == 3)))
 	    gfc_conv_expr (&parmse, e);
+	  else if (e->expr_type == EXPR_ARRAY)
+	    {
+	      gfc_conv_expr (&parmse, e);
+	      if (e->ts.type != BT_CHARACTER)
+		parmse.expr = gfc_build_addr_expr (NULL_TREE, parmse.expr);
+	    }
 	  else
 	    gfc_conv_expr_reference (&parmse, e);

@@ -7930,11 +7936,11 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
 	  /* It is known the e returns a structure type with at least one
 	     allocatable component.  When e is a function, ensure that the
 	     function is called once only by using a temporary variable.  */
-	  if (!DECL_P (parmse.expr))
+	  if (!DECL_P (parmse.expr) && e->expr_type == EXPR_FUNCTION)
 	    parmse.expr = gfc_evaluate_now_loc (input_location,
 						parmse.expr, &se->pre);

-	  if (fsym && fsym->attr.value)
+	  if ((fsym && fsym->attr.value) || e->expr_type == EXPR_ARRAY)
 	    tmp = parmse.expr;
 	  else
 	    tmp = build_fold_indirect_ref_loc (input_location,
@@ -7993,7 +7999,8 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
 	      /* Scalars passed to an assumed rank argument are converted to
 		 a descriptor. Obtain the data field before deallocating any
 		 allocatable components.  */
-	      if (parm_rank == 0 && GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (tmp)))
+	      if (parm_rank == 0 && e->expr_type != EXPR_ARRAY
+		  && GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (tmp)))
 		tmp = gfc_conv_descriptor_data_get (tmp);

 	      if (scalar_res_outside_loop)
diff --git a/gcc/testsuite/gfortran.dg/alloc_comp_auto_array_4.f90 b/gcc/testsuite/gfortran.dg/alloc_comp_auto_array_4.f90
new file mode 100644
index 00000000000..06bd8b50b96
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/alloc_comp_auto_array_4.f90
@@ -0,0 +1,27 @@
+!{ dg-do run }
+
+! Check freeing derived typed result's allocatable components is not done twice.
+! Contributed by Damian Rouson  <damian@archaeologic.codes>
+
+program pr118747
+  implicit none
+
+  type string_t
+    character(len=:), allocatable :: string_
+  end type
+
+  call check_allocation([foo(), foo()])
+
+contains
+
+  type(string_t) function foo()
+    foo%string_ = "foo"
+  end function
+
+  elemental subroutine check_allocation(string)
+    type(string_t), intent(in) ::  string
+    if (.not. allocated(string%string_)) error stop "unallocated"
+  end subroutine
+
+end program
+
--
2.48.1

Reply via email to