This change fixes a defect whereby GNAT would fail to generate secondary
stack cleanup code for a scope containing a local object of a limited
discriminated type initialized by a (build-in-place) function call,
thus causing a storage leak.

The following test case must not leak memory for each iteration of the loop:

package Limited_Factory is
   type Lim (D : Integer) is limited private;
   function Create_In_Place return Lim;
private
   type Lim (D : Integer) is limited record
      S : String (1 .. 1024);
   end record;
end Limited_Factory;
package body Limited_Factory is
   function Create_In_Place return Lim is
   begin
      return Lim'(D => 42, S => (others => 'x'));
   end;
end Limited_Factory;
with Limited_Factory; use Limited_Factory;
procedure Sec_Stack_BIP is
   procedure Leak is
      Obj : Lim := Create_In_Place;
   begin
      null;
   end;
begin
   for J in 1 .. 1000 loop
      Leak;
   end loop;
end;

Tested on x86_64-pc-linux-gnu, committed on trunk

2014-07-17  Thomas Quinot  <qui...@adacore.com>

        * exp_ch7.adb (Establish_Transient_Scope.Find_Node_To_Be_Wrapped):
        Start examining the tree at the node passed to
        Establish_Transient_Scope (not its parent).
        * exp_ch6.adb (Make_Build_In_Place_Call_In_Object_Declaration):
        The access type for the variable storing the reference to
        the call must be declared and frozen prior to establishing a
        transient scope.

Index: exp_ch7.adb
===================================================================
--- exp_ch7.adb (revision 212716)
+++ exp_ch7.adb (working copy)
@@ -4208,11 +4208,8 @@
 
    begin
       The_Parent := N;
+      P          := Empty;
       loop
-         P := The_Parent;
-         pragma Assert (P /= Empty);
-         The_Parent := Parent (P);
-
          case Nkind (The_Parent) is
 
             --  Simple statement can be wrapped
@@ -4263,7 +4260,7 @@
 
             --  The expression itself is to be wrapped if its parent is a
             --  compound statement or any other statement where the expression
-            --  is known to be scalar
+            --  is known to be scalar.
 
             when N_Accept_Alternative               |
                  N_Attribute_Definition_Clause      |
@@ -4279,6 +4276,7 @@
                  N_If_Statement                     |
                  N_Iteration_Scheme                 |
                  N_Terminate_Alternative            =>
+               pragma Assert (Present (P));
                return P;
 
             when N_Attribute_Reference =>
@@ -4344,6 +4342,9 @@
             when others =>
                null;
          end case;
+
+         P          := The_Parent;
+         The_Parent := Parent (P);
       end loop;
    end Find_Node_To_Be_Wrapped;
 
Index: exp_ch6.adb
===================================================================
--- exp_ch6.adb (revision 212657)
+++ exp_ch6.adb (working copy)
@@ -10181,10 +10181,9 @@
       Func_Call       : Node_Id := Function_Call;
       Function_Id     : Entity_Id;
       Pool_Actual     : Node_Id;
+      Ptr_Typ         : Entity_Id;
       Ptr_Typ_Decl    : Node_Id;
       Pass_Caller_Acc : Boolean := False;
-      New_Expr        : Node_Id;
-      Ref_Type        : Entity_Id;
       Res_Decl        : Node_Id;
       Result_Subt     : Entity_Id;
 
@@ -10224,6 +10223,53 @@
 
       Result_Subt := Etype (Function_Id);
 
