diff --git a/gcc/ada/contracts.adb b/gcc/ada/contracts.adb index 21f438f90f37..218fd66852fd 100644 --- a/gcc/ada/contracts.adb +++ b/gcc/ada/contracts.adb @@ -107,6 +107,11 @@ package body Contracts is -- well as Contract_Cases, Subprogram_Variant, invariants and predicates. -- Body_Id denotes the entity of the subprogram body. + procedure Preanalyze_Condition + (Subp : Entity_Id; + Expr : Node_Id); + -- Preanalyze the class-wide condition Expr of Subp + procedure Set_Class_Condition (Kind : Condition_Kind; Subp : Entity_Id; @@ -4548,242 +4553,10 @@ package body Contracts is procedure Merge_Class_Conditions (Spec_Id : Entity_Id) is - procedure Preanalyze_Condition - (Subp : Entity_Id; - Expr : Node_Id); - -- Preanalyze the class-wide condition Expr of Subp - procedure Process_Inherited_Conditions (Kind : Condition_Kind); -- Collect all inherited class-wide conditions of Spec_Id and merge -- them into one big condition. - -------------------------- - -- Preanalyze_Condition -- - -------------------------- - - procedure Preanalyze_Condition - (Subp : Entity_Id; - Expr : Node_Id) - is - procedure Clear_Unset_References; - -- Clear unset references on formals of Subp since preanalysis - -- occurs in a place unrelated to the actual code. - - procedure Remove_Controlling_Arguments; - -- Traverse Expr and clear the Controlling_Argument of calls to - -- nonabstract functions. - - procedure Remove_Formals (Id : Entity_Id); - -- Remove formals from homonym chains and make them not visible - - procedure Restore_Original_Selected_Component; - -- Traverse Expr searching for dispatching calls to functions whose - -- original node was a selected component, and replace them with - -- their original node. - - ---------------------------- - -- Clear_Unset_References -- - ---------------------------- - - procedure Clear_Unset_References is - F : Entity_Id := First_Formal (Subp); - - begin - while Present (F) loop - Set_Unset_Reference (F, Empty); - Next_Formal (F); - end loop; - end Clear_Unset_References; - - ---------------------------------- - -- Remove_Controlling_Arguments -- - ---------------------------------- - - procedure Remove_Controlling_Arguments is - function Remove_Ctrl_Arg (N : Node_Id) return Traverse_Result; - -- Reset the Controlling_Argument of calls to nonabstract - -- function calls. - - --------------------- - -- Remove_Ctrl_Arg -- - --------------------- - - function Remove_Ctrl_Arg (N : Node_Id) return Traverse_Result is - begin - if Nkind (N) = N_Function_Call - and then Present (Controlling_Argument (N)) - and then not Is_Abstract_Subprogram (Entity (Name (N))) - then - Set_Controlling_Argument (N, Empty); - end if; - - return OK; - end Remove_Ctrl_Arg; - - procedure Remove_Ctrl_Args is new Traverse_Proc (Remove_Ctrl_Arg); - begin - Remove_Ctrl_Args (Expr); - end Remove_Controlling_Arguments; - - -------------------- - -- Remove_Formals -- - -------------------- - - procedure Remove_Formals (Id : Entity_Id) is - F : Entity_Id := First_Formal (Id); - - begin - while Present (F) loop - Set_Is_Immediately_Visible (F, False); - Remove_Homonym (F); - Next_Formal (F); - end loop; - end Remove_Formals; - - ----------------------------------------- - -- Restore_Original_Selected_Component -- - ----------------------------------------- - - procedure Restore_Original_Selected_Component is - Restored_Nodes_List : Elist_Id := No_Elist; - - procedure Fix_Parents (N : Node_Id); - -- Traverse the subtree of N fixing the Parent field of all the - -- nodes. - - function Restore_Node (N : Node_Id) return Traverse_Result; - -- Process dispatching calls to functions whose original node was - -- a selected component, and replace them with their original - -- node. Restored nodes are stored in the Restored_Nodes_List - -- to fix the parent fields of their subtrees in a separate - -- tree traversal. - - ----------------- - -- Fix_Parents -- - ----------------- - - procedure Fix_Parents (N : Node_Id) is - - function Fix_Parent - (Parent_Node : Node_Id; - Node : Node_Id) return Traverse_Result; - -- Process a single node - - ---------------- - -- Fix_Parent -- - ---------------- - - function Fix_Parent - (Parent_Node : Node_Id; - Node : Node_Id) return Traverse_Result - is - Par : constant Node_Id := Parent (Node); - - begin - if Par /= Parent_Node then - pragma Assert (not Is_List_Member (Node)); - Set_Parent (Node, Parent_Node); - end if; - - return OK; - end Fix_Parent; - - procedure Fix_Parents is - new Traverse_Proc_With_Parent (Fix_Parent); - - begin - Fix_Parents (N); - end Fix_Parents; - - ------------------ - -- Restore_Node -- - ------------------ - - function Restore_Node (N : Node_Id) return Traverse_Result is - begin - if Nkind (N) = N_Function_Call - and then Nkind (Original_Node (N)) = N_Selected_Component - and then Is_Dispatching_Operation (Entity (Name (N))) - then - Rewrite (N, Original_Node (N)); - Set_Original_Node (N, N); - - -- Save the restored node in the Restored_Nodes_List to fix - -- the parent fields of their subtrees in a separate tree - -- traversal. - - Append_New_Elmt (N, Restored_Nodes_List); - end if; - - return OK; - end Restore_Node; - - procedure Restore_Nodes is new Traverse_Proc (Restore_Node); - - -- Start of processing for Restore_Original_Selected_Component - - begin - Restore_Nodes (Expr); - - -- After restoring the original node we must fix the decoration - -- of the Parent attribute to ensure tree consistency; required - -- because when the class-wide condition is inherited, calls to - -- New_Copy_Tree will perform copies of this subtree, and formal - -- occurrences with wrong Parent field cannot be mapped to the - -- new formals. - - if Present (Restored_Nodes_List) then - declare - Elmt : Elmt_Id := First_Elmt (Restored_Nodes_List); - - begin - while Present (Elmt) loop - Fix_Parents (Node (Elmt)); - Next_Elmt (Elmt); - end loop; - end; - end if; - end Restore_Original_Selected_Component; - - -- Start of processing for Preanalyze_Condition - - begin - pragma Assert (Present (Expr)); - pragma Assert (Inside_Class_Condition_Preanalysis = False); - - Push_Scope (Subp); - Install_Formals (Subp); - Inside_Class_Condition_Preanalysis := True; - - Preanalyze_Spec_Expression (Expr, Standard_Boolean); - - Inside_Class_Condition_Preanalysis := False; - Remove_Formals (Subp); - Pop_Scope; - - -- If this preanalyzed condition has occurrences of dispatching calls - -- using the Object.Operation notation, during preanalysis such calls - -- are rewritten as dispatching function calls; if at later stages - -- this condition is inherited we must have restored the original - -- selected-component node to ensure that the preanalysis of the - -- inherited condition rewrites these dispatching calls in the - -- correct context to avoid reporting spurious errors. - - Restore_Original_Selected_Component; - - -- Traverse Expr and clear the Controlling_Argument of calls to - -- nonabstract functions. Required since the preanalyzed condition - -- is not yet installed on its definite context and will be cloned - -- and extended in derivations with additional conditions. - - Remove_Controlling_Arguments; - - -- Clear also attribute Unset_Reference; again because preanalysis - -- occurs in a place unrelated to the actual code. - - Clear_Unset_References; - end Preanalyze_Condition; - ---------------------------------- -- Process_Inherited_Conditions -- ---------------------------------- @@ -5116,6 +4889,250 @@ package body Contracts is end loop; end Merge_Class_Conditions; + --------------------------------- + -- Preanalyze_Class_Conditions -- + --------------------------------- + + procedure Preanalyze_Class_Conditions (Spec_Id : Entity_Id) is + Cond : Node_Id; + + begin + for Kind in Condition_Kind loop + Cond := Class_Condition (Kind, Spec_Id); + + if Present (Cond) then + Preanalyze_Condition (Spec_Id, Cond); + end if; + end loop; + end Preanalyze_Class_Conditions; + + -------------------------- + -- Preanalyze_Condition -- + -------------------------- + + procedure Preanalyze_Condition + (Subp : Entity_Id; + Expr : Node_Id) + is + procedure Clear_Unset_References; + -- Clear unset references on formals of Subp since preanalysis + -- occurs in a place unrelated to the actual code. + + procedure Remove_Controlling_Arguments; + -- Traverse Expr and clear the Controlling_Argument of calls to + -- nonabstract functions. + + procedure Remove_Formals (Id : Entity_Id); + -- Remove formals from homonym chains and make them not visible + + procedure Restore_Original_Selected_Component; + -- Traverse Expr searching for dispatching calls to functions whose + -- original node was a selected component, and replace them with + -- their original node. + + ---------------------------- + -- Clear_Unset_References -- + ---------------------------- + + procedure Clear_Unset_References is + F : Entity_Id := First_Formal (Subp); + + begin + while Present (F) loop + Set_Unset_Reference (F, Empty); + Next_Formal (F); + end loop; + end Clear_Unset_References; + + ---------------------------------- + -- Remove_Controlling_Arguments -- + ---------------------------------- + + procedure Remove_Controlling_Arguments is + function Remove_Ctrl_Arg (N : Node_Id) return Traverse_Result; + -- Reset the Controlling_Argument of calls to nonabstract + -- function calls. + + --------------------- + -- Remove_Ctrl_Arg -- + --------------------- + + function Remove_Ctrl_Arg (N : Node_Id) return Traverse_Result is + begin + if Nkind (N) = N_Function_Call + and then Present (Controlling_Argument (N)) + and then not Is_Abstract_Subprogram (Entity (Name (N))) + then + Set_Controlling_Argument (N, Empty); + end if; + + return OK; + end Remove_Ctrl_Arg; + + procedure Remove_Ctrl_Args is new Traverse_Proc (Remove_Ctrl_Arg); + begin + Remove_Ctrl_Args (Expr); + end Remove_Controlling_Arguments; + + -------------------- + -- Remove_Formals -- + -------------------- + + procedure Remove_Formals (Id : Entity_Id) is + F : Entity_Id := First_Formal (Id); + + begin + while Present (F) loop + Set_Is_Immediately_Visible (F, False); + Remove_Homonym (F); + Next_Formal (F); + end loop; + end Remove_Formals; + + ----------------------------------------- + -- Restore_Original_Selected_Component -- + ----------------------------------------- + + procedure Restore_Original_Selected_Component is + Restored_Nodes_List : Elist_Id := No_Elist; + + procedure Fix_Parents (N : Node_Id); + -- Traverse the subtree of N fixing the Parent field of all the + -- nodes. + + function Restore_Node (N : Node_Id) return Traverse_Result; + -- Process dispatching calls to functions whose original node was + -- a selected component, and replace them with their original + -- node. Restored nodes are stored in the Restored_Nodes_List + -- to fix the parent fields of their subtrees in a separate + -- tree traversal. + + ----------------- + -- Fix_Parents -- + ----------------- + + procedure Fix_Parents (N : Node_Id) is + + function Fix_Parent + (Parent_Node : Node_Id; + Node : Node_Id) return Traverse_Result; + -- Process a single node + + ---------------- + -- Fix_Parent -- + ---------------- + + function Fix_Parent + (Parent_Node : Node_Id; + Node : Node_Id) return Traverse_Result + is + Par : constant Node_Id := Parent (Node); + + begin + if Par /= Parent_Node then + pragma Assert (not Is_List_Member (Node)); + Set_Parent (Node, Parent_Node); + end if; + + return OK; + end Fix_Parent; + + procedure Fix_Parents is + new Traverse_Proc_With_Parent (Fix_Parent); + + begin + Fix_Parents (N); + end Fix_Parents; + + ------------------ + -- Restore_Node -- + ------------------ + + function Restore_Node (N : Node_Id) return Traverse_Result is + begin + if Nkind (N) = N_Function_Call + and then Nkind (Original_Node (N)) = N_Selected_Component + and then Is_Dispatching_Operation (Entity (Name (N))) + then + Rewrite (N, Original_Node (N)); + Set_Original_Node (N, N); + + -- Save the restored node in the Restored_Nodes_List to fix + -- the parent fields of their subtrees in a separate tree + -- traversal. + + Append_New_Elmt (N, Restored_Nodes_List); + end if; + + return OK; + end Restore_Node; + + procedure Restore_Nodes is new Traverse_Proc (Restore_Node); + + -- Start of processing for Restore_Original_Selected_Component + + begin + Restore_Nodes (Expr); + + -- After restoring the original node we must fix the decoration + -- of the Parent attribute to ensure tree consistency; required + -- because when the class-wide condition is inherited, calls to + -- New_Copy_Tree will perform copies of this subtree, and formal + -- occurrences with wrong Parent field cannot be mapped to the + -- new formals. + + if Present (Restored_Nodes_List) then + declare + Elmt : Elmt_Id := First_Elmt (Restored_Nodes_List); + + begin + while Present (Elmt) loop + Fix_Parents (Node (Elmt)); + Next_Elmt (Elmt); + end loop; + end; + end if; + end Restore_Original_Selected_Component; + + -- Start of processing for Preanalyze_Condition + + begin + pragma Assert (Present (Expr)); + pragma Assert (Inside_Class_Condition_Preanalysis = False); + + Push_Scope (Subp); + Install_Formals (Subp); + Inside_Class_Condition_Preanalysis := True; + + Preanalyze_Spec_Expression (Expr, Standard_Boolean); + + Inside_Class_Condition_Preanalysis := False; + Remove_Formals (Subp); + Pop_Scope; + + -- If this preanalyzed condition has occurrences of dispatching calls + -- using the Object.Operation notation, during preanalysis such calls + -- are rewritten as dispatching function calls; if at later stages + -- this condition is inherited we must have restored the original + -- selected-component node to ensure that the preanalysis of the + -- inherited condition rewrites these dispatching calls in the + -- correct context to avoid reporting spurious errors. + + Restore_Original_Selected_Component; + + -- Traverse Expr and clear the Controlling_Argument of calls to + -- nonabstract functions. Required since the preanalyzed condition + -- is not yet installed on its definite context and will be cloned + -- and extended in derivations with additional conditions. + + Remove_Controlling_Arguments; + + -- Clear also attribute Unset_Reference; again because preanalysis + -- occurs in a place unrelated to the actual code. + + Clear_Unset_References; + end Preanalyze_Condition; + ---------------------------------------- -- Save_Global_References_In_Contract -- ---------------------------------------- diff --git a/gcc/ada/contracts.ads b/gcc/ada/contracts.ads index bde32ffc5b44..ae6355ef410b 100644 --- a/gcc/ada/contracts.ads +++ b/gcc/ada/contracts.ads @@ -276,6 +276,10 @@ package Contracts is -- which are invoked from the caller side; they are also used to build -- the dispatch-table wrapper (DTW), if required. + procedure Preanalyze_Class_Conditions (Spec_Id : Entity_Id); + -- Preanalyze class-wide pre-/postconditions of the given subprogram + -- specification. + procedure Process_Class_Conditions_At_Freeze_Point (Typ : Entity_Id); -- Merge, preanalyze, and check class-wide pre/postconditions of Typ -- primitives. diff --git a/gcc/ada/sem_prag.adb b/gcc/ada/sem_prag.adb index 2a3aca85a796..615c6d2110c6 100644 --- a/gcc/ada/sem_prag.adb +++ b/gcc/ada/sem_prag.adb @@ -26201,6 +26201,20 @@ package body Sem_Prag is Check_Postcondition_Use_In_Inlined_Subprogram (N, Spec_Id); Set_Is_Analyzed_Pragma (N); + -- If the subprogram is frozen then its class-wide pre- and post- + -- conditions have been preanalyzed (see Merge_Class_Conditions); + -- otherwise they must be preanalyzed now to ensure the correct + -- visibility of their referenced entities. This scenario occurs + -- when the subprogram is defined in a nested package (since the + -- end of the package does not cause freezing). + + if Class_Present (N) + and then Is_Dispatching_Operation (Spec_Id) + and then not Is_Frozen (Spec_Id) + then + Preanalyze_Class_Conditions (Spec_Id); + end if; + Restore_Ghost_Region (Saved_GM, Saved_IGR); end Analyze_Pre_Post_Condition_In_Decl_Part;