This patch implements the following SPARK 2014 rule:

   4.4.1 (1) - The box symbol, <>, may not appear in any expression appearing
   in an update expression.

The patch also cleans up the analysis of attribute 'Update.

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

--  box_update.ads

package Box_Update with SPARK_Mode => On is
   type I  is range 1 .. 5;
   type T1 is range 1 .. 10
     with Default_Value => 5;

   type A1 is array (I) of T1;

   procedure Init1 (X : out A1);
end Box_Update;

--  box_update.adb

package body Box_Update with SPARK_Mode => On is
   procedure Init1 (X : out A1) is
      T : constant A1 := A1'(1 => 6, others => <>);
   begin
      X := T'Update(1 => <>);
   end Init1;
end Box_Update;

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

$ gcc -c box_update.adb
box_update.adb:5:23: default initialization not allowed in attribute "Update"

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

2014-07-30  Hristian Kirtchev  <kirtc...@adacore.com>

        * sem_attr.adb (Analyze_Array_Component_Update): New routine.
        (Analyze_Attribute): Major cleanup of attribute
        'Update. The logic is now split into two distinct routines
        depending on the type of the prefix. The use of <> is now illegal
        in attribute 'Update.
        (Analyze_Record_Component_Update): New routine.
        (Check_Component_Reference): Removed.
        (Resolve_Attribute): Remove the return statement and ??? comment
        following the processing for attribute 'Update. As a result,
        the attribute now freezes its prefix.

Index: sem_attr.adb
===================================================================
--- sem_attr.adb        (revision 213212)
+++ sem_attr.adb        (working copy)
@@ -6220,69 +6220,158 @@
       ------------
 
       when Attribute_Update => Update : declare
+         Common_Typ : Entity_Id;
+         --  The common type of a multiple component update for a record
+
          Comps : Elist_Id := No_Elist;
-         Expr  : Node_Id;
+         --  A list used in the resolution of a record update. It contains the
+         --  entities of all record components processed so far.
 
-         procedure Check_Component_Reference
-           (Comp : Entity_Id;
-            Typ  : Entity_Id);
-         --  Comp is a record component (possibly a discriminant) and Typ is a
-         --  record type. Determine whether Comp is a legal component of Typ.
-         --  Emit an error if Comp mentions a discriminant or is not a unique
-         --  component reference in the update aggregate.
+         procedure Analyze_Array_Component_Update (Assoc : Node_Id);
+         --  Analyze and resolve array_component_association Assoc against the
+         --  index of array type P_Type.
 
-         -------------------------------
-         -- Check_Component_Reference --
-         -------------------------------
+         procedure Analyze_Record_Component_Update (Comp : Node_Id);
+         --  Analyze and resolve record_component_association Comp against
+         --  record type P_Type.
 
-         procedure Check_Component_Reference
-           (Comp : Entity_Id;
-            Typ  : Entity_Id)
-         is
-            Comp_Name : constant Name_Id := Chars (Comp);
+         ------------------------------------
+         -- Analyze_Array_Component_Update --
+         ------------------------------------
 
-            function Is_Duplicate_Component return Boolean;
-            --  Determine whether component Comp already appears in list Comps
+         procedure Analyze_Array_Component_Update (Assoc : Node_Id) is
+            Expr      : Node_Id;
+            High      : Node_Id;
+            Index     : Node_Id;
+            Index_Typ : Entity_Id;
+            Low       : Node_Id;
 
-            ----------------------------
-            -- Is_Duplicate_Component --
-            ----------------------------
+         begin
+            --  The current association contains a sequence of indexes denoting
+            --  an element of a multidimensional array:
 
-            function Is_Duplicate_Component return Boolean is
-               Comp_Elmt : Elmt_Id;
+            --    (Index_1, ..., Index_N)
 
