This patch supresses the finalization of an intermediate copy produced when a
cursor iterates over a collection. The intermediate copy is still finalized
when the associated loop goes out of scope.

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

--  types.ads

with Ada.Containers.Doubly_Linked_Lists;
package Types is
   package Lists is new Ada.Containers.Doubly_Linked_Lists (Integer);
   use Lists;

   function  Get_List   (L : List) return List;
   procedure Print_List (L : List);
   procedure Zero_List  (L : in out List) with
     Post => (for all Index in Get_List (L) => Element (Index) = 0);
end Types;

--  types.adb

with Ada.Text_IO; use Ada.Text_IO;
package body Types is
   function Get_List (L : List) return List is
   begin
      return L;
   end Get_List;

   procedure Print_List (L : List) is
   begin
      for Element of Get_List (L) loop
         Put_Line (Integer'Image (Element));
      end loop;
   end Print_List;

   procedure Zero_List (L : in out List) is
      Result : Lists.List;
   begin
      for I of L loop
         Put_Line (Integer'Image (I));
      end loop;

      Result.Append (0);
      L := Result;
   end Zero_List;
end Types;

--  main.adb

with Types; use Types;
procedure Main is
   L : Lists.List;
begin
   L.Append (111);
   L.Append (1234);
   L.Append (-9999);

   Zero_List  (L);
   Print_List (L);
end Main;

-----------------
-- Compilation --
-----------------

gnatmake -q -gnat12 -gnata main.adb

---------------------
-- Expected output --
---------------------

 111
 1234
-9999
 0

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

2011-08-02  Hristian Kirtchev  <kirtc...@adacore.com>

        * exp_ch5.adb (Expand_Iterator_Loop): Code cleanup and reorganization.
        Set the associated loop as the related expression of internally
        generated cursors.
        * exp_ch7.adb (Is_Container_Cursor): New routine.
        (Wrap_Transient_Declaration): Supress the finalization of the list
        controller when the declaration denotes a container cursor.

Index: exp_ch5.adb
===================================================================
--- exp_ch5.adb (revision 177147)
+++ exp_ch5.adb (working copy)
@@ -2859,13 +2859,10 @@
          --  with the obvious replacements if "reverse" is specified.
 
          declare
-            Element_Type  : constant Entity_Id := Etype (Id);
-            Pack          : constant Entity_Id := Scope (Base_Type (Typ));
-            Name_Init     : Name_Id;
-            Name_Step     : Name_Id;
-            Cond          : Node_Id;
-            Cursor_Decl   : Node_Id;
-            Renaming_Decl : Node_Id;
+            Element_Type : constant Entity_Id := Etype (Id);
+            Pack         : constant Entity_Id := Scope (Base_Type (Typ));
+            Name_Init    : Name_Id;
+            Name_Step    : Name_Id;
 
          begin
             Stats := Statements (N);
@@ -2876,52 +2873,24 @@
                Cursor := Id;
             end if;
 
+            --  Must verify that the container has a reverse iterator ???
+
             if Reverse_Present (I_Spec) then
-
-               --  Must verify that the container has a reverse iterator ???
-
                Name_Init := Name_Last;
                Name_Step := Name_Previous;
-
             else
                Name_Init := Name_First;
                Name_Step := Name_Next;
             end if;
 
-            --  C : Cursor_Type := Container.First;
+            --  The code below only handles containers where Element is not a
+            --  primitive operation of the container. This excludes for now the
+            --  Hi-Lite formal containers. Generate:
+            --
+            --    Id : Element_Type renames Container.Element (Cursor);
 
-            Cursor_Decl :=
-              Make_Object_Declaration (Loc,
-                Defining_Identifier => Cursor,
-                Object_Definition   =>
-                  Make_Selected_Component (Loc,
-                    Prefix        => New_Occurrence_Of (Pack, Loc),
-                    Selector_Name => Make_Identifier (Loc, Name_Cursor)),
-                Expression =>
-                  Make_Selected_Component (Loc,
-                    Prefix        => Relocate_Node (Container),
-                    Selector_Name => Make_Identifier (Loc, Name_Init)));
-
-            Insert_Action (N, Cursor_Decl);
-
-            --  while C /= No_Element loop
-
-            Cond := Make_Op_Ne (Loc,
-                      Left_Opnd  => New_Occurrence_Of (Cursor, Loc),
-                      Right_Opnd => Make_Selected_Component (Loc,
-                         Prefix        => New_Occurrence_Of (Pack, Loc),
-                         Selector_Name =>
-                           Make_Identifier (Loc, Name_No_Element)));
-
             if Of_Present (I_Spec) then
-
-               --  Id : Element_Type renames Container.Element (Cursor);
-
-               --  The code below only handles containers where Element is not
-               --  a primitive operation of the container. This excludes
-               --  for now the Hi-Lite formal containers.
-
-               Renaming_Decl :=
+               Prepend_To (Stats,
                  Make_Object_Renaming_Declaration (Loc,
                    Defining_Identifier => Id,
                    Subtype_Mark        =>
@@ -2934,9 +2903,7 @@
                            Selector_Name =>
                              Make_Identifier (Loc, Chars => Name_Element)),
                        Expressions =>
-                         New_List (New_Occurrence_Of (Cursor, Loc))));
-
-               Prepend (Renaming_Decl, Stats);
+                         New_List (New_Occurrence_Of (Cursor, Loc)))));
             end if;
 
             --  For both iterator forms, add call to step operation (Next or
@@ -2951,12 +2918,53 @@
                 Parameter_Associations =>
                   New_List (New_Occurrence_Of (Cursor, Loc))));
 
