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

...in aggregates.  This prevents a temporary from being created on the
primary stack to hold the result of the function calls before it is copied
to the component of the aggregate in the nonlimited by-reference case.

This requires a small tweak to Check_Function_Writable_Actuals to avoid
giving a spurious error in a specific case.

gcc/ada/ChangeLog:

        * exp_aggr.ads (Parent_Is_Regular_Aggregate): New predicate.
        * exp_aggr.adb (In_Place_Assign_OK.Safe_Component): Implement more
        accurate criterion for function calls.
        (Convert_To_Assignments): Use Parent_Is_Regular_Aggregate predicate.
        (Expand_Array_Aggregate): Likewise.  Remove obsolete comment.
        (Initialize_Component): Do not adjust when the expression is a naked
        function call and Back_End_Return_Slot is True.
        (Parent_Is_Regular_Aggregate): New predicate.
        * exp_ch3.adb (Build_Record_Init_Proc.Build_Assignment): Add test of
        Back_End_Return_Slot in conjunction with a function call.
        * exp_ch4.adb (Expand_Allocator_Expression): Likewise.  Use the
        Is_Container_Aggregate predicate to detect container aggregates.
        (Expand_N_Case_Expression): Delay the expansion if the parent is a
        regular aggregate and the type should not be copied.
        (Expand_N_If_Expression): Likewise.
        (New_Assign_Copy): New function.
        * exp_ch6.adb (Expand_Ctrl_Function_Call): Bail out when the parent
        is a regular aggregate.
        * sem_util.adb (Check_Function_Writable_Actuals): Do not take into
        account attribute references created by the compiler.

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

---
 gcc/ada/exp_aggr.adb | 66 ++++++++++++++++++++++----------
 gcc/ada/exp_aggr.ads |  4 ++
 gcc/ada/exp_ch3.adb  | 10 +++--
 gcc/ada/exp_ch4.adb  | 89 +++++++++++++++++++++++++-------------------
 gcc/ada/exp_ch6.adb  | 18 +++++++--
 gcc/ada/sem_util.adb |  9 +++++
 6 files changed, 131 insertions(+), 65 deletions(-)

diff --git a/gcc/ada/exp_aggr.adb b/gcc/ada/exp_aggr.adb
index 8aad7217935..48478f350bb 100644
--- a/gcc/ada/exp_aggr.adb
+++ b/gcc/ada/exp_aggr.adb
@@ -3865,8 +3865,8 @@ package body Exp_Aggr is
 
       function Safe_Component (Expr : Node_Id) return Boolean;
       --  Verify that an expression cannot depend on the target being assigned
-      --  to. Return true for compile-time known values, stand-alone objects,
-      --  parameters passed by copy, calls to functions that return by copy,
+      --  (which is Target_Object if it is set), return true for compile-time
+      --  known values, stand-alone objects, formal parameters passed by copy,
       --  selected components thereof only if the aggregate's type is an array,
       --  indexed components and slices thereof only if the aggregate's type is
       --  a record, and simple expressions involving only these as operands.
@@ -3877,7 +3877,8 @@ package body Exp_Aggr is
       --  which is excluded by the above condition. Additionally, if the target
       --  is statically known, return true for arbitrarily nested selections,
       --  indexations or slicings, provided that their ultimate prefix is not
-      --  the target itself.
+      --  the target itself, and calls to functions that take only these as
+      --  actual parameters provided that the target is not aliased.
 
       --------------------
       -- Safe_Aggregate --
@@ -3982,12 +3983,26 @@ package body Exp_Aggr is
                   return Check_Component (Prefix (C), T_OK);
 
                when N_Function_Call =>
-                  if Nkind (Name (C)) = N_Explicit_Dereference then
-                     return not Returns_By_Ref (Etype (Name (C)));
-                  else
-                     return not Returns_By_Ref (Entity (Name (C)));
+                  if No (Target_Object) or else Is_Aliased (Target_Object) then
+                     return False;
                   end if;
 