-            begin
-               if Present (Comps) then
-                  Comp_Elmt := First_Elmt (Comps);
-                  while Present (Comp_Elmt) loop
-                     if Chars (Node (Comp_Elmt)) = Comp_Name then
-                        return True;
+            --  Examine each individual index and resolve it against the proper
+            --  index type of the array.
+
+            if Nkind (First (Choices (Assoc))) = N_Aggregate then
+               Expr := First (Choices (Assoc));
+               while Present (Expr) loop
+
+                  --  The use of others is illegal (SPARK RM 4.4.1(12))
+
+                  if Nkind (Expr) = N_Others_Choice then
+                     Error_Attr
+                       ("others choice not allowed in attribute %", Expr);
+
+                  --  Otherwise analyze and resolve all indexes
+
+                  else
+                     Index     := First (Expressions (Expr));
+                     Index_Typ := First_Index (P_Type);
+                     while Present (Index) and then Present (Index_Typ) loop
+                        Analyze_And_Resolve (Index, Etype (Index_Typ));
+                        Next (Index);
+                        Next_Index (Index_Typ);
+                     end loop;
+
+                     --  Detect a case where the association either lacks an
+                     --  index or contains an extra index.
+
+                     if Present (Index) or else Present (Index_Typ) then
+                        Error_Msg_N
+                          ("dimension mismatch in index list", Assoc);
                      end if;
+                  end if;
 
-                     Next_Elmt (Comp_Elmt);
-                  end loop;
+                  Next (Expr);
+               end loop;
+
+            --  The current association denotes either a single component or a
+            --  range of components of a one dimensional array:
+
+            --    1, 2 .. 5
+
+            --  Resolve the index or its high and low bounds (if range) against
+            --  the proper index type of the array.
+
+            else
+               Index     := First (Choices (Assoc));
+               Index_Typ := First_Index (P_Type);
+
+               if Present (Next_Index (Index_Typ)) then
+                  Error_Msg_N ("too few subscripts in array reference", Assoc);
                end if;
 
-               return False;
-            end Is_Duplicate_Component;
+               while Present (Index) loop
 
-            --  Local variables
+                  --  The use of others is illegal (SPARK RM 4.4.1(12))
 
+                  if Nkind (Index) = N_Others_Choice then
+                     Error_Attr
+                       ("others choice not allowed in attribute %", Index);
+
+                  --  The index denotes a range of elements
+
+                  elsif Nkind (Index) = N_Range then
+                     Low  := Low_Bound  (Index);
+                     High := High_Bound (Index);
+
+                     Analyze_And_Resolve (Low,  Etype (Index_Typ));
+                     Analyze_And_Resolve (High, Etype (Index_Typ));
+
+                     --  Add a range check to ensure that the bounds of the
+                     --  range are within the index type when this cannot be
+                     --  determined statically.
+
+                     if not Is_OK_Static_Expression (Low) then
+                        Set_Do_Range_Check (Low);
+                     end if;
+
+                     if not Is_OK_Static_Expression (High) then
+                        Set_Do_Range_Check (High);
+                     end if;
+
+                  --  Otherwise the index denotes a single element
+
+                  else
+                     Analyze_And_Resolve (Index, Etype (Index_Typ));
+
+                     --  Add a range check to ensure that the index is within
+                     --  the index type when it is not possible to determine
+                     --  this statically.
+
+                     if not Is_OK_Static_Expression (Index) then
+                        Set_Do_Range_Check (Index);
+                     end if;
+                  end if;
+
+                  Next (Index);
+               end loop;
+            end if;
+         end Analyze_Array_Component_Update;
+
+         -------------------------------------
+         -- Analyze_Record_Component_Update --
+         -------------------------------------
+
+         procedure Analyze_Record_Component_Update (Comp : Node_Id) is
+            Comp_Name     : constant Name_Id := Chars (Comp);
+            Base_Typ      : Entity_Id;
             Comp_Or_Discr : Entity_Id;
 
-         --  Start of processing for Check_Component_Reference
-
          begin
             --  Find the discriminant or component whose name corresponds to
             --  Comp. A simple character comparison is sufficient because all
             --  visible names within a record type are unique.
 
-            Comp_Or_Discr := First_Entity (Typ);
+            Comp_Or_Discr := First_Entity (P_Type);
             while Present (Comp_Or_Discr) loop
                if Chars (Comp_Or_Discr) = Comp_Name then
 
-                  --  Record component entity and type in the given aggregate
-                  --  choice, for subsequent resolution.
+                  --  Decorate the component reference by setting its entity
+                  --  and type for resolution purposes.
 
                   Set_Entity (Comp, Comp_Or_Discr);
                   Set_Etype  (Comp, Etype (Comp_Or_Discr));
