diff mbox

[Ada] Ada2020 feature: partial aggregate notation (Delta aggregates).

Message ID 20170123120740.GA85185@adacore.com
State New
Headers show

Commit Message

Arnaud Charlet Jan. 23, 2017, 12:07 p.m. UTC
This patch implements AI12-0127, which describes a new constructor for
aggregate in terms of an existing record or array aggregate and a series
of component-wise modifications of its value.

Executing:

   gnatmake -gnat2020 -q a2020
   a2020


must yield:

 1
 2
 9
 16
 25

---
with Text_IO; use Text_IO;
procedure A2020 is
   type Powers is array (1..5) of integer;
   type Table is array (1..4) of Powers;
   Thing : Table;
begin
   Thing := (others => (for I in Powers'range => I));
   Thing (2) := (@ with delta for J in 3..5 => @(j) ** 2);
   for I in Powers'range loop
      Put_Line (Integer'Image (Thing (2)(I)));
   end loop;
end;

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

2017-01-23  Ed Schonberg  <schonberg@adacore.com>

	* par-ch4.adb (P_Aggregate_Or_Parent_Expr): Recognize delta
	aggregate construct.
	(P_Record_Or_Array_Component_Association): An array aggregate
	can start with an Iterated_Component_Association.
	* scng.adb: Modify error message on improper use of @ in earlier
	versions of the language.
	* sinfo.ads: New node kind N_Delta_Aggregate.
	* sinfo.adb: An N_Delta_Aggregate has component associations and
	an expression.
	* sem_res.adb (Resolve): Call Resolve_Delta_Aggregate.
	* sem_aggr.ads, sem_aggr.adb (Resolve_Iterated_Component_Association):
	Create a new index for each one of the choices in the association,
	to prevent spurious homonyms in the scope.
	(Resolve_Delta_Aggregate): New.
	* sem.adb: An N_Delta_Aggregate is analyzed like an aggregate.
	* exp_util.adb (Insert_Actions): Take into account
	N_Delta_Aggregate.
	* exp_aggr.ads: New procedure Expand_N_Delta_Aggregate.
	* exp_aggr.adb: New procedure Expand_N_Delta_Aggregate,
	and local procedures Expand_Delta_Array_Aggregate and
	expand_Delta_Record_Aggregate.
	* sprint.adb: Handle N_Delta_Aggregate.

Comments

Jakub Jelinek Jan. 23, 2017, 3:05 p.m. UTC | #1
On Mon, Jan 23, 2017 at 07:07:40AM -0500, Arnaud Charlet wrote:
> This patch implements AI12-0127, which describes a new constructor for
> aggregate in terms of an existing record or array aggregate and a series
> of component-wise modifications of its value.

Please note that the trunk is in stage4, only regression bugfixes and
documentation fixes are allowed.  It is possible to seek exceptions,
but committing dozens of changes that do not fix regressions or
documentation is not desirable right now.

	Jakub
Arnaud Charlet Jan. 23, 2017, 4:06 p.m. UTC | #2
> Please note that the trunk is in stage4, only regression bugfixes and
> documentation fixes are allowed.  It is possible to seek exceptions,
> but committing dozens of changes that do not fix regressions or
> documentation is not desirable right now.

Yes I understand, although Ada isn't considered "part of the critical
release criterias", but point taken.

Arno
diff mbox

Patch

Index: sem_aggr.adb
===================================================================
--- sem_aggr.adb	(revision 244784)
+++ sem_aggr.adb	(working copy)
@@ -1678,11 +1678,17 @@ 
          Set_Etype  (Ent, Standard_Void_Type);
          Set_Parent (Ent, Parent (N));
 
-         Enter_Name (Id);
-         Set_Etype (Id, Index_Typ);
-         Set_Ekind (Id, E_Variable);
-         Set_Scope (Id, Ent);
+         --  Decorate the index variable in the current scope. The association
+         --  may have several choices, each one leading to a loop, so we create
+         --  this variable only once to prevent homonyms in this scope.
 
+         if No (Scope (Id)) then
+            Enter_Name (Id);
+            Set_Etype (Id, Index_Typ);
+            Set_Ekind (Id, E_Variable);
+            Set_Scope (Id, Ent);
+         end if;
+
          Push_Scope (Ent);
          Dummy := Resolve_Aggr_Expr (Expression (N), False);
          End_Scope;
@@ -2082,6 +2088,9 @@ 
                      return Failure;
                   end if;
 
+               elsif Nkind (Assoc) = N_Iterated_Component_Association then
+                  null;   --  handled above, in a loop context.
+
                elsif not Resolve_Aggr_Expr
                            (Expression (Assoc), Single_Elmt => Single_Choice)
                then
@@ -2726,6 +2735,143 @@ 
       return Success;
    end Resolve_Array_Aggregate;
 
+   -----------------------------
+   -- Resolve_Delta_Aggregate --
+   -----------------------------
+
+   procedure Resolve_Delta_Aggregate (N : Node_Id; Typ : Entity_Id) is
+      Base       : constant Node_Id   := Expression (N);
+      Deltas     : constant List_Id   := Component_Associations (N);
+      Assoc      : Node_Id;
+      Choice     : Node_Id;
+      Comp_Type  : Entity_Id;
+      Index_Type : Entity_Id;
+
+      function Get_Component_Type (Nam : Node_Id) return Entity_Id;
+
+      ------------------------
+      -- Get_Component_Type --
+      ------------------------
+
+      function Get_Component_Type (Nam : Node_Id) return Entity_Id is
+         Comp : Entity_Id;
+
+      begin
+         Comp := First_Entity (Typ);
+
+         while Present (Comp) loop
+            if Chars (Comp) = Chars (Nam) then
+               if Ekind (Comp) = E_Discriminant then
+                  Error_Msg_N ("delta cannot apply to discriminant", Nam);
+               end if;
+
+               return Etype (Comp);
+            end if;
+
+            Comp := Next_Entity (Comp);
+         end loop;
+
+         Error_Msg_NE ("type& has no component with this name", Nam, Typ);
+         return Any_Type;
+      end Get_Component_Type;
+
+   begin
+      if not Is_Composite_Type (Typ) then
+         Error_Msg_N ("not a composite type", N);
+      end if;
+
+      Analyze_And_Resolve (Base, Typ);
+      if Is_Array_Type (Typ) then
+         Index_Type := Etype (First_Index (Typ));
+         Assoc := First (Deltas);
+         while Present (Assoc) loop
+            if Nkind (Assoc) = N_Iterated_Component_Association then
+               Choice := First (Choice_List (Assoc));
+               while Present (Choice) loop
+                  if Nkind (Choice) = N_Others_Choice then
+                     Error_Msg_N
+                       ("others not allowed in delta aggregate", Choice);
+
+                  else
+                     Analyze_And_Resolve (Choice, Index_Type);
+                  end if;
+
+                  Next (Choice);
+               end loop;
+
+               declare
+                  Id  : constant Entity_Id  := Defining_Identifier (Assoc);
+                  Ent : constant Entity_Id  :=
+                    New_Internal_Entity
+                      (E_Loop, Current_Scope, Sloc (Assoc), 'L');
+
+               begin
+                  Set_Etype  (Ent, Standard_Void_Type);
+                  Set_Parent (Ent, Assoc);
+
+                  if No (Scope (Id)) then
+                     Enter_Name (Id);
+                     Set_Etype (Id, Index_Type);
+                     Set_Ekind (Id, E_Variable);
+                     Set_Scope (Id, Ent);
+                  end if;
+
+                  Push_Scope (Ent);
+                  Analyze_And_Resolve
+                    (New_Copy_Tree (Expression (Assoc)), Component_Type (Typ));
+                  End_Scope;
+               end;
+
+            else
+               Choice := First (Choice_List (Assoc));
+               while Present (Choice) loop
+                  if Nkind (Choice) = N_Others_Choice then
+                     Error_Msg_N
+                       ("others not allowed in delta aggregate", Choice);
+
+                  else
+                     Analyze (Choice);
+                     if Is_Entity_Name (Choice)
+                       and then Is_Type (Entity (Choice))
+                     then
+                        --  Choice covers a range of values.
+                        if Base_Type (Entity (Choice)) /=
+                           Base_Type (Index_Type)
+                        then
+                           Error_Msg_NE ("choice does mat match index type of",
+                             Choice, Typ);
+                        end if;
+                     else
+                        Resolve (Choice, Index_Type);
+                     end if;
+                  end if;
+
+                  Next (Choice);
+               end loop;
+
+               Analyze_And_Resolve (Expression (Assoc), Component_Type (Typ));
+            end if;
+
+            Next (Assoc);
+         end loop;
+
+      else
+         Assoc := First (Deltas);
+         while Present (Assoc) loop
+            Choice := First (Choice_List (Assoc));
+            while Present (Choice) loop
+               Comp_Type := Get_Component_Type (Choice);
+               Next (Choice);
+            end loop;
+
+            Analyze_And_Resolve (Expression (Assoc), Comp_Type);
+            Next (Assoc);
+         end loop;
+      end if;
+
+      Set_Etype (N, Typ);
+   end Resolve_Delta_Aggregate;
+
    ---------------------------------
    -- Resolve_Extension_Aggregate --
    ---------------------------------
Index: sem_aggr.ads
===================================================================
--- sem_aggr.ads	(revision 244773)
+++ sem_aggr.ads	(working copy)
@@ -6,7 +6,7 @@ 
 --                                                                          --
 --                                 S p e c                                  --
 --                                                                          --
---          Copyright (C) 1992-2011, Free Software Foundation, Inc.         --
+--          Copyright (C) 1992-2016, Free Software Foundation, Inc.         --
 --                                                                          --
 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
 -- terms of the  GNU General Public License as published  by the Free Soft- --
@@ -30,6 +30,7 @@ 
 
 package Sem_Aggr is
 
+   procedure Resolve_Delta_Aggregate     (N : Node_Id; Typ : Entity_Id);
    procedure Resolve_Aggregate           (N : Node_Id; Typ : Entity_Id);
    procedure Resolve_Extension_Aggregate (N : Node_Id; Typ : Entity_Id);
 
Index: exp_util.adb
===================================================================
--- exp_util.adb	(revision 244792)
+++ exp_util.adb	(working copy)
@@ -5831,6 +5831,7 @@ 
                | N_Defining_Operator_Symbol
                | N_Defining_Program_Unit_Name
                | N_Delay_Alternative
+               | N_Delta_Aggregate
                | N_Delta_Constraint
                | N_Derived_Type_Definition
                | N_Designator
Index: sinfo.adb
===================================================================
--- sinfo.adb	(revision 244783)
+++ sinfo.adb	(working copy)
@@ -466,6 +466,7 @@ 
    begin
       pragma Assert (False
         or else NT (N).Nkind = N_Aggregate
+        or else NT (N).Nkind = N_Delta_Aggregate
         or else NT (N).Nkind = N_Extension_Aggregate);
       return List2 (N);
    end Component_Associations;
@@ -1265,6 +1266,7 @@ 
         or else NT (N).Nkind = N_Component_Declaration
         or else NT (N).Nkind = N_Delay_Relative_Statement
         or else NT (N).Nkind = N_Delay_Until_Statement
+        or else NT (N).Nkind = N_Delta_Aggregate
         or else NT (N).Nkind = N_Discriminant_Association
         or else NT (N).Nkind = N_Discriminant_Specification
         or else NT (N).Nkind = N_Exception_Declaration
@@ -3775,6 +3777,7 @@ 
    begin
       pragma Assert (False
         or else NT (N).Nkind = N_Aggregate
+        or else NT (N).Nkind = N_Delta_Aggregate
         or else NT (N).Nkind = N_Extension_Aggregate);
       Set_List2_With_Parent (N, Val);
    end Set_Component_Associations;
@@ -4565,6 +4568,7 @@ 
         or else NT (N).Nkind = N_Component_Declaration
         or else NT (N).Nkind = N_Delay_Relative_Statement
         or else NT (N).Nkind = N_Delay_Until_Statement
+        or else NT (N).Nkind = N_Delta_Aggregate
         or else NT (N).Nkind = N_Discriminant_Association
         or else NT (N).Nkind = N_Discriminant_Specification
         or else NT (N).Nkind = N_Exception_Declaration
Index: sinfo.ads
===================================================================
--- sinfo.ads	(revision 244788)
+++ sinfo.ads	(working copy)
@@ -4133,6 +4133,15 @@ 
       --  Note that Box_Present is always False, but it is intentionally added
       --  for completeness.
 
+      ----------------------------
+      --  4.3.4 Delta Aggregate --
+      ----------------------------
+
+      --  N_Delta_Aggregate
+      --  Sloc points to left parenthesis
+      --  Expression (Node3)
+      --  Component_Associations (List2)
+
       --------------------------------------------------
       -- 4.4  Expression/Relation/Term/Factor/Primary --
       --------------------------------------------------
@@ -8475,6 +8484,7 @@ 
       N_Aggregate,
       N_Allocator,
       N_Case_Expression,
+      N_Delta_Aggregate,
       N_Extension_Aggregate,
       N_Raise_Expression,
       N_Range,
@@ -11524,6 +11534,13 @@ 
         4 => True,    --  Discrete_Choices (List4)
         5 => False),  --  unused
 
+     N_Delta_Aggregate =>
+       (1 => False,   --  Expressions (List1)
+        2 => True,    --  Component_Associations (List2)
+        3 => True,    --  Expression (Node3)
+        4 => False,   --  Unused
+        5 => False),  --  Etype (Node5-Sem)
+
      N_Extension_Aggregate =>
        (1 => True,    --  Expressions (List1)
         2 => True,    --  Component_Associations (List2)
Index: scng.adb
===================================================================
--- scng.adb	(revision 244788)
+++ scng.adb	(working copy)
@@ -1613,7 +1613,7 @@ 
 
          when '@' =>
             if Ada_Version < Ada_2020 then
-               Error_Illegal_Character;
+               Error_Msg ("target_name is an Ada2020 feature", Scan_Ptr);
                Scan_Ptr := Scan_Ptr + 1;
 
             else
Index: sem.adb
===================================================================
--- sem.adb	(revision 244783)
+++ sem.adb	(working copy)
@@ -196,6 +196,9 @@ 
          when N_Delay_Relative_Statement =>
             Analyze_Delay_Relative (N);
 
+         when N_Delta_Aggregate =>
+            Analyze_Aggregate (N);
+
          when N_Delay_Until_Statement =>
             Analyze_Delay_Until (N);
 
Index: par-ch4.adb
===================================================================
--- par-ch4.adb	(revision 244788)
+++ par-ch4.adb	(working copy)
@@ -1381,7 +1381,7 @@ 
             Expr_Node := P_Expression_Or_Range_Attribute_If_OK;
          end if;
 
-         --  Extension aggregate
+         --  Extension or Delta aggregate
 
          if Token = Tok_With then
             if Nkind (Expr_Node) = N_Attribute_Reference
@@ -1395,10 +1395,19 @@ 
                Error_Msg_SC ("(Ada 83) extension aggregate not allowed");
             end if;
 
-            Aggregate_Node := New_Node (N_Extension_Aggregate, Lparen_Sloc);
-            Set_Ancestor_Part (Aggregate_Node, Expr_Node);
             Scan; -- past WITH
+            if Token = Tok_Delta then
+               Scan; -- past DELTA
+               Aggregate_Node := New_Node (N_Delta_Aggregate, Lparen_Sloc);
+               Set_Expression (Aggregate_Node, Expr_Node);
+               Expr_Node := Empty;
+               goto Aggregate;
 
+            else
+               Aggregate_Node := New_Node (N_Extension_Aggregate, Lparen_Sloc);
+               Set_Ancestor_Part (Aggregate_Node, Expr_Node);
+            end if;
+
             --  Deal with WITH NULL RECORD case
 
             if Token = Tok_Null then
@@ -1586,7 +1595,11 @@ 
       --  All component associations (positional and named) have been scanned
 
       T_Right_Paren;
-      Set_Expressions (Aggregate_Node, Expr_List);
+
+      if Nkind (Aggregate_Node) /= N_Delta_Aggregate then
+         Set_Expressions (Aggregate_Node, Expr_List);
+      end if;
+
       Set_Component_Associations (Aggregate_Node, Assoc_List);
       return Aggregate_Node;
    end P_Aggregate_Or_Paren_Expr;
@@ -1622,6 +1635,10 @@ 
       Assoc_Node : Node_Id;
 
    begin
+      if Token = Tok_For then
+         return P_Iterated_Component_Association;
+      end if;
+
       Assoc_Node := New_Node (N_Component_Association, Token_Ptr);
       Set_Choices (Assoc_Node, P_Discrete_Choice_List);
       Set_Sloc (Assoc_Node, Token_Ptr);
Index: sem_res.adb
===================================================================
--- sem_res.adb	(revision 244783)
+++ sem_res.adb	(working copy)
@@ -2870,6 +2870,9 @@ 
             when N_Character_Literal =>
                Resolve_Character_Literal         (N, Ctx_Type);
 
+            when N_Delta_Aggregate =>
+               Resolve_Delta_Aggregate           (N, Ctx_Type);
+
             when N_Expanded_Name =>
                Resolve_Entity_Name               (N, Ctx_Type);
 
Index: exp_aggr.adb
===================================================================
--- exp_aggr.adb	(revision 244773)
+++ exp_aggr.adb	(working copy)
@@ -84,6 +84,9 @@ 
    --  expression with actions, which becomes the Initialization_Statements for
    --  Obj.
 
+   procedure Expand_Delta_Array_Aggregate  (N : Node_Id; Deltas : List_Id);
+   procedure Expand_Delta_Record_Aggregate (N : Node_Id; Deltas : List_Id);
+
    function Has_Default_Init_Comps (N : Node_Id) return Boolean;
    --  N is an aggregate (record or array). Checks the presence of default
    --  initialization (<>) in any component (Ada 2005: AI-287).
@@ -6436,7 +6439,152 @@ 
          return;
    end Expand_N_Aggregate;
 
+   ------------------------------
+   -- Expand_N_Delta_Aggregate --
+   ------------------------------
+
+   procedure Expand_N_Delta_Aggregate (N : Node_Id) is
+      Loc :  constant Source_Ptr := Sloc (N);
+      Temp : constant Entity_Id := Make_Temporary (Loc, 'T');
+      Typ  : constant Entity_Id := Etype (N);
+      Decl : Node_Id;
+
+   begin
+      Decl := Make_Object_Declaration (Loc,
+         Defining_Identifier => Temp,
+         Object_Definition => New_Occurrence_Of (Typ, Loc),
+         Expression => New_Copy_Tree (Expression (N)));
+
+      if Is_Array_Type (Etype (N)) then
+         Expand_Delta_Array_Aggregate (N, New_List (Decl));
+      else
+         Expand_Delta_Record_Aggregate (N, New_List (Decl));
+      end if;
+   end Expand_N_Delta_Aggregate;
+
    ----------------------------------
+   -- Expand_Delta_Array_Aggregate --
+   ----------------------------------
+
+   procedure Expand_Delta_Array_Aggregate (N : Node_Id; Deltas : List_Id) is
+      Loc    : constant Source_Ptr := Sloc (N);
+      Temp   : constant Entity_Id  := Defining_Identifier (First (Deltas));
+      Assoc  : Node_Id;
+      Choice : Node_Id;
+      function Generate_Loop (C : Node_Id) return Node_Id;
+      --  Generate a loop containing individual component assignments for
+      --  choices that are ranges, subtype indications, subtype names, and
+      --  iterated component associations.
+
+      function Generate_Loop (C : Node_Id) return Node_Id is
+         Sl : constant Source_Ptr := Sloc (C);
+         Ix : Entity_Id;
+
+      begin
+         if Nkind (Parent (C)) = N_Iterated_Component_Association then
+            Ix :=
+              Make_Defining_Identifier (Loc,
+                Chars => (Chars (Defining_Identifier (Parent (C)))));
+         else
+            Ix := Make_Temporary (Sl, 'I');
+         end if;
+
+         return
+           Make_Loop_Statement (Loc,
+              Iteration_Scheme => Make_Iteration_Scheme (Sl,
+                Loop_Parameter_Specification =>
+                Make_Loop_Parameter_Specification (Sl,
+                  Defining_Identifier => Ix,
+                  Discrete_Subtype_Definition => New_Copy_Tree (C))),
+              End_Label => Empty,
+              Statements =>
+                New_List (
+                  Make_Assignment_Statement (Sl,
+                    Name       => Make_Indexed_Component (Sl,
+                      Prefix      => New_Occurrence_Of (Temp, Sl),
+                      Expressions => New_List (New_Occurrence_Of (Ix, Sl))),
+                    Expression => New_Copy_Tree (Expression (Assoc)))));
+      end Generate_Loop;
+
+   begin
+      Assoc := First (Component_Associations (N));
+      while Present (Assoc) loop
+         Choice := First (Choice_List (Assoc));
+         if Nkind (Assoc) = N_Iterated_Component_Association then
+            while Present (Choice) loop
+               Append_To (Deltas, Generate_Loop (Choice));
+               Next (Choice);
+            end loop;
+
+         else
+            while Present (Choice) loop
+
+               --  Choice can be given by a range, a subtype indication, a
+               --  subtype name, a scalar value, or an entity.
+
+               if Nkind (Choice) = N_Range
+                 or else (Is_Entity_Name (Choice)
+                   and then Is_Type (Entity (Choice)))
+               then
+                  Append_To (Deltas, Generate_Loop (Choice));
+
+               elsif Nkind (Choice) = N_Subtype_Indication then
+                  Append_To (Deltas,
+                    Generate_Loop (Range_Expression (Constraint (Choice))));
+
+               else
+                  Append_To (Deltas,
+                     Make_Assignment_Statement (Sloc (Choice),
+                       Name => Make_Indexed_Component (Sloc (Choice),
+                         Prefix => New_Occurrence_Of (Temp, Loc),
+                         Expressions => New_List (New_Copy_Tree (Choice))),
+                       Expression => New_Copy_Tree (Expression (Assoc))));
+               end if;
+
+               Next (Choice);
+            end loop;
+         end if;
+
+         Next (Assoc);
+      end loop;
+
+      Insert_Actions (N, Deltas);
+      Rewrite (N, New_Occurrence_Of (Temp, Loc));
+   end Expand_Delta_Array_Aggregate;
+
+   -----------------------------------
+   -- Expand_Delta_Record_Aggregate --
+   -----------------------------------
+
+   procedure Expand_Delta_Record_Aggregate (N : Node_Id; Deltas : List_Id) is
+      Loc    : constant Source_Ptr := Sloc (N);
+      Temp   : constant Entity_Id  := Defining_Identifier (First (Deltas));
+      Assoc  : Node_Id;
+      Choice : Node_Id;
+
+   begin
+      Assoc := First (Component_Associations (N));
+
+      while Present (Assoc) loop
+         Choice := First (Choice_List (Assoc));
+         while Present (Choice) loop
+            Append_To (Deltas,
+               Make_Assignment_Statement (Sloc (Choice),
+                 Name => Make_Selected_Component (Sloc (Choice),
+                   Prefix => New_Occurrence_Of (Temp, Loc),
+                   Selector_Name => Make_Identifier (Loc, Chars (Choice))),
+                 Expression => New_Copy_Tree (Expression (Assoc))));
+            Next (Choice);
+         end loop;
+
+         Next (Assoc);
+      end loop;
+
+      Insert_Actions (N, Deltas);
+      Rewrite (N, New_Occurrence_Of (Temp, Loc));
+   end Expand_Delta_Record_Aggregate;
+
+   ----------------------------------
    -- Expand_N_Extension_Aggregate --
    ----------------------------------
 
Index: exp_aggr.ads
===================================================================
--- exp_aggr.ads	(revision 244773)
+++ exp_aggr.ads	(working copy)
@@ -28,6 +28,7 @@ 
 package Exp_Aggr is
 
    procedure Expand_N_Aggregate           (N : Node_Id);
+   procedure Expand_N_Delta_Aggregate     (N : Node_Id);
    procedure Expand_N_Extension_Aggregate (N : Node_Id);
 
    function Is_Delayed_Aggregate (N : Node_Id) return Boolean;
Index: sprint.adb
===================================================================
--- sprint.adb	(revision 244783)
+++ sprint.adb	(working copy)
@@ -1775,6 +1775,13 @@ 
                Write_Indent_Str (";");
             end if;
 
+         when N_Delta_Aggregate =>
+            Write_Str_With_Col_Check_Sloc ("(");
+            Sprint_Node (Expression (Node));
+            Write_Str_With_Col_Check (" with delta ");
+            Sprint_Comma_List (Component_Associations (Node));
+            Write_Char (')');
+
          when N_Extension_Aggregate =>
             Write_Str_With_Col_Check_Sloc ("(");
             Sprint_Node (Ancestor_Part (Node));