From: Eric Botcazou <ebotca...@adacore.com>

The problem is that Insert_Actions gets confused as to where it should
insert actions coming from within an N_Iterated_Component_Association,
because some actions may be generated during semantic analysis and some
others during expansion.

Instead of another ad-hoc fix, this change extends the processing done
for N_Component_Association, that is to say waiting for the Loop_Actions
field to be set during expansion before inserting actions in there.

This in turn requires semantic analysis to stop generating actions for
N_Iterated_Component_Association nodes.  The current processing is a
little unstable:
  - for container aggregates, Resolve_Iterated_Association preanalyzes
    a copy of the expression,
  - for delta aggregates, Resolve_Delta_Array_Aggregate fully analyzes
    a copy of the expression,
  - for array aggregate, Resolve_Aggr_Expr entirely skips the analysis.

The change implements a preanalysis of a copy of the expression using
Copy_Separate_Tree, which should be sufficient since the expression is
supposed to be unanalyzed at this point, recursively in the context of
N_Iterated_Component_Association nodes.

gcc/ada/ChangeLog:

        PR ada/117018
        * exp_aggr.adb (Build_Array_Aggr_Code): Do not expect the
        Loop_Actions field to be already present on association nodes.
        * exp_util.adb (Insert_Actions): For association nodes, insert
        into the Loop_Actions field only if it is already present.
        * sem_aggr.adb (Resolve_Array_Aggregate): Add Iterated parameter.
        (Resolve_Aggregate): Adjust calls to Resolve_Array_Aggregate.
        (Resolve_Aggr_Expr): Add Iterated_Elmt defaulted parameter and
        a default for Single_Elmt.  Adjust call to Resolve_Array_Aggregate.
        Preanalyze a copy of the expression in an iteration context.
        (Resolve_Iterated_Component_Association): Pass Iterated_Elmt as
        True to Resolve_Aggr_Expr and remove processing of Loop_Actions.
        Do not check incorrect use of dynamically tagged expression in
        an iteration context.
        (Resolve_Iterated_Association): Use Copy_Separate_Tree instead of
        New_Copy_Tree and set the Parent field of the result.
        (Resolve_Delta_Array_Aggregate): Likewise.  Only preanalyze the
        copy instead of analyzing it.

Tested on x86_64-pc-linux-gnu, committed on master.

---
 gcc/ada/exp_aggr.adb | 51 +++++++++++++-----------
 gcc/ada/exp_util.adb | 23 ++---------
 gcc/ada/sem_aggr.adb | 94 ++++++++++++++++++++++++++++----------------
 3 files changed, 91 insertions(+), 77 deletions(-)

diff --git a/gcc/ada/exp_aggr.adb b/gcc/ada/exp_aggr.adb
index f4844b74842..ed50d94d3c8 100644
--- a/gcc/ada/exp_aggr.adb
+++ b/gcc/ada/exp_aggr.adb
@@ -1973,30 +1973,35 @@ package body Exp_Aggr is
 
          Assoc := First (Component_Associations (N));
          while Present (Assoc) loop
-            Choice := First (Choice_List (Assoc));
-            while Present (Choice) loop
-               if Nkind (Choice) = N_Others_Choice then
-                  Others_Assoc := Assoc;
-                  exit;
-               end if;
+            declare
+               First_Range : Boolean := True;
 
-               Bounds := Get_Index_Bounds (Choice);
+            begin
+               Choice := First (Choice_List (Assoc));
+               while Present (Choice) loop
+                  if Nkind (Choice) = N_Others_Choice then
+                     Others_Assoc := Assoc;
+                     exit;
+                  end if;
 
-               if Low /= High
-                 and then No (Loop_Actions (Assoc))
-               then
-                  Set_Loop_Actions (Assoc, New_List);
-               end if;
+                  Bounds := Get_Index_Bounds (Choice);
 
-               Nb_Choices := Nb_Choices + 1;
+                  if First_Range and then Low /= High then
+                     pragma Assert (No (Loop_Actions (Assoc)));
+                     Set_Loop_Actions (Assoc, New_List);
+                     First_Range := False;
+                  end if;
 