@@ -6292,7 +6381,7 @@
                Comp_Or_Discr := Next_Entity (Comp_Or_Discr);
             end loop;
 
-            --  Diagnose possible illegal references
+            --  Diagnose an illegal reference
 
             if Present (Comp_Or_Discr) then
                if Ekind (Comp_Or_Discr) = E_Discriminant then
@@ -6300,8 +6389,8 @@
                     ("attribute % may not modify record discriminants", Comp);
 
                else pragma Assert (Ekind (Comp_Or_Discr) = E_Component);
-                  if Is_Duplicate_Component then
-                     Error_Msg_NE ("component & already updated", Comp, Comp);
+                  if Contains (Comps, Comp_Or_Discr) then
+                     Error_Msg_N ("component & already updated", Comp);
 
                   --  Mark this component as processed
 
@@ -6310,7 +6399,7 @@
                         Comps := New_Elmt_List;
                      end if;
 
-                     Append_Elmt (Comp, Comps);
+                     Append_Elmt (Comp_Or_Discr, Comps);
                   end if;
                end if;
 
@@ -6318,16 +6407,34 @@
             --  the record type.
 
             else
-               Error_Msg_NE
-                 ("& is not a component of aggregate subtype", Comp, Comp);
+               Error_Msg_N ("& is not a component of aggregate subtype", Comp);
             end if;
-         end Check_Component_Reference;
 
+            --  Verify the consistency of types when the current component is
+            --  part of a miltiple component update.
+
+            --    Comp_1, ..., Comp_N => <value>
+
+            if Present (Etype (Comp)) then
+               Base_Typ := Base_Type (Etype (Comp));
+
+               --  Save the type of the first component reference as the
+               --  remaning references (if any) must resolve to this type.
+
+               if No (Common_Typ) then
+                  Common_Typ := Base_Typ;
+
+               elsif Base_Typ /= Common_Typ then
+                  Error_Msg_N
+                    ("components in choice list must have same type", Comp);
+               end if;
+            end if;
+         end Analyze_Record_Component_Update;
+
          --  Local variables
 
-         Assoc     : Node_Id;
-         Comp      : Node_Id;
-         Comp_Type : Entity_Id;
+         Assoc : Node_Id;
+         Comp  : Node_Id;
 
       --  Start of processing for Update
 
@@ -6353,128 +6460,64 @@
          --  choices. Perform the following checks:
 
          --    1) Legality of "others" in all cases
-         --    2) Component legality for records
+         --    2) Legality of <>
+         --    3) Component legality for arrays
+         --    4) Component legality for records
 
          --  The remaining checks are performed on the expanded attribute
 
          Assoc := First (Component_Associations (E1));
          while Present (Assoc) loop
-            Comp := First (Choices (Assoc));
-            Analyze (Expression (Assoc));
-            Comp_Type := Empty;
-            while Present (Comp) loop
-               if Nkind (Comp) = N_Others_Choice then
-                  Error_Attr
-                    ("others choice not allowed in attribute %", Comp);
 
-               elsif Is_Array_Type (P_Type) then
-                  declare
-                     Index      : Node_Id;
-                     Index_Type : Entity_Id;
-                     Lo, Hi     : Node_Id;
+            --  The use of <> is illegal (SPARK RM 4.4.1(1))
 
-                  begin
-                     if Nkind (First (Choices (Assoc))) /= N_Aggregate then
+            if Box_Present (Assoc) then
+               Error_Attr
+                 ("default initialization not allowed in attribute %", Assoc);
 
-                        --  Choices denote separate components of one-
-                        --  dimensional array.
+            --  Otherwise process the association
 
-                        Index_Type := First_Index (P_Type);
+            else
+               Analyze (Expression (Assoc));
 
-                        if Present (Next_Index (Index_Type)) then
-                           Error_Msg_N
-                             ("too few subscripts in array reference", Comp);
-                        end if;
+               if Is_Array_Type (P_Type) then
+                  Analyze_Array_Component_Update (Assoc);
 
