[Ada] Premature secondary stack reclamation
authorHristian Kirtchev <kirtchev@adacore.com>
Mon, 21 May 2018 14:49:52 +0000 (14:49 +0000)
committerPierre-Marie de Rodat <pmderodat@gcc.gnu.org>
Mon, 21 May 2018 14:49:52 +0000 (14:49 +0000)
commit66c0fa2cc9a2dbe62db5bed4fe5310d2e5912baf
tree61aca7f8c17ef32e5181606e88f16255c0dd5157
parent8016e5676bfde592826b45bc297da0750c15d6e3
[Ada] Premature secondary stack reclamation

This patch modifies the creation of transient scopes to eliminate potential
premature secondary stack reclamations when there is no suitable transient
context and the scope was intended to manage the secondary stack. Instead,
the logic was changed to accommodate a special case where an assignment with
suppressed controlled actions that appears within a type initialization
procedure requires secondary stack reclamation.

The patch also corrects the handling of function calls which utilize the
secondary stack in loop parameter specifications. Previously the predicate
which determined whether the function will utilize the secondary stack was
not accurate enough, and in certain cases could lead to leaks.

------------
-- Source --
------------

--  iterators.ads

package Iterators is
   type Iterator is limited interface;
   type Iterator_Access is access all Iterator'Class;

   function Next
     (I       : in out Iterator;
      Element : out Character) return Boolean is abstract;

   procedure Iterate
     (I    : in out Iterator'Class;
      Proc : access procedure (Element : Character));
end Iterators;

--  iterators.adb

package body Iterators is
   procedure Iterate
     (I    : in out Iterator'Class;
      Proc : access procedure (Element : Character))
   is
      Element : Character;
   begin
      while I.Next (Element) loop
         Proc (Element);
      end loop;
   end Iterate;
end Iterators;

--  base.ads

with Iterators; use Iterators;

package Base is
   type String_Access is access all String;
   type Node is tagged record
      S : String_Access;
   end record;

   type Node_Access is access all Node'Class;
   type Node_Array is array (Positive range <>) of Node_Access;

   function As_Array (N : Node_Access) return Node_Array;
   function Get_String (C : Character) return String;

   type Node_Iterator is limited new Iterator with record
      Node : Node_Access;
      I    : Positive;
   end record;

   overriding function Next
     (It      : in out Node_Iterator;
      Element : out Character) return Boolean;

   function Constructor_1 (N : Node_Access) return Node_Iterator;
   function Constructor_2 (N : Node_Access) return Node_Iterator;
end Base;

--  base.adb

package body Base is
   function As_Array (N : Node_Access) return Node_Array is
   begin
      return (1 => N);
   end As_Array;

   function Get_String (C : Character) return String is
   begin
      return (1 .. 40 => C);
   end Get_String;

   function Next
     (It      : in out Node_Iterator;
      Element : out Character) return Boolean
   is
   begin
      if It.I > It.Node.S'Last then
         return False;
      else
         It.I := It.I + 1;
         Element := It.Node.S (It.I - 1);
         return True;
      end if;
   end Next;

   function Constructor_1 (N : Node_Access) return Node_Iterator is
   begin
      return Node_Iterator'(N, 1);
   end Constructor_1;

   function Constructor_2 (N : Node_Access) return Node_Iterator is
   begin
      return Constructor_1 (As_Array (N) (1));
   end Constructor_2;
end Base;

--  main.adb

with Ada.Text_IO; use Ada.Text_IO;
with Base;        use Base;
with Iterators;   use Iterators;

procedure Main is
   N : constant Node_Access := new Node'(S => new String'("hello world"));

   procedure Process (C : Character) is
   begin
      Put_Line (Get_String (C));
   end Process;

   C : Iterator'Class := Constructor_2 (N);

begin
   C.Iterate (Process'Access);
end Main;

----------------------------
-- Compilation and output --
----------------------------

$ gnatmake -q main.adb
$ ./main
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
llllllllllllllllllllllllllllllllllllllll
llllllllllllllllllllllllllllllllllllllll
oooooooooooooooooooooooooooooooooooooooo

wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
oooooooooooooooooooooooooooooooooooooooo
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr
llllllllllllllllllllllllllllllllllllllll
dddddddddddddddddddddddddddddddddddddddd

2018-05-21  Hristian Kirtchev  <kirtchev@adacore.com>

gcc/ada/

* exp_ch7.adb (Establish_Transient_Scope): Code cleanup. Do not
delegate the secondary stack management when there is no suitable
transient context, and the transient scope was intended to manage the
secondary stack because this causes premature reclamation. Change the
transient scope creation logic by special casing assignment statements
of controlled components for type initialization procedures.
(Find_Node_To_Be_Wrapped): Renamed to Find_Transient_Context. Update
the comment on usage.
(Find_Transient_Context): Change the initinte loop into a while loop.
Iterations schemes and iterator specifications are not valid transient
contexts because they rely on special processing. Assignment statements
are now treated as a normal transient context, special cases are
handled by the caller. Add special processing for pragma Check.
(Is_OK_Construct): Removed. Its functionality has been merged in
routine Find_Transient_Context.
* sem_ch5.adb (Check_Call): Reimplemented. Add code to properly
retrieve the subprogram being invoked. Use a more accurate predicate
(Requires_Transient_Scope) to determine that the function will emply
the secondary stack.

From-SVN: r260443
gcc/ada/ChangeLog
gcc/ada/exp_ch7.adb
gcc/ada/sem_ch5.adb