+                  if Present (Parameter_Associations (C)) then
+                     declare
+                        Actual : Node_Id;
+                     begin
+                        Actual := First_Actual (C);
+                        while Present (Actual) loop
+                           if not Check_Component (Actual, T_OK) then
+                              return False;
+                           end if;
+                           Next_Actual (Actual);
+                        end loop;
+                     end;
+                  end if;
+
+                  return True;
+
                when N_Indexed_Component | N_Slice =>
                   --  In a target record, these operations cannot determine
                   --  alone a component so we can recurse whatever the target.
@@ -4179,11 +4194,7 @@ package body Exp_Aggr is
          --  excluding container aggregates as these are transformed into
          --  subprogram calls later.
 
-         (Nkind (Parent_Node) = N_Component_Association
-           and then not Is_Container_Aggregate (Parent (Parent_Node)))
-
-         or else (Nkind (Parent_Node) in N_Aggregate | N_Extension_Aggregate
-                   and then not Is_Container_Aggregate (Parent_Node))
+         Parent_Is_Regular_Aggregate (Parent_Node)
 
          --  Allocator (see Convert_Aggr_In_Allocator)
 
@@ -6150,14 +6161,9 @@ package body Exp_Aggr is
       if
          --  Internal aggregates (transformed when expanding the parent),
          --  excluding container aggregates as these are transformed into
-         --  subprogram calls later. So far aggregates with self-references
-         --  are not supported if they appear in a conditional expression.
+         --  subprogram calls later.
 
-         (Nkind (Parent_Node) = N_Component_Association
-           and then not Is_Container_Aggregate (Parent (Parent_Node)))
-
-         or else (Nkind (Parent_Node) in N_Aggregate | N_Extension_Aggregate
-                   and then not Is_Container_Aggregate (Parent_Node))
+         Parent_Is_Regular_Aggregate (Parent_Node)
 
          --  Allocator (see Convert_Aggr_In_Allocator). Sliding cannot be done
          --  in place for the time being.
@@ -8703,6 +8709,8 @@ package body Exp_Aggr is
       --  generated by Make_Tag_Ctrl_Assignment). But, in the case of an array
       --  aggregate, controlled subaggregates are not considered because each
       --  of their individual elements will receive an adjustment of its own.
+      --  Moreover, the result of a function call need not be adjusted if it
+      --  has already been adjusted in the called function.
 
       if Finalization_OK
         and then not Is_Inherently_Limited_Type (Comp_Typ)
@@ -8711,6 +8719,8 @@ package body Exp_Aggr is
             and then Is_Array_Type (Comp_Typ)
             and then Needs_Finalization (Component_Type (Comp_Typ))
             and then Nkind (Unqualify (Init_Expr)) = N_Aggregate)
+        and then not (Back_End_Return_Slot
+                       and then Nkind (Init_Expr) = N_Function_Call)
       then
          Set_No_Finalize_Actions (Init_Stmt);
 
@@ -9437,6 +9447,24 @@ package body Exp_Aggr is
       return False;
    end Must_Slide;
 
+   ---------------------------------
+   -- Parent_Is_Regular_Aggregate --
+   ---------------------------------
+
+   function Parent_Is_Regular_Aggregate (Par : Node_Id) return Boolean is
+   begin
+      case Nkind (Par) is
+         when N_Component_Association =>
+            return Parent_Is_Regular_Aggregate (Parent (Par));
+
+         when N_Extension_Aggregate | N_Aggregate =>
+            return not Is_Container_Aggregate (Par);
+
+         when others =>
+            return False;
+      end case;
+   end Parent_Is_Regular_Aggregate;
+
    ---------------------
    -- Sort_Case_Table --
    ---------------------
diff --git a/gcc/ada/exp_aggr.ads b/gcc/ada/exp_aggr.ads
index c071e7604ad..0da0d8fe8a8 100644
--- a/gcc/ada/exp_aggr.ads
+++ b/gcc/ada/exp_aggr.ads
@@ -59,6 +59,10 @@ package Exp_Aggr is
    --  This is the case if it consists only of iterated component associations
    --  with iterator specifications, see RM 4.3.3(20.2/5).
 