-                        Index := First (Choices (Assoc));
-                        while Present (Index) loop
-                           if Nkind (Index) = N_Range then
-                              Lo := Low_Bound  (Index);
-                              Hi := High_Bound (Index);
+               elsif Is_Record_Type (P_Type) then
 
-                              Analyze_And_Resolve (Lo, Etype (Index_Type));
+                  --  Reset the common type used in a multiple component update
+                  --  as we are processing the contents of a new association.
 
-                              if not Is_OK_Static_Expression (Lo) then
-                                 Set_Do_Range_Check (Lo);
-                              end if;
+                  Common_Typ := Empty;
 
-                              Analyze_And_Resolve (Hi, Etype (Index_Type));
+                  Comp := First (Choices (Assoc));
+                  while Present (Comp) loop
+                     if Nkind (Comp) = N_Identifier then
+                        Analyze_Record_Component_Update (Comp);
 
-                              if not Is_OK_Static_Expression (Hi) then
-                                 Set_Do_Range_Check (Hi);
-                              end if;
+                     --  The use of others is illegal (SPARK RM 4.4.1(5))
 
-                           else
-                              Analyze_And_Resolve (Index, Etype (Index_Type));
+                     elsif Nkind (Comp) = N_Others_Choice then
+                        Error_Attr
+                          ("others choice not allowed in attribute %", Comp);
 
-                              if not Is_OK_Static_Expression (Index) then
-                                 Set_Do_Range_Check (Index);
-                              end if;
-                           end if;
+                     --  The name of a record component cannot appear in any
+                     --  other form.
 
-                           Next (Index);
-                        end loop;
-
-                     --  Choice is a sequence of indexes for each dimension
-
                      else
-                        Expr := First (Choices (Assoc));
-                        while Present (Expr) loop
-                           Index_Type := First_Index (P_Type);
-                           Index := First (Expressions (Expr));
-                           while Present (Index_Type)
-                             and then Present (Index)
-                           loop
-                              Analyze_And_Resolve (Index, Etype (Index_Type));
-                              Next_Index (Index_Type);
-                              Next (Index);
-                           end loop;
-
-                           if Present (Index) or else Present (Index_Type) then
-                              Error_Msg_N
-                                ("dimension mismatch in index list", Assoc);
-                           end if;
-
-                           Next (Expr);
-                        end loop;
+                        Error_Msg_N
+                          ("name should be identifier or OTHERS", Comp);
                      end if;
-                  end;
 
-               elsif Is_Record_Type (P_Type) then
-
-                  --  Make sure we have an identifier. Old SPARK allowed
-                  --  a component selection e.g. A.B in the corresponding
-                  --  context, but we do not yet permit this for 'Update.
-
-                  if Nkind (Comp) /= N_Identifier then
-                     Error_Msg_N ("name should be identifier or OTHERS", Comp);
-                  else
-                     Check_Component_Reference (Comp, P_Type);
-
-                     --  Verify that all choices in an association denote
-                     --  components of the same type.
-
-                     if No (Etype (Comp)) then
-                        null;
-
-                     elsif No (Comp_Type) then
-                        Comp_Type := Base_Type (Etype (Comp));
-
-                     elsif Comp_Type /= Base_Type (Etype (Comp)) then
-                        Error_Msg_N
-                          ("components in choice list must have same type",
-                           Assoc);
-                     end if;
-                  end if;
+                     Next (Comp);
+                  end loop;
                end if;
+            end if;
 
-               Next (Comp);
-            end loop;
-
             Next (Assoc);
          end loop;
 
-         --  The type of attribute Update is that of the prefix
+         --  The type of attribute 'Update is that of the prefix
 
          Set_Etype (N, P_Type);
       end Update;
@@ -11044,7 +11087,7 @@
                if Is_Array_Type (Typ) then
                   Assoc := First (Component_Associations (Aggr));
                   while Present (Assoc) loop
-                     Expr  := Expression (Assoc);
+                     Expr := Expression (Assoc);
                      Resolve (Expr, Component_Type (Typ));
 
                      --  For scalar array components set Do_Range_Check when
@@ -11129,10 +11172,6 @@
                end if;
             end;
 
-            --  Premature return requires comment ???
-
-            return;
-
          ---------
          -- Val --
          ---------

Reply via email to