[Ada] Secondary stack leak in statements block located in a loop
authorJavier Miranda <miranda@adacore.com>
Tue, 17 Jul 2018 08:07:00 +0000 (08:07 +0000)
committerPierre-Marie de Rodat <pmderodat@gcc.gnu.org>
Tue, 17 Jul 2018 08:07:00 +0000 (08:07 +0000)
When a loop iterator has a block declaration containing statements that invoke
functions whose result is returned on the secondary stack (such as a
string-returning function), the compiler fails to generate code to release the
allocated memory when the loop terminates.

After this patch the following test works fine.

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
pragma Warnings (Off);
with System.Secondary_Stack;
pragma Warnings (On);

procedure Small is
   procedure Info is new System.Secondary_Stack.Ss_Info (Put_Line);

   US : Unbounded_String;
begin
   Info;
   for J in 1 .. 100_000 loop
      Leaky_Block : declare
      begin
         if (J mod 20000) = 0 then
            Info;
         end if;

         Ada.Text_IO.Put_Line (To_String (US));  --  Test

         if (J mod 20000) = 0 then
            Info;
         end if;
      end Leaky_Block;
   end loop;
   Info;
end;

Command:
  gnatmake small.adb; small | grep "Current allocated space :" | uniq
Output:
  Current allocated space :  0 bytes

2018-07-17  Javier Miranda  <miranda@adacore.com>

gcc/ada/

* exp_ch7.adb (Make_Transient_Block): When determining whether an
enclosing scope already handles the secondary stack, take into account
transient blocks nested in a block that do not manage the secondary
stack and are located within a loop.

From-SVN: r262779

gcc/ada/ChangeLog
gcc/ada/exp_ch7.adb

index 4f747f22b759f42217fdfdcbce081f9723d70c69..af38e50fa5fa8b16afabdb2cb9aff26fbf10d5cf 100644 (file)
@@ -1,3 +1,10 @@
+2018-07-17  Javier Miranda  <miranda@adacore.com>
+
+       * exp_ch7.adb (Make_Transient_Block): When determining whether an
+       enclosing scope already handles the secondary stack, take into account
+       transient blocks nested in a block that do not manage the secondary
+       stack and are located within a loop.
+
 2018-07-17  Ed Schonberg  <schonberg@adacore.com>
 
        * sem_util.adb (Enclosing_Subprogram): Protected entries and task
index 2f3092d98b10e5faba053e7f23eef2aeebcaf306..781456fdfea94dc90ea87b1a1e4c50c0c78170d1 100644 (file)
@@ -8695,9 +8695,33 @@ package body Exp_Ch7 is
       Action : Node_Id;
       Par    : Node_Id) return Node_Id
    is
+      function Within_Loop_Statement (N : Node_Id) return Boolean;
+      --  Return True when N appears within a loop and no block is containing N
+
       function Manages_Sec_Stack (Id : Entity_Id) return Boolean;
       --  Determine whether scoping entity Id manages the secondary stack
 
+      ---------------------------
+      -- Within_Loop_Statement --
+      ---------------------------
+
+      function Within_Loop_Statement (N : Node_Id) return Boolean is
+         Par : Node_Id := Parent (N);
+
+      begin
+         while not (Nkind_In (Par,
+                              N_Loop_Statement,
+                              N_Handled_Sequence_Of_Statements,
+                              N_Package_Specification)
+                      or else Nkind (Par) in N_Proper_Body)
+         loop
+            pragma Assert (Present (Par));
+            Par := Parent (Par);
+         end loop;
+
+         return Nkind (Par) = N_Loop_Statement;
+      end Within_Loop_Statement;
+
       -----------------------
       -- Manages_Sec_Stack --
       -----------------------
@@ -8780,6 +8804,16 @@ package body Exp_Ch7 is
             elsif Ekind (Scop) = E_Loop then
                exit;
 
+            --  Ditto when the block appears without a block that does not
+            --  manage the secondary stack and is located within a loop.
+
+            elsif Ekind (Scop) = E_Block
+              and then not Manages_Sec_Stack (Scop)
+              and then Present (Block_Node (Scop))
+              and then Within_Loop_Statement (Block_Node (Scop))
+            then
+               exit;
+
             --  The transient block does not need to manage the secondary stack
             --  when there is an enclosing construct which already does that.
             --  This optimization saves on SS_Mark and SS_Release calls but may