This patch provides the initial implementation of aspect/pragma Refined_State. The construct is intended for formal verification proofs.
The syntax of the aspect/pragma is as follows: pragma Refined_State (REFINEMENT_LIST); REFINEMENT_LIST ::= REFINEMENT_CLAUSE | (REFINEMENT_CLAUSE {, REFINEMENT_CLAUSE}) REFINEMENT_CLAUSE ::= state_NAME => CONSTITUENT_LIST CONSTITUENT_LIST ::= null | CONSTITUENT | (CONSTITUENT {, CONSTITUENT}) CONSTITUENT ::= object_NAME | state_NAME The semantics of the aspect/pragma are as follows: A Refined_State aspect may only appear in the aspect_specification of a package_body. [The use of package_body rather than package body allows this aspect to be specified for generic package bodies.] If a package_specification has a non-null Abstract_State aspect its body shall have a Refined_State aspect. If a package_specification does not have an Abstract_State aspect, then the corresponding package_body shall not have a Refined_State aspect. A Refined_State Aspect of a package_body has visibility extended to the declarative_part of the body. Each constituent is either a variable or a state abstraction. An object which is a constituent shall be an entire object. Each abstract_state_name declared in the package specification shall be denoted as the state_name of a refinement_clause in the Refined_State aspect of the body of the package. Every entity of the hidden state of a package shall be denoted as a constituent of exactly one abstract_state_name in the Refined_State aspect of the package and shall not be denoted more than once. [These constituents are either variables declared in the private part or body of the package, or the declarations from the visible part of nested packages declared immediately therein.] ------------ -- Source -- ------------ -- semantics.ads package Semantics with Abstract_State => ((Refinement_State_1 with External, Input_Only), (Refinement_State_2 with External, Output_Only), (Refinement_State_3 with Non_Volatile), (Refinement_State_4 with Non_Volatile)) -- not refined is procedure Enforce_Body; Var_1 : Integer; package In_Visible_Part with Abstract_State => (State_1 with External, Input_Only) is Var_2 : Integer; private Var_3 : Integer; end In_Visible_Part; package Has_AS_No_RS with Abstract_State => (Has_AS_No_RS_State with Non_Volatile) is end Has_AS_No_RS; package Null_AS_No_RS with Abstract_State => null is end Null_AS_No_RS; package No_AS_Has_RS with Abstract_State => (No_AS_Has_RS_State with Non_Volatile) is end No_AS_Has_RS; private Var_4 : Integer; Extra_1 : Integer; -- hidden state not used package In_Private_Part with Abstract_State => (State_2 with External, Output_Only) is Var_5 : Integer; private Var_6 : Integer; end In_Private_Part; end Semantics; -- semantics.adb package body Semantics with Refined_State => (Refinement_State_1 => (Var_1, -- not a hidden state In_Visible_Part.State_1, -- not a hidden state In_Visible_Part.Var_2, -- not a hidden state In_Visible_Part.Var_3), -- not a hidden state Refinement_State_2 => (Var_4, In_Private_Part.State_2, In_Private_Part.Var_5, In_Private_Part.Var_6, -- not a hidden state In_Private_Part.State_2), -- duplicate use of hidden state Refinement_State_3 => (Var_7, In_Body.State_3, In_Body.Var_8, In_Body.Var_9, -- not a hidden state Var_7), -- duplicate use of hidden state Refinement_State_2 => null, -- duplicate refinement In_Visible_Part.State_1 => null) -- not a valid abstract state of -- package Semantics is procedure Enforce_Body is begin null; end Enforce_Body; package body In_Visible_Part with Refined_State => (State_1 => Var_3) is end In_Visible_Part; package body Has_AS_No_RS is end Has_AS_No_RS; -- requires refinement package body Null_AS_No_RS is end Null_AS_No_RS; package body No_AS_Has_RS with Refined_State => (No_AS_Has_RS_State => null) -- useless is end No_AS_Has_RS; -- refinement package body In_Private_Part with Refined_State => (State_2 => Var_6) is end In_Private_Part; Var_7 : Integer; package In_Body with Abstract_State => (State_3 with Non_Volatile) is Var_8 : Integer; Extra_2 : Integer; -- hidden state not used private Var_9 : Integer; end In_Body; package body In_Body with Refined_State => (State_3 => Var_9) is end In_Body; end Semantics; ---------------------------- -- Compilation and output -- ---------------------------- $ gcc -c -gnat12 -gnatd.V semantics.adb semantics.adb:2:08: package "Semantics" has unused hidden states semantics.adb:2:08: variable "Extra_1" defined at semantics.ads:37 semantics.adb:2:08: variable "Extra_2" defined at line 53 semantics.adb:4:14: cannot use "Var_1" in refinement, constituent is not a hidden state of package "Semantics" semantics.adb:5:29: cannot use "State_1" in refinement, constituent is not a hidden state of package "Semantics" semantics.adb:6:29: cannot use "Var_2" in refinement, constituent is not a hidden state of package "Semantics" semantics.adb:7:29: "Var_3" is not a visible entity of "In_Visible_Part" semantics.adb:13:29: "Var_6" is not a visible entity of "In_Private_Part" semantics.adb:14:29: duplicate use of constituent "State_2" semantics.adb:20:21: "Var_9" is not a visible entity of "In_Body" semantics.adb:21:14: duplicate use of constituent "Var_7" semantics.adb:23:11: duplicate refinement of state "Refinement_State_2" semantics.adb:25:26: cannot refine state, "State_1" is not defined in package "Semantics" semantics.adb:34:04: package "Has_AS_No_RS" requires state refinement semantics.ads:6:11: abstract state "Refinement_State_4" must be refined Tested on x86_64-pc-linux-gnu, committed on trunk 2013-10-10 Hristian Kirtchev <kirtc...@adacore.com> * aspects.adb: Add an entry in table Canonical_Aspect for Refined_State. * aspects.ads: Add entries in tables Aspect_Id, Aspect_Argument, Aspect_Names and Aspect_Delay for Refined_State. * einfo.adb: Add with and use clauses for Elists. Remove Refined_State from the list of node usage. Add Refined_State_Pragma to the list of node usage. (Has_Null_Abstract_State): New routine. (Refined_State): Removed. (Refined_State_Pragma): New routine. (Set_Refined_State): Removed. (Set_Refined_State_Pragma): New routine. (Write_Field8_Name): Add output for Refined_State_Pragma. (Write_Field9_Name): Remove the output for Refined_State. * einfo.ads: Add new synthesized attribute Has_Null_Abstract_State along with usage in nodes. Remove attribute Refined_State along with usage in nodes. Add new attribute Refined_State_Pragma along with usage in nodes. (Has_Null_Abstract_State): New routine. (Refined_State): Removed. (Refined_State_Pragma): New routine. (Set_Refined_State): Removed. (Set_Refined_State_Pragma): New routine. * elists.adb (Clone): New routine. * elists.ads (Clone): New routine. * par-prag.adb: Add Refined_State to the pragmas that do not require special processing by the parser. * sem_ch3.adb: Add with and use clause for Sem_Prag. (Analyze_Declarations): Add local variables Body_Id, Context and Spec_Id. Add processing for delayed aspect/pragma Refined_State. * sem_ch13.adb (Analyze_Aspect_Specifications): Update the handling of aspect Abstract_State. Add processing for aspect Refined_State. Remove the bizzare insertion policy for aspect Abstract_State. (Check_Aspect_At_Freeze_Point): Add an entry for Refined_State. * sem_prag.adb: Add an entry to table Sig_Flags for pragma Refined_State. (Add_Item): Update the comment on usage. The inserted items need not be unique. (Analyze_Contract_Cases_In_Decl_Part): Rename variable Restore to Restore_Scope and update all its occurrences. (Analyze_Pragma): Update the handling of pragma Abstract_State. Add processing for pragma Refined_State. (Analyze_Pre_Post_Condition_In_Decl_Part): Rename variable Restore to Restore_Scope and update all its occurrences. (Analyze_Refined_State_In_Decl_Part): New routine. * sem_prag.ads (Analyze_Refined_State_In_Decl_Part): New routine. * snames.ads-tmpl: Add new predefined name for Refined_State. Add new Pragma_Id for Refined_State.
Index: sem_ch3.adb =================================================================== --- sem_ch3.adb (revision 203365) +++ sem_ch3.adb (working copy) @@ -64,6 +64,7 @@ with Sem_Elim; use Sem_Elim; with Sem_Eval; use Sem_Eval; with Sem_Mech; use Sem_Mech; +with Sem_Prag; use Sem_Prag; with Sem_Res; use Sem_Res; with Sem_Smem; use Sem_Smem; with Sem_Type; use Sem_Type; @@ -2079,8 +2080,11 @@ -- Local variables + Body_Id : Entity_Id; + Context : Node_Id; Freeze_From : Entity_Id := Empty; Next_Decl : Node_Id; + Spec_Id : Entity_Id; -- Start of processing for Analyze_Declarations @@ -2190,6 +2194,37 @@ Decl := Next_Decl; end loop; + -- Analyze the state refinements within a package body now, after all + -- hidden states have been encountered and freely visible. Refinements + -- must be processed before pragmas Refined_Depends and Refined_Global + -- because the last two may mention constituents. + + if Present (L) then + Context := Parent (L); + + if Nkind (Context) = N_Package_Body then + Body_Id := Defining_Entity (Context); + Spec_Id := Corresponding_Spec (Context); + + -- The analysis of pragma Refined_State detects whether the spec + -- has abstract states available for refinement. + + if Present (Refined_State_Pragma (Body_Id)) then + Analyze_Refined_State_In_Decl_Part + (Refined_State_Pragma (Body_Id)); + + -- State refinement is required when the package declaration has + -- abstract states. Null states are not considered. + + elsif Present (Abstract_States (Spec_Id)) + and then not Has_Null_Abstract_State (Spec_Id) + then + Error_Msg_NE + ("package & requires state refinement", Context, Spec_Id); + end if; + end if; + end if; + -- Analyze the contracts of a subprogram declaration or a body now due -- to delayed visibility requirements of aspects. Index: einfo.adb =================================================================== --- einfo.adb (revision 203370) +++ einfo.adb (working copy) @@ -33,6 +33,7 @@ -- Turn off subprogram ordering, not used for this unit with Atree; use Atree; +with Elists; use Elists; with Namet; use Namet; with Nlists; use Nlists; with Output; use Output; @@ -79,12 +80,12 @@ -- Mechanism Uint8 (but returns Mechanism_Type) -- Normalized_First_Bit Uint8 -- Postcondition_Proc Node8 + -- Refined_State_Pragma Node8 -- Return_Applies_To Node8 -- First_Exit_Statement Node8 -- Class_Wide_Type Node9 -- Current_Value Node9 - -- Refined_State Node9 -- Renaming_Map Uint9 -- Direct_Primitive_Operations Elist10 @@ -2647,11 +2648,11 @@ return Flag227 (Id); end Referenced_As_Out_Parameter; - function Refined_State (Id : E) return E is + function Refined_State_Pragma (Id : E) return N is begin - pragma Assert (Ekind (Id) = E_Abstract_State); - return Node9 (Id); - end Refined_State; + pragma Assert (Ekind (Id) = E_Package_Body); + return Node8 (Id); + end Refined_State_Pragma; function Register_Exception_Call (Id : E) return N is begin @@ -5307,11 +5308,11 @@ Set_Flag227 (Id, V); end Set_Referenced_As_Out_Parameter; - procedure Set_Refined_State (Id : E; V : E) is + procedure Set_Refined_State_Pragma (Id : E; V : N) is begin - pragma Assert (Ekind (Id) = E_Abstract_State); - Set_Node9 (Id, V); - end Set_Refined_State; + pragma Assert (Ekind (Id) = E_Package_Body); + Set_Node8 (Id, V); + end Set_Refined_State_Pragma; procedure Set_Register_Exception_Call (Id : E; V : N) is begin @@ -6427,6 +6428,19 @@ return False; end Has_Interrupt_Handler; + ----------------------------- + -- Has_Null_Abstract_State -- + ----------------------------- + + function Has_Null_Abstract_State (Id : E) return B is + begin + pragma Assert (Ekind_In (Id, E_Generic_Package, E_Package)); + + return + Present (Abstract_States (Id)) + and then Is_Null_State (Node (First_Elmt (Abstract_States (Id)))); + end Has_Null_Abstract_State; + -------------------- -- Has_Unmodified -- -------------------- @@ -8292,6 +8306,9 @@ when E_Procedure => Write_Str ("Postcondition_Proc"); + when E_Package_Body => + Write_Str ("Refined_State_Pragma"); + when E_Return_Statement => Write_Str ("Return_Applies_To"); @@ -8313,9 +8330,6 @@ when Object_Kind => Write_Str ("Current_Value"); - when E_Abstract_State => - Write_Str ("Refined_State"); - when E_Function | E_Generic_Function | E_Generic_Package | Index: einfo.ads =================================================================== --- einfo.ads (revision 203368) +++ einfo.ads (working copy) @@ -1645,6 +1645,10 @@ -- are not considered to be significant since they do not affect -- stored bit patterns. +-- Has_Null_Abstract_State (synth) +-- Defined in package entities. True if the package is subject to a null +-- Abstract_State aspect/pragma. + -- Has_Object_Size_Clause (Flag172) -- Defined in entities for types and subtypes. Set if an Object_Size -- clause has been processed for the type Used to prevent multiple @@ -3533,9 +3537,9 @@ -- we have a separate warning for variables that are only assigned and -- never read, and out parameters are a special case. --- Refined_State (Node9) --- Defined in E_Abstract_State entities. Contains the entity of the --- abstract state completion which is usually foung in package bodies. +-- Refined_State_Pragma (Node8) +-- Defined in [generic] package bodies. Contains the pragma that refines +-- all abstract states defined in the corresponding package declaration. -- Register_Exception_Call (Node20) -- Defined in exception entities. When an exception is declared, @@ -5092,7 +5096,6 @@ ------------------------------------------ -- E_Abstract_State - -- Refined_State (Node9) -- Is_External_State (synth) -- Is_Input_Only_State (synth) -- Is_Null_State (synth) @@ -5636,10 +5639,12 @@ -- Is_Visible_Lib_Unit (Flag116) -- Renamed_In_Spec (Flag231) (non-generic case only) -- Static_Elaboration_Desired (Flag77) (non-generic case only) + -- Has_Null_Abstract_State (synth) -- Is_Wrapper_Package (synth) (non-generic case only) -- Scope_Depth (synth) -- E_Package_Body + -- Refined_State_Pragma (Node8) -- Handler_Records (List10) (non-generic case only) -- Related_Instance (Node15) (non-generic case only) -- First_Entity (Node17) @@ -6535,7 +6540,7 @@ function Referenced (Id : E) return B; function Referenced_As_LHS (Id : E) return B; function Referenced_As_Out_Parameter (Id : E) return B; - function Refined_State (Id : E) return E; + function Refined_State_Pragma (Id : E) return E; function Register_Exception_Call (Id : E) return N; function Related_Array_Object (Id : E) return E; function Related_Expression (Id : E) return N; @@ -6674,6 +6679,7 @@ function Has_Attach_Handler (Id : E) return B; function Has_Entries (Id : E) return B; function Has_Foreign_Convention (Id : E) return B; + function Has_Null_Abstract_State (Id : E) return B; function Implementation_Base_Type (Id : E) return E; function Is_Base_Type (Id : E) return B; function Is_Boolean_Type (Id : E) return B; @@ -7152,7 +7158,7 @@ procedure Set_Referenced (Id : E; V : B := True); procedure Set_Referenced_As_LHS (Id : E; V : B := True); procedure Set_Referenced_As_Out_Parameter (Id : E; V : B := True); - procedure Set_Refined_State (Id : E; V : E); + procedure Set_Refined_State_Pragma (Id : E; V : N); procedure Set_Register_Exception_Call (Id : E; V : N); procedure Set_Related_Array_Object (Id : E; V : E); procedure Set_Related_Expression (Id : E; V : N); @@ -7902,7 +7908,7 @@ pragma Inline (Referenced); pragma Inline (Referenced_As_LHS); pragma Inline (Referenced_As_Out_Parameter); - pragma Inline (Refined_State); + pragma Inline (Refined_State_Pragma); pragma Inline (Register_Exception_Call); pragma Inline (Related_Array_Object); pragma Inline (Related_Expression); @@ -8318,7 +8324,7 @@ pragma Inline (Set_Referenced); pragma Inline (Set_Referenced_As_LHS); pragma Inline (Set_Referenced_As_Out_Parameter); - pragma Inline (Set_Refined_State); + pragma Inline (Set_Refined_State_Pragma); pragma Inline (Set_Register_Exception_Call); pragma Inline (Set_Related_Array_Object); pragma Inline (Set_Related_Expression); Index: sem_prag.adb =================================================================== --- sem_prag.adb (revision 203365) +++ sem_prag.adb (working copy) @@ -168,9 +168,9 @@ ------------------------------------- procedure Add_Item (Item : Entity_Id; To_List : in out Elist_Id); - -- Subsidiary routine to the analysis of pragmas Depends and Global. Append - -- an input or output item to a list. If the list is empty, a new one is - -- created. + -- Subsidiary routine to the analysis of pragmas Depends, Global and + -- Refined_State. Append an entity to a list. If the list is empty, create + -- a new list. function Adjust_External_Name_Case (N : Node_Id) return Node_Id; -- This routine is used for possible casing adjustment of an explicit @@ -285,7 +285,7 @@ To_List := New_Elmt_List; end if; - Append_Unique_Elmt (Item, To_List); + Append_Elmt (Item, To_List); end Add_Item; ------------------------------- @@ -404,10 +404,12 @@ Arg1 : constant Node_Id := First (Pragma_Argument_Associations (N)); All_Cases : Node_Id; CCase : Node_Id; - Restore : Boolean := False; Subp_Decl : Node_Id; Subp_Id : Entity_Id; + Restore_Scope : Boolean := False; + -- Gets set True if we do a Push_Scope needing a Pop_Scope on exit + -- Start of processing for Analyze_Contract_Cases_In_Decl_Part begin @@ -432,7 +434,7 @@ -- for subprogram bodies because the formals are already visible. if Requires_Profile_Installation (N, Subp_Decl) then - Restore := True; + Restore_Scope := True; Push_Scope (Subp_Id); Install_Formals (Subp_Id); end if; @@ -443,7 +445,7 @@ Next (CCase); end loop; - if Restore then + if Restore_Scope then End_Scope; end if; end if; @@ -8494,7 +8496,6 @@ Set_Parent (Id, State); Set_Ekind (Id, E_Abstract_State); Set_Etype (Id, Standard_Void_Type); - Set_Refined_State (Id, Empty); -- Every non-null state must be nameable and resolvable the -- same way a constant is. @@ -8523,8 +8524,8 @@ -- Local variables - Par : Node_Id; - State : Node_Id; + Context : constant Node_Id := Parent (Parent (N)); + State : Node_Id; -- Start of processing for Abstract_State @@ -8536,24 +8537,14 @@ -- Ensure the proper placement of the pragma. Abstract states must -- be associated with a package declaration. - if From_Aspect_Specification (N) then - Par := Parent (Corresponding_Aspect (N)); - else - Par := Parent (Parent (N)); - end if; - - if Nkind (Par) = N_Compilation_Unit then - Par := Unit (Par); - end if; - - if not Nkind_In (Par, N_Generic_Package_Declaration, - N_Package_Declaration) + if not Nkind_In (Context, N_Generic_Package_Declaration, + N_Package_Declaration) then Pragma_Misplaced; return; end if; - Pack_Id := Defining_Entity (Par); + Pack_Id := Defining_Entity (Context); State := Expression (Arg1); -- Multiple abstract states appear as an aggregate @@ -15974,6 +15965,62 @@ when Pragma_Refined_Pre => Analyze_Refined_Pre_Post_Condition; + ------------------- + -- Refined_State -- + ------------------- + + -- pragma Refined_State (REFINEMENT_LIST); + + -- REFINEMENT_LIST ::= + -- REFINEMENT_CLAUSE + -- | (REFINEMENT_CLAUSE {, REFINEMENT_CLAUSE}) + + -- REFINEMENT_CLAUSE ::= state_NAME => CONSTITUENT_LIST + + -- CONSTITUENT_LIST ::= + -- null + -- | CONSTITUENT + -- | (CONSTITUENT {, CONSTITUENT}) + + -- CONSTITUENT ::= object_NAME | state_NAME + + when Pragma_Refined_State => Refined_State : declare + Context : constant Node_Id := Parent (N); + Spec_Id : Entity_Id; + + begin + GNAT_Pragma; + S14_Pragma; + Check_Arg_Count (1); + + -- Ensure the proper placement of the pragma. Refined states must + -- be associated with a package body. + + if Nkind (Context) /= N_Package_Body then + Pragma_Misplaced; + return; + end if; + + -- State refinement is allowed only when the corresponding package + -- declaration has a non-null aspect/pragma Abstract_State. + + Spec_Id := Corresponding_Spec (Context); + + if No (Abstract_States (Spec_Id)) + or else Has_Null_Abstract_State (Spec_Id) + then + Error_Pragma + ("useless pragma %, package does not define abstract states"); + return; + end if; + + -- The pragma must be analyzed at the end of the declarations as + -- it has visibility over the whole declarative region. Save the + -- pragma for later (see Analyze_Refined_Depends_In_Decl_Part). + + Set_Refined_State_Pragma (Defining_Entity (Context), N); + end Refined_State; + ----------------------- -- Relative_Deadline -- ----------------------- @@ -18313,17 +18360,18 @@ (Prag : Node_Id; Subp_Id : Entity_Id) is - Arg1 : constant Node_Id := - First (Pragma_Argument_Associations (Prag)); - Expr : Node_Id; - Restore : Boolean := False; + Arg1 : constant Node_Id := First (Pragma_Argument_Associations (Prag)); + Expr : Node_Id; + Restore_Scope : Boolean := False; + -- Gets set True if we do a Push_Scope needing a Pop_Scope on exit + begin -- Ensure that the subprogram and its formals are visible when analyzing -- the expression of the pragma. if Current_Scope /= Subp_Id then - Restore := True; + Restore_Scope := True; Push_Scope (Subp_Id); Install_Formals (Subp_Id); end if; @@ -18465,7 +18513,7 @@ -- Remove the subprogram from the scope stack now that the pre-analysis -- of the precondition/postcondition is done. - if Restore then + if Restore_Scope then End_Scope; end if; end Analyze_Pre_Post_Condition_In_Decl_Part; @@ -18494,6 +18542,497 @@ null; end Analyze_Refined_Global_In_Decl_Part; + ---------------------------------------- + -- Analyze_Refined_State_In_Decl_Part -- + ---------------------------------------- + + procedure Analyze_Refined_State_In_Decl_Part (N : Node_Id) is + Pack_Body : constant Node_Id := Parent (N); + Spec_Id : constant Entity_Id := Corresponding_Spec (Pack_Body); + + Abstr_States : Elist_Id := No_Elist; + -- A list of all abstract states defined in the package declaration. The + -- list is used to report unrefined states. + + Constituents_Seen : Elist_Id := No_Elist; + -- A list that contains all constituents processed so far. The list is + -- used to detect multiple uses of the same constituent. + + Hidden_States : Elist_Id := No_Elist; + -- A list of all hidden states (abstract states and variables) that + -- appear in the package spec and body. The list is used to report + -- unused hidden states. + + Refined_States_Seen : Elist_Id := No_Elist; + -- A list that contains all refined states processed so far. The list is + -- used to detect duplicate refinements. + + procedure Analyze_Refinement_Clause (Clause : Node_Id); + -- Perform full analysis of a single refinement clause + + function Collect_Hidden_States return Elist_Id; + -- Gather the entities of all hidden states that appear in the spec and + -- body of the related package. + + procedure Report_Unrefined_States; + -- Emit errors for all abstract states that have not been refined by + -- the pragma. + + procedure Report_Unused_Hidden_States; + -- Emit errors for all hidden states of the related package that do not + -- participate in a refinement. + + ------------------------------- + -- Analyze_Refinement_Clause -- + ------------------------------- + + procedure Analyze_Refinement_Clause (Clause : Node_Id) is + Non_Null_Seen : Boolean := False; + Null_Seen : Boolean := False; + -- Flags used to detect multiple uses of null in a single clause or a + -- mixture of null and non-null constituents. + + procedure Analyze_Constituent (Constit : Node_Id); + -- Perform full analysis of a single constituent + + procedure Check_Matching_State + (State : Node_Id; + State_Id : Entity_Id); + -- Determine whether state State denoted by its name State_Id appears + -- in Abstr_States. Emit an error when attempting to re-refine the + -- state or when the state is not defined in the package declaration. + -- Otherwise remove the state from Abstr_States. + + ------------------------- + -- Analyze_Constituent -- + ------------------------- + + procedure Analyze_Constituent (Constit : Node_Id) is + procedure Check_Matching_Constituent (Constit_Id : Entity_Id); + -- Determine whether constituent Constit denoted by its entity + -- Constit_Id appears in Hidden_States. Emit an error when the + -- constituent is not a valid hidden state of the related package + -- or when it is used more than once. Otherwise remove the + -- constituent from Hidden_States. + + -------------------------------- + -- Check_Matching_Constituent -- + -------------------------------- + + procedure Check_Matching_Constituent (Constit_Id : Entity_Id) is + State_Elmt : Elmt_Id; + + begin + -- Detect a duplicate use of a constituent + + if Contains (Constituents_Seen, Constit_Id) then + Error_Msg_NE + ("duplicate use of constituent &", Constit, Constit_Id); + return; + end if; + + -- Inspect the hidden states of the related package looking for + -- a match. + + State_Elmt := First_Elmt (Hidden_States); + while Present (State_Elmt) loop + + -- A valid hidden state or variable participates in a + -- refinement. Add the constituent to the list of processed + -- items to aid with the detection of duplicate constituent + -- use. Remove the constituent from Hidden_States to signal + -- that it has already been used. + + if Node (State_Elmt) = Constit_Id then + Add_Item (Constit_Id, Constituents_Seen); + Remove_Elmt (Hidden_States, State_Elmt); + + return; + end if; + + Next_Elmt (State_Elmt); + end loop; + + -- If we get here, we are refining a state that is not hidden + -- with respect to the related package. + + Error_Msg_Name_1 := Chars (Spec_Id); + Error_Msg_NE + ("cannot use & in refinement, constituent is not a hidden " + & "state of package %", Constit, Constit_Id); + end Check_Matching_Constituent; + + -- Local variables + + Constit_Id : Entity_Id; + + -- Start of processing for Analyze_Constituent + + begin + -- Detect multiple uses of null in a single refinement clause or a + -- mixture of null and non-null constituents. + + if Nkind (Constit) = N_Null then + if Null_Seen then + Error_Msg_N + ("multiple null constituents not allowed", Constit); + + elsif Non_Null_Seen then + Error_Msg_N + ("cannot mix null and non-null constituents", Constit); + + else + Null_Seen := True; + end if; + + -- Non-null constituents + + else + Non_Null_Seen := True; + + if Null_Seen then + Error_Msg_N + ("cannot mix null and non-null constituents", Constit); + end if; + + Analyze (Constit); + + -- Ensure that the constituent denotes a valid state or a + -- whole variable. + + if Is_Entity_Name (Constit) then + Constit_Id := Entity (Constit); + + if Ekind_In (Constit_Id, E_Abstract_State, E_Variable) then + Check_Matching_Constituent (Constit_Id); + else + Error_Msg_NE + ("constituent & must denote a variable or state", + Constit, Constit_Id); + end if; + + -- The constituent is illegal + + else + Error_Msg_N ("malformed constituent", Constit); + end if; + end if; + end Analyze_Constituent; + + -------------------------- + -- Check_Matching_State -- + -------------------------- + + procedure Check_Matching_State + (State : Node_Id; + State_Id : Entity_Id) + is + State_Elmt : Elmt_Id; + + begin + -- Detect a duplicate refinement of a state + + if Contains (Refined_States_Seen, State_Id) then + Error_Msg_NE + ("duplicate refinement of state &", State, State_Id); + return; + end if; + + -- Inspect the abstract states defined in the package declaration + -- looking for a match. + + State_Elmt := First_Elmt (Abstr_States); + while Present (State_Elmt) loop + + -- A valid abstract state is being refined in the body. Add + -- the state to the list of processed refined states to aid + -- with the detection of duplicate refinements. Remove the + -- state from Abstr_States to signal that it has already been + -- refined. + + if Node (State_Elmt) = State_Id then + Add_Item (State_Id, Refined_States_Seen); + Remove_Elmt (Abstr_States, State_Elmt); + + return; + end if; + + Next_Elmt (State_Elmt); + end loop; + + -- If we get here, we are refining a state that is not defined in + -- the package declaration. + + Error_Msg_Name_1 := Chars (Spec_Id); + Error_Msg_NE + ("cannot refine state, & is not defined in package %", + State, State_Id); + end Check_Matching_State; + + -- Local declarations + + Constit : Node_Id; + State : Node_Id; + State_Id : Entity_Id := Empty; + + -- Start of processing for Analyze_Refinement_Clause + + begin + -- Analyze the state name of a refinement clause + + State := First (Choices (Clause)); + while Present (State) loop + if Present (State_Id) then + Error_Msg_N + ("refinement clause cannot cover multiple states", State); + + else + Analyze (State); + + -- Ensure that the state name denotes a valid abstract state + -- that is defined in the spec of the related package. + + if Is_Entity_Name (State) then + State_Id := Entity (State); + + -- Catch any attempts to re-refine a state or refine a + -- state that is not defined in the package declaration. + + if Ekind (State_Id) = E_Abstract_State then + Check_Matching_State (State, State_Id); + else + Error_Msg_NE + ("& must denote an abstract state", State, State_Id); + end if; + + -- The state name is illegal + + else + Error_Msg_N + ("malformed state name in refinement clause", State); + end if; + end if; + + Next (State); + end loop; + + -- Analyze all constituents of the refinement. Multiple constituents + -- appear as an aggregate. + + Constit := Expression (Clause); + + if Nkind (Constit) = N_Aggregate then + if Present (Component_Associations (Constit)) then + Error_Msg_N + ("constituents of refinement clause must appear in " + & "positional form", Constit); + + else pragma Assert (Present (Expressions (Constit))); + Constit := First (Expressions (Constit)); + while Present (Constit) loop + Analyze_Constituent (Constit); + + Next (Constit); + end loop; + end if; + + -- Various forms of a single constituent. Note that these may include + -- malformed constituents. + + else + Analyze_Constituent (Constit); + end if; + end Analyze_Refinement_Clause; + + --------------------------- + -- Collect_Hidden_States -- + --------------------------- + + function Collect_Hidden_States return Elist_Id is + Result : Elist_Id := No_Elist; + + procedure Collect_Hidden_States_In_Decls (Decls : List_Id); + -- Find all hidden states that appear in declarative list Decls and + -- append their entities to Result. + + ------------------------------------ + -- Collect_Hidden_States_In_Decls -- + ------------------------------------ + + procedure Collect_Hidden_States_In_Decls (Decls : List_Id) is + procedure Collect_Abstract_States (States : Elist_Id); + -- Copy the abstract states defined in list States to list Result + + ----------------------------- + -- Collect_Abstract_States -- + ----------------------------- + + procedure Collect_Abstract_States (States : Elist_Id) is + State_Elmt : Elmt_Id; + + begin + State_Elmt := First_Elmt (States); + while Present (State_Elmt) loop + Add_Item (Node (State_Elmt), Result); + + Next_Elmt (State_Elmt); + end loop; + end Collect_Abstract_States; + + -- Local variables + + Decl : Node_Id; + + -- Start of processing for Collect_Hidden_States_In_Decls + + begin + Decl := First (Decls); + while Present (Decl) loop + + -- Objects (non-constants) are valid hidden states + + if Nkind (Decl) = N_Object_Declaration + and then not Constant_Present (Decl) + then + Add_Item (Defining_Entity (Decl), Result); + + -- Gather the abstract states of a package along with all + -- hidden states in its visible declarations. + + elsif Nkind (Decl) = N_Package_Declaration then + Collect_Abstract_States + (Abstract_States (Defining_Entity (Decl))); + + Collect_Hidden_States_In_Decls + (Visible_Declarations (Specification (Decl))); + end if; + + Next (Decl); + end loop; + end Collect_Hidden_States_In_Decls; + + -- Local variables + + Pack_Spec : constant Node_Id := Parent (Spec_Id); + + -- Start of processing for Collect_Hidden_States + + begin + -- Process the private declarations of the package spec and the + -- declarations of the body. + + Collect_Hidden_States_In_Decls (Private_Declarations (Pack_Spec)); + Collect_Hidden_States_In_Decls (Declarations (Pack_Body)); + + return Result; + end Collect_Hidden_States; + + ----------------------------- + -- Report_Unrefined_States -- + ----------------------------- + + procedure Report_Unrefined_States is + State_Elmt : Elmt_Id; + + begin + if Present (Abstr_States) then + State_Elmt := First_Elmt (Abstr_States); + while Present (State_Elmt) loop + Error_Msg_N + ("abstract state & must be refined", Node (State_Elmt)); + + Next_Elmt (State_Elmt); + end loop; + end if; + end Report_Unrefined_States; + + --------------------------------- + -- Report_Unused_Hidden_States -- + --------------------------------- + + procedure Report_Unused_Hidden_States is + Posted : Boolean := False; + State_Elmt : Elmt_Id; + State_Id : Entity_Id; + + begin + if Present (Hidden_States) then + State_Elmt := First_Elmt (Hidden_States); + while Present (State_Elmt) loop + State_Id := Node (State_Elmt); + + -- Generate an error message of the form: + + -- package ... has unused hidden states + -- abstract state ... defined at ... + -- variable ... defined at ... + + if not Posted then + Posted := True; + Error_Msg_NE + ("package & has unused hidden states", N, Spec_Id); + end if; + + Error_Msg_Sloc := Sloc (State_Id); + + if Ekind (State_Id) = E_Abstract_State then + Error_Msg_NE ("\ abstract state & defined #", N, State_Id); + else + Error_Msg_NE ("\ variable & defined #", N, State_Id); + end if; + + Next_Elmt (State_Elmt); + end loop; + end if; + end Report_Unused_Hidden_States; + + -- Local declarations + + Clauses : constant Node_Id := + Expression (First (Pragma_Argument_Associations (N))); + Clause : Node_Id; + + -- Start of processing for Analyze_Refined_State_In_Decl_Part + + begin + Set_Analyzed (N); + + -- Initialize the various lists used during analysis + + Abstr_States := Clone (Abstract_States (Spec_Id)); + Hidden_States := Collect_Hidden_States; + + -- Multiple state refinements appear as an aggregate + + if Nkind (Clauses) = N_Aggregate then + if Present (Expressions (Clauses)) then + Error_Msg_N + ("state refinements must appear as component associations", + Clauses); + + else pragma Assert (Present (Component_Associations (Clauses))); + Clause := First (Component_Associations (Clauses)); + while Present (Clause) loop + Analyze_Refinement_Clause (Clause); + + Next (Clause); + end loop; + end if; + + -- Various forms of a single state refinement. Note that these may + -- include malformed refinements. + + else + Analyze_Refinement_Clause (Clauses); + end if; + + -- Ensure that all abstract states have been refined and all hidden + -- states of the related package unilized in refinements. + + Report_Unrefined_States; + Report_Unused_Hidden_States; + end Analyze_Refined_State_In_Decl_Part; + ------------------------------------ -- Analyze_Test_Case_In_Decl_Part -- ------------------------------------ @@ -19250,6 +19789,7 @@ Pragma_Refined_Global => -1, Pragma_Refined_Post => -1, Pragma_Refined_Pre => -1, + Pragma_Refined_State => -1, Pragma_Relative_Deadline => -1, Pragma_Remote_Access_Type => -1, Pragma_Remote_Call_Interface => -1, Index: sem_prag.ads =================================================================== --- sem_prag.ads (revision 203365) +++ sem_prag.ads (working copy) @@ -77,6 +77,9 @@ procedure Analyze_Refined_Global_In_Decl_Part (N : Node_Id); -- Perform full analysis of delayed pragma Refined_Global + procedure Analyze_Refined_State_In_Decl_Part (N : Node_Id); + -- Perform full analysis of delayed pragma Refined_State + procedure Analyze_Test_Case_In_Decl_Part (N : Node_Id; S : Entity_Id); -- Perform preanalysis of pragma Test_Case that applies to a subprogram -- declaration. Parameter N denotes the pragma, S is the entity of the Index: elists.adb =================================================================== --- elists.adb (revision 203342) +++ elists.adb (working copy) @@ -158,6 +158,34 @@ end loop; end Append_Unique_Elmt; + ----------- + -- Clone -- + ------------ + + function Clone (List : Elist_Id) return Elist_Id is + Result : Elist_Id; + Elmt : Elmt_Id; + + begin + if List = No_Elist then + return No_Elist; + + -- Replicate the contents of the input list while preserving the + -- original order. + + else + Result := New_Elmt_List; + + Elmt := First_Elmt (List); + while Present (Elmt) loop + Append_Elmt (Node (Elmt), Result); + Next_Elmt (Elmt); + end loop; + + return Result; + end if; + end Clone; + -------------- -- Contains -- -------------- Index: elists.ads =================================================================== --- elists.ads (revision 203342) +++ elists.ads (working copy) @@ -153,6 +153,10 @@ -- affected, but the space used by the list element may be (but is not -- required to be) freed for reuse in a subsequent Append_Elmt call. + function Clone (List : Elist_Id) return Elist_Id; + -- Create a copy of the input list. Internal list nodes are not shared and + -- order of elements is preserved. + function Contains (List : Elist_Id; N : Node_Or_Entity_Id) return Boolean; -- Perform a sequential search to determine whether the given list contains -- a node or an entity. Index: aspects.adb =================================================================== --- aspects.adb (revision 203365) +++ aspects.adb (working copy) @@ -470,6 +470,7 @@ Aspect_Refined_Global => Aspect_Refined_Global, Aspect_Refined_Post => Aspect_Refined_Post, Aspect_Refined_Pre => Aspect_Refined_Pre, + Aspect_Refined_State => Aspect_Refined_State, Aspect_Remote_Access_Type => Aspect_Remote_Access_Type, Aspect_Remote_Call_Interface => Aspect_Remote_Call_Interface, Aspect_Remote_Types => Aspect_Remote_Types, Index: aspects.ads =================================================================== --- aspects.ads (revision 203365) +++ aspects.ads (working copy) @@ -115,6 +115,7 @@ Aspect_Refined_Global, -- GNAT Aspect_Refined_Post, -- GNAT Aspect_Refined_Pre, -- GNAT + Aspect_Refined_State, -- GNAT Aspect_Relative_Deadline, Aspect_Scalar_Storage_Order, -- GNAT Aspect_Simple_Storage_Pool, -- GNAT @@ -327,6 +328,7 @@ Aspect_Refined_Global => Expression, Aspect_Refined_Post => Expression, Aspect_Refined_Pre => Expression, + Aspect_Refined_State => Expression, Aspect_Relative_Deadline => Expression, Aspect_Scalar_Storage_Order => Expression, Aspect_Simple_Storage_Pool => Name, @@ -427,6 +429,7 @@ Aspect_Refined_Global => Name_Refined_Global, Aspect_Refined_Post => Name_Refined_Post, Aspect_Refined_Pre => Name_Refined_Pre, + Aspect_Refined_State => Name_Refined_State, Aspect_Relative_Deadline => Name_Relative_Deadline, Aspect_Remote_Access_Type => Name_Remote_Access_Type, Aspect_Remote_Call_Interface => Name_Remote_Call_Interface, @@ -620,6 +623,7 @@ Aspect_Read => Always_Delay, Aspect_Refined_Depends => Always_Delay, Aspect_Refined_Global => Always_Delay, + Aspect_Refined_State => Always_Delay, Aspect_Relative_Deadline => Always_Delay, Aspect_Remote_Access_Type => Always_Delay, Aspect_Remote_Call_Interface => Always_Delay, Index: par-prag.adb =================================================================== --- par-prag.adb (revision 203365) +++ par-prag.adb (working copy) @@ -1254,6 +1254,7 @@ Pragma_Refined_Global | Pragma_Refined_Post | Pragma_Refined_Pre | + Pragma_Refined_State | Pragma_Relative_Deadline | Pragma_Remote_Access_Type | Pragma_Remote_Call_Interface | Index: sem_ch13.adb =================================================================== --- sem_ch13.adb (revision 203368) +++ sem_ch13.adb (working copy) @@ -1883,13 +1883,46 @@ -- Abstract_State - when Aspect_Abstract_State => - Make_Aitem_Pragma - (Pragma_Argument_Associations => New_List ( - Make_Pragma_Argument_Association (Loc, - Expression => Relocate_Node (Expr))), - Pragma_Name => Name_Abstract_State); + when Aspect_Abstract_State => Abstract_State : declare + Decls : List_Id; + Spec : Node_Id; + begin + -- Aspect Abstract_State introduces implicit declarations + -- for all state abstraction entities it defines. To emulate + -- this behavior, insert the pragma at the beginning of the + -- visible declarations of the related package so that it is + -- analyzed immediately. + + if Nkind_In (N, N_Generic_Package_Declaration, + N_Package_Declaration) + then + Spec := Specification (N); + Decls := Visible_Declarations (Spec); + + Make_Aitem_Pragma + (Pragma_Argument_Associations => New_List ( + Make_Pragma_Argument_Association (Loc, + Expression => Relocate_Node (Expr))), + Pragma_Name => Name_Abstract_State); + Decorate_Delayed_Aspect_And_Pragma (Aspect, Aitem); + + if No (Decls) then + Decls := New_List; + Set_Visible_Declarations (N, Decls); + end if; + + Prepend_To (Decls, Aitem); + + else + Error_Msg_NE + ("aspect & must apply to a package declaration", + Aspect, Id); + end if; + + goto Continue; + end Abstract_State; + -- Depends -- Aspect Depends must be delayed because it mentions names @@ -1967,6 +2000,42 @@ Expression => Relocate_Node (Expr))), Pragma_Name => Name_Refined_Pre); + -- Refined_State + + when Aspect_Refined_State => Refined_State : declare + Decls : List_Id; + + begin + -- The corresponding pragma for Refined_State is inserted in + -- the declarations of the related package body. This action + -- synchronizes both the source and from-aspect versions of + -- the pragma. + + if Nkind (N) = N_Package_Body then + Decls := Declarations (N); + + Make_Aitem_Pragma + (Pragma_Argument_Associations => New_List ( + Make_Pragma_Argument_Association (Loc, + Expression => Relocate_Node (Expr))), + Pragma_Name => Name_Refined_State); + Decorate_Delayed_Aspect_And_Pragma (Aspect, Aitem); + + if No (Decls) then + Decls := New_List; + Set_Declarations (N, Decls); + end if; + + Prepend_To (Decls, Aitem); + + else + Error_Msg_NE + ("aspect & must apply to a package body", Aspect, Id); + end if; + + goto Continue; + end Refined_State; + -- Relative_Deadline when Aspect_Relative_Deadline => @@ -2411,21 +2480,6 @@ Set_From_Aspect_Specification (Aitem, True); end if; - -- Aspect Abstract_State introduces implicit declarations for all - -- state abstraction entities it defines. To emulate this behavior - -- insert the pragma at the start of the visible declarations of - -- the related package. - - if Nam = Name_Abstract_State - and then Nkind (N) = N_Package_Declaration - then - if No (Visible_Declarations (Specification (N))) then - Set_Visible_Declarations (Specification (N), New_List); - end if; - - Prepend (Aitem, Visible_Declarations (Specification (N))); - goto Continue; - -- In the context of a compilation unit, we directly put the -- pragma in the Pragmas_After list of the N_Compilation_Unit_Aux -- node (no delay is required here) except for aspects on a @@ -2434,7 +2488,7 @@ -- copy (see sem_ch12), and for package instantiations, where -- the library unit pragmas are better handled early. - elsif Nkind (Parent (N)) = N_Compilation_Unit + if Nkind (Parent (N)) = N_Compilation_Unit and then (Present (Aitem) or else Is_Boolean_Aspect (Aspect)) then declare @@ -7651,6 +7705,7 @@ Aspect_Refined_Global | Aspect_Refined_Post | Aspect_Refined_Pre | + Aspect_Refined_State | Aspect_SPARK_Mode | Aspect_Test_Case => raise Program_Error; Index: snames.ads-tmpl =================================================================== --- snames.ads-tmpl (revision 203365) +++ snames.ads-tmpl (working copy) @@ -584,6 +584,10 @@ Name_Refined_Global : constant Name_Id := N + $; -- GNAT Name_Refined_Post : constant Name_Id := N + $; -- GNAT Name_Refined_Pre : constant Name_Id := N + $; -- GNAT + + -- Kirchev + + Name_Refined_State : constant Name_Id := N + $; -- GNAT Name_Relative_Deadline : constant Name_Id := N + $; -- Ada 05 Name_Remote_Access_Type : constant Name_Id := N + $; -- GNAT Name_Remote_Call_Interface : constant Name_Id := N + $; @@ -1871,6 +1875,7 @@ Pragma_Refined_Global, Pragma_Refined_Post, Pragma_Refined_Pre, + Pragma_Refined_State, Pragma_Relative_Deadline, Pragma_Remote_Access_Type, Pragma_Remote_Call_Interface,