blob: 1720fe01cb83709ed3d1aff11f17e2ca5d76977d [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- G H O S T --
-- --
-- B o d y --
-- --
-- Copyright (C) 2014-2021, 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;
with Aspects; use Aspects;
with Atree; use Atree;
with Einfo; use Einfo;
with Einfo.Entities; use Einfo.Entities;
with Einfo.Utils; use Einfo.Utils;
with Elists; use Elists;
with Errout; use Errout;
with Namet; use Namet;
with Nlists; use Nlists;
with Nmake; use Nmake;
with Sem; use Sem;
with Sem_Aux; use Sem_Aux;
with Sem_Disp; use Sem_Disp;
with Sem_Eval; use Sem_Eval;
with Sem_Prag; use Sem_Prag;
with Sem_Res; use Sem_Res;
with Sem_Util; use Sem_Util;
with Sinfo; use Sinfo;
with Sinfo.Nodes; use Sinfo.Nodes;
with Sinfo.Utils; use Sinfo.Utils;
with Snames; use Snames;
with Table;
package body Ghost is
---------------------
-- Data strictures --
---------------------
-- The following table contains all ignored Ghost nodes that must be
-- eliminated from the tree by routine Remove_Ignored_Ghost_Code.
package Ignored_Ghost_Nodes is new Table.Table (
Table_Component_Type => Node_Id,
Table_Index_Type => Int,
Table_Low_Bound => 0,
Table_Initial => Alloc.Ignored_Ghost_Nodes_Initial,
Table_Increment => Alloc.Ignored_Ghost_Nodes_Increment,
Table_Name => "Ignored_Ghost_Nodes");
-----------------------
-- Local subprograms --
-----------------------
function Whole_Object_Ref (Ref : Node_Id) return Node_Id;
-- For a name that denotes an object, returns a name that denotes the whole
-- object, declared by an object declaration, formal parameter declaration,
-- etc. For example, for P.X.Comp (J), if P is a package X is a record
-- object, this returns P.X.
function Ghost_Entity (Ref : Node_Id) return Entity_Id;
pragma Inline (Ghost_Entity);
-- Obtain the entity of a Ghost entity from reference Ref. Return Empty if
-- no such entity exists.
procedure Install_Ghost_Mode (Mode : Ghost_Mode_Type);
pragma Inline (Install_Ghost_Mode);
-- Install Ghost mode Mode as the Ghost mode in effect
procedure Install_Ghost_Region (Mode : Name_Id; N : Node_Id);
pragma Inline (Install_Ghost_Region);
-- Install a Ghost region comprised of mode Mode and ignored region start
-- node N.
function Is_Subject_To_Ghost (N : Node_Id) return Boolean;
-- Determine whether declaration or body N is subject to aspect or pragma
-- Ghost. This routine must be used in cases where pragma Ghost has not
-- been analyzed yet, but the context needs to establish the "ghostness"
-- of N.
procedure Mark_Ghost_Declaration_Or_Body
(N : Node_Id;
Mode : Name_Id);
-- Mark the defining entity of declaration or body N as Ghost depending on
-- mode Mode. Mark all formals parameters when N denotes a subprogram or a
-- body.
function Name_To_Ghost_Mode (Mode : Name_Id) return Ghost_Mode_Type;
pragma Inline (Name_To_Ghost_Mode);
-- Convert a Ghost mode denoted by name Mode into its respective enumerated
-- value.
procedure Record_Ignored_Ghost_Node (N : Node_Or_Entity_Id);
-- Save ignored Ghost node or entity N in table Ignored_Ghost_Nodes for
-- later elimination.
----------------------------
-- Check_Ghost_Completion --
----------------------------
procedure Check_Ghost_Completion
(Prev_Id : Entity_Id;
Compl_Id : Entity_Id)
is
Policy : constant Name_Id := Policy_In_Effect (Name_Ghost);
begin
-- Nothing to do if one of the views is missing
if No (Prev_Id) or else No (Compl_Id) then
null;
-- The Ghost policy in effect at the point of declaration and at the
-- point of completion must match (SPARK RM 6.9(14)).
elsif Is_Checked_Ghost_Entity (Prev_Id)
and then Policy = Name_Ignore
then
Error_Msg_Sloc := Sloc (Compl_Id);
Error_Msg_N ("incompatible ghost policies in effect", Prev_Id);
Error_Msg_N ("\& declared with ghost policy `Check`", Prev_Id);
Error_Msg_N ("\& completed # with ghost policy `Ignore`", Prev_Id);
elsif Is_Ignored_Ghost_Entity (Prev_Id)
and then Policy = Name_Check
then
Error_Msg_Sloc := Sloc (Compl_Id);
Error_Msg_N ("incompatible ghost policies in effect", Prev_Id);
Error_Msg_N ("\& declared with ghost policy `Ignore`", Prev_Id);
Error_Msg_N ("\& completed # with ghost policy `Check`", Prev_Id);
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; Ref : Node_Id);
-- Verify that the Ghost policy at the point of declaration of entity Id
-- matches the policy at the point of reference Ref. If this is not the
-- case emit an error at Ref.
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 (SPARK RM 6.9(10)).
function In_Aspect_Or_Pragma_Predicate (N : Node_Id) return Boolean;
-- Return True iff N is enclosed in an aspect or pragma Predicate
-------------------------
-- Is_OK_Ghost_Context --
-------------------------
function Is_OK_Ghost_Context (Context : Node_Id) return Boolean is
function Is_OK_Declaration (Decl : Node_Id) return Boolean;
-- Determine whether node Decl is a suitable context for a reference
-- to a Ghost entity. To qualify as such, Decl must either
--
-- * Define a Ghost entity
--
-- * Be subject to pragma Ghost
function Is_OK_Pragma (Prag : Node_Id) return Boolean;
-- Determine whether node Prag is a suitable context for a reference
-- to a Ghost entity. To qualify as such, Prag must either
--
-- * Be an assertion expression pragma
--
-- * Denote pragma Global, Depends, Initializes, Refined_Global,
-- Refined_Depends or Refined_State.
--
-- * Specify an aspect of a Ghost entity
--
-- * Contain a reference to a Ghost entity
function Is_OK_Statement (Stmt : Node_Id) return Boolean;
-- Determine whether node Stmt is a suitable context for a reference
-- to a Ghost entity. To qualify as such, Stmt must either
--
-- * Denote a procedure call to a Ghost procedure
--
-- * Denote an assignment statement whose target is Ghost
-----------------------
-- Is_OK_Declaration --
-----------------------
function Is_OK_Declaration (Decl : Node_Id) return Boolean is
function In_Subprogram_Body_Profile (N : Node_Id) return Boolean;
-- Determine whether node N appears in the profile of a subprogram
-- body.
--------------------------------
-- In_Subprogram_Body_Profile --
--------------------------------
function In_Subprogram_Body_Profile (N : Node_Id) return Boolean is
Spec : constant Node_Id := Parent (N);
begin
-- The node appears in a parameter specification in which case
-- it is either the parameter type or the default expression or
-- the node appears as the result definition of a function.
return
(Nkind (N) = N_Parameter_Specification
or else
(Nkind (Spec) = N_Function_Specification
and then N = Result_Definition (Spec)))
and then Nkind (Parent (Spec)) = N_Subprogram_Body;
end In_Subprogram_Body_Profile;
-- Local variables
Subp_Decl : Node_Id;
Subp_Id : Entity_Id;
-- Start of processing for Is_OK_Declaration
begin
if Is_Ghost_Declaration (Decl) then
return True;
-- Special cases
-- A reference to a Ghost entity may appear within the profile of
-- a subprogram body. This context is treated as suitable because
-- it duplicates the context of the corresponding spec. The real
-- check was already performed during the analysis of the spec.
elsif In_Subprogram_Body_Profile (Decl) then
return True;
-- A reference to a Ghost entity may appear within an expression
-- function which is still being analyzed. This context is treated
-- as suitable because it is not yet known whether the expression
-- function is an initial declaration or a completion. The real
-- check is performed when the expression function is expanded.
elsif Nkind (Decl) = N_Expression_Function
and then not Analyzed (Decl)
then
return True;
-- References to Ghost entities may be relocated in internally
-- generated bodies.
elsif Nkind (Decl) = N_Subprogram_Body
and then not Comes_From_Source (Decl)
then
Subp_Id := Corresponding_Spec (Decl);
if Present (Subp_Id) then
-- The context is the internally built _Postconditions
-- procedure, which is OK because the real check was done
-- before expansion activities.
if Chars (Subp_Id) = Name_uPostconditions then
return True;
-- The context is the internally built predicate function,
-- which is OK because the real check was done before the
-- predicate function was generated.
elsif Is_Predicate_Function (Subp_Id) then
return True;
else
Subp_Decl :=
Original_Node (Unit_Declaration_Node (Subp_Id));
-- 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 initial declaration is Ghost.
if Nkind (Subp_Decl) = N_Expression_Function then
return Is_Ghost_Declaration (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.
else
return True;
end if;
end if;
return False;
end Is_OK_Declaration;
------------------
-- Is_OK_Pragma --
------------------
function Is_OK_Pragma (Prag : Node_Id) return Boolean is
procedure Check_Policies (Prag_Nam : Name_Id);
-- Verify that the Ghost policy in effect is the same as the
-- assertion policy for pragma name Prag_Nam. Emit an error if
-- this is not the case.
--------------------
-- Check_Policies --
--------------------
procedure Check_Policies (Prag_Nam : Name_Id) is
AP : constant Name_Id := Check_Kind (Prag_Nam);
GP : constant Name_Id := Policy_In_Effect (Name_Ghost);
begin
-- If the Ghost policy in effect at the point of a Ghost entity
-- reference is Ignore, then the assertion policy of the pragma
-- must be Ignore (SPARK RM 6.9(18)).
if GP = Name_Ignore and then AP /= Name_Ignore then
Error_Msg_N
("incompatible ghost policies in effect",
Ghost_Ref);
Error_Msg_NE
("\ghost entity & has policy `Ignore`",
Ghost_Ref, Ghost_Id);
Error_Msg_Name_1 := AP;
Error_Msg_N
("\assertion expression has policy %", Ghost_Ref);
end if;
end Check_Policies;
-- Local variables
Prag_Id : Pragma_Id;
Prag_Nam : Name_Id;
-- Start of processing for Is_OK_Pragma
begin
if Nkind (Prag) = N_Pragma then
Prag_Id := Get_Pragma_Id (Prag);
Prag_Nam := Original_Aspect_Pragma_Name (Prag);
-- A pragma that applies to a Ghost construct or specifies an
-- aspect of a Ghost entity is a Ghost pragma (SPARK RM 6.9(3))
if Is_Ghost_Pragma (Prag) then
return True;
-- An assertion expression pragma is Ghost when it contains a
-- reference to a Ghost entity (SPARK RM 6.9(10)), except for
-- predicate pragmas (SPARK RM 6.9(11)).
elsif Assertion_Expression_Pragma (Prag_Id)
and then Prag_Id /= Pragma_Predicate
then
-- Ensure that the assertion policy and the Ghost policy are
-- compatible (SPARK RM 6.9(18)).
Check_Policies (Prag_Nam);
return True;
-- Several pragmas that may apply to a non-Ghost entity are
-- treated as Ghost when they contain a reference to a Ghost
-- entity (SPARK RM 6.9(11)).
elsif Prag_Nam in Name_Global
| Name_Depends
| Name_Initializes
| Name_Refined_Global
| Name_Refined_Depends
| Name_Refined_State
then
return True;
end if;
end if;
return False;
end Is_OK_Pragma;
---------------------
-- Is_OK_Statement --
---------------------
function Is_OK_Statement (Stmt : Node_Id) return Boolean is
begin
-- An assignment statement is Ghost when the target is a Ghost
-- entity.
if Nkind (Stmt) = N_Assignment_Statement then
return Is_Ghost_Assignment (Stmt);
-- A procedure call is Ghost when it calls a Ghost procedure
elsif Nkind (Stmt) = N_Procedure_Call_Statement then
return Is_Ghost_Procedure_Call (Stmt);
-- Special cases
-- An if statement is a suitable context for a Ghost entity if it
-- is the byproduct of assertion expression expansion. Note that
-- the assertion expression may not be related to a Ghost entity,
-- but it may still contain references to Ghost entities.
elsif Nkind (Stmt) = N_If_Statement
and then Nkind (Original_Node (Stmt)) = N_Pragma
and then Assertion_Expression_Pragma
(Get_Pragma_Id (Original_Node (Stmt)))
then
return True;
end if;
return False;
end Is_OK_Statement;
-- Local variables
Par : Node_Id;
-- Start of processing for Is_OK_Ghost_Context
begin
-- The context is Ghost when it appears within a Ghost package or
-- subprogram.
if Ghost_Mode > None then
return True;
-- A Ghost type may be referenced in a use_type clause
-- (SPARK RM 6.9.10).
elsif Present (Parent (Context))
and then Nkind (Parent (Context)) = N_Use_Type_Clause
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;
-- Otherwise climb the parent chain looking for a suitable Ghost
-- context.
else
Par := Context;
while Present (Par) loop
if Is_Ignored_Ghost_Node (Par) then
return True;
-- A reference to a Ghost entity can appear within an aspect
-- specification (SPARK RM 6.9(10)). The precise checking will
-- occur when analyzing the corresponding pragma. We make an
-- exception for predicate aspects that only allow referencing
-- a Ghost entity when the corresponding type declaration is
-- Ghost (SPARK RM 6.9(11)).
elsif Nkind (Par) = N_Aspect_Specification
and then not Same_Aspect
(Get_Aspect_Id (Par), Aspect_Predicate)
then
return True;
elsif Is_OK_Declaration (Par) then
return True;
elsif Is_OK_Pragma (Par) then
return True;
elsif Is_OK_Statement (Par) then
return True;
-- Prevent the search from going too far
elsif Is_Body_Or_Package_Declaration (Par) then
exit;
end if;
Par := Parent (Par);
end loop;
-- The expansion of assertion expression pragmas and attribute Old
-- may cause a legal Ghost entity reference to become illegal due
-- to node relocation. Check the In_Assertion_Expr counter as last
-- resort to try and infer the original legal context.
if In_Assertion_Expr > 0 then
return True;
-- Otherwise the context is not suitable for a reference to a
-- Ghost entity.
else
return False;
end if;
end if;
end Is_OK_Ghost_Context;
------------------------
-- Check_Ghost_Policy --
------------------------
procedure Check_Ghost_Policy (Id : Entity_Id; Ref : 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(13)).
if Is_Checked_Ghost_Entity (Id)
and then Policy = Name_Ignore
and then May_Be_Lvalue (Ref)
then
Error_Msg_Sloc := Sloc (Ref);
Error_Msg_N ("incompatible ghost policies in effect", Ref);
Error_Msg_NE ("\& declared with ghost policy `Check`", Ref, Id);
Error_Msg_NE ("\& used # with ghost policy `Ignore`", Ref, Id);
elsif Is_Ignored_Ghost_Entity (Id) and then Policy = Name_Check then
Error_Msg_Sloc := Sloc (Ref);
Error_Msg_N ("incompatible ghost policies in effect", Ref);
Error_Msg_NE ("\& declared with ghost policy `Ignore`", Ref, Id);
Error_Msg_NE ("\& used # with ghost policy `Check`", Ref, Id);
end if;
end Check_Ghost_Policy;
-----------------------------------
-- In_Aspect_Or_Pragma_Predicate --
-----------------------------------
function In_Aspect_Or_Pragma_Predicate (N : Node_Id) return Boolean is
Par : Node_Id := N;
begin
while Present (Par) loop
if Nkind (Par) = N_Pragma
and then Get_Pragma_Id (Par) = Pragma_Predicate
then
return True;
elsif Nkind (Par) = N_Aspect_Specification
and then Same_Aspect (Get_Aspect_Id (Par), Aspect_Predicate)
then
return True;
-- Stop the search when it's clear it cannot be inside an aspect
-- or pragma.
elsif Is_Declaration (Par)
or else Is_Statement (Par)
or else Is_Body (Par)
then
return False;
end if;
Par := Parent (Par);
end loop;
return False;
end In_Aspect_Or_Pragma_Predicate;
-- Start of processing for Check_Ghost_Context
begin
-- Class-wide pre/postconditions of ignored pragmas are preanalyzed
-- to report errors on wrong conditions; however, ignored pragmas may
-- also have references to ghost entities and we must disable checking
-- their context to avoid reporting spurious errors.
if Inside_Class_Condition_Preanalysis then
return;
end if;
-- 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 (SPARK RM 6.9(10,11)).
else
Error_Msg_N ("ghost entity cannot appear in this context", Ghost_Ref);
-- When the Ghost entity appears in a pragma Predicate, explain the
-- reason for this being illegal, and suggest a fix instead.
if In_Aspect_Or_Pragma_Predicate (Ghost_Ref) then
Error_Msg_N
("\as predicates are checked in membership tests, "
& "the type and its predicate must be both ghost",
Ghost_Ref);
Error_Msg_N
("\either make the type ghost "
& "or use a type invariant on a private type", Ghost_Ref);
end if;
end if;
end Check_Ghost_Context;
----------------------------
-- Check_Ghost_Overriding --
----------------------------
procedure Check_Ghost_Overriding
(Subp : Entity_Id;
Overridden_Subp : Entity_Id)
is
Deriv_Typ : Entity_Id;
Over_Subp : Entity_Id;
begin
if Present (Subp) and then Present (Overridden_Subp) then
Over_Subp := Ultimate_Alias (Overridden_Subp);
Deriv_Typ := Find_Dispatching_Type (Subp);
-- A Ghost primitive of a non-Ghost type extension cannot override an
-- inherited non-Ghost primitive (SPARK RM 6.9(8)).
if Is_Ghost_Entity (Subp)
and then Present (Deriv_Typ)
and then not Is_Ghost_Entity (Deriv_Typ)
and then not Is_Ghost_Entity (Over_Subp)
and then not Is_Abstract_Subprogram (Over_Subp)
then
Error_Msg_N ("incompatible overriding in effect", Subp);
Error_Msg_Sloc := Sloc (Over_Subp);
Error_Msg_N ("\& declared # as non-ghost subprogram", Subp);
Error_Msg_Sloc := Sloc (Subp);
Error_Msg_N ("\overridden # with ghost subprogram", Subp);
end if;
-- A non-Ghost primitive of a type extension cannot override an
-- inherited Ghost primitive (SPARK RM 6.9(8)).
if Is_Ghost_Entity (Over_Subp)
and then not Is_Ghost_Entity (Subp)
and then not Is_Abstract_Subprogram (Subp)
then
Error_Msg_N ("incompatible overriding in effect", Subp);
Error_Msg_Sloc := Sloc (Over_Subp);
Error_Msg_N ("\& declared # as ghost subprogram", Subp);
Error_Msg_Sloc := Sloc (Subp);
Error_Msg_N ("\overridden # with non-ghost subprogram", Subp);
end if;
if Present (Deriv_Typ)
and then not Is_Ignored_Ghost_Entity (Deriv_Typ)
then
-- When a tagged type is either non-Ghost or checked Ghost and
-- one of its primitives overrides an inherited operation, the
-- overridden operation of the ancestor type must be ignored Ghost
-- if the primitive is ignored Ghost (SPARK RM 6.9(17)).
if Is_Ignored_Ghost_Entity (Subp) then
-- Both the parent subprogram and overriding subprogram are
-- ignored Ghost.
if Is_Ignored_Ghost_Entity (Over_Subp) then
null;
-- The parent subprogram carries policy Check
elsif Is_Checked_Ghost_Entity (Over_Subp) then
Error_Msg_N
("incompatible ghost policies in effect", Subp);
Error_Msg_Sloc := Sloc (Over_Subp);
Error_Msg_N
("\& declared # with ghost policy `Check`", Subp);
Error_Msg_Sloc := Sloc (Subp);
Error_Msg_N
("\overridden # with ghost policy `Ignore`", Subp);
-- The parent subprogram is non-Ghost
else
Error_Msg_N
("incompatible ghost policies in effect", Subp);
Error_Msg_Sloc := Sloc (Over_Subp);
Error_Msg_N ("\& declared # as non-ghost subprogram", Subp);
Error_Msg_Sloc := Sloc (Subp);
Error_Msg_N
("\overridden # with ghost policy `Ignore`", Subp);
end if;
-- When a tagged type is either non-Ghost or checked Ghost and
-- one of its primitives overrides an inherited operation, the
-- the primitive of the tagged type must be ignored Ghost if the
-- overridden operation is ignored Ghost (SPARK RM 6.9(17)).
elsif Is_Ignored_Ghost_Entity (Over_Subp) then
-- Both the parent subprogram and the overriding subprogram are
-- ignored Ghost.
if Is_Ignored_Ghost_Entity (Subp) then
null;
-- The overriding subprogram carries policy Check
elsif Is_Checked_Ghost_Entity (Subp) then
Error_Msg_N
("incompatible ghost policies in effect", Subp);
Error_Msg_Sloc := Sloc (Over_Subp);
Error_Msg_N
("\& declared # with ghost policy `Ignore`", Subp);
Error_Msg_Sloc := Sloc (Subp);
Error_Msg_N
("\overridden # with Ghost policy `Check`", Subp);
-- The overriding subprogram is non-Ghost
else
Error_Msg_N
("incompatible ghost policies in effect", Subp);
Error_Msg_Sloc := Sloc (Over_Subp);
Error_Msg_N
("\& declared # with ghost policy `Ignore`", Subp);
Error_Msg_Sloc := Sloc (Subp);
Error_Msg_N
("\overridden # with non-ghost subprogram", Subp);
end if;
end if;
end if;
end if;
end Check_Ghost_Overriding;
---------------------------
-- Check_Ghost_Primitive --
---------------------------
procedure Check_Ghost_Primitive (Prim : Entity_Id; Typ : Entity_Id) is
begin
-- The Ghost policy in effect at the point of declaration of a primitive
-- operation and a tagged type must match (SPARK RM 6.9(16)).
if Is_Tagged_Type (Typ) then
if Is_Checked_Ghost_Entity (Prim)
and then Is_Ignored_Ghost_Entity (Typ)
then
Error_Msg_N ("incompatible ghost policies in effect", Prim);
Error_Msg_Sloc := Sloc (Typ);
Error_Msg_NE
("\tagged type & declared # with ghost policy `Ignore`",
Prim, Typ);
Error_Msg_Sloc := Sloc (Prim);
Error_Msg_N
("\primitive subprogram & declared # with ghost policy `Check`",
Prim);
elsif Is_Ignored_Ghost_Entity (Prim)
and then Is_Checked_Ghost_Entity (Typ)
then
Error_Msg_N ("incompatible ghost policies in effect", Prim);
Error_Msg_Sloc := Sloc (Typ);
Error_Msg_NE
("\tagged type & declared # with ghost policy `Check`",
Prim, Typ);
Error_Msg_Sloc := Sloc (Prim);
Error_Msg_N
("\primitive subprogram & declared # with ghost policy `Ignore`",
Prim);
end if;
end if;
end Check_Ghost_Primitive;
----------------------------
-- Check_Ghost_Refinement --
----------------------------
procedure Check_Ghost_Refinement
(State : Node_Id;
State_Id : Entity_Id;
Constit : Node_Id;
Constit_Id : Entity_Id)
is
begin
if Is_Ghost_Entity (State_Id) then
if Is_Ghost_Entity (Constit_Id) then
-- The Ghost policy in effect at the point of abstract state
-- declaration and constituent must match (SPARK RM 6.9(15)).
if Is_Checked_Ghost_Entity (State_Id)
and then Is_Ignored_Ghost_Entity (Constit_Id)
then
Error_Msg_Sloc := Sloc (Constit);
SPARK_Msg_N ("incompatible ghost policies in effect", State);
SPARK_Msg_NE
("\abstract state & declared with ghost policy `Check`",
State, State_Id);
SPARK_Msg_NE
("\constituent & declared # with ghost policy `Ignore`",
State, Constit_Id);
elsif Is_Ignored_Ghost_Entity (State_Id)
and then Is_Checked_Ghost_Entity (Constit_Id)
then
Error_Msg_Sloc := Sloc (Constit);
SPARK_Msg_N ("incompatible ghost policies in effect", State);
SPARK_Msg_NE
("\abstract state & declared with ghost policy `Ignore`",
State, State_Id);
SPARK_Msg_NE
("\constituent & declared # with ghost policy `Check`",
State, Constit_Id);
end if;
-- A constituent of a Ghost abstract state must be a Ghost entity
-- (SPARK RM 7.2.2(12)).
else
SPARK_Msg_NE
("constituent of ghost state & must be ghost",
Constit, State_Id);
end if;
end if;
end Check_Ghost_Refinement;
----------------------
-- Check_Ghost_Type --
----------------------
procedure Check_Ghost_Type (Typ : Entity_Id) is
Conc_Typ : Entity_Id;
Full_Typ : Entity_Id;
begin
if Is_Ghost_Entity (Typ) then
Conc_Typ := Empty;
Full_Typ := Typ;
if Is_Single_Concurrent_Type (Typ) then
Conc_Typ := Anonymous_Object (Typ);
Full_Typ := Conc_Typ;
elsif Is_Concurrent_Type (Typ) then
Conc_Typ := Typ;
end if;
-- A Ghost type cannot be concurrent (SPARK RM 6.9(19)). Verify this
-- legality rule first to give a finer-grained diagnostic.
if Present (Conc_Typ) then
Error_Msg_N ("ghost type & cannot be concurrent", Conc_Typ);
end if;
-- A Ghost type cannot be effectively volatile (SPARK RM 6.9(7))
if Is_Effectively_Volatile (Full_Typ) then
Error_Msg_N ("ghost type & cannot be volatile", Full_Typ);
end if;
end if;
end Check_Ghost_Type;
------------------
-- Ghost_Entity --
------------------
function Ghost_Entity (Ref : Node_Id) return Entity_Id is
Obj_Ref : constant Node_Id := Ultimate_Prefix (Ref);
begin
-- When the reference denotes a subcomponent, recover the related whole
-- object (SPARK RM 6.9(1)).
if Is_Entity_Name (Obj_Ref) then
return Entity (Obj_Ref);
-- Otherwise the reference cannot possibly denote a Ghost entity
else
return Empty;
end if;
end Ghost_Entity;
--------------------------------
-- 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_Nodes.Init;
-- Set the soft link which enables Atree.Mark_New_Ghost_Node to record
-- an ignored Ghost node or entity.
Set_Ignored_Ghost_Recording_Proc (Record_Ignored_Ghost_Node'Access);
end Initialize;
------------------------
-- Install_Ghost_Mode --
------------------------
procedure Install_Ghost_Mode (Mode : Ghost_Mode_Type) is
begin
Install_Ghost_Region (Mode, Empty);
end Install_Ghost_Mode;
--------------------------
-- Install_Ghost_Region --
--------------------------
procedure Install_Ghost_Region (Mode : Ghost_Mode_Type; N : Node_Id) is
begin
-- The context is already within an ignored Ghost region. Maintain the
-- start of the outermost ignored Ghost region.
if Present (Ignored_Ghost_Region) then
null;
-- The current region is the outermost ignored Ghost region. Save its
-- starting node.
elsif Present (N) and then Mode = Ignore then
Ignored_Ghost_Region := N;
-- Otherwise the current region is not ignored, nothing to save
else
Ignored_Ghost_Region := Empty;
end if;
Ghost_Mode := Mode;
end Install_Ghost_Region;
procedure Install_Ghost_Region (Mode : Name_Id; N : Node_Id) is
begin
Install_Ghost_Region (Name_To_Ghost_Mode (Mode), N);
end Install_Ghost_Region;
-------------------------
-- Is_Ghost_Assignment --
-------------------------
function Is_Ghost_Assignment (N : Node_Id) return Boolean is
Id : Entity_Id;
begin
-- An assignment statement is Ghost when its target denotes a Ghost
-- entity.
if Nkind (N) = N_Assignment_Statement then
Id := Ghost_Entity (Name (N));
return Present (Id) and then Is_Ghost_Entity (Id);
end if;
return False;
end Is_Ghost_Assignment;
--------------------------
-- Is_Ghost_Declaration --
--------------------------
function Is_Ghost_Declaration (N : Node_Id) return Boolean is
Id : Entity_Id;
begin
-- A declaration is Ghost when it elaborates a Ghost entity or is
-- subject to pragma Ghost.
if Is_Declaration (N) then
Id := Defining_Entity (N);
return Is_Ghost_Entity (Id) or else Is_Subject_To_Ghost (N);
end if;
return False;
end Is_Ghost_Declaration;
---------------------
-- Is_Ghost_Pragma --
---------------------
function Is_Ghost_Pragma (N : Node_Id) return Boolean is
begin
return Is_Checked_Ghost_Pragma (N) or else Is_Ignored_Ghost_Pragma (N);
end Is_Ghost_Pragma;
-----------------------------
-- Is_Ghost_Procedure_Call --
-----------------------------
function Is_Ghost_Procedure_Call (N : Node_Id) return Boolean is
Id : Entity_Id;
begin
-- A procedure call is Ghost when it invokes a Ghost procedure
if Nkind (N) = N_Procedure_Call_Statement then
Id := Ghost_Entity (Name (N));
return Present (Id) and then Is_Ghost_Entity (Id);
end if;
return False;
end Is_Ghost_Procedure_Call;
---------------------------
-- Is_Ignored_Ghost_Unit --
---------------------------
function Is_Ignored_Ghost_Unit (N : Node_Id) return Boolean is
function Ultimate_Original_Node (Nod : Node_Id) return Node_Id;
-- Obtain the original node of arbitrary node Nod following a potential
-- chain of rewritings.
----------------------------
-- Ultimate_Original_Node --
----------------------------
function Ultimate_Original_Node (Nod : Node_Id) return Node_Id is
Res : Node_Id := Nod;
begin
while Original_Node (Res) /= Res loop
Res := Original_Node (Res);
end loop;
return Res;
end Ultimate_Original_Node;
-- Start of processing for Is_Ignored_Ghost_Unit
begin
-- Inspect the original node of the unit in case removal of ignored
-- Ghost code has already taken place.
return
Nkind (N) = N_Compilation_Unit
and then Is_Ignored_Ghost_Entity
(Defining_Entity (Ultimate_Original_Node (Unit (N))));
end Is_Ignored_Ghost_Unit;
-------------------------
-- 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 or pragma 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 (N) in 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 (N) in 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. Check the original node as source constructs
-- may be rewritten into something else by expansion.
elsif Comes_From_Source (Original_Node (Decl)) then
exit;
end if;
Next (Decl);
end loop;
return False;
end Is_Subject_To_Ghost;
----------
-- Lock --
----------
procedure Lock is
begin
Ignored_Ghost_Nodes.Release;
Ignored_Ghost_Nodes.Locked := True;
end Lock;
-----------------------------------
-- Mark_And_Set_Ghost_Assignment --
-----------------------------------
procedure Mark_And_Set_Ghost_Assignment (N : Node_Id) is
-- A ghost assignment is an assignment whose left-hand side denotes a
-- ghost object. Subcomponents are not marked "ghost", so we need to
-- find the containing "whole" object. So, for "P.X.Comp (J) := ...",
-- where P is a package, X is a record, and Comp is an array, we need
-- to check the ghost flags of X.
Orig_Lhs : constant Node_Id := Name (N);
begin
-- Ghost assignments are irrelevant when the expander is inactive, and
-- processing them in that mode can lead to spurious errors.
if Expander_Active then
-- Cases where full analysis is needed, involving array indexing
-- which would otherwise be missing array-bounds checks:
if not Analyzed (Orig_Lhs)
and then
((Nkind (Orig_Lhs) = N_Indexed_Component
and then Nkind (Prefix (Orig_Lhs)) = N_Selected_Component
and then Nkind (Prefix (Prefix (Orig_Lhs))) =
N_Indexed_Component)
or else
(Nkind (Orig_Lhs) = N_Selected_Component
and then Nkind (Prefix (Orig_Lhs)) = N_Indexed_Component
and then Nkind (Prefix (Prefix (Orig_Lhs))) =
N_Selected_Component
and then Nkind (Parent (N)) /= N_Loop_Statement))
then
Analyze (Orig_Lhs);
end if;
-- Make sure Lhs is at least preanalyzed, so we can tell whether
-- it denotes a ghost variable. In some cases we need to do a full
-- analysis, or else the back end gets confused. Note that in the
-- preanalysis case, we are preanalyzing a copy of the left-hand
-- side name, temporarily attached to the tree.
declare
Lhs : constant Node_Id :=
(if Analyzed (Orig_Lhs) then Orig_Lhs
else New_Copy_Tree (Orig_Lhs));
begin
if not Analyzed (Lhs) then
Set_Name (N, Lhs);
Set_Parent (Lhs, N);
Preanalyze_Without_Errors (Lhs);
Set_Name (N, Orig_Lhs);
end if;
declare
Whole : constant Node_Id := Whole_Object_Ref (Lhs);
Id : Entity_Id;
begin
if Is_Entity_Name (Whole) then
Id := Entity (Whole);
if Present (Id) then
-- Left-hand side denotes a Checked ghost entity, so
-- install the region.
if Is_Checked_Ghost_Entity (Id) then
Install_Ghost_Region (Check, N);
-- Left-hand side denotes an Ignored ghost entity, so
-- install the region, and mark the assignment statement
-- as an ignored ghost assignment, so it will be removed
-- later.
elsif Is_Ignored_Ghost_Entity (Id) then
Install_Ghost_Region (Ignore, N);
Set_Is_Ignored_Ghost_Node (N);
Record_Ignored_Ghost_Node (N);
end if;
end if;
end if;
end;
end;
end if;
end Mark_And_Set_Ghost_Assignment;
-----------------------------
-- Mark_And_Set_Ghost_Body --
-----------------------------
procedure Mark_And_Set_Ghost_Body
(N : Node_Id;
Spec_Id : Entity_Id)
is
Body_Id : constant Entity_Id := Defining_Entity (N);
Policy : Name_Id := No_Name;
begin
-- A body becomes Ghost when it is subject to aspect or pragma Ghost
if Is_Subject_To_Ghost (N) then
Policy := Policy_In_Effect (Name_Ghost);
-- A body declared within a Ghost region is automatically Ghost
-- (SPARK RM 6.9(2)).
elsif Ghost_Mode = Check then
Policy := Name_Check;
elsif Ghost_Mode = Ignore then
Policy := Name_Ignore;
-- Inherit the "ghostness" of the previous declaration when the body
-- acts as a completion.
elsif Present (Spec_Id) then
if Is_Checked_Ghost_Entity (Spec_Id) then
Policy := Name_Check;
elsif Is_Ignored_Ghost_Entity (Spec_Id) then
Policy := Name_Ignore;
end if;
end if;
-- The Ghost policy in effect at the point of declaration and at the
-- point of completion must match (SPARK RM 6.9(14)).
Check_Ghost_Completion
(Prev_Id => Spec_Id,
Compl_Id => Body_Id);
-- Mark the body as its formals as Ghost
Mark_Ghost_Declaration_Or_Body (N, Policy);
-- Install the appropriate Ghost region
Install_Ghost_Region (Policy, N);
end Mark_And_Set_Ghost_Body;
-----------------------------------
-- Mark_And_Set_Ghost_Completion --
-----------------------------------
procedure Mark_And_Set_Ghost_Completion
(N : Node_Id;
Prev_Id : Entity_Id)
is
Compl_Id : constant Entity_Id := Defining_Entity (N);
Policy : Name_Id := No_Name;
begin
-- A completion elaborated in a Ghost region is automatically Ghost
-- (SPARK RM 6.9(2)).
if Ghost_Mode = Check then
Policy := Name_Check;
elsif Ghost_Mode = Ignore then
Policy := Name_Ignore;
-- The completion becomes Ghost when its initial declaration is also
-- Ghost.
elsif Is_Checked_Ghost_Entity (Prev_Id) then
Policy := Name_Check;
elsif Is_Ignored_Ghost_Entity (Prev_Id) then
Policy := Name_Ignore;
end if;
-- The Ghost policy in effect at the point of declaration and at the
-- point of completion must match (SPARK RM 6.9(14)).
Check_Ghost_Completion
(Prev_Id => Prev_Id,
Compl_Id => Compl_Id);
-- Mark the completion as Ghost
Mark_Ghost_Declaration_Or_Body (N, Policy);
-- Install the appropriate Ghost region
Install_Ghost_Region (Policy, N);
end Mark_And_Set_Ghost_Completion;
------------------------------------
-- Mark_And_Set_Ghost_Declaration --
------------------------------------
procedure Mark_And_Set_Ghost_Declaration (N : Node_Id) is
Par_Id : Entity_Id;
Policy : Name_Id := No_Name;
begin
-- A declaration becomes Ghost when it is subject to aspect or pragma
-- Ghost.
if Is_Subject_To_Ghost (N) then
Policy := Policy_In_Effect (Name_Ghost);
-- A declaration elaborated in a Ghost region is automatically Ghost
-- (SPARK RM 6.9(2)).
elsif Ghost_Mode = Check then
Policy := Name_Check;
elsif Ghost_Mode = Ignore then
Policy := Name_Ignore;
-- A child package or subprogram declaration becomes Ghost when its
-- parent is Ghost (SPARK RM 6.9(2)).
elsif Nkind (N) in N_Generic_Function_Renaming_Declaration
| N_Generic_Package_Declaration
| N_Generic_Package_Renaming_Declaration
| N_Generic_Procedure_Renaming_Declaration
| N_Generic_Subprogram_Declaration
| N_Package_Declaration
| N_Package_Renaming_Declaration
| N_Subprogram_Declaration
| N_Subprogram_Renaming_Declaration
and then Present (Parent_Spec (N))
then
Par_Id := Defining_Entity (Unit (Parent_Spec (N)));
if Is_Checked_Ghost_Entity (Par_Id) then
Policy := Name_Check;
elsif Is_Ignored_Ghost_Entity (Par_Id) then
Policy := Name_Ignore;
end if;
end if;
-- Mark the declaration and its formals as Ghost
Mark_Ghost_Declaration_Or_Body (N, Policy);
-- Install the appropriate Ghost region
Install_Ghost_Region (Policy, N);
end Mark_And_Set_Ghost_Declaration;
--------------------------------------
-- Mark_And_Set_Ghost_Instantiation --
--------------------------------------
procedure Mark_And_Set_Ghost_Instantiation
(N : Node_Id;
Gen_Id : Entity_Id)
is
procedure Check_Ghost_Actuals;
-- Check the context of ghost actuals
-------------------------
-- Check_Ghost_Actuals --
-------------------------
procedure Check_Ghost_Actuals is
Assoc : Node_Id := First (Generic_Associations (N));
Act : Node_Id;
begin
while Present (Assoc) loop
if Nkind (Assoc) /= N_Others_Choice then
Act := Explicit_Generic_Actual_Parameter (Assoc);
-- Within a nested instantiation, a defaulted actual is an
-- empty association, so nothing to check.
if No (Act) then
null;
elsif Comes_From_Source (Act)
and then Nkind (Act) in N_Has_Etype
and then Present (Etype (Act))
and then Is_Ghost_Entity (Etype (Act))
then
Check_Ghost_Context (Etype (Act), Act);
end if;
end if;
Next (Assoc);
end loop;
end Check_Ghost_Actuals;
-- Local variables
Policy : Name_Id := No_Name;
begin
-- An instantiation becomes Ghost when it is subject to pragma Ghost
if Is_Subject_To_Ghost (N) then
Policy := Policy_In_Effect (Name_Ghost);
-- An instantiation declaration within a Ghost region is automatically
-- Ghost (SPARK RM 6.9(2)).
elsif Ghost_Mode = Check then
Policy := Name_Check;
elsif Ghost_Mode = Ignore then
Policy := Name_Ignore;
-- Inherit the "ghostness" of the generic unit
elsif Is_Checked_Ghost_Entity (Gen_Id) then
Policy := Name_Check;
elsif Is_Ignored_Ghost_Entity (Gen_Id) then
Policy := Name_Ignore;
end if;
-- Mark the instantiation as Ghost
Mark_Ghost_Declaration_Or_Body (N, Policy);
-- Install the appropriate Ghost region
Install_Ghost_Region (Policy, N);
-- Check Ghost actuals. Given that this routine is unconditionally
-- invoked with subprogram and package instantiations, this check
-- verifies the context of all the ghost entities passed in generic
-- instantiations.
Check_Ghost_Actuals;
end Mark_And_Set_Ghost_Instantiation;
---------------------------------------
-- Mark_And_Set_Ghost_Procedure_Call --
---------------------------------------
procedure Mark_And_Set_Ghost_Procedure_Call (N : Node_Id) is
Id : Entity_Id;
begin
-- A procedure call becomes Ghost when the procedure being invoked is
-- Ghost. Install the Ghost mode of the procedure.
Id := Ghost_Entity (Name (N));
if Present (Id) then
if Is_Checked_Ghost_Entity (Id) then
Install_Ghost_Region (Check, N);
elsif Is_Ignored_Ghost_Entity (Id) then
Install_Ghost_Region (Ignore, N);
Set_Is_Ignored_Ghost_Node (N);
Record_Ignored_Ghost_Node (N);
end if;
end if;
end Mark_And_Set_Ghost_Procedure_Call;
-----------------------
-- Mark_Ghost_Clause --
-----------------------
procedure Mark_Ghost_Clause (N : Node_Id) is
Nam : Node_Id := Empty;
begin
if Nkind (N) = N_Use_Package_Clause then
Nam := Name (N);
elsif Nkind (N) = N_Use_Type_Clause then
Nam := Subtype_Mark (N);
elsif Nkind (N) = N_With_Clause then
Nam := Name (N);
end if;
if Present (Nam)
and then Is_Entity_Name (Nam)
and then Present (Entity (Nam))
and then Is_Ignored_Ghost_Entity (Entity (Nam))
then
Set_Is_Ignored_Ghost_Node (N);
Record_Ignored_Ghost_Node (N);
end if;
end Mark_Ghost_Clause;
------------------------------------
-- Mark_Ghost_Declaration_Or_Body --
------------------------------------
procedure Mark_Ghost_Declaration_Or_Body
(N : Node_Id;
Mode : Name_Id)
is
Id : constant Entity_Id := Defining_Entity (N);
Mark_Formals : Boolean := False;
Param : Node_Id;
Param_Id : Entity_Id;
begin
-- Mark the related node and its entity
if Mode = Name_Check then
Mark_Formals := True;
Set_Is_Checked_Ghost_Entity (Id);
elsif Mode = Name_Ignore then
Mark_Formals := True;
Set_Is_Ignored_Ghost_Entity (Id);
Set_Is_Ignored_Ghost_Node (N);
Record_Ignored_Ghost_Node (N);
end if;
-- Mark all formal parameters when the related node denotes a subprogram
-- or a body. The traversal is performed via the specification because
-- the related subprogram or body may be unanalyzed.
-- ??? could extra formal parameters cause a Ghost leak?
if Mark_Formals
and then Nkind (N) in N_Abstract_Subprogram_Declaration
| N_Formal_Abstract_Subprogram_Declaration
| N_Formal_Concrete_Subprogram_Declaration
| N_Generic_Subprogram_Declaration
| N_Subprogram_Body
| N_Subprogram_Body_Stub
| N_Subprogram_Declaration
| N_Subprogram_Renaming_Declaration
then
Param := First (Parameter_Specifications (Specification (N)));
while Present (Param) loop
Param_Id := Defining_Entity (Param);
if Mode = Name_Check then
Set_Is_Checked_Ghost_Entity (Param_Id);
elsif Mode = Name_Ignore then
Set_Is_Ignored_Ghost_Entity (Param_Id);
end if;
Next (Param);
end loop;
end if;
end Mark_Ghost_Declaration_Or_Body;
-----------------------
-- Mark_Ghost_Pragma --
-----------------------
procedure Mark_Ghost_Pragma
(N : Node_Id;
Id : Entity_Id)
is
begin
-- A pragma becomes Ghost when it encloses a Ghost entity or relates to
-- a Ghost entity.
if Is_Checked_Ghost_Entity (Id) then
Set_Is_Checked_Ghost_Pragma (N);
elsif Is_Ignored_Ghost_Entity (Id) then
Set_Is_Ignored_Ghost_Pragma (N);
Set_Is_Ignored_Ghost_Node (N);
Record_Ignored_Ghost_Node (N);
end if;
end Mark_Ghost_Pragma;
-------------------------
-- Mark_Ghost_Renaming --
-------------------------
procedure Mark_Ghost_Renaming
(N : Node_Id;
Id : Entity_Id)
is
Policy : Name_Id := No_Name;
begin
-- A renaming becomes Ghost when it renames a Ghost entity
if Is_Checked_Ghost_Entity (Id) then
Policy := Name_Check;
elsif Is_Ignored_Ghost_Entity (Id) then
Policy := Name_Ignore;
end if;
Mark_Ghost_Declaration_Or_Body (N, Policy);
end Mark_Ghost_Renaming;
------------------------
-- Name_To_Ghost_Mode --
------------------------
function Name_To_Ghost_Mode (Mode : Name_Id) return Ghost_Mode_Type is
begin
if Mode = Name_Check then
return Check;
elsif Mode = Name_Ignore then
return Ignore;
-- Otherwise the mode must denote one of the following:
--
-- * Disable indicates that the Ghost policy in effect is Disable
--
-- * None or No_Name indicates that the associated construct is not
-- subject to any Ghost annotation.
else
pragma Assert (Mode in Name_Disable | Name_None | No_Name);
return None;
end if;
end Name_To_Ghost_Mode;
-------------------------------
-- Record_Ignored_Ghost_Node --
-------------------------------
procedure Record_Ignored_Ghost_Node (N : Node_Or_Entity_Id) is
begin
-- Save all "top level" ignored Ghost nodes which can be safely replaced
-- with a null statement. Note that there is need to save other kinds of
-- nodes because those will always be enclosed by some top level ignored
-- Ghost node.
if Is_Body (N)
or else Is_Declaration (N)
or else Nkind (N) in N_Generic_Instantiation
| N_Push_Pop_xxx_Label
| N_Raise_xxx_Error
| N_Representation_Clause
| N_Statement_Other_Than_Procedure_Call
| N_Call_Marker
| N_Freeze_Entity
| N_Freeze_Generic_Entity
| N_Itype_Reference
| N_Pragma
| N_Procedure_Call_Statement
| N_Use_Package_Clause
| N_Use_Type_Clause
| N_Variable_Reference_Marker
| N_With_Clause
then
-- Only ignored Ghost nodes must be recorded in the table
pragma Assert (Is_Ignored_Ghost_Node (N));
Ignored_Ghost_Nodes.Append (N);
end if;
end Record_Ignored_Ghost_Node;
-------------------------------
-- Remove_Ignored_Ghost_Code --
-------------------------------
procedure Remove_Ignored_Ghost_Code is
procedure Remove_Ignored_Ghost_Node (N : Node_Id);
-- Eliminate ignored Ghost node N from the tree
-------------------------------
-- Remove_Ignored_Ghost_Node --
-------------------------------
procedure Remove_Ignored_Ghost_Node (N : Node_Id) is
begin
-- The generation and processing of ignored Ghost nodes may cause the
-- same node to be saved multiple times. Reducing the number of saves
-- to one involves costly solutions such as a hash table or the use
-- of a flag shared by all nodes. To solve this problem, the removal
-- machinery allows for multiple saves, but does not eliminate a node
-- which has already been eliminated.
if Nkind (N) = N_Null_Statement then
null;
-- Otherwise the ignored Ghost node must be eliminated
else
-- Only ignored Ghost nodes must be eliminated from the tree
pragma Assert (Is_Ignored_Ghost_Node (N));
-- Eliminate the node by rewriting it into null. Another option
-- is to remove it from the tree, however multiple corner cases
-- emerge which have be dealt individually.
Rewrite (N, Make_Null_Statement (Sloc (N)));
-- Eliminate any aspects hanging off the ignored Ghost node
Remove_Aspects (N);
end if;
end Remove_Ignored_Ghost_Node;
-- Start of processing for Remove_Ignored_Ghost_Code
begin
for Index in Ignored_Ghost_Nodes.First .. Ignored_Ghost_Nodes.Last loop
Remove_Ignored_Ghost_Node (Ignored_Ghost_Nodes.Table (Index));
end loop;
end Remove_Ignored_Ghost_Code;
--------------------------
-- Restore_Ghost_Region --
--------------------------
procedure Restore_Ghost_Region (Mode : Ghost_Mode_Type; N : Node_Id) is
begin
Ghost_Mode := Mode;
Ignored_Ghost_Region := N;
end Restore_Ghost_Region;
--------------------
-- Set_Ghost_Mode --
--------------------
procedure Set_Ghost_Mode (N : Node_Or_Entity_Id) is
procedure Set_Ghost_Mode_From_Entity (Id : Entity_Id);
-- Install the Ghost mode of entity Id
--------------------------------
-- Set_Ghost_Mode_From_Entity --
--------------------------------
procedure Set_Ghost_Mode_From_Entity (Id : Entity_Id) is
begin
if Is_Checked_Ghost_Entity (Id) then
Install_Ghost_Mode (Check);
elsif Is_Ignored_Ghost_Entity (Id) then
Install_Ghost_Mode (Ignore);
else
Install_Ghost_Mode (None);
end if;
end Set_Ghost_Mode_From_Entity;
-- Local variables
Id : Entity_Id;
-- Start of processing for Set_Ghost_Mode
begin
-- The Ghost mode of an assignment statement depends on the Ghost mode
-- of the target.
if Nkind (N) = N_Assignment_Statement then
Id := Ghost_Entity (Name (N));
if Present (Id) then
Set_Ghost_Mode_From_Entity (Id);
end if;
-- The Ghost mode of a body or a declaration depends on the Ghost mode
-- of its defining entity.
elsif Is_Body (N) or else Is_Declaration (N) then
Set_Ghost_Mode_From_Entity (Defining_Entity (N));
-- The Ghost mode of an entity depends on the entity itself
elsif Nkind (N) in N_Entity then
Set_Ghost_Mode_From_Entity (N);
-- The Ghost mode of a [generic] freeze node depends on the Ghost mode
-- of the entity being frozen.
elsif Nkind (N) in N_Freeze_Entity | N_Freeze_Generic_Entity then
Set_Ghost_Mode_From_Entity (Entity (N));
-- The Ghost mode of a pragma depends on the associated entity. The
-- property is encoded in the pragma itself.
elsif Nkind (N) = N_Pragma then
if Is_Checked_Ghost_Pragma (N) then
Install_Ghost_Mode (Check);
elsif Is_Ignored_Ghost_Pragma (N) then
Install_Ghost_Mode (Ignore);
else
Install_Ghost_Mode (None);
end if;
-- The Ghost mode of a procedure call depends on the Ghost mode of the
-- procedure being invoked.
elsif Nkind (N) = N_Procedure_Call_Statement then
Id := Ghost_Entity (Name (N));
if Present (Id) then
Set_Ghost_Mode_From_Entity (Id);
end if;
end if;
end Set_Ghost_Mode;
-------------------------
-- 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;
----------------------
-- Whole_Object_Ref --
----------------------
function Whole_Object_Ref (Ref : Node_Id) return Node_Id is
begin
if Nkind (Ref) in N_Indexed_Component | N_Slice
or else (Nkind (Ref) = N_Selected_Component
and then Is_Object_Reference (Prefix (Ref)))
then
if Is_Access_Type (Etype (Prefix (Ref))) then
return Ref;
else
return Whole_Object_Ref (Prefix (Ref));
end if;
else
return Ref;
end if;
end Whole_Object_Ref;
end Ghost;