+   function Parent_Is_Regular_Aggregate (Par : Node_Id) return Boolean;
+   --  Return True if Par is an aggregate that is not a container aggregate, or
+   --  a component association of such an aggregate.
+
    function Static_Array_Aggregate (N : Node_Id) return Boolean;
    --  N is an array aggregate that may have a component association with
    --  an others clause and a range. If bounds are static and the expressions
diff --git a/gcc/ada/exp_ch3.adb b/gcc/ada/exp_ch3.adb
index cf2238e9ee1..a4e6512410a 100644
--- a/gcc/ada/exp_ch3.adb
+++ b/gcc/ada/exp_ch3.adb
@@ -2675,9 +2675,10 @@ package body Exp_Ch3 is
 
          Exp_Q := Unqualify (Exp);
 
-         --  Adjust the component if controlled, except if it is an aggregate
-         --  that will be expanded inline (but note that the case of container
-         --  aggregates does require component adjustment), or a function call.
+         --  Adjust the component if controlled, except if the expression is an
+         --  aggregate that will be expanded inline (but note that the case of
+         --  container aggregates does require component adjustment), or else
+         --  a function call whose result is adjusted in the called function.
          --  Note that, when we don't inhibit component adjustment, the tag
          --  will be automatically inserted by Make_Tag_Ctrl_Assignment in the
          --  tagged case. Otherwise, we have to generate a tag assignment here.
@@ -2686,7 +2687,8 @@ package body Exp_Ch3 is
            and then (Nkind (Exp_Q) not in N_Aggregate | N_Extension_Aggregate
                       or else Is_Container_Aggregate (Exp_Q))
            and then not Is_Build_In_Place_Function_Call (Exp)
-           and then Nkind (Exp) /= N_Function_Call
+           and then not (Back_End_Return_Slot
+                          and then Nkind (Exp) = N_Function_Call)
          then
             Set_No_Finalize_Actions (First (Res));
 
diff --git a/gcc/ada/exp_ch4.adb b/gcc/ada/exp_ch4.adb
index 0cf5643ebe4..ce90f33aeda 100644
--- a/gcc/ada/exp_ch4.adb
+++ b/gcc/ada/exp_ch4.adb
@@ -240,6 +240,10 @@ package body Exp_Ch4 is
    --  skipped if the operation is done in Bignum mode but that's fine, since
    --  the Bignum call takes care of everything.
 
+   function New_Assign_Copy (N : Node_Id; Expr : Node_Id) return Node_Id;
+   --  N is an assignment statement. Return a copy of N with the same name but
+   --  expression changed to Expr and perform a couple of adjustments.
+
    procedure Narrow_Large_Operation (N : Node_Id);
    --  Try to compute the result of a large operation in a narrower type than
    --  its nominal type. This is mainly aimed at getting rid of operations done
@@ -727,7 +731,7 @@ package body Exp_Ch4 is
          --  adjust after the assignment but, in either case, we do not
          --  finalize before since the target is newly allocated memory.
 
-         if Nkind (Exp) = N_Function_Call then
+         if Back_End_Return_Slot and then Nkind (Exp) = N_Function_Call then
             Set_No_Ctrl_Actions (Assign);
          else
             Set_No_Finalize_Actions (Assign);
@@ -869,10 +873,7 @@ package body Exp_Ch4 is
       --  as qualified expression must be rewritten into the form expected by
       --  Expand_Container_Aggregate, resp. Two_Pass_Aggregate_Expansion.
 