-            New_Loop := Make_Loop_Statement (Loc,
-              Iteration_Scheme =>
-                Make_Iteration_Scheme (Loc, Condition => Cond),
-              Statements       => Stats,
-              End_Label        => Empty);
+            --  Generate:
+            --    while Cursor /= No_Element loop
+            --       <Stats>
+            --    end loop;
 
+            New_Loop :=
+              Make_Loop_Statement (Loc,
+                Iteration_Scheme =>
+                  Make_Iteration_Scheme (Loc,
+                    Condition =>
+                      Make_Op_Ne (Loc,
+                        Left_Opnd =>
+                          New_Occurrence_Of (Cursor, Loc),
+                        Right_Opnd =>
+                          Make_Selected_Component (Loc,
+                            Prefix =>
+                              New_Occurrence_Of (Pack, Loc),
+                            Selector_Name =>
+                              Make_Identifier (Loc, Name_No_Element)))),
+                Statements => Stats,
+                End_Label  => Empty);
+
+            --  When the cursor is internally generated, associate it with the
+            --  loop statement.
+
+            if Of_Present (I_Spec) then
+               Set_Ekind (Cursor, E_Variable);
+               Set_Related_Expression (Cursor, New_Loop);
+            end if;
+
+            --  Create the declaration of the cursor and insert it before the
+            --  source loop. Generate:
+            --
+            --    C : Cursor_Type := Container.First;
+
+            Insert_Action (N,
+              Make_Object_Declaration (Loc,
+                Defining_Identifier => Cursor,
+                Object_Definition   =>
+                  Make_Selected_Component (Loc,
+                    Prefix        => New_Occurrence_Of (Pack, Loc),
+                    Selector_Name => Make_Identifier (Loc, Name_Cursor)),
+                Expression =>
+                  Make_Selected_Component (Loc,
+                    Prefix        => Relocate_Node (Container),
+                    Selector_Name => Make_Identifier (Loc, Name_Init))));
+
             --  If the range of iteration is given by a function call that
             --  returns a container, the finalization actions have been saved
             --  in the Condition_Actions of the iterator. Insert them now at
Index: exp_ch7.adb
===================================================================
--- exp_ch7.adb (revision 177087)
+++ exp_ch7.adb (working copy)
@@ -1517,9 +1517,10 @@
       if Present (Len_Ref) then
          Action :=
            Make_Implicit_If_Statement (N,
-             Condition => Make_Op_Gt (Loc,
-               Left_Opnd  => Len_Ref,
-               Right_Opnd => Make_Integer_Literal (Loc, 0)),
+             Condition =>
+               Make_Op_Gt (Loc,
+                 Left_Opnd  => Len_Ref,
+                 Right_Opnd => Make_Integer_Literal (Loc, 0)),
              Then_Statements => New_List (Action));
       end if;
 
@@ -3417,15 +3418,45 @@
    --    Finalize_One (_v2);
 
    procedure Wrap_Transient_Declaration (N : Node_Id) is
-      S              : Entity_Id;
+      Loc            : constant Source_Ptr := Sloc (N);
+      Next_N         : constant Node_Id := Next (N);
+      Enclosing_S    : Entity_Id;
+      First_Decl_Loc : Source_Ptr;
       LC             : Entity_Id := Empty;
       Nodes          : List_Id;
-      Loc            : constant Source_Ptr := Sloc (N);
-      First_Decl_Loc : Source_Ptr;
-      Enclosing_S    : Entity_Id;
+      S              : Entity_Id;
       Uses_SS        : Boolean;
-      Next_N         : constant Node_Id := Next (N);
 
+      function Is_Container_Cursor (Decl : Node_Id) return Boolean;
+      --  Determine whether object declaration Decl is a cursor used to iterate
+      --  over an Ada 2005/12 container.
+
+      -------------------------
+      -- Is_Container_Cursor --
+      -------------------------
+
+      function Is_Container_Cursor (Decl : Node_Id) return Boolean is
+         Def_Id : constant Entity_Id := Defining_Identifier (Decl);
+         Expr   : constant Node_Id   := Expression (Decl);
+
+      begin
+         --  A cursor declaration appears in the following form:
+         --
+         --    Index : Pack.Cursor := First (...);
+
+         return
+           Chars (Etype (Def_Id)) = Name_Cursor
+             and then Present (Expr)
+             and then Nkind (Expr) = N_Function_Call
+             and then Chars (Name (Expr)) = Name_First
+             and then
+               (Nkind (Parent (Decl)) = N_Expression_With_Actions
+                  or else
+                Nkind (Related_Expression (Def_Id)) = N_Loop_Statement);
+      end Is_Container_Cursor;
+
+   --  Start of processing for Wrap_Transient_Declaration
+
    begin
       S := Current_Scope;
       Enclosing_S := Scope (S);
@@ -3503,6 +3534,29 @@
          then
             null;
 
+         --  The declaration of a container cursor is a special context where
+         --  the finalization of the list controller needs to be supressed. In
+         --  the following simplified example:
+         --
+         --    LC   : Simple_List_Controller;
+         --    Temp : Ptr_Typ := Container_Creator_Function'Reference;
+         --    Deep_Tag_Attach (Temp, LC);
+         --    Obj  : Pack.Cursor := First (Temp.all);
+         --    Finalize (LC);
+         --    <execute the loop>
+         --
+         --  the finalization of the list controller destroys the contents of
+         --  container Temp, and as a result Obj points to nothing. Note that
+         --  Temp will be finalized by the finalization list of the enclosing
+         --  scope.
+
+         elsif Ada_Version >= Ada_2012
+           and then Is_Container_Cursor (N)
+         then
+            null;
+
+         --  Finalize the list controller
+
          else
             Nodes :=
               Make_Final_Call

Reply via email to