https://gcc.gnu.org/g:2f97d98d174e3ef9f3a9a83c179d787abde5e066

commit r15-891-g2f97d98d174e3ef9f3a9a83c179d787abde5e066
Author: Andre Vehreschild <ve...@gcc.gnu.org>
Date:   Wed Jul 12 16:52:15 2023 +0200

    Fix memory leak.
    
    Prevent double call of function return class object
    and free the object after copy.
    
    gcc/fortran/ChangeLog:
    
            PR fortran/90069
            * trans-expr.cc (gfc_conv_procedure_call): Evaluate
            expressions with side-effects only ones and ensure
            old is freeed.
    
    gcc/testsuite/ChangeLog:
    
            PR fortran/90069
            * gfortran.dg/class_76.f90: New test.

Diff:
---
 gcc/fortran/trans-expr.cc              | 29 +++++++++++++--
 gcc/testsuite/gfortran.dg/class_76.f90 | 66 ++++++++++++++++++++++++++++++++++
 2 files changed, 92 insertions(+), 3 deletions(-)

diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index dfc5b8e9b4a..9f6cc8f871e 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -6725,9 +6725,32 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
                            {
                              tree efield;
 
-                             /* Evaluate arguments just once.  */
-                             if (e->expr_type != EXPR_VARIABLE)
-                               parmse.expr = save_expr (parmse.expr);
+                             /* Evaluate arguments just once, when they have
+                                side effects.  */
+                             if (TREE_SIDE_EFFECTS (parmse.expr))
+                               {
+                                 tree cldata, zero;
+
+                                 parmse.expr = gfc_evaluate_now (parmse.expr,
+                                                                 &parmse.pre);
+
+                                 /* Prevent memory leak, when old component
+                                    was allocated already.  */
+                                 cldata = gfc_class_data_get (parmse.expr);
+                                 zero = build_int_cst (TREE_TYPE (cldata),
+                                                       0);
+                                 tmp = fold_build2_loc (input_location, 
NE_EXPR,
+                                                        logical_type_node,
+                                                        cldata, zero);
+                                 tmp = build3_v (COND_EXPR, tmp,
+                                                 gfc_call_free (cldata),
+                                                 build_empty_stmt (
+                                                   input_location));
+                                 gfc_add_expr_to_block (&parmse.finalblock,
+                                                        tmp);
+                                 gfc_add_modify (&parmse.finalblock,
+                                                 cldata, zero);
+                               }
 
                              /* Set the _data field.  */
                              tmp = gfc_class_data_get (var);
diff --git a/gcc/testsuite/gfortran.dg/class_76.f90 
b/gcc/testsuite/gfortran.dg/class_76.f90
new file mode 100644
index 00000000000..1ee1e1fc25f
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/class_76.f90
@@ -0,0 +1,66 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-original" }
+!
+! PR fortran/90069
+!
+! Contributed by Brad Richardson  <everythingfunctio...@protonmail.com>
+!
+
+program returned_memory_leak
+    implicit none
+
+    type, abstract :: base
+    end type base
+
+    type, extends(base) :: extended
+    end type extended
+
+    type :: container
+        class(*), allocatable :: thing
+    end type
+
+    call run()
+contains
+    subroutine run()
+        type(container) :: a_container
+
+        a_container = theRightWay()
+        a_container = theWrongWay()
+    end subroutine
+
+    function theRightWay()
+        type(container) :: theRightWay
+
+        class(base), allocatable :: thing
+
+        allocate(thing, source = newAbstract())
+        theRightWay = newContainer(thing)
+    end function theRightWay
+
+    function theWrongWay()
+        type(container) :: theWrongWay
+
+        theWrongWay = newContainer(newAbstract())
+    end function theWrongWay
+
+    function  newAbstract()
+        class(base), allocatable :: newAbstract
+
+        allocate(newAbstract, source = newExtended())
+    end function newAbstract
+
+    function newExtended()
+        type(extended) :: newExtended
+    end function newExtended
+
+    function newContainer(thing)
+        class(*), intent(in) :: thing
+        type(container) :: newContainer
+
+        allocate(newContainer%thing, source = thing)
+    end function newContainer
+end program returned_memory_leak
+
+! { dg-final { scan-tree-dump-times "newabstract" 14 "original" } }
+! { dg-final { scan-tree-dump-times "__builtin_free" 8 "original" } }
+

Reply via email to