-               Table (Nb_Choices) :=
-                  (Choice_Lo   => Low,
-                   Choice_Hi   => High,
-                   Choice_Node => Get_Assoc_Expr (Assoc));
+                  Nb_Choices := Nb_Choices + 1;
 
-               Next (Choice);
-            end loop;
+                  Table (Nb_Choices) :=
+                     (Choice_Lo   => Low,
+                      Choice_Hi   => High,
+                      Choice_Node => Get_Assoc_Expr (Assoc));
+
+                  Next (Choice);
+               end loop;
+            end;
 
             Next (Assoc);
          end loop;
@@ -2059,12 +2064,10 @@ package body Exp_Aggr is
                   end if;
 
                   if First or else not Empty_Range (Low, High) then
-                     First := False;
-                     if Present (Loop_Actions (Others_Assoc)) then
-                        pragma Assert
-                          (Is_Empty_List (Loop_Actions (Others_Assoc)));
-                     else
+                     if First then
+                        pragma Assert (No (Loop_Actions (Others_Assoc)));
                         Set_Loop_Actions (Others_Assoc, New_List);
+                        First := False;
                      end if;
                      Expr := Get_Assoc_Expr (Others_Assoc);
                      Append_List (Gen_Loop (Low, High, Expr), To => New_Code);
diff --git a/gcc/ada/exp_util.adb b/gcc/ada/exp_util.adb
index b400505db7b..666c9bae04f 100644
--- a/gcc/ada/exp_util.adb
+++ b/gcc/ada/exp_util.adb
@@ -8116,9 +8116,9 @@ package body Exp_Util is
             --  If a component association appears within a loop created for
             --  an array aggregate, attach the actions to the association so
             --  they can be subsequently inserted within the loop. For other
-            --  component associations insert outside of the aggregate. For
+            --  component associations, insert outside of the aggregate. For
             --  an association that will generate a loop, its Loop_Actions
-            --  attribute is already initialized (see exp_aggr.adb).
+            --  field is already initialized (see exp_aggr.adb).
 
             --  The list of Loop_Actions can in turn generate additional ones,
             --  that are inserted before the associated node. If the associated
@@ -8131,27 +8131,12 @@ package body Exp_Util is
                | N_Iterated_Element_Association
             =>
                if Nkind (Parent (P)) in N_Aggregate | N_Delta_Aggregate
-
-                 --  We must not climb up out of an N_Iterated_xxx_Association
-                 --  because the actions might contain references to the loop
-                 --  parameter, except if we come from the Discrete_Choices of
-                 --  N_Iterated_Component_Association which cannot contain any.
-                 --  But it turns out that setting the Loop_Actions field in
-                 --  the case of an N_Component_Association when the field was
-                 --  not already set can lead to gigi assertion failures that
-                 --  are presumably due to malformed trees, so don't do that.
-
-                 and then
-                   not (Nkind (P) = N_Iterated_Component_Association
-                          and then Is_List_Member (N)
-                          and then List_Containing (N) = Discrete_Choices (P))
-                 and then
-                   not (Nkind (P) = N_Component_Association
-                          and then No (Loop_Actions (P)))
+                 and then Present (Loop_Actions (P))
                then
                   if Is_Empty_List (Loop_Actions (P)) then
                      Set_Loop_Actions (P, Ins_Actions);
                      Analyze_List (Ins_Actions);
+
                   else
                      declare
                         Decl : Node_Id;
diff --git a/gcc/ada/sem_aggr.adb b/gcc/ada/sem_aggr.adb
index b05b0b267fe..0faca2863d3 100644
--- a/gcc/ada/sem_aggr.adb
+++ b/gcc/ada/sem_aggr.adb
@@ -253,6 +253,7 @@ package body Sem_Aggr is
       Index          : Node_Id;
       Index_Constr   : Node_Id;
       Component_Typ  : Entity_Id;
+      Iterated       : Boolean;
       Others_Allowed : Boolean) return Boolean;
    --  This procedure performs the semantic checks for an array aggregate.
    --  True is returned if the aggregate resolution succeeds.
@@ -278,6 +279,9 @@ package body Sem_Aggr is
    --
    --    Component_Typ is the array component type.
    --