-      if Nkind (Exp) = N_Aggregate
-        and then (Has_Aspect (T, Aspect_Aggregate)
-                   or else Is_Two_Pass_Aggregate (Exp))
-      then
+      if Is_Container_Aggregate (Exp) or else Is_Two_Pass_Aggregate (Exp) then
          Temp := Make_Temporary (Loc, 'P', N);
          Set_Analyzed (Exp, False);
          Insert_Action (N,
@@ -5191,6 +5192,8 @@ package body Exp_Ch4 is
       --  expansion until the (immediate) parent is rewritten as a return
       --  statement (or is already the return statement). Likewise if it is
       --  in the context of an object declaration that can be optimized.
+      --  Likewise if it is in the context of a regular agggregate and the
+      --  type should not be copied.
 
       if not Expansion_Delayed (N) then
          declare
@@ -5198,6 +5201,8 @@ package body Exp_Ch4 is
          begin
             if Nkind (Uncond_Par) = N_Simple_Return_Statement
               or else Is_Optimizable_Declaration (Uncond_Par)
+              or else (Parent_Is_Regular_Aggregate (Uncond_Par)
+                        and then not Is_Copy_Type (Typ))
             then
                Delay_Conditional_Expressions_Between (N, Uncond_Par);
             end if;
@@ -5377,17 +5382,7 @@ package body Exp_Ch4 is
             if Optimize_Assignment_Stmt then
                --  We directly copy the parent node to preserve its flags
 
-               Stmts := New_List (New_Copy (Par));
-               Set_Sloc       (First (Stmts), Alt_Loc);
-               Set_Name       (First (Stmts), New_Copy_Tree (Name (Par)));
-               Set_Expression (First (Stmts), Alt_Expr);
-
-               --  If the expression is itself a conditional expression whose
-               --  expansion has been delayed, analyze it again and expand it.
-
-               if Is_Delayed_Conditional_Expression (Alt_Expr) then
-                  Unanalyze_Delayed_Conditional_Expression (Alt_Expr);
-               end if;
+               Stmts := New_List (New_Assign_Copy (Par, Alt_Expr));
 
             --  Generate:
             --    return AX;
@@ -5805,8 +5800,9 @@ package body Exp_Ch4 is
       --  expansion until the (immediate) parent is rewritten as a return
       --  statement (or is already the return statement). Likewise if it is
       --  in the context of an object declaration that can be optimized.
-      --  Note that this deals with the case of the elsif part of the if
-      --  expression, if it exists.
+      --  Likewise if it is in the context of a regular agggregate and the
+      --  type should not be copied. Note that this deals with the case of
+      --  the elsif part of the if expression, if it exists.
 
       if not Expansion_Delayed (N) then
          declare
@@ -5814,6 +5810,8 @@ package body Exp_Ch4 is
          begin
             if Nkind (Uncond_Par) = N_Simple_Return_Statement
               or else Is_Optimizable_Declaration (Uncond_Par)
+              or else (Parent_Is_Regular_Aggregate (Uncond_Par)
+                        and then not Is_Copy_Type (Typ))
             then
                Delay_Conditional_Expressions_Between (N, Uncond_Par);
             end if;
@@ -5916,26 +5914,8 @@ package body Exp_Ch4 is
 
          --  We directly copy the parent node to preserve its flags
 
-         New_Then := New_Copy (Par);
-         Set_Sloc       (New_Then, Sloc (Thenx));
-         Set_Name       (New_Then, New_Copy_Tree (Name (Par)));
-         Set_Expression (New_Then, Relocate_Node (Thenx));
-
-         --  If the expression is itself a conditional expression whose
-         --  expansion has been delayed, analyze it again and expand it.
-
-         if Is_Delayed_Conditional_Expression (Expression (New_Then)) then
-            Unanalyze_Delayed_Conditional_Expression (Expression (New_Then));
-         end if;
-
-         New_Else := New_Copy (Par);
-         Set_Sloc       (New_Else, Sloc (Elsex));
-         Set_Name       (New_Else, New_Copy_Tree (Name (Par)));
-         Set_Expression (New_Else, Relocate_Node (Elsex));
-
-         if Is_Delayed_Conditional_Expression (Expression (New_Else)) then
-            Unanalyze_Delayed_Conditional_Expression (Expression (New_Else));
-         end if;
+         New_Then := New_Assign_Copy (Par, Relocate_Node (Thenx));
+         New_Else := New_Assign_Copy (Par, Relocate_Node (Elsex));
 
          If_Stmt :=
            Make_Implicit_If_Statement (N,
@@ -14223,6 +14203,39 @@ package body Exp_Ch4 is
       end if;
    end Narrow_Large_Operation;
 
+   ---------------------
+   -- New_Assign_Copy --
+   ---------------------
+
+   function New_Assign_Copy (N : Node_Id; Expr : Node_Id) return Node_Id is
+      New_N : constant Node_Id := New_Copy (N);
+
+   begin
+      Set_Sloc       (New_N, Sloc (Expr));
+      Set_Name       (New_N, New_Copy_Tree (Name (N)));
+      Set_Expression (New_N, Expr);
+
+      --  The result of a function call need not be adjusted if it has
+      --  already been adjusted in the called function.
+
+      if No_Finalize_Actions (New_N)
+        and then Back_End_Return_Slot
+        and then Nkind (Expr) = N_Function_Call
+      then
+         Set_No_Finalize_Actions (New_N, False);
+         Set_No_Ctrl_Actions (New_N);
+      end if;
+
+      --  If the expression is itself a conditional expression whose
+      --  expansion has been delayed, analyze it again and expand it.
+
+      if Is_Delayed_Conditional_Expression (Expr) then
+         Unanalyze_Delayed_Conditional_Expression (Expr);
+      end if;
+
+      return New_N;
+   end New_Assign_Copy;
+
    --------------------------------
    -- Optimize_Length_Comparison --
    --------------------------------
diff --git a/gcc/ada/exp_ch6.adb b/gcc/ada/exp_ch6.adb
index 2a246adbb8a..26302baad64 100644
--- a/gcc/ada/exp_ch6.adb
+++ b/gcc/ada/exp_ch6.adb
@@ -289,8 +289,8 @@ package body Exp_Ch6 is
    --  denoted by the call needs finalization in the current subprogram, which
    --  excludes return statements, and is not identified with another object
    --  that will be finalized, which excludes (statically) declared objects,
-   --  dynamically allocated objects, and targets of assignments that are done
-   --  directly (without intermediate temporaries).
+   --  dynamically allocated objects, components of aggregates, and targets of
+   --  assignments that are done directly (without intermediate temporaries).
 
    procedure Expand_Non_Function_Return (N : Node_Id);
    --  Expand a simple return statement found in a procedure body, entry body,
@@ -5365,7 +5365,7 @@ package body Exp_Ch6 is
       --  to copy/readjust/finalize, we can just pass the value through (see
       --  Expand_N_Simple_Return_Statement), and thus no attachment is needed.
       --  Note that simple return statements are distributed into conditional
-      --  expressions but we may be invoked before this distribution is done.
+      --  expressions, but we may be invoked before this distribution is done.
 
       if Nkind (Uncond_Par) = N_Simple_Return_Statement then
          return;
@@ -5386,7 +5386,7 @@ package body Exp_Ch6 is
          end if;
 
       --  Note that object declarations are also distributed into conditional
-      --  expressions but we may be invoked before this distribution is done.
+      --  expressions, but we may be invoked before this distribution is done.
 
       elsif Nkind (Uncond_Par) = N_Object_Declaration then
          return;
@@ -5402,6 +5402,16 @@ package body Exp_Ch6 is
          return;
       end if;
 
+      --  Another optimization: if the returned value is used to initialize the
+      --  component of an aggregate, then no need to copy/readjust/finalize, we
+      --  can initialize it in place. Note that assignments for aggregates are
+      --  also distributed into conditional expressions, but we may be invoked
+      --  before this distribution is done.
+
+      if Parent_Is_Regular_Aggregate (Uncond_Par) then
+         return;
+      end if;
+
       --  Avoid expansion to catch the error when the function call is on the
       --  left-hand side of an assignment. Likewise if it is on the right-hand
       --  side and no controlling actions will be performed for the assignment,
diff --git a/gcc/ada/sem_util.adb b/gcc/ada/sem_util.adb
index a62b7013d70..5322dea410b 100644
--- a/gcc/ada/sem_util.adb
+++ b/gcc/ada/sem_util.adb
@@ -2709,6 +2709,15 @@ package body Sem_Util is
 
                   Append_Unique_Elmt (N, Identifiers_List);
                end if;
+
+            --  Skip attribute references created by the compiler, typically
+            --  'Constrained applied to one of the writable actuals, to avoid
+            --  spurious errors.
+
+            elsif Nkind (N) = N_Attribute_Reference
+              and then not Comes_From_Source (N)
+            then
+               return Skip;
             end if;
 
             return OK;
-- 
2.43.0

Reply via email to