| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- G H O S T -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2014-2015, 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- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING3. If not, go to -- |
| -- http://www.gnu.org/licenses for a complete copy of the license. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Alloc; use Alloc; |
| with Aspects; use Aspects; |
| with Atree; use Atree; |
| with Einfo; use Einfo; |
| with Elists; use Elists; |
| with Errout; use Errout; |
| with Lib; use Lib; |
| with Namet; use Namet; |
| with Nlists; use Nlists; |
| with Nmake; use Nmake; |
| with Opt; use Opt; |
| with Sem; use Sem; |
| with Sem_Aux; use Sem_Aux; |
| with Sem_Eval; use Sem_Eval; |
| with Sem_Res; use Sem_Res; |
| with Sem_Util; use Sem_Util; |
| with Sinfo; use Sinfo; |
| with Snames; use Snames; |
| with Table; |
| |
| package body Ghost is |
| |
| -- The following table contains the N_Compilation_Unit node for a unit that |
| -- is either subject to pragma Ghost with policy Ignore or contains ignored |
| -- Ghost code. The table is used in the removal of ignored Ghost code from |
| -- units. |
| |
| package Ignored_Ghost_Units is new Table.Table ( |
| Table_Component_Type => Node_Id, |
| Table_Index_Type => Int, |
| Table_Low_Bound => 0, |
| Table_Initial => Alloc.Ignored_Ghost_Units_Initial, |
| Table_Increment => Alloc.Ignored_Ghost_Units_Increment, |
| Table_Name => "Ignored_Ghost_Units"); |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| procedure Propagate_Ignored_Ghost_Code (N : Node_Id); |
| -- Subsidiary to Set_Ghost_Mode_xxx. Signal all enclosing scopes that they |
| -- now contain ignored Ghost code. Add the compilation unit containing N to |
| -- table Ignored_Ghost_Units for post processing. |
| |
| ---------------------------- |
| -- Add_Ignored_Ghost_Unit -- |
| ---------------------------- |
| |
| procedure Add_Ignored_Ghost_Unit (Unit : Node_Id) is |
| begin |
| pragma Assert (Nkind (Unit) = N_Compilation_Unit); |
| |
| -- Avoid duplicates in the table as pruning the same unit more than once |
| -- is wasteful. Since ignored Ghost code tends to be grouped up, check |
| -- the contents of the table in reverse. |
| |
| for Index in reverse Ignored_Ghost_Units.First .. |
| Ignored_Ghost_Units.Last |
| loop |
| -- If the unit is already present in the table, do not add it again |
| |
| if Unit = Ignored_Ghost_Units.Table (Index) then |
| return; |
| end if; |
| end loop; |
| |
| -- If we get here, then this is the first time the unit is being added |
| |
| Ignored_Ghost_Units.Append (Unit); |
| end Add_Ignored_Ghost_Unit; |
| |
| ---------------------------- |
| -- Check_Ghost_Completion -- |
| ---------------------------- |
| |
| procedure Check_Ghost_Completion |
| (Partial_View : Entity_Id; |
| Full_View : Entity_Id) |
| is |
| Policy : constant Name_Id := Policy_In_Effect (Name_Ghost); |
| |
| begin |
| -- The Ghost policy in effect at the point of declaration and at the |
| -- point of completion must match (SPARK RM 6.9(15)). |
| |
| if Is_Checked_Ghost_Entity (Partial_View) |
| and then Policy = Name_Ignore |
| then |
| Error_Msg_Sloc := Sloc (Full_View); |
| |
| Error_Msg_N ("incompatible ghost policies in effect", Partial_View); |
| Error_Msg_N ("\& declared with ghost policy Check", Partial_View); |
| Error_Msg_N ("\& completed # with ghost policy Ignore", Partial_View); |
| |
| elsif Is_Ignored_Ghost_Entity (Partial_View) |
| and then Policy = Name_Check |
| then |
| Error_Msg_Sloc := Sloc (Full_View); |
| |
| Error_Msg_N ("incompatible ghost policies in effect", Partial_View); |
| Error_Msg_N ("\& declared with ghost policy Ignore", Partial_View); |
| Error_Msg_N ("\& completed # with ghost policy Check", Partial_View); |
| end if; |
| end Check_Ghost_Completion; |
| |
| ------------------------- |
| -- Check_Ghost_Context -- |
| ------------------------- |
| |
| procedure Check_Ghost_Context (Ghost_Id : Entity_Id; Ghost_Ref : Node_Id) is |
| procedure Check_Ghost_Policy (Id : Entity_Id; Err_N : Node_Id); |
| -- Verify that the Ghost policy at the point of declaration of entity Id |
| -- matches the policy at the point of reference. If this is not the case |
| -- emit an error at Err_N. |
| |
| function Is_OK_Ghost_Context (Context : Node_Id) return Boolean; |
| -- Determine whether node Context denotes a Ghost-friendly context where |
| -- a Ghost entity can safely reside. |
| |
| ------------------------- |
| -- Is_OK_Ghost_Context -- |
| ------------------------- |
| |
| function Is_OK_Ghost_Context (Context : Node_Id) return Boolean is |
| function Is_Ghost_Declaration (Decl : Node_Id) return Boolean; |
| -- Determine whether node Decl is a Ghost declaration or appears |
| -- within a Ghost declaration. |
| |
| function Is_Ghost_Statement_Or_Pragma (N : Node_Id) return Boolean; |
| -- Determine whether statement or pragma N is Ghost or appears within |
| -- a Ghost statement or pragma. To qualify as such, N must either |
| -- 1) Occur within a ghost subprogram or package |
| -- 2) Denote a call to a ghost procedure |
| -- 3) Denote an assignment statement whose target is a ghost |
| -- variable. |
| -- 4) Denote a pragma that mentions a ghost entity |
| |
| -------------------------- |
| -- Is_Ghost_Declaration -- |
| -------------------------- |
| |
| function Is_Ghost_Declaration (Decl : Node_Id) return Boolean is |
| Par : Node_Id; |
| Subp_Decl : Node_Id; |
| Subp_Id : Entity_Id; |
| |
| begin |
| -- Climb the parent chain looking for an object declaration |
| |
| Par := Decl; |
| while Present (Par) loop |
| if Is_Declaration (Par) then |
| |
| -- A declaration is Ghost when it appears within a Ghost |
| -- package or subprogram. |
| |
| if Ghost_Mode > None then |
| return True; |
| |
| -- Otherwise the declaration may not have been analyzed yet, |
| -- determine whether it is subject to aspect/pragma Ghost. |
| |
| else |
| return Is_Subject_To_Ghost (Par); |
| end if; |
| |
| -- Special cases |
| |
| -- A reference to a Ghost entity may appear as the default |
| -- expression of a formal parameter of a subprogram body. This |
| -- context must be treated as suitable because the relation |
| -- between the spec and the body has not been established and |
| -- the body is not marked as Ghost yet. The real check was |
| -- performed on the spec. |
| |
| elsif Nkind (Par) = N_Parameter_Specification |
| and then Nkind (Parent (Parent (Par))) = N_Subprogram_Body |
| then |
| return True; |
| |
| -- References to Ghost entities may be relocated in internally |
| -- generated bodies. |
| |
| elsif Nkind (Par) = N_Subprogram_Body |
| and then not Comes_From_Source (Par) |
| then |
| Subp_Id := Corresponding_Spec (Par); |
| |
| -- The original context is an expression function that has |
| -- been split into a spec and a body. The context is OK as |
| -- long as the the initial declaration is Ghost. |
| |
| if Present (Subp_Id) then |
| Subp_Decl := |
| Original_Node (Unit_Declaration_Node (Subp_Id)); |
| |
| if Nkind (Subp_Decl) = N_Expression_Function then |
| return Is_Subject_To_Ghost (Subp_Decl); |
| end if; |
| end if; |
| |
| -- Otherwise this is either an internal body or an internal |
| -- completion. Both are OK because the real check was done |
| -- before expansion activities. |
| |
| return True; |
| end if; |
| |
| -- Prevent the search from going too far |
| |
| if Is_Body_Or_Package_Declaration (Par) then |
| return False; |
| end if; |
| |
| Par := Parent (Par); |
| end loop; |
| |
| return False; |
| end Is_Ghost_Declaration; |
| |
| ---------------------------------- |
| -- Is_Ghost_Statement_Or_Pragma -- |
| ---------------------------------- |
| |
| function Is_Ghost_Statement_Or_Pragma (N : Node_Id) return Boolean is |
| function Is_Ghost_Entity_Reference (N : Node_Id) return Boolean; |
| -- Determine whether an arbitrary node denotes a reference to a |
| -- Ghost entity. |
| |
| ------------------------------- |
| -- Is_Ghost_Entity_Reference -- |
| ------------------------------- |
| |
| function Is_Ghost_Entity_Reference (N : Node_Id) return Boolean is |
| Ref : Node_Id; |
| |
| begin |
| -- When the reference extracts a subcomponent, recover the |
| -- related object (SPARK RM 6.9(1)). |
| |
| Ref := N; |
| while Nkind_In (Ref, N_Explicit_Dereference, |
| N_Indexed_Component, |
| N_Selected_Component, |
| N_Slice) |
| loop |
| Ref := Prefix (Ref); |
| end loop; |
| |
| return |
| Is_Entity_Name (Ref) |
| and then Present (Entity (Ref)) |
| and then Is_Ghost_Entity (Entity (Ref)); |
| end Is_Ghost_Entity_Reference; |
| |
| -- Local variables |
| |
| Arg : Node_Id; |
| Stmt : Node_Id; |
| |
| -- Start of processing for Is_Ghost_Statement_Or_Pragma |
| |
| begin |
| if Nkind (N) = N_Pragma then |
| |
| -- A pragma is Ghost when it appears within a Ghost package or |
| -- subprogram. |
| |
| if Ghost_Mode > None then |
| return True; |
| end if; |
| |
| -- A pragma is Ghost when it mentions a Ghost entity |
| |
| Arg := First (Pragma_Argument_Associations (N)); |
| while Present (Arg) loop |
| if Is_Ghost_Entity_Reference (Get_Pragma_Arg (Arg)) then |
| return True; |
| end if; |
| |
| Next (Arg); |
| end loop; |
| end if; |
| |
| Stmt := N; |
| while Present (Stmt) loop |
| if Is_Statement (Stmt) then |
| |
| -- A statement is Ghost when it appears within a Ghost |
| -- package or subprogram. |
| |
| if Ghost_Mode > None then |
| return True; |
| |
| -- An assignment statement or a procedure call is Ghost when |
| -- the name denotes a Ghost entity. |
| |
| elsif Nkind_In (Stmt, N_Assignment_Statement, |
| N_Procedure_Call_Statement) |
| then |
| return Is_Ghost_Entity_Reference (Name (Stmt)); |
| end if; |
| |
| -- Prevent the search from going too far |
| |
| elsif Is_Body_Or_Package_Declaration (Stmt) then |
| return False; |
| end if; |
| |
| Stmt := Parent (Stmt); |
| end loop; |
| |
| return False; |
| end Is_Ghost_Statement_Or_Pragma; |
| |
| -- Start of processing for Is_OK_Ghost_Context |
| |
| begin |
| -- The Ghost entity appears within an assertion expression |
| |
| if In_Assertion_Expr > 0 then |
| return True; |
| |
| -- The Ghost entity is part of a declaration or its completion |
| |
| elsif Is_Ghost_Declaration (Context) then |
| return True; |
| |
| -- The Ghost entity is referenced within a Ghost statement |
| |
| elsif Is_Ghost_Statement_Or_Pragma (Context) then |
| return True; |
| |
| -- Routine Expand_Record_Extension creates a parent subtype without |
| -- inserting it into the tree. There is no good way of recognizing |
| -- this special case as there is no parent. Try to approximate the |
| -- context. |
| |
| elsif No (Parent (Context)) and then Is_Tagged_Type (Ghost_Id) then |
| return True; |
| |
| else |
| return False; |
| end if; |
| end Is_OK_Ghost_Context; |
| |
| ------------------------ |
| -- Check_Ghost_Policy -- |
| ------------------------ |
| |
| procedure Check_Ghost_Policy (Id : Entity_Id; Err_N : Node_Id) is |
| Policy : constant Name_Id := Policy_In_Effect (Name_Ghost); |
| |
| begin |
| -- The Ghost policy in effect a the point of declaration and at the |
| -- point of use must match (SPARK RM 6.9(14)). |
| |
| if Is_Checked_Ghost_Entity (Id) and then Policy = Name_Ignore then |
| Error_Msg_Sloc := Sloc (Err_N); |
| |
| Error_Msg_N ("incompatible ghost policies in effect", Err_N); |
| Error_Msg_NE ("\& declared with ghost policy Check", Err_N, Id); |
| Error_Msg_NE ("\& used # with ghost policy Ignore", Err_N, Id); |
| |
| elsif Is_Ignored_Ghost_Entity (Id) and then Policy = Name_Check then |
| Error_Msg_Sloc := Sloc (Err_N); |
| |
| Error_Msg_N ("incompatible ghost policies in effect", Err_N); |
| Error_Msg_NE ("\& declared with ghost policy Ignore", Err_N, Id); |
| Error_Msg_NE ("\& used # with ghost policy Check", Err_N, Id); |
| end if; |
| end Check_Ghost_Policy; |
| |
| -- Start of processing for Check_Ghost_Context |
| |
| begin |
| -- Once it has been established that the reference to the Ghost entity |
| -- is within a suitable context, ensure that the policy at the point of |
| -- declaration and at the point of use match. |
| |
| if Is_OK_Ghost_Context (Ghost_Ref) then |
| Check_Ghost_Policy (Ghost_Id, Ghost_Ref); |
| |
| -- Otherwise the Ghost entity appears in a non-Ghost context and affects |
| -- its behavior or value. |
| |
| else |
| Error_Msg_N |
| ("ghost entity cannot appear in this context (SPARK RM 6.9(12))", |
| Ghost_Ref); |
| end if; |
| end Check_Ghost_Context; |
| |
| ---------------------------- |
| -- Check_Ghost_Derivation -- |
| ---------------------------- |
| |
| procedure Check_Ghost_Derivation (Typ : Entity_Id) is |
| Parent_Typ : constant Entity_Id := Etype (Typ); |
| Iface : Entity_Id; |
| Iface_Elmt : Elmt_Id; |
| |
| begin |
| -- Allow untagged derivations from predefined types such as Integer as |
| -- those are not Ghost by definition. |
| |
| if Is_Scalar_Type (Typ) and then Parent_Typ = Base_Type (Typ) then |
| null; |
| |
| -- The parent type of a Ghost type extension must be Ghost |
| |
| elsif not Is_Ghost_Entity (Parent_Typ) then |
| Error_Msg_N ("type extension & cannot be ghost", Typ); |
| Error_Msg_NE ("\parent type & is not ghost", Typ, Parent_Typ); |
| return; |
| end if; |
| |
| -- All progenitors (if any) must be Ghost as well |
| |
| if Is_Tagged_Type (Typ) and then Present (Interfaces (Typ)) then |
| Iface_Elmt := First_Elmt (Interfaces (Typ)); |
| while Present (Iface_Elmt) loop |
| Iface := Node (Iface_Elmt); |
| |
| if not Is_Ghost_Entity (Iface) then |
| Error_Msg_N ("type extension & cannot be ghost", Typ); |
| Error_Msg_NE ("\interface type & is not ghost", Typ, Iface); |
| return; |
| end if; |
| |
| Next_Elmt (Iface_Elmt); |
| end loop; |
| end if; |
| end Check_Ghost_Derivation; |
| |
| -------------------------------- |
| -- Implements_Ghost_Interface -- |
| -------------------------------- |
| |
| function Implements_Ghost_Interface (Typ : Entity_Id) return Boolean is |
| Iface_Elmt : Elmt_Id; |
| |
| begin |
| -- Traverse the list of interfaces looking for a Ghost interface |
| |
| if Is_Tagged_Type (Typ) and then Present (Interfaces (Typ)) then |
| Iface_Elmt := First_Elmt (Interfaces (Typ)); |
| while Present (Iface_Elmt) loop |
| if Is_Ghost_Entity (Node (Iface_Elmt)) then |
| return True; |
| end if; |
| |
| Next_Elmt (Iface_Elmt); |
| end loop; |
| end if; |
| |
| return False; |
| end Implements_Ghost_Interface; |
| |
| ---------------- |
| -- Initialize -- |
| ---------------- |
| |
| procedure Initialize is |
| begin |
| Ignored_Ghost_Units.Init; |
| end Initialize; |
| |
| --------------------- |
| -- Is_Ghost_Entity -- |
| --------------------- |
| |
| function Is_Ghost_Entity (Id : Entity_Id) return Boolean is |
| begin |
| return Is_Checked_Ghost_Entity (Id) or else Is_Ignored_Ghost_Entity (Id); |
| end Is_Ghost_Entity; |
| |
| ------------------------- |
| -- Is_Subject_To_Ghost -- |
| ------------------------- |
| |
| function Is_Subject_To_Ghost (N : Node_Id) return Boolean is |
| function Enables_Ghostness (Arg : Node_Id) return Boolean; |
| -- Determine whether aspect or pragma argument Arg enables "ghostness" |
| |
| ----------------------- |
| -- Enables_Ghostness -- |
| ----------------------- |
| |
| function Enables_Ghostness (Arg : Node_Id) return Boolean is |
| Expr : Node_Id; |
| |
| begin |
| Expr := Arg; |
| |
| if Nkind (Expr) = N_Pragma_Argument_Association then |
| Expr := Get_Pragma_Arg (Expr); |
| end if; |
| |
| -- Determine whether the expression of the aspect is static and |
| -- denotes True. |
| |
| if Present (Expr) then |
| Preanalyze_And_Resolve (Expr); |
| |
| return |
| Is_OK_Static_Expression (Expr) |
| and then Is_True (Expr_Value (Expr)); |
| |
| -- Otherwise Ghost defaults to True |
| |
| else |
| return True; |
| end if; |
| end Enables_Ghostness; |
| |
| -- Local variables |
| |
| Id : constant Entity_Id := Defining_Entity (N); |
| Asp : Node_Id; |
| Decl : Node_Id; |
| Prev_Id : Entity_Id; |
| |
| -- Start of processing for Is_Subject_To_Ghost |
| |
| begin |
| -- The related entity of the declaration has not been analyzed yet, do |
| -- not inspect its attributes. |
| |
| if Ekind (Id) = E_Void then |
| null; |
| |
| elsif Is_Ghost_Entity (Id) then |
| return True; |
| |
| -- The completion of a type or a constant is not fully analyzed when the |
| -- reference to the Ghost entity is resolved. Because the completion is |
| -- not marked as Ghost yet, inspect the partial view. |
| |
| elsif Is_Record_Type (Id) |
| or else Ekind (Id) = E_Constant |
| or else (Nkind (N) = N_Object_Declaration |
| and then Constant_Present (N)) |
| then |
| Prev_Id := Incomplete_Or_Partial_View (Id); |
| |
| if Present (Prev_Id) and then Is_Ghost_Entity (Prev_Id) then |
| return True; |
| end if; |
| end if; |
| |
| -- Examine the aspect specifications (if any) looking for aspect Ghost |
| |
| if Permits_Aspect_Specifications (N) then |
| Asp := First (Aspect_Specifications (N)); |
| while Present (Asp) loop |
| if Chars (Identifier (Asp)) = Name_Ghost then |
| return Enables_Ghostness (Expression (Asp)); |
| end if; |
| |
| Next (Asp); |
| end loop; |
| end if; |
| |
| Decl := Empty; |
| |
| -- When the context is a [generic] package declaration, pragma Ghost |
| -- resides in the visible declarations. |
| |
| if Nkind_In (N, N_Generic_Package_Declaration, |
| N_Package_Declaration) |
| then |
| Decl := First (Visible_Declarations (Specification (N))); |
| |
| -- When the context is a package or a subprogram body, pragma Ghost |
| -- resides in the declarative part. |
| |
| elsif Nkind_In (N, N_Package_Body, N_Subprogram_Body) then |
| Decl := First (Declarations (N)); |
| |
| -- Otherwise pragma Ghost appears in the declarations following N |
| |
| elsif Is_List_Member (N) then |
| Decl := Next (N); |
| end if; |
| |
| while Present (Decl) loop |
| if Nkind (Decl) = N_Pragma |
| and then Pragma_Name (Decl) = Name_Ghost |
| then |
| return |
| Enables_Ghostness (First (Pragma_Argument_Associations (Decl))); |
| |
| -- A source construct ends the region where pragma Ghost may appear, |
| -- stop the traversal. |
| |
| elsif Comes_From_Source (Decl) then |
| exit; |
| end if; |
| |
| Next (Decl); |
| end loop; |
| |
| return False; |
| end Is_Subject_To_Ghost; |
| |
| ---------- |
| -- Lock -- |
| ---------- |
| |
| procedure Lock is |
| begin |
| Ignored_Ghost_Units.Locked := True; |
| Ignored_Ghost_Units.Release; |
| end Lock; |
| |
| ---------------------------------- |
| -- Propagate_Ignored_Ghost_Code -- |
| ---------------------------------- |
| |
| procedure Propagate_Ignored_Ghost_Code (N : Node_Id) is |
| Nod : Node_Id; |
| Scop : Entity_Id; |
| |
| begin |
| -- Traverse the parent chain looking for blocks, packages and |
| -- subprograms or their respective bodies. |
| |
| Nod := Parent (N); |
| while Present (Nod) loop |
| Scop := Empty; |
| |
| if Nkind (Nod) = N_Block_Statement then |
| Scop := Entity (Identifier (Nod)); |
| |
| elsif Nkind_In (Nod, N_Package_Body, |
| N_Package_Declaration, |
| N_Subprogram_Body, |
| N_Subprogram_Declaration) |
| then |
| Scop := Defining_Entity (Nod); |
| end if; |
| |
| -- The current node denotes a scoping construct |
| |
| if Present (Scop) then |
| |
| -- Stop the traversal when the scope already contains ignored |
| -- Ghost code as all enclosing scopes have already been marked. |
| |
| if Contains_Ignored_Ghost_Code (Scop) then |
| exit; |
| |
| -- Otherwise mark this scope and keep climbing |
| |
| else |
| Set_Contains_Ignored_Ghost_Code (Scop); |
| end if; |
| end if; |
| |
| Nod := Parent (Nod); |
| end loop; |
| |
| -- The unit containing the ignored Ghost code must be post processed |
| -- before invoking the back end. |
| |
| Add_Ignored_Ghost_Unit (Cunit (Get_Code_Unit (N))); |
| end Propagate_Ignored_Ghost_Code; |
| |
| ------------------------------- |
| -- Remove_Ignored_Ghost_Code -- |
| ------------------------------- |
| |
| procedure Remove_Ignored_Ghost_Code is |
| procedure Prune_Tree (Root : Node_Id); |
| -- Remove all code marked as ignored Ghost from the tree of denoted by |
| -- Root. |
| |
| ---------------- |
| -- Prune_Tree -- |
| ---------------- |
| |
| procedure Prune_Tree (Root : Node_Id) is |
| procedure Prune (N : Node_Id); |
| -- Remove a given node from the tree by rewriting it into null |
| |
| function Prune_Node (N : Node_Id) return Traverse_Result; |
| -- Determine whether node N denotes an ignored Ghost construct. If |
| -- this is the case, rewrite N as a null statement. See the body for |
| -- special cases. |
| |
| ----------- |
| -- Prune -- |
| ----------- |
| |
| procedure Prune (N : Node_Id) is |
| begin |
| -- Destroy any aspects that may be associated with the node |
| |
| if Permits_Aspect_Specifications (N) and then Has_Aspects (N) then |
| Remove_Aspects (N); |
| end if; |
| |
| Rewrite (N, Make_Null_Statement (Sloc (N))); |
| end Prune; |
| |
| ---------------- |
| -- Prune_Node -- |
| ---------------- |
| |
| function Prune_Node (N : Node_Id) return Traverse_Result is |
| Id : Entity_Id; |
| |
| begin |
| -- The node is either declared as ignored Ghost or is a byproduct |
| -- of expansion. Destroy it and stop the traversal on this branch. |
| |
| if Is_Ignored_Ghost_Node (N) then |
| Prune (N); |
| return Skip; |
| |
| -- Scoping constructs such as blocks, packages, subprograms and |
| -- bodies offer some flexibility with respect to pruning. |
| |
| elsif Nkind_In (N, N_Block_Statement, |
| N_Package_Body, |
| N_Package_Declaration, |
| N_Subprogram_Body, |
| N_Subprogram_Declaration) |
| then |
| if Nkind (N) = N_Block_Statement then |
| Id := Entity (Identifier (N)); |
| else |
| Id := Defining_Entity (N); |
| end if; |
| |
| -- The scoping construct contains both living and ignored Ghost |
| -- code, let the traversal prune all relevant nodes. |
| |
| if Contains_Ignored_Ghost_Code (Id) then |
| return OK; |
| |
| -- Otherwise the construct contains only living code and should |
| -- not be pruned. |
| |
| else |
| return Skip; |
| end if; |
| |
| -- Otherwise keep searching for ignored Ghost nodes |
| |
| else |
| return OK; |
| end if; |
| end Prune_Node; |
| |
| procedure Prune_Nodes is new Traverse_Proc (Prune_Node); |
| |
| -- Start of processing for Prune_Tree |
| |
| begin |
| Prune_Nodes (Root); |
| end Prune_Tree; |
| |
| -- Start of processing for Remove_Ignored_Ghost_Code |
| |
| begin |
| for Index in Ignored_Ghost_Units.First .. Ignored_Ghost_Units.Last loop |
| Prune_Tree (Unit (Ignored_Ghost_Units.Table (Index))); |
| end loop; |
| end Remove_Ignored_Ghost_Code; |
| |
| -------------------- |
| -- Set_Ghost_Mode -- |
| -------------------- |
| |
| procedure Set_Ghost_Mode (N : Node_Id; Prev_Id : Entity_Id := Empty) is |
| procedure Set_Ghost_Mode_From_Entity (Id : Entity_Id); |
| -- Set the value of global variable Ghost_Mode depending on the mode of |
| -- entity Id. |
| |
| procedure Set_Ghost_Mode_From_Policy; |
| -- Set the value of global variable Ghost_Mode depending on the current |
| -- Ghost policy in effect. |
| |
| -------------------------------- |
| -- Set_Ghost_Mode_From_Entity -- |
| -------------------------------- |
| |
| procedure Set_Ghost_Mode_From_Entity (Id : Entity_Id) is |
| begin |
| if Is_Checked_Ghost_Entity (Id) then |
| Ghost_Mode := Check; |
| |
| elsif Is_Ignored_Ghost_Entity (Id) then |
| Ghost_Mode := Ignore; |
| |
| Set_Is_Ignored_Ghost_Node (N); |
| Propagate_Ignored_Ghost_Code (N); |
| end if; |
| end Set_Ghost_Mode_From_Entity; |
| |
| -------------------------------- |
| -- Set_Ghost_Mode_From_Policy -- |
| -------------------------------- |
| |
| procedure Set_Ghost_Mode_From_Policy is |
| Policy : constant Name_Id := Policy_In_Effect (Name_Ghost); |
| |
| begin |
| if Policy = Name_Check then |
| Ghost_Mode := Check; |
| |
| elsif Policy = Name_Ignore then |
| Ghost_Mode := Ignore; |
| |
| Set_Is_Ignored_Ghost_Node (N); |
| Propagate_Ignored_Ghost_Code (N); |
| end if; |
| end Set_Ghost_Mode_From_Policy; |
| |
| -- Local variables |
| |
| Nam : Node_Id; |
| |
| -- Start of processing for Set_Ghost_Mode |
| |
| begin |
| -- The input node denotes one of the many declaration kinds that may be |
| -- subject to pragma Ghost. |
| |
| if Is_Declaration (N) then |
| if Is_Subject_To_Ghost (N) then |
| Set_Ghost_Mode_From_Policy; |
| |
| -- The declaration denotes the completion of a deferred constant, |
| -- pragma Ghost appears on the partial declaration. |
| |
| elsif Nkind (N) = N_Object_Declaration |
| and then Constant_Present (N) |
| and then Present (Prev_Id) |
| then |
| Set_Ghost_Mode_From_Entity (Prev_Id); |
| |
| -- The declaration denotes the full view of a private type, pragma |
| -- Ghost appears on the partial declaration. |
| |
| elsif Nkind (N) = N_Full_Type_Declaration |
| and then Is_Private_Type (Defining_Entity (N)) |
| and then Present (Prev_Id) |
| then |
| Set_Ghost_Mode_From_Entity (Prev_Id); |
| end if; |
| |
| -- The input denotes an assignment or a procedure call. In this case |
| -- the Ghost mode is dictated by the name of the construct. |
| |
| elsif Nkind_In (N, N_Assignment_Statement, |
| N_Procedure_Call_Statement) |
| then |
| -- When the reference extracts a subcomponent, recover the related |
| -- object (SPARK RM 6.9(1)). |
| |
| Nam := Name (N); |
| while Nkind_In (Nam, N_Explicit_Dereference, |
| N_Indexed_Component, |
| N_Selected_Component, |
| N_Slice) |
| loop |
| Nam := Prefix (Nam); |
| end loop; |
| |
| if Is_Entity_Name (Nam) |
| and then Present (Entity (Nam)) |
| then |
| Set_Ghost_Mode_From_Entity (Entity (Nam)); |
| end if; |
| |
| -- The input denotes a package or subprogram body |
| |
| elsif Nkind_In (N, N_Package_Body, N_Subprogram_Body) then |
| if (Present (Prev_Id) and then Is_Ghost_Entity (Prev_Id)) |
| or else Is_Subject_To_Ghost (N) |
| then |
| Set_Ghost_Mode_From_Policy; |
| end if; |
| end if; |
| end Set_Ghost_Mode; |
| |
| ------------------------------- |
| -- Set_Ghost_Mode_For_Freeze -- |
| ------------------------------- |
| |
| procedure Set_Ghost_Mode_For_Freeze (Id : Entity_Id; N : Node_Id) is |
| begin |
| if Is_Checked_Ghost_Entity (Id) then |
| Ghost_Mode := Check; |
| elsif Is_Ignored_Ghost_Entity (Id) then |
| Ghost_Mode := Ignore; |
| Propagate_Ignored_Ghost_Code (N); |
| end if; |
| end Set_Ghost_Mode_For_Freeze; |
| |
| ------------------------- |
| -- Set_Is_Ghost_Entity -- |
| ------------------------- |
| |
| procedure Set_Is_Ghost_Entity (Id : Entity_Id) is |
| Policy : constant Name_Id := Policy_In_Effect (Name_Ghost); |
| begin |
| if Policy = Name_Check then |
| Set_Is_Checked_Ghost_Entity (Id); |
| elsif Policy = Name_Ignore then |
| Set_Is_Ignored_Ghost_Entity (Id); |
| end if; |
| end Set_Is_Ghost_Entity; |
| |
| end Ghost; |