+      --  Create an access type designating the function's result subtype. We
+      --  use the type of the original call because it may be a call to an
+      --  inherited operation, which the expansion has replaced with the parent
+      --  operation that yields the parent type. Note that this access type
+      --  must be declared before we establish a transient scope, so that it
+      --  receives the proper accessibility level.
+
+      Ptr_Typ := Make_Temporary (Loc, 'A');
+      Ptr_Typ_Decl :=
+        Make_Full_Type_Declaration (Loc,
+          Defining_Identifier => Ptr_Typ,
+          Type_Definition     =>
+            Make_Access_To_Object_Definition (Loc,
+              All_Present        => True,
+              Subtype_Indication =>
+                New_Occurrence_Of (Etype (Function_Call), Loc)));
+
+      --  The access type and its accompanying object must be inserted after
+      --  the object declaration in the constrained case, so that the function
+      --  call can be passed access to the object. In the unconstrained case,
+      --  or if the object declaration is for a return object, the access type
+      --  and object must be inserted before the object, since the object
+      --  declaration is rewritten to be a renaming of a dereference of the
+      --  access object. Note: we need to freeze Ptr_Typ explicitly, because
+      --  the result object is in a different (transient) scope, so won't
+      --  cause freezing.
+
+      if Is_Constrained (Underlying_Type (Result_Subt))
+        and then not Is_Return_Object (Defining_Identifier (Object_Decl))
+      then
+         Insert_After_And_Analyze (Object_Decl, Ptr_Typ_Decl);
+      else
+         Insert_Action (Object_Decl, Ptr_Typ_Decl);
+      end if;
+
+      --  Force immediate freezing of Ptr_Typ because Res_Decl will be
+      --  elaborated in an inner (transient) scope and thus won't cause
+      --  freezing by itself.
+
+      declare
+         Ptr_Typ_Freeze_Ref : constant Node_Id :=
+                                New_Occurrence_Of (Ptr_Typ, Loc);
+      begin
+         Set_Parent (Ptr_Typ_Freeze_Ref, Ptr_Typ_Decl);
+         Freeze_Expression (Ptr_Typ_Freeze_Ref);
+      end;
+
       --  If the the object is a return object of an enclosing build-in-place
       --  function, then the implicit build-in-place parameters of the
       --  enclosing function are simply passed along to the called function.
@@ -10356,53 +10402,22 @@
       Add_Access_Actual_To_Build_In_Place_Call
         (Func_Call, Function_Id, Caller_Object, Is_Access => Pass_Caller_Acc);
 
-      --  Create an access type designating the function's result subtype. We
-      --  use the type of the original expression because it may be a call to
-      --  an inherited operation, which the expansion has replaced with the
-      --  parent operation that yields the parent type.
-
-      Ref_Type := Make_Temporary (Loc, 'A');
-
-      Ptr_Typ_Decl :=
-        Make_Full_Type_Declaration (Loc,
-          Defining_Identifier => Ref_Type,
-          Type_Definition     =>
-            Make_Access_To_Object_Definition (Loc,
-              All_Present        => True,
-              Subtype_Indication =>
-                New_Occurrence_Of (Etype (Function_Call), Loc)));
-
-      --  The access type and its accompanying object must be inserted after
-      --  the object declaration in the constrained case, so that the function
-      --  call can be passed access to the object. In the unconstrained case,
-      --  or if the object declaration is for a return object, the access type
-      --  and object must be inserted before the object, since the object
-      --  declaration is rewritten to be a renaming of a dereference of the
-      --  access object.
-
-      if Is_Constrained (Underlying_Type (Result_Subt))
-        and then not Is_Return_Object (Defining_Identifier (Object_Decl))
-      then
-         Insert_After_And_Analyze (Object_Decl, Ptr_Typ_Decl);
-      else
-         Insert_Action (Object_Decl, Ptr_Typ_Decl);
-      end if;
-
       --  Finally, create an access object initialized to a reference to the
       --  function call. We know this access value cannot be null, so mark the
       --  entity accordingly to suppress the access check.
 
-      New_Expr := Make_Reference (Loc, Relocate_Node (Func_Call));
-
-      Def_Id := Make_Temporary (Loc, 'R', New_Expr);
-      Set_Etype (Def_Id, Ref_Type);
+      Def_Id := Make_Temporary (Loc, 'R', Func_Call);
+      Set_Etype (Def_Id, Ptr_Typ);
       Set_Is_Known_Non_Null (Def_Id);
 
       Res_Decl :=
         Make_Object_Declaration (Loc,
           Defining_Identifier => Def_Id,
-          Object_Definition   => New_Occurrence_Of (Ref_Type, Loc),
-          Expression          => New_Expr);
+          Constant_Present    => True,
+          Object_Definition   => New_Occurrence_Of (Ptr_Typ, Loc),
+          Expression          =>
+            Make_Reference (Loc, Relocate_Node (Func_Call)));
+
       Insert_After_And_Analyze (Ptr_Typ_Decl, Res_Decl);
 
       --  If the result subtype of the called function is constrained and

Reply via email to