+   --    Iterated indicates whether the aggregate appears in the context of an
+   --    iterated association for a parent aggregate.
+   --
    --    Others_Allowed indicates whether an others choice is allowed
    --    in the context where the top-level aggregate appeared.
    --
@@ -1499,6 +1503,7 @@ package body Sem_Aggr is
                     Index          => First_Index (Aggr_Typ),
                     Index_Constr   => First_Index (Typ),
                     Component_Typ  => Component_Type (Typ),
+                    Iterated       => False,
                     Others_Allowed => True);
             else
                Aggr_Resolved :=
@@ -1507,6 +1512,7 @@ package body Sem_Aggr is
                     Index          => First_Index (Aggr_Typ),
                     Index_Constr   => First_Index (Aggr_Typ),
                     Component_Typ  => Component_Type (Typ),
+                    Iterated       => False,
                     Others_Allowed => False);
             end if;
 
@@ -1575,6 +1581,7 @@ package body Sem_Aggr is
       Index          : Node_Id;
       Index_Constr   : Node_Id;
       Component_Typ  : Entity_Id;
+      Iterated       : Boolean;
       Others_Allowed : Boolean) return Boolean
    is
       Loc : constant Source_Ptr := Sloc (N);
@@ -1633,8 +1640,9 @@ package body Sem_Aggr is
       --  cause raising CE at runtime.
 
       function Resolve_Aggr_Expr
-        (Expr        : Node_Id;
-         Single_Elmt : Boolean) return Boolean;
+        (Expr          : Node_Id;
+         Iterated_Elmt : Boolean := False;
+         Single_Elmt   : Boolean := True) return Boolean;
       --  Resolves aggregate expression Expr. Returns False if resolution
       --  fails. If Single_Elmt is set to False, the expression Expr may be
       --  used to initialize several array aggregate elements (this can happen
@@ -1966,9 +1974,13 @@ package body Sem_Aggr is
       -----------------------
 
       function Resolve_Aggr_Expr
-        (Expr        : Node_Id;
-         Single_Elmt : Boolean) return Boolean
+        (Expr          : Node_Id;
+         Iterated_Elmt : Boolean := False;
+         Single_Elmt   : Boolean := True) return Boolean
       is
+         Iterated_Expr  : constant Boolean := Iterated_Elmt or else Iterated;
+         --  True if the Expr is in an iteration context, possibly nested
+
          Nxt_Ind        : constant Node_Id := Next_Index (Index);
          Nxt_Ind_Constr : constant Node_Id := Next_Index (Index_Constr);
          --  Index is the current index corresponding to the expression
@@ -2040,12 +2052,15 @@ package body Sem_Aggr is
 
             Set_Etype (Expr, Etype (N));
 
-            Resolution_OK := Resolve_Array_Aggregate
-              (Expr, Nxt_Ind, Nxt_Ind_Constr, Component_Typ, Others_Allowed);
+            Resolution_OK :=
+              Resolve_Array_Aggregate
+                (Expr, Nxt_Ind, Nxt_Ind_Constr, Component_Typ,
+                 Iterated => Iterated_Expr, Others_Allowed => Others_Allowed);
 
             if Resolution_OK = Failure then
                return Failure;
             end if;
+
          else
             --  If it's "... => <>", nothing to resolve
 
@@ -2055,15 +2070,15 @@ package body Sem_Aggr is
             end if;
 
             --  Do not resolve the expressions of discrete or others choices
-            --  unless the expression covers a single component, or the
-            --  expander is inactive.
+            --  unless the expression covers a single component, or else the
+            --  expander is inactive or this is a spec expression.
 
             --  In SPARK mode, expressions that can perform side effects will
             --  be recognized by the gnat2why back-end, and the whole
             --  subprogram will be ignored. So semantic analysis can be
             --  performed safely.
 
-            if Single_Elmt
+            if (Single_Elmt and then not Iterated_Expr)
               or else not Expander_Active
               or else In_Spec_Expression
             then
@@ -2072,6 +2087,20 @@ package body Sem_Aggr is
                Check_Non_Static_Context (Expr);
                Aggregate_Constraint_Checks (Expr, Component_Typ);
                Check_Unset_Reference (Expr);
+
+            --  Analyze a copy of the expression, to verify legality. We use
+            --  a copy because the expression will be analyzed anew when the
+            --  enclosing aggregate is expanded, and the construct is rewritten
+            --  as a loop with a new index variable.
+
+            elsif Iterated_Expr then
+               declare
+                  New_Expr : constant Node_Id := Copy_Separate_Tree (Expr);
+
+               begin
+                  Set_Parent (New_Expr, Parent (Expr));
+                  Preanalyze_And_Resolve (New_Expr, Component_Typ);
+               end;
             end if;
          end if;
 
@@ -2212,23 +2241,15 @@ package body Sem_Aggr is
 
          --  Analyze expression without expansion, to verify legality.
          --  When generating code, we then remove references to the index
-         --  variable, because the expression will be analyzed anew after
-         --  rewritting as a loop with a new index variable; when not
-         --  generating code we leave the analyzed expression as it is.
+         --  variable, because the expression will be analyzed anew when the
+         --  enclosing aggregate is expanded, and the construct is rewritten
+         --  as a loop with a new index variable; when not generating code we
+         --  leave the analyzed expression as it is.
 
-         Resolution_OK := Resolve_Aggr_Expr (Expr, Single_Elmt => False);
+         Resolution_OK := Resolve_Aggr_Expr (Expr, Iterated_Elmt => True);
 
          if Operating_Mode /= Check_Semantics then
             Remove_References (Expr);
-            declare
-               Loop_Action : Node_Id;
-            begin
-               Loop_Action := First (Loop_Actions (N));
-               while Present (Loop_Action) loop
-                  Remove_References (Loop_Action);
-                  Next (Loop_Action);
-               end loop;
-            end;
          end if;
 
          End_Scope;
@@ -3582,17 +3603,19 @@ package body Sem_Aggr is
                Check_Can_Never_Be_Null (Etype (N), Expr);
             end if;
 
-            if not Resolve_Aggr_Expr (Expr, Single_Elmt => True) then
+            if not Resolve_Aggr_Expr (Expr) then
                return Failure;
             end if;
 
             --  Check incorrect use of dynamically tagged expression
 
-            if Is_Tagged_Type (Etype (Expr)) then
-               Check_Dynamically_Tagged_Expression
-                 (Expr => Expr,
-                  Typ  => Component_Type (Etype (N)),
-                  Related_Nod => N);
+            if not Iterated then
+               if Is_Tagged_Type (Etype (Expr)) then
+                  Check_Dynamically_Tagged_Expression
+                    (Expr => Expr,
+                     Typ  => Component_Type (Etype (N)),
+                     Related_Nod => N);
+               end if;
             end if;
 
             Next (Expr);
@@ -4030,10 +4053,10 @@ package body Sem_Aggr is
          --  enclosing aggregate is expanded, and the construct is rewritten
          --  as a loop with a new index variable.
 
-         Expr := New_Copy_Tree (Expression (Comp));
+         Expr := Copy_Separate_Tree (Expression (Comp));
+         Set_Parent (Expr, Comp);
          Preanalyze_And_Resolve (Expr, Elmt_Type);
          End_Scope;
-
       end Resolve_Iterated_Association;
 
    --  Start of processing for Resolve_Container_Aggregate
@@ -4468,14 +4491,17 @@ package body Sem_Aggr is
                   Set_Is_Not_Self_Hidden (Id);
                   Set_Scope (Id, Ent);
                end if;
+
                Enter_Name (Id);
 
-               --  Resolve a copy of the expression, after setting
-               --  its parent properly to preserve its context.
+               --  Analyze a copy of the expression, to verify legality. We use
+               --  a copy because the expression will be analyzed anew when the
+               --  enclosing aggregate is expanded, and the construct is
+               --  rewritten as a loop with a new index variable.
 
-               Expr := New_Copy_Tree (Expression (Assoc));
+               Expr := Copy_Separate_Tree (Expression (Assoc));
                Set_Parent (Expr, Assoc);
-               Analyze_And_Resolve (Expr, Component_Type (Typ));
+               Preanalyze_And_Resolve (Expr, Component_Type (Typ));
                End_Scope;
             end;
 
-- 
2.43.0

Reply via email to