blob: bc897d1ef18c2efdec094923c9ca2bc1ead41cfa [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- A C C E S S I B I L I T Y --
-- --
-- B o d y --
-- --
-- Copyright (C) 2022-2023, 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 Atree; use Atree;
with Checks; use Checks;
with Debug; use Debug;
with Einfo; use Einfo;
with Einfo.Entities; use Einfo.Entities;
with Elists; use Elists;
with Errout; use Errout;
with Einfo.Utils; use Einfo.Utils;
with Exp_Atag; use Exp_Atag;
with Exp_Ch3; use Exp_Ch3;
with Exp_Ch7; use Exp_Ch7;
with Exp_Tss; use Exp_Tss;
with Exp_Util; use Exp_Util;
with Namet; use Namet;
with Nlists; use Nlists;
with Nmake; use Nmake;
with Opt; use Opt;
with Restrict; use Restrict;
with Rtsfind; use Rtsfind;
with Sem; use Sem;
with Sem_Aux; use Sem_Aux;
with Sem_Ch8; use Sem_Ch8;
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 Stand; use Stand;
with Tbuild; use Tbuild;
package body Accessibility is
---------------------------
-- Accessibility_Message --
---------------------------
procedure Accessibility_Message (N : Node_Id; Typ : Entity_Id) is
Loc : constant Source_Ptr := Sloc (N);
P : constant Node_Id := Prefix (N);
Indic : Node_Id := Parent (Parent (N));
begin
-- In an instance, this is a runtime check, but one we know will fail,
-- so generate an appropriate warning.
if In_Instance_Body then
Error_Msg_Warn := SPARK_Mode /= On;
Error_Msg_F
("non-local pointer cannot point to local object<<", P);
Error_Msg_F ("\Program_Error [<<", P);
Rewrite (N,
Make_Raise_Program_Error (Loc,
Reason => PE_Accessibility_Check_Failed));
Set_Etype (N, Typ);
return;
else
Error_Msg_F ("non-local pointer cannot point to local object", P);
-- Check for case where we have a missing access definition
if Is_Record_Type (Current_Scope)
and then
Nkind (Parent (N)) in N_Discriminant_Association
| N_Index_Or_Discriminant_Constraint
then
Indic := Parent (Parent (N));
while Present (Indic)
and then Nkind (Indic) /= N_Subtype_Indication
loop
Indic := Parent (Indic);
end loop;
if Present (Indic) then
Error_Msg_NE
("\use an access definition for" &
" the access discriminant of&",
N, Entity (Subtype_Mark (Indic)));
end if;
end if;
end if;
end Accessibility_Message;
-------------------------
-- Accessibility_Level --
-------------------------
function Accessibility_Level
(Expr : Node_Id;
Level : Accessibility_Level_Kind;
In_Return_Context : Boolean := False;
Allow_Alt_Model : Boolean := True) return Node_Id
is
Loc : constant Source_Ptr := Sloc (Expr);
function Accessibility_Level (Expr : Node_Id) return Node_Id
is (Accessibility_Level (Expr, Level, In_Return_Context));
-- Renaming of the enclosing function to facilitate recursive calls
function Make_Level_Literal (Level : Uint) return Node_Id;
-- Construct an integer literal representing an accessibility level with
-- its type set to Natural.
function Innermost_Master_Scope_Depth (N : Node_Id) return Uint;
-- Returns the scope depth of the given node's innermost enclosing scope
-- (effectively the accessibility level of the innermost enclosing
-- master).
function Function_Call_Or_Allocator_Level (N : Node_Id) return Node_Id;
-- Centralized processing of subprogram calls which may appear in prefix
-- notation.
function Typ_Access_Level (Typ : Entity_Id) return Uint
is (Type_Access_Level (Typ, Allow_Alt_Model));
-- Renaming of Type_Access_Level with Allow_Alt_Model specified to avoid
-- passing the parameter specifically in every call.
----------------------------------
-- Innermost_Master_Scope_Depth --
----------------------------------
function Innermost_Master_Scope_Depth (N : Node_Id) return Uint is
Encl_Scop : Entity_Id;
Ent : Entity_Id;
Node_Par : Node_Id := Parent (N);
Master_Lvl_Modifier : Int := 0;
begin
-- Locate the nearest enclosing node (by traversing Parents)
-- that Defining_Entity can be applied to, and return the
-- depth of that entity's nearest enclosing scope.
-- The RM 7.6.1(3) definition of "master" includes statements
-- and conditions for loops among other things. Are these cases
-- detected properly ???
while Present (Node_Par) loop
Ent := Defining_Entity_Or_Empty (Node_Par);
if Present (Ent) then
Encl_Scop := Find_Enclosing_Scope (Ent);
-- Ignore transient scopes made during expansion while also
-- taking into account certain expansions - like iterators
-- which get expanded into renamings and thus not marked
-- as coming from source.
if Comes_From_Source (Node_Par)
or else (Nkind (Node_Par) = N_Object_Renaming_Declaration
and then Comes_From_Iterator (Node_Par))
then
-- Note that in some rare cases the scope depth may not be
-- set, for example, when we are in the middle of analyzing
-- a type and the enclosing scope is said type. So, instead,
-- continue to move up the parent chain since the scope
-- depth of the type's parent is the same as that of the
-- type.
if not Scope_Depth_Set (Encl_Scop) then
pragma Assert (Nkind (Parent (Encl_Scop))
= N_Full_Type_Declaration);
else
return
Scope_Depth (Encl_Scop) + Master_Lvl_Modifier;
end if;
end if;
-- For a return statement within a function, return
-- the depth of the function itself. This is not just
-- a small optimization, but matters when analyzing
-- the expression in an expression function before
-- the body is created.
elsif Nkind (Node_Par) in N_Extended_Return_Statement
| N_Simple_Return_Statement
then
return Scope_Depth (Enclosing_Subprogram (Node_Par));
-- Statements are counted as masters
elsif Is_Master (Node_Par) then
Master_Lvl_Modifier := Master_Lvl_Modifier + 1;
end if;
Node_Par := Parent (Node_Par);
end loop;
-- Should never reach the following return
pragma Assert (False);
return Scope_Depth (Current_Scope) + 1;
end Innermost_Master_Scope_Depth;
------------------------
-- Make_Level_Literal --
------------------------
function Make_Level_Literal (Level : Uint) return Node_Id is
Result : constant Node_Id := Make_Integer_Literal (Loc, Level);
begin
Set_Etype (Result, Standard_Natural);
return Result;
end Make_Level_Literal;
--------------------------------------
-- Function_Call_Or_Allocator_Level --
--------------------------------------
function Function_Call_Or_Allocator_Level (N : Node_Id) return Node_Id is
Par : Node_Id;
Prev_Par : Node_Id;
begin
-- Results of functions are objects, so we either get the
-- accessibility of the function or, in case of a call which is
-- indirect, the level of the access-to-subprogram type.
-- This code looks wrong ???
if Nkind (N) = N_Function_Call
and then Ada_Version < Ada_2005
then
if Is_Entity_Name (Name (N)) then
return Make_Level_Literal
(Subprogram_Access_Level (Entity (Name (N))));
else
return Make_Level_Literal
(Typ_Access_Level (Etype (Prefix (Name (N)))));
end if;
-- We ignore coextensions as they cannot be implemented under the
-- "small-integer" model.
elsif Nkind (N) = N_Allocator
and then (Is_Static_Coextension (N)
or else Is_Dynamic_Coextension (N))
then
return Make_Level_Literal (Scope_Depth (Standard_Standard));
end if;
-- Named access types have a designated level
if Is_Named_Access_Type (Etype (N)) then
return Make_Level_Literal (Typ_Access_Level (Etype (N)));
-- Otherwise, the level is dictated by RM 3.10.2 (10.7/3)
else
-- Check No_Dynamic_Accessibility_Checks restriction override for
-- alternative accessibility model.
if Allow_Alt_Model
and then No_Dynamic_Accessibility_Checks_Enabled (N)
and then Is_Anonymous_Access_Type (Etype (N))
then
-- In the alternative model the level is that of the
-- designated type.
if Debug_Flag_Underscore_B then
return Make_Level_Literal (Typ_Access_Level (Etype (N)));
-- For function calls the level is that of the innermost
-- master, otherwise (for allocators etc.) we get the level
-- of the corresponding anonymous access type, which is
-- calculated through the normal path of execution.
elsif Nkind (N) = N_Function_Call then
return Make_Level_Literal
(Innermost_Master_Scope_Depth (Expr));
end if;
end if;
if Nkind (N) = N_Function_Call then
-- Dynamic checks are generated when we are within a return
-- value or we are in a function call within an anonymous
-- access discriminant constraint of a return object (signified
-- by In_Return_Context) on the side of the callee.
-- So, in this case, return accessibility level of the
-- enclosing subprogram.
if In_Return_Value (N)
or else In_Return_Context
then
return Make_Level_Literal
(Subprogram_Access_Level (Current_Subprogram));
end if;
end if;
-- When the call is being dereferenced the level is that of the
-- enclosing master of the dereferenced call.
if Nkind (Parent (N)) in N_Explicit_Dereference
| N_Indexed_Component
| N_Selected_Component
then
return Make_Level_Literal
(Innermost_Master_Scope_Depth (Expr));
end if;
-- Find any relevant enclosing parent nodes that designate an
-- object being initialized.
-- Note: The above is only relevant if the result is used "in its
-- entirety" as RM 3.10.2 (10.2/3) states. However, this is
-- accounted for in the case statement in the main body of
-- Accessibility_Level for N_Selected_Component.
Par := Parent (Expr);
Prev_Par := Empty;
while Present (Par) loop
-- Detect an expanded implicit conversion, typically this
-- occurs on implicitly converted actuals in calls.
-- Does this catch all implicit conversions ???
if Nkind (Par) = N_Type_Conversion
and then Is_Named_Access_Type (Etype (Par))
then
return Make_Level_Literal
(Typ_Access_Level (Etype (Par)));
end if;
-- Jump out when we hit an object declaration or the right-hand
-- side of an assignment, or a construct such as an aggregate
-- subtype indication which would be the result is not used
-- "in its entirety."
exit when Nkind (Par) in N_Object_Declaration
or else (Nkind (Par) = N_Assignment_Statement
and then Name (Par) /= Prev_Par);
Prev_Par := Par;
Par := Parent (Par);
end loop;
-- Assignment statements are handled in a similar way in
-- accordance to the left-hand part. However, strictly speaking,
-- this is illegal according to the RM, but this change is needed
-- to pass an ACATS C-test and is useful in general ???
case Nkind (Par) is
when N_Object_Declaration =>
return Make_Level_Literal
(Scope_Depth
(Scope (Defining_Identifier (Par))));
when N_Assignment_Statement =>
-- Return the accessibility level of the left-hand part
return Accessibility_Level
(Expr => Name (Par),
Level => Object_Decl_Level,
In_Return_Context => In_Return_Context);
when others =>
return Make_Level_Literal
(Innermost_Master_Scope_Depth (Expr));
end case;
end if;
end Function_Call_Or_Allocator_Level;
-- Local variables
E : Node_Id := Original_Node (Expr);
Pre : Node_Id;
-- Start of processing for Accessibility_Level
begin
-- We could be looking at a reference to a formal due to the expansion
-- of entries and other cases, so obtain the renaming if necessary.
if Present (Param_Entity (Expr)) then
E := Param_Entity (Expr);
end if;
-- Extract the entity
if Nkind (E) in N_Has_Entity and then Present (Entity (E)) then
E := Entity (E);
-- Deal with a possible renaming of a private protected component
if Ekind (E) in E_Constant | E_Variable and then Is_Prival (E) then
E := Prival_Link (E);
end if;
end if;
-- Perform the processing on the expression
case Nkind (E) is
-- The level of an aggregate is that of the innermost master that
-- evaluates it as defined in RM 3.10.2 (10/4).
when N_Aggregate =>
return Make_Level_Literal (Innermost_Master_Scope_Depth (Expr));
-- The accessibility level is that of the access type, except for an
-- anonymous allocators which have special rules defined in RM 3.10.2
-- (14/3).
when N_Allocator =>
return Function_Call_Or_Allocator_Level (E);
-- We could reach this point for two reasons. Either the expression
-- applies to a special attribute ('Loop_Entry, 'Result, or 'Old), or
-- we are looking at the access attributes directly ('Access,
-- 'Address, or 'Unchecked_Access).
when N_Attribute_Reference =>
Pre := Original_Node (Prefix (E));
-- Regular 'Access attribute presence means we have to look at the
-- prefix.
if Attribute_Name (E) = Name_Access then
return Accessibility_Level (Prefix (E));
-- Unchecked or unrestricted attributes have unlimited depth
elsif Attribute_Name (E) in Name_Address
| Name_Unchecked_Access
| Name_Unrestricted_Access
then
return Make_Level_Literal (Scope_Depth (Standard_Standard));
-- 'Access can be taken further against other special attributes,
-- so handle these cases explicitly.
elsif Attribute_Name (E)
in Name_Old | Name_Loop_Entry | Name_Result
then
-- Named access types
if Is_Named_Access_Type (Etype (Pre)) then
return Make_Level_Literal
(Typ_Access_Level (Etype (Pre)));
-- Anonymous access types
elsif Nkind (Pre) in N_Has_Entity
and then Ekind (Entity (Pre)) not in Subprogram_Kind
and then Present (Get_Dynamic_Accessibility (Entity (Pre)))
and then Level = Dynamic_Level
then
return New_Occurrence_Of
(Get_Dynamic_Accessibility (Entity (Pre)), Loc);
-- Otherwise the level is treated in a similar way as
-- aggregates according to RM 6.1.1 (35.1/4) which concerns
-- an implicit constant declaration - in turn defining the
-- accessibility level to be that of the implicit constant
-- declaration.
else
return Make_Level_Literal
(Innermost_Master_Scope_Depth (Expr));
end if;
else
raise Program_Error;
end if;
-- This is the "base case" for accessibility level calculations which
-- means we are near the end of our recursive traversal.
when N_Defining_Identifier =>
-- A dynamic check is performed on the side of the callee when we
-- are within a return statement, so return a library-level
-- accessibility level to null out checks on the side of the
-- caller.
if Is_Explicitly_Aliased (E)
and then (In_Return_Context
or else (Level /= Dynamic_Level
and then In_Return_Value (Expr)))
then
return Make_Level_Literal (Scope_Depth (Standard_Standard));
-- Something went wrong and an extra accessibility formal has not
-- been generated when one should have ???
elsif Is_Formal (E)
and then No (Get_Dynamic_Accessibility (E))
and then Ekind (Etype (E)) = E_Anonymous_Access_Type
then
return Make_Level_Literal (Scope_Depth (Standard_Standard));
-- Stand-alone object of an anonymous access type "SAOAAT"
elsif (Is_Formal (E)
or else Ekind (E) in E_Variable
| E_Constant)
and then Present (Get_Dynamic_Accessibility (E))
and then (Level = Dynamic_Level
or else Level = Zero_On_Dynamic_Level)
then
if Level = Zero_On_Dynamic_Level then
return Make_Level_Literal
(Scope_Depth (Standard_Standard));
end if;
-- No_Dynamic_Accessibility_Checks restriction override for
-- alternative accessibility model.
if Allow_Alt_Model
and then No_Dynamic_Accessibility_Checks_Enabled (E)
then
-- In the alternative model the level is that of the
-- designated type entity's context.
if Debug_Flag_Underscore_B then
return Make_Level_Literal (Typ_Access_Level (Etype (E)));
-- Otherwise the level depends on the entity's context
elsif Is_Formal (E) then
return Make_Level_Literal
(Subprogram_Access_Level
(Enclosing_Subprogram (E)));
else
return Make_Level_Literal
(Scope_Depth (Enclosing_Dynamic_Scope (E)));
end if;
end if;
-- Return the dynamic level in the normal case
return New_Occurrence_Of
(Get_Dynamic_Accessibility (E), Loc);
-- Initialization procedures have a special extra accessibility
-- parameter associated with the level at which the object
-- being initialized exists
elsif Ekind (E) = E_Record_Type
and then Is_Limited_Record (E)
and then Current_Scope = Init_Proc (E)
and then Present (Init_Proc_Level_Formal (Current_Scope))
then
return New_Occurrence_Of
(Init_Proc_Level_Formal (Current_Scope), Loc);
-- Current instance of the type is deeper than that of the type
-- according to RM 3.10.2 (21).
elsif Is_Type (E) then
-- When restriction No_Dynamic_Accessibility_Checks is active
-- along with -gnatd_b.
if Allow_Alt_Model
and then No_Dynamic_Accessibility_Checks_Enabled (E)
and then Debug_Flag_Underscore_B
then
return Make_Level_Literal (Typ_Access_Level (E));
end if;
-- Normal path
return Make_Level_Literal (Typ_Access_Level (E) + 1);
-- Move up the renamed entity or object if it came from source
-- since expansion may have created a dummy renaming under
-- certain circumstances.
-- Note: We check if the original node of the renaming comes
-- from source because the node may have been rewritten.
elsif Present (Renamed_Entity_Or_Object (E))
and then Comes_From_Source
(Original_Node (Renamed_Entity_Or_Object (E)))
then
return Accessibility_Level (Renamed_Entity_Or_Object (E));
-- Named access types get their level from their associated type
elsif Is_Named_Access_Type (Etype (E)) then
return Make_Level_Literal
(Typ_Access_Level (Etype (E)));
-- Check if E is an expansion-generated renaming of an iterator
-- by examining Related_Expression. If so, determine the
-- accessibility level based on the original expression.
elsif Ekind (E) in E_Constant | E_Variable
and then Present (Related_Expression (E))
then
return Accessibility_Level (Related_Expression (E));
elsif Level = Dynamic_Level
and then Ekind (E) in E_In_Parameter | E_In_Out_Parameter
and then Present (Init_Proc_Level_Formal (Scope (E)))
then
return New_Occurrence_Of
(Init_Proc_Level_Formal (Scope (E)), Loc);
-- Normal object - get the level of the enclosing scope
else
return Make_Level_Literal
(Scope_Depth (Enclosing_Dynamic_Scope (E)));
end if;
-- Handle indexed and selected components including the special cases
-- whereby there is an implicit dereference, a component of a
-- composite type, or a function call in prefix notation.
-- We don't handle function calls in prefix notation correctly ???
when N_Indexed_Component | N_Selected_Component | N_Slice =>
Pre := Prefix (E);
-- Fetch the original node when the prefix comes from the result
-- of expanding a function call since we want to find the level
-- of the original source call.
if not Comes_From_Source (Pre)
and then Nkind (Original_Node (Pre)) = N_Function_Call
then
Pre := Original_Node (Pre);
end if;
-- When E is an indexed component or selected component and
-- the current Expr is a function call, we know that we are
-- looking at an expanded call in prefix notation.
if Nkind (Expr) = N_Function_Call then
return Function_Call_Or_Allocator_Level (Expr);
-- If the prefix is a named access type, then we are dealing
-- with an implicit deferences. In that case the level is that
-- of the named access type in the prefix.
elsif Is_Named_Access_Type (Etype (Pre)) then
return Make_Level_Literal
(Typ_Access_Level (Etype (Pre)));
-- The current expression is a named access type, so there is no
-- reason to look at the prefix. Instead obtain the level of E's
-- named access type.
elsif Is_Named_Access_Type (Etype (E)) then
return Make_Level_Literal
(Typ_Access_Level (Etype (E)));
-- A nondiscriminant selected component where the component
-- is an anonymous access type means that its associated
-- level is that of the containing type - see RM 3.10.2 (16).
-- Note that when restriction No_Dynamic_Accessibility_Checks is
-- in effect we treat discriminant components as regular
-- components.
elsif
(Nkind (E) = N_Selected_Component
and then Ekind (Etype (E)) = E_Anonymous_Access_Type
and then Ekind (Etype (Pre)) /= E_Anonymous_Access_Type
and then (not (Nkind (Selector_Name (E)) in N_Has_Entity
and then Ekind (Entity (Selector_Name (E)))
= E_Discriminant)
-- The alternative accessibility models both treat
-- discriminants as regular components.
or else (No_Dynamic_Accessibility_Checks_Enabled (E)
and then Allow_Alt_Model)))
-- Arrays featuring components of anonymous access components
-- get their corresponding level from their containing type's
-- declaration.
or else
(Nkind (E) = N_Indexed_Component
and then Ekind (Etype (E)) = E_Anonymous_Access_Type
and then Ekind (Etype (Pre)) in Array_Kind
and then Ekind (Component_Type (Base_Type (Etype (Pre))))
= E_Anonymous_Access_Type)
then
-- When restriction No_Dynamic_Accessibility_Checks is active
-- and -gnatd_b set, the level is that of the designated type.
if Allow_Alt_Model
and then No_Dynamic_Accessibility_Checks_Enabled (E)
and then Debug_Flag_Underscore_B
then
return Make_Level_Literal
(Typ_Access_Level (Etype (E)));
end if;
-- Otherwise proceed normally
return Make_Level_Literal
(Typ_Access_Level (Etype (Prefix (E))));
-- The accessibility calculation routine that handles function
-- calls (Function_Call_Level) assumes, in the case the
-- result is of an anonymous access type, that the result will be
-- used "in its entirety" when the call is present within an
-- assignment or object declaration.
-- To properly handle cases where the result is not used in its
-- entirety, we test if the prefix of the component in question is
-- a function call, which tells us that one of its components has
-- been identified and is being accessed. Therefore we can
-- conclude that the result is not used "in its entirety"
-- according to RM 3.10.2 (10.2/3).
elsif Nkind (Pre) = N_Function_Call
and then not Is_Named_Access_Type (Etype (Pre))
then
-- Dynamic checks are generated when we are within a return
-- value or we are in a function call within an anonymous
-- access discriminant constraint of a return object (signified
-- by In_Return_Context) on the side of the callee.
-- So, in this case, return a library accessibility level to
-- null out the check on the side of the caller.
if (In_Return_Value (E)
or else In_Return_Context)
and then Level /= Dynamic_Level
then
return Make_Level_Literal
(Scope_Depth (Standard_Standard));
end if;
return Make_Level_Literal
(Innermost_Master_Scope_Depth (Expr));
-- Otherwise, continue recursing over the expression prefixes
else
return Accessibility_Level (Prefix (E));
end if;
-- Qualified expressions
when N_Qualified_Expression =>
if Is_Named_Access_Type (Etype (E)) then
return Make_Level_Literal
(Typ_Access_Level (Etype (E)));
else
return Accessibility_Level (Expression (E));
end if;
-- Handle function calls
when N_Function_Call =>
return Function_Call_Or_Allocator_Level (E);
-- Explicit dereference accessibility level calculation
when N_Explicit_Dereference =>
Pre := Original_Node (Prefix (E));
-- The prefix is a named access type so the level is taken from
-- its type.
if Is_Named_Access_Type (Etype (Pre)) then
return Make_Level_Literal (Typ_Access_Level (Etype (Pre)));
-- Otherwise, recurse deeper
else
return Accessibility_Level (Prefix (E));
end if;
-- Type conversions
when N_Type_Conversion | N_Unchecked_Type_Conversion =>
-- View conversions are special in that they require use to
-- inspect the expression of the type conversion.
-- Allocators of anonymous access types are internally generated,
-- so recurse deeper in that case as well.
if Is_View_Conversion (E)
or else Ekind (Etype (E)) = E_Anonymous_Access_Type
then
return Accessibility_Level (Expression (E));
-- We don't care about the master if we are looking at a named
-- access type.
elsif Is_Named_Access_Type (Etype (E)) then
return Make_Level_Literal
(Typ_Access_Level (Etype (E)));
-- In section RM 3.10.2 (10/4) the accessibility rules for
-- aggregates and value conversions are outlined. Are these
-- followed in the case of initialization of an object ???
-- Should use Innermost_Master_Scope_Depth ???
else
return Accessibility_Level (Current_Scope);
end if;
-- Default to the type accessibility level for the type of the
-- expression's entity.
when others =>
return Make_Level_Literal (Typ_Access_Level (Etype (E)));
end case;
end Accessibility_Level;
-------------------------------
-- Apply_Accessibility_Check --
-------------------------------
procedure Apply_Accessibility_Check
(N : Node_Id;
Typ : Entity_Id;
Insert_Node : Node_Id)
is
Loc : constant Source_Ptr := Sloc (N);
Check_Cond : Node_Id;
Param_Ent : Entity_Id := Param_Entity (N);
Param_Level : Node_Id;
Type_Level : Node_Id;
begin
-- Verify we haven't tried to add a dynamic accessibility check when we
-- shouldn't.
pragma Assert (not No_Dynamic_Accessibility_Checks_Enabled (N));
if Ada_Version >= Ada_2012
and then No (Param_Ent)
and then Is_Entity_Name (N)
and then Ekind (Entity (N)) in E_Constant | E_Variable
and then Present (Effective_Extra_Accessibility (Entity (N)))
then
Param_Ent := Entity (N);
while Present (Renamed_Object (Param_Ent)) loop
-- Renamed_Object must return an Entity_Name here
-- because of preceding "Present (E_E_A (...))" test.
Param_Ent := Entity (Renamed_Object (Param_Ent));
end loop;
end if;
if Inside_A_Generic then
return;
-- Only apply the run-time check if the access parameter has an
-- associated extra access level parameter and when accessibility checks
-- are enabled.
elsif Present (Param_Ent)
and then Present (Get_Dynamic_Accessibility (Param_Ent))
and then not Accessibility_Checks_Suppressed (Param_Ent)
and then not Accessibility_Checks_Suppressed (Typ)
then
-- Obtain the parameter's accessibility level
Param_Level :=
New_Occurrence_Of (Get_Dynamic_Accessibility (Param_Ent), Loc);
-- Use the dynamic accessibility parameter for the function's result
-- when one has been created instead of statically referring to the
-- deepest type level so as to appropriatly handle the rules for
-- RM 3.10.2 (10.1/3).
if Ekind (Scope (Param_Ent)) = E_Function
and then In_Return_Value (N)
and then Ekind (Typ) = E_Anonymous_Access_Type
then
-- Associate the level of the result type to the extra result
-- accessibility parameter belonging to the current function.
if Present (Extra_Accessibility_Of_Result (Scope (Param_Ent))) then
Type_Level :=
New_Occurrence_Of
(Extra_Accessibility_Of_Result (Scope (Param_Ent)), Loc);
-- In Ada 2005 and earlier modes, a result extra accessibility
-- parameter is not generated and no dynamic check is performed.
else
return;
end if;
-- Otherwise get the type's accessibility level normally
else
Type_Level :=
Make_Integer_Literal (Loc, Deepest_Type_Access_Level (Typ));
end if;
-- Raise Program_Error if the accessibility level of the access
-- parameter is deeper than the level of the target access type.
Check_Cond :=
Make_Op_Gt (Loc,
Left_Opnd => Param_Level,
Right_Opnd => Type_Level);
Insert_Action (Insert_Node,
Make_Raise_Program_Error (Loc,
Condition => Check_Cond,
Reason => PE_Accessibility_Check_Failed));
Analyze_And_Resolve (N);
-- If constant folding has happened on the condition for the
-- generated error, then warn about it being unconditional.
if Nkind (Check_Cond) = N_Identifier
and then Entity (Check_Cond) = Standard_True
then
Error_Msg_Warn := SPARK_Mode /= On;
Error_Msg_N ("accessibility check fails<<", N);
Error_Msg_N ("\Program_Error [<<", N);
end if;
end if;
end Apply_Accessibility_Check;
---------------------------------------------
-- Apply_Accessibility_Check_For_Allocator --
---------------------------------------------
procedure Apply_Accessibility_Check_For_Allocator
(N : Node_Id;
Exp : Node_Id;
Ref : Node_Id;
Built_In_Place : Boolean := False)
is
Loc : constant Source_Ptr := Sloc (N);
PtrT : constant Entity_Id := Etype (N);
DesigT : constant Entity_Id := Designated_Type (PtrT);
Pool_Id : constant Entity_Id := Associated_Storage_Pool (PtrT);
Cond : Node_Id;
Fin_Call : Node_Id;
Free_Stmt : Node_Id;
Obj_Ref : Node_Id;
Stmts : List_Id;
begin
if Ada_Version >= Ada_2005
and then Is_Class_Wide_Type (DesigT)
and then Tagged_Type_Expansion
and then not Scope_Suppress.Suppress (Accessibility_Check)
and then not No_Dynamic_Accessibility_Checks_Enabled (Ref)
and then
(Type_Access_Level (Etype (Exp)) > Type_Access_Level (PtrT)
or else
(Is_Class_Wide_Type (Etype (Exp))
and then Scope (PtrT) /= Current_Scope))
then
-- If the allocator was built in place, Ref is already a reference
-- to the access object initialized to the result of the allocator
-- (see Exp_Ch6.Make_Build_In_Place_Call_In_Allocator). We call
-- Remove_Side_Effects for cases where the build-in-place call may
-- still be the prefix of the reference (to avoid generating
-- duplicate calls). Otherwise, it is the entity associated with
-- the object containing the address of the allocated object.
if Built_In_Place then
Remove_Side_Effects (Ref);
Obj_Ref := New_Copy_Tree (Ref);
else
Obj_Ref := New_Occurrence_Of (Ref, Loc);
end if;
-- For access to interface types we must generate code to displace
-- the pointer to the base of the object since the subsequent code
-- references components located in the TSD of the object (which
-- is associated with the primary dispatch table --see a-tags.ads)
-- and also generates code invoking Free, which requires also a
-- reference to the base of the unallocated object.
if Is_Interface (DesigT) and then Tagged_Type_Expansion then
Obj_Ref :=
Unchecked_Convert_To (Etype (Obj_Ref),
Make_Function_Call (Loc,
Name =>
New_Occurrence_Of (RTE (RE_Base_Address), Loc),
Parameter_Associations => New_List (
Unchecked_Convert_To (RTE (RE_Address),
New_Copy_Tree (Obj_Ref)))));
end if;
-- Step 1: Create the object clean up code
Stmts := New_List;
-- Deallocate the object if the accessibility check fails. This is
-- done only on targets or profiles that support deallocation.
-- Free (Obj_Ref);
if RTE_Available (RE_Free) then
Free_Stmt := Make_Free_Statement (Loc, New_Copy_Tree (Obj_Ref));
Set_Storage_Pool (Free_Stmt, Pool_Id);
Append_To (Stmts, Free_Stmt);
-- The target or profile cannot deallocate objects
else
Free_Stmt := Empty;
end if;
-- Finalize the object if applicable. Generate:
-- [Deep_]Finalize (Obj_Ref.all);
if Needs_Finalization (DesigT)
and then not No_Heap_Finalization (PtrT)
then
Fin_Call :=
Make_Final_Call
(Obj_Ref =>
Make_Explicit_Dereference (Loc, New_Copy (Obj_Ref)),
Typ => DesigT);
-- Guard against a missing [Deep_]Finalize when the designated
-- type was not properly frozen.
if No (Fin_Call) then
Fin_Call := Make_Null_Statement (Loc);
end if;
-- When the target or profile supports deallocation, wrap the
-- finalization call in a block to ensure proper deallocation even
-- if finalization fails. Generate:
-- begin
-- <Fin_Call>
-- exception
-- when others =>
-- <Free_Stmt>
-- raise;
-- end;
if Present (Free_Stmt) then
Fin_Call :=
Make_Block_Statement (Loc,
Handled_Statement_Sequence =>
Make_Handled_Sequence_Of_Statements (Loc,
Statements => New_List (Fin_Call),
Exception_Handlers => New_List (
Make_Exception_Handler (Loc,
Exception_Choices => New_List (
Make_Others_Choice (Loc)),
Statements => New_List (
New_Copy_Tree (Free_Stmt),
Make_Raise_Statement (Loc))))));
end if;
Prepend_To (Stmts, Fin_Call);
end if;
-- Signal the accessibility failure through a Program_Error
Append_To (Stmts,
Make_Raise_Program_Error (Loc,
Reason => PE_Accessibility_Check_Failed));
-- Step 2: Create the accessibility comparison
-- Generate:
-- Ref'Tag
Obj_Ref :=
Make_Attribute_Reference (Loc,
Prefix => Obj_Ref,
Attribute_Name => Name_Tag);
-- For tagged types, determine the accessibility level by looking at
-- the type specific data of the dispatch table. Generate:
-- Type_Specific_Data (Address (Ref'Tag)).Access_Level
if Tagged_Type_Expansion then
Cond := Build_Get_Access_Level (Loc, Obj_Ref);
-- Use a runtime call to determine the accessibility level when
-- compiling on virtual machine targets. Generate:
-- Get_Access_Level (Ref'Tag)
else
Cond :=
Make_Function_Call (Loc,
Name =>
New_Occurrence_Of (RTE (RE_Get_Access_Level), Loc),
Parameter_Associations => New_List (Obj_Ref));
end if;
Cond :=
Make_Op_Gt (Loc,
Left_Opnd => Cond,
Right_Opnd => Accessibility_Level (N, Dynamic_Level));
-- Due to the complexity and side effects of the check, utilize an if
-- statement instead of the regular Program_Error circuitry.
Insert_Action (N,
Make_Implicit_If_Statement (N,
Condition => Cond,
Then_Statements => Stmts));
end if;
end Apply_Accessibility_Check_For_Allocator;
------------------------------------------
-- Check_Return_Construct_Accessibility --
------------------------------------------
procedure Check_Return_Construct_Accessibility
(Return_Stmt : Node_Id;
Stm_Entity : Entity_Id)
is
Loc : constant Source_Ptr := Sloc (Return_Stmt);
Scope_Id : constant Entity_Id := Return_Applies_To (Stm_Entity);
R_Type : constant Entity_Id := Etype (Scope_Id);
-- Function result subtype
function First_Selector (Assoc : Node_Id) return Node_Id;
-- Obtain the first selector or choice from a given association
function Is_Formal_Of_Current_Function
(Assoc_Expr : Node_Id) return Boolean;
-- Predicate to test if a given expression associated with a
-- discriminant is a formal parameter to the function in which the
-- return construct we checking applies to.
--------------------
-- First_Selector --
--------------------
function First_Selector (Assoc : Node_Id) return Node_Id is
begin
if Nkind (Assoc) = N_Component_Association then
return First (Choices (Assoc));
elsif Nkind (Assoc) = N_Discriminant_Association then
return (First (Selector_Names (Assoc)));
else
raise Program_Error;
end if;
end First_Selector;
-----------------------------------
-- Is_Formal_Of_Current_Function --
-----------------------------------
function Is_Formal_Of_Current_Function
(Assoc_Expr : Node_Id) return Boolean is
begin
return Is_Entity_Name (Assoc_Expr)
and then Enclosing_Subprogram
(Entity (Assoc_Expr)) = Scope_Id
and then Is_Formal (Entity (Assoc_Expr));
end Is_Formal_Of_Current_Function;
-- Local declarations
Assoc : Node_Id := Empty;
-- Assoc should perhaps be renamed and declared as a
-- Node_Or_Entity_Id since it encompasses not only component and
-- discriminant associations, but also discriminant components within
-- a type declaration or subtype indication ???
Assoc_Expr : Node_Id;
Assoc_Present : Boolean := False;
Check_Cond : Node_Id;
Unseen_Disc_Count : Nat := 0;
Seen_Discs : Elist_Id;
Disc : Entity_Id;
First_Disc : Entity_Id;
Obj_Decl : Node_Id;
Return_Con : Node_Id;
Unqual : Node_Id;
-- Start of processing for Check_Return_Construct_Accessibility
begin
-- Only perform checks on record types with access discriminants and
-- non-internally generated functions.
if not Is_Record_Type (R_Type)
or else not Has_Anonymous_Access_Discriminant (R_Type)
or else not Comes_From_Source (Return_Stmt)
then
return;
end if;
-- We are only interested in return statements
if Nkind (Return_Stmt) not in
N_Extended_Return_Statement | N_Simple_Return_Statement
then
return;
end if;
-- Fetch the object from the return statement, in the case of a
-- simple return statement the expression is part of the node.
if Nkind (Return_Stmt) = N_Extended_Return_Statement then
-- Obtain the object definition from the expanded extended return
Return_Con := First (Return_Object_Declarations (Return_Stmt));
while Present (Return_Con) loop
-- Inspect the original node to avoid object declarations
-- expanded into renamings.
if Nkind (Original_Node (Return_Con)) = N_Object_Declaration
and then Comes_From_Source (Original_Node (Return_Con))
then
exit;
end if;
Nlists.Next (Return_Con);
end loop;
pragma Assert (Present (Return_Con));
-- Could be dealing with a renaming
Return_Con := Original_Node (Return_Con);
else
Return_Con := Expression (Return_Stmt);
end if;
-- Obtain the accessibility levels of the expressions associated
-- with all anonymous access discriminants, then generate a
-- dynamic check or static error when relevant.
-- Note the repeated use of Original_Node to avoid checking
-- expanded code.
Unqual := Original_Node (Unqualify (Original_Node (Return_Con)));
-- Get the corresponding declaration based on the return object's
-- identifier.
if Nkind (Unqual) = N_Identifier
and then Nkind (Parent (Entity (Unqual)))
in N_Object_Declaration
| N_Object_Renaming_Declaration
then
Obj_Decl := Original_Node (Parent (Entity (Unqual)));
-- We were passed the object declaration directly, so use it
elsif Nkind (Unqual) in N_Object_Declaration
| N_Object_Renaming_Declaration
then
Obj_Decl := Unqual;
-- Otherwise, we are looking at something else
else
Obj_Decl := Empty;
end if;
-- Hop up object renamings when present
if Present (Obj_Decl)
and then Nkind (Obj_Decl) = N_Object_Renaming_Declaration
then
while Nkind (Obj_Decl) = N_Object_Renaming_Declaration loop
if Nkind (Name (Obj_Decl)) not in N_Entity then
-- We may be looking at the expansion of iterators or
-- some other internally generated construct, so it is safe
-- to ignore checks ???
if not Comes_From_Source (Obj_Decl) then
return;
end if;
Obj_Decl := Original_Node
(Declaration_Node
(Ultimate_Prefix (Name (Obj_Decl))));
-- Move up to the next declaration based on the object's name
else
Obj_Decl := Original_Node
(Declaration_Node (Name (Obj_Decl)));
end if;
end loop;
end if;
-- Obtain the discriminant values from the return aggregate
-- Do we cover extension aggregates correctly ???
if Nkind (Unqual) = N_Aggregate then
if Present (Expressions (Unqual)) then
Assoc := First (Expressions (Unqual));
else
Assoc := First (Component_Associations (Unqual));
end if;
-- There is an object declaration for the return object
elsif Present (Obj_Decl) then
-- When a subtype indication is present in an object declaration
-- it must contain the object's discriminants.
if Nkind (Object_Definition (Obj_Decl)) = N_Subtype_Indication then
Assoc := First
(Constraints
(Constraint
(Object_Definition (Obj_Decl))));
-- The object declaration contains an aggregate
elsif Present (Expression (Obj_Decl)) then
if Nkind (Unqualify (Expression (Obj_Decl))) = N_Aggregate then
-- Grab the first associated discriminant expresion
if Present
(Expressions (Unqualify (Expression (Obj_Decl))))
then
Assoc := First
(Expressions
(Unqualify (Expression (Obj_Decl))));
else
Assoc := First
(Component_Associations
(Unqualify (Expression (Obj_Decl))));
end if;
-- Otherwise, this is something else
else
return;
end if;
-- There are no supplied discriminants in the object declaration,
-- so get them from the type definition since they must be default
-- initialized.
-- Do we handle constrained subtypes correctly ???
elsif Nkind (Unqual) = N_Object_Declaration then
Assoc := First_Discriminant
(Etype (Object_Definition (Obj_Decl)));
else
Assoc := First_Discriminant (Etype (Unqual));
end if;
-- When we are not looking at an aggregate or an identifier, return
-- since any other construct (like a function call) is not
-- applicable since checks will be performed on the side of the
-- callee.
else
return;
end if;
-- Obtain the discriminants so we know the actual type in case the
-- value of their associated expression gets implicitly converted.
if No (Obj_Decl) then
pragma Assert (Nkind (Unqual) = N_Aggregate);
Disc := First_Discriminant (Etype (Unqual));
else
Disc := First_Discriminant
(Etype (Defining_Identifier (Obj_Decl)));
end if;
-- Preserve the first discriminant for checking named associations
First_Disc := Disc;
-- Count the number of discriminants for processing an aggregate
-- which includes an others.
Disc := First_Disc;
while Present (Disc) loop
Unseen_Disc_Count := Unseen_Disc_Count + 1;
Next_Discriminant (Disc);
end loop;
Seen_Discs := New_Elmt_List;
-- Loop through each of the discriminants and check each expression
-- associated with an anonymous access discriminant.
-- When named associations occur in the return aggregate then
-- discriminants can be in any order, so we need to ensure we do
-- not continue to loop when all discriminants have been seen.
Disc := First_Disc;
while Present (Assoc)
and then (Present (Disc) or else Assoc_Present)
and then Unseen_Disc_Count > 0
loop
-- Handle named associations by searching through the names of
-- the relevant discriminant components.
if Nkind (Assoc)
in N_Component_Association | N_Discriminant_Association
then
Assoc_Expr := Expression (Assoc);
Assoc_Present := True;
-- We currently don't handle box initialized discriminants,
-- however, since default initialized anonymous access
-- discriminants are a corner case, this is ok for now ???
if Nkind (Assoc) = N_Component_Association
and then Box_Present (Assoc)
then
if Nkind (First_Selector (Assoc)) = N_Others_Choice then
Unseen_Disc_Count := 0;
end if;
-- When others is present we must identify a discriminant we
-- haven't already seen so as to get the appropriate type for
-- the static accessibility check.
-- This works because all components within an others clause
-- must have the same type.
elsif Nkind (First_Selector (Assoc)) = N_Others_Choice then
Disc := First_Disc;
Outer : while Present (Disc) loop
declare
Current_Seen_Disc : Elmt_Id;
begin
-- Move through the list of identified discriminants
Current_Seen_Disc := First_Elmt (Seen_Discs);
while Present (Current_Seen_Disc) loop
-- Exit the loop when we found a match
exit when
Chars (Node (Current_Seen_Disc)) = Chars (Disc);
Next_Elmt (Current_Seen_Disc);
end loop;
-- When we have exited the above loop without finding
-- a match then we know that Disc has not been seen.
exit Outer when No (Current_Seen_Disc);
end;
Next_Discriminant (Disc);
end loop Outer;
-- If we got to an others clause with a non-zero
-- discriminant count there must be a discriminant left to
-- check.
pragma Assert (Present (Disc));
-- Set the unseen discriminant count to zero because we know
-- an others clause sets all remaining components of an
-- aggregate.
Unseen_Disc_Count := 0;
-- Move through each of the selectors in the named association
-- and obtain a discriminant for accessibility checking if one
-- is referenced in the list. Also track which discriminants
-- are referenced for the purpose of handling an others clause.
else
declare
Assoc_Choice : Node_Id;
Curr_Disc : Node_Id;
begin
Disc := Empty;
Curr_Disc := First_Disc;
while Present (Curr_Disc) loop
-- Check each of the choices in the associations for a
-- match to the name of the current discriminant.
Assoc_Choice := First_Selector (Assoc);
while Present (Assoc_Choice) loop
-- When the name matches we track that we have seen
-- the discriminant, but instead of exiting the
-- loop we continue iterating to make sure all the
-- discriminants within the named association get
-- tracked.
if Chars (Assoc_Choice) = Chars (Curr_Disc) then
Append_Elmt (Curr_Disc, Seen_Discs);
Disc := Curr_Disc;
Unseen_Disc_Count := Unseen_Disc_Count - 1;
end if;
Next (Assoc_Choice);
end loop;
Next_Discriminant (Curr_Disc);
end loop;
end;
end if;
-- Unwrap the associated expression if we are looking at a default
-- initialized type declaration. In this case Assoc is not really
-- an association, but a component declaration. Should Assoc be
-- renamed in some way to be more clear ???
-- This occurs when the return object does not initialize
-- discriminant and instead relies on the type declaration for
-- their supplied values.
elsif Nkind (Assoc) in N_Entity
and then Ekind (Assoc) = E_Discriminant
then
Append_Elmt (Disc, Seen_Discs);
Assoc_Expr := Discriminant_Default_Value (Assoc);
Unseen_Disc_Count := Unseen_Disc_Count - 1;
-- Otherwise, there is nothing to do because Assoc is an
-- expression within the return aggregate itself.
else
Append_Elmt (Disc, Seen_Discs);
Assoc_Expr := Assoc;
Unseen_Disc_Count := Unseen_Disc_Count - 1;
end if;
-- Check the accessibility level of the expression when the
-- discriminant is of an anonymous access type.
if Present (Assoc_Expr)
and then Present (Disc)
and then Ekind (Etype (Disc)) = E_Anonymous_Access_Type
-- We disable the check when we have a tagged return type and
-- the associated expression for the discriminant is a formal
-- parameter since the check would require us to compare the
-- accessibility level of Assoc_Expr to the level of the
-- Extra_Accessibility_Of_Result of the function - which is
-- currently disabled for functions with tagged return types.
-- This may change in the future ???
-- See Needs_Result_Accessibility_Level for details.
and then not
(No (Extra_Accessibility_Of_Result (Scope_Id))
and then Is_Formal_Of_Current_Function (Assoc_Expr)
and then Is_Tagged_Type (Etype (Scope_Id)))
then
-- Generate a dynamic check based on the extra accessibility of
-- the result or the scope of the current function.
Check_Cond :=
Make_Op_Gt (Loc,
Left_Opnd => Accessibility_Level
(Expr => Assoc_Expr,
Level => Dynamic_Level,
In_Return_Context => True),
Right_Opnd =>
(if Present (Extra_Accessibility_Of_Result (Scope_Id))
-- When Assoc_Expr is a formal we have to look at the
-- extra accessibility-level formal associated with
-- the result.
and then Is_Formal_Of_Current_Function (Assoc_Expr)
then
New_Occurrence_Of
(Extra_Accessibility_Of_Result (Scope_Id), Loc)
-- Otherwise, we compare the level of Assoc_Expr to the
-- scope of the current function.
else
Make_Integer_Literal
(Loc, Scope_Depth (Scope (Scope_Id)))));
Insert_Before_And_Analyze (Return_Stmt,
Make_Raise_Program_Error (Loc,
Condition => Check_Cond,
Reason => PE_Accessibility_Check_Failed));
-- If constant folding has happened on the condition for the
-- generated error, then warn about it being unconditional when
-- we know an error will be raised.
if Nkind (Check_Cond) = N_Identifier
and then Entity (Check_Cond) = Standard_True
then
Error_Msg_N
("access discriminant in return object would be a dangling"
& " reference", Return_Stmt);
end if;
end if;
-- Iterate over the discriminants, except when we have encountered
-- a named association since the discriminant order becomes
-- irrelevant in that case.
if not Assoc_Present then
Next_Discriminant (Disc);
end if;
-- Iterate over associations
if not Is_List_Member (Assoc) then
exit;
else
Nlists.Next (Assoc);
end if;
end loop;
end Check_Return_Construct_Accessibility;
-------------------------------
-- Deepest_Type_Access_Level --
-------------------------------
function Deepest_Type_Access_Level
(Typ : Entity_Id;
Allow_Alt_Model : Boolean := True) return Uint
is
begin
if Ekind (Typ) = E_Anonymous_Access_Type
and then not Is_Local_Anonymous_Access (Typ)
and then Nkind (Associated_Node_For_Itype (Typ)) = N_Object_Declaration
then
-- No_Dynamic_Accessibility_Checks override for alternative
-- accessibility model.
if Allow_Alt_Model
and then No_Dynamic_Accessibility_Checks_Enabled (Typ)
then
return Type_Access_Level (Typ, Allow_Alt_Model);
end if;
-- Typ is the type of an Ada 2012 stand-alone object of an anonymous
-- access type.
return
Scope_Depth (Enclosing_Dynamic_Scope
(Defining_Identifier
(Associated_Node_For_Itype (Typ))));
-- For generic formal type, return Int'Last (infinite).
-- See comment preceding Is_Generic_Type call in Type_Access_Level.
elsif Is_Generic_Type (Root_Type (Typ)) then
return UI_From_Int (Int'Last);
else
return Type_Access_Level (Typ, Allow_Alt_Model);
end if;
end Deepest_Type_Access_Level;
-----------------------------------
-- Effective_Extra_Accessibility --
-----------------------------------
function Effective_Extra_Accessibility (Id : Entity_Id) return Entity_Id is
begin
if Present (Renamed_Object (Id))
and then Is_Entity_Name (Renamed_Object (Id))
then
return Effective_Extra_Accessibility (Entity (Renamed_Object (Id)));
else
return Extra_Accessibility (Id);
end if;
end Effective_Extra_Accessibility;
-------------------------------
-- Get_Dynamic_Accessibility --
-------------------------------
function Get_Dynamic_Accessibility (E : Entity_Id) return Entity_Id is
begin
-- When minimum accessibility is set for E then we utilize it - except
-- in a few edge cases like the expansion of select statements where
-- generated subprogram may attempt to unnecessarily use a minimum
-- accessibility object declared outside of scope.
-- To avoid these situations where expansion may get complex we verify
-- that the minimum accessibility object is within scope.
if Is_Formal (E)
and then Present (Minimum_Accessibility (E))
and then In_Open_Scopes (Scope (Minimum_Accessibility (E)))
then
return Minimum_Accessibility (E);
end if;
return Extra_Accessibility (E);
end Get_Dynamic_Accessibility;
-----------------------
-- Has_Access_Values --
-----------------------
function Has_Access_Values (T : Entity_Id) return Boolean
is
Typ : constant Entity_Id := Underlying_Type (T);
begin
-- Case of a private type which is not completed yet. This can only
-- happen in the case of a generic formal type appearing directly, or
-- as a component of the type to which this function is being applied
-- at the top level. Return False in this case, since we certainly do
-- not know that the type contains access types.
if No (Typ) then
return False;
elsif Is_Access_Type (Typ) then
return True;
elsif Is_Array_Type (Typ) then
return Has_Access_Values (Component_Type (Typ));
elsif Is_Record_Type (Typ) then
declare
Comp : Entity_Id;
begin
-- Loop to check components
Comp := First_Component_Or_Discriminant (Typ);
while Present (Comp) loop
-- Check for access component, tag field does not count, even
-- though it is implemented internally using an access type.
if Has_Access_Values (Etype (Comp))
and then Chars (Comp) /= Name_uTag
then
return True;
end if;
Next_Component_Or_Discriminant (Comp);
end loop;
end;
return False;
else
return False;
end if;
end Has_Access_Values;
---------------------------------------
-- Has_Anonymous_Access_Discriminant --
---------------------------------------
function Has_Anonymous_Access_Discriminant (Typ : Entity_Id) return Boolean
is
Disc : Node_Id;
begin
if not Has_Discriminants (Typ) then
return False;
end if;
Disc := First_Discriminant (Typ);
while Present (Disc) loop
if Ekind (Etype (Disc)) = E_Anonymous_Access_Type then
return True;
end if;
Next_Discriminant (Disc);
end loop;
return False;
end Has_Anonymous_Access_Discriminant;
--------------------------------------------
-- Has_Unconstrained_Access_Discriminants --
--------------------------------------------
function Has_Unconstrained_Access_Discriminants
(Subtyp : Entity_Id) return Boolean
is
Discr : Entity_Id;
begin
if Has_Discriminants (Subtyp)
and then not Is_Constrained (Subtyp)
then
Discr := First_Discriminant (Subtyp);
while Present (Discr) loop
if Ekind (Etype (Discr)) = E_Anonymous_Access_Type then
return True;
end if;
Next_Discriminant (Discr);
end loop;
end if;
return False;
end Has_Unconstrained_Access_Discriminants;
--------------------------------
-- Is_Anonymous_Access_Actual --
--------------------------------
function Is_Anonymous_Access_Actual (N : Node_Id) return Boolean is
Par : Node_Id;
begin
if Ekind (Etype (N)) /= E_Anonymous_Access_Type then
return False;
end if;
Par := Parent (N);
while Present (Par)
and then Nkind (Par) in N_Case_Expression
| N_If_Expression
| N_Parameter_Association
loop
Par := Parent (Par);
end loop;
return Nkind (Par) in N_Subprogram_Call;
end Is_Anonymous_Access_Actual;
--------------------------------------
-- Is_Special_Aliased_Formal_Access --
--------------------------------------
function Is_Special_Aliased_Formal_Access
(Exp : Node_Id;
In_Return_Context : Boolean := False) return Boolean
is
Scop : constant Entity_Id := Current_Subprogram;
begin
-- Verify the expression is an access reference to 'Access within a
-- return statement as this is the only time an explicitly aliased
-- formal has different semantics.
if Nkind (Exp) /= N_Attribute_Reference
or else Get_Attribute_Id (Attribute_Name (Exp)) /= Attribute_Access
or else not (In_Return_Value (Exp)
or else In_Return_Context)
or else not Needs_Result_Accessibility_Level (Scop)
then
return False;
end if;
-- Check if the prefix of the reference is indeed an explicitly aliased
-- formal parameter for the function Scop. Additionally, we must check
-- that Scop returns an anonymous access type, otherwise the special
-- rules dictating a need for a dynamic check are not in effect.
return Is_Entity_Name (Prefix (Exp))
and then Is_Explicitly_Aliased (Entity (Prefix (Exp)));
end Is_Special_Aliased_Formal_Access;
--------------------------------------
-- Needs_Result_Accessibility_Level --
--------------------------------------
function Needs_Result_Accessibility_Level
(Func_Id : Entity_Id) return Boolean
is
Func_Typ : constant Entity_Id := Underlying_Type (Etype (Func_Id));
function Has_Unconstrained_Access_Discriminant_Component
(Comp_Typ : Entity_Id) return Boolean;
-- Returns True if any component of the type has an unconstrained access
-- discriminant.
-----------------------------------------------------
-- Has_Unconstrained_Access_Discriminant_Component --
-----------------------------------------------------
function Has_Unconstrained_Access_Discriminant_Component
(Comp_Typ : Entity_Id) return Boolean
is
begin
if not Is_Limited_Type (Comp_Typ) then
return False;
-- Only limited types can have access discriminants with
-- defaults.
elsif Has_Unconstrained_Access_Discriminants (Comp_Typ) then
return True;
elsif Is_Array_Type (Comp_Typ) then
return Has_Unconstrained_Access_Discriminant_Component
(Underlying_Type (Component_Type (Comp_Typ)));
elsif Is_Record_Type (Comp_Typ) then
declare
Comp : Entity_Id;
begin
Comp := First_Component (Comp_Typ);
while Present (Comp) loop
if Has_Unconstrained_Access_Discriminant_Component
(Underlying_Type (Etype (Comp)))
then
return True;
end if;
Next_Component (Comp);
end loop;
end;
end if;
return False;
end Has_Unconstrained_Access_Discriminant_Component;
Disable_Tagged_Cases : constant Boolean := True;
-- Flag used to temporarily disable a "True" result for tagged types.
-- See comments further below for details.
-- Start of processing for Needs_Result_Accessibility_Level
begin
-- False if completion unavailable, which can happen when we are
-- analyzing an abstract subprogram or if the subprogram has
-- delayed freezing.
if No (Func_Typ) then
return False;
-- False if not a function, also handle enum-lit renames case
elsif Func_Typ = Standard_Void_Type
or else Is_Scalar_Type (Func_Typ)
then
return False;
-- Handle a corner case, a cross-dialect subp renaming. For example,
-- an Ada 2012 renaming of an Ada 2005 subprogram. This can occur when
-- an Ada 2005 (or earlier) unit references predefined run-time units.
elsif Present (Alias (Func_Id)) then
-- Unimplemented: a cross-dialect subp renaming which does not set
-- the Alias attribute (e.g., a rename of a dereference of an access
-- to subprogram value). ???
return Present (Extra_Accessibility_Of_Result (Alias (Func_Id)));
-- Remaining cases require Ada 2012 mode, unless they are dispatching
-- operations, since they may be overridden by Ada_2012 primitives.
elsif Ada_Version < Ada_2012
and then not Is_Dispatching_Operation (Func_Id)
then
return False;
-- Handle the situation where a result is an anonymous access type
-- RM 3.10.2 (10.3/3).
elsif Ekind (Func_Typ) = E_Anonymous_Access_Type then
return True;
-- In the case of, say, a null tagged record result type, the need for
-- this extra parameter might not be obvious so this function returns
-- True for all tagged types for compatibility reasons.
-- A function with, say, a tagged null controlling result type might
-- be overridden by a primitive of an extension having an access
-- discriminant and the overrider and overridden must have compatible
-- calling conventions (including implicitly declared parameters).
-- Similarly, values of one access-to-subprogram type might designate
-- both a primitive subprogram of a given type and a function which is,
-- for example, not a primitive subprogram of any type. Again, this
-- requires calling convention compatibility. It might be possible to
-- solve these issues by introducing wrappers, but that is not the
-- approach that was chosen.
-- Note: Despite the reasoning noted above, the extra accessibility
-- parameter for tagged types is disabled for performance reasons.
elsif Is_Tagged_Type (Func_Typ) then
return not Disable_Tagged_Cases;
elsif Has_Unconstrained_Access_Discriminants (Func_Typ) then
return True;
elsif Has_Unconstrained_Access_Discriminant_Component (Func_Typ) then
return True;
-- False for all other cases
else
return False;
end if;
end Needs_Result_Accessibility_Level;
------------------------------------------
-- Prefix_With_Safe_Accessibility_Level --
------------------------------------------
function Prefix_With_Safe_Accessibility_Level
(N : Node_Id;
Typ : Entity_Id) return Boolean
is
P : constant Node_Id := Prefix (N);
Aname : constant Name_Id := Attribute_Name (N);
Attr_Id : constant Attribute_Id := Get_Attribute_Id (Aname);
Btyp : constant Entity_Id := Base_Type (Typ);
function Safe_Value_Conversions return Boolean;
-- Return False if the prefix has a value conversion of an array type
----------------------------
-- Safe_Value_Conversions --
----------------------------
function Safe_Value_Conversions return Boolean is
PP : Node_Id := P;
begin
loop
if Nkind (PP) in N_Selected_Component | N_Indexed_Component then
PP := Prefix (PP);
elsif Comes_From_Source (PP)
and then Nkind (PP) in N_Type_Conversion
| N_Unchecked_Type_Conversion
and then Is_Array_Type (Etype (PP))
then
return False;
elsif Comes_From_Source (PP)
and then Nkind (PP) = N_Qualified_Expression
and then Is_Array_Type (Etype (PP))
and then Nkind (Original_Node (Expression (PP))) in
N_Aggregate | N_Extension_Aggregate
then
return False;
else
exit;
end if;
end loop;
return True;
end Safe_Value_Conversions;
-- Start of processing for Prefix_With_Safe_Accessibility_Level
begin
-- No check required for unchecked and unrestricted access
if Attr_Id = Attribute_Unchecked_Access
or else Attr_Id = Attribute_Unrestricted_Access
then
return True;
-- Check value conversions
elsif Ekind (Btyp) = E_General_Access_Type
and then not Safe_Value_Conversions
then
return False;
end if;
return True;
end Prefix_With_Safe_Accessibility_Level;
-----------------------------
-- Subprogram_Access_Level --
-----------------------------
function Subprogram_Access_Level (Subp : Entity_Id) return Uint is
begin
if Present (Alias (Subp)) then
return Subprogram_Access_Level (Alias (Subp));
else
return Scope_Depth (Enclosing_Dynamic_Scope (Subp));
end if;
end Subprogram_Access_Level;
--------------------------------
-- Static_Accessibility_Level --
--------------------------------
function Static_Accessibility_Level
(Expr : Node_Id;
Level : Static_Accessibility_Level_Kind;
In_Return_Context : Boolean := False) return Uint
is
begin
return Intval
(Accessibility_Level (Expr, Level, In_Return_Context));
end Static_Accessibility_Level;
-----------------------
-- Type_Access_Level --
-----------------------
function Type_Access_Level
(Typ : Entity_Id;
Allow_Alt_Model : Boolean := True;
Assoc_Ent : Entity_Id := Empty) return Uint
is
Btyp : Entity_Id := Base_Type (Typ);
Def_Ent : Entity_Id;
begin
-- Ada 2005 (AI-230): For most cases of anonymous access types, we
-- simply use the level where the type is declared. This is true for
-- stand-alone object declarations, and for anonymous access types
-- associated with components the level is the same as that of the
-- enclosing composite type. However, special treatment is needed for
-- the cases of access parameters, return objects of an anonymous access
-- type, and, in Ada 95, access discriminants of limited types.
if Is_Access_Type (Btyp) then
if Ekind (Btyp) = E_Anonymous_Access_Type then
-- No_Dynamic_Accessibility_Checks restriction override for
-- alternative accessibility model.
if Allow_Alt_Model
and then No_Dynamic_Accessibility_Checks_Enabled (Btyp)
then
-- In the -gnatd_b model, the level of an anonymous access
-- type is always that of the designated type.
if Debug_Flag_Underscore_B then
return Type_Access_Level
(Designated_Type (Btyp), Allow_Alt_Model);
end if;
-- When an anonymous access type's Assoc_Ent is specified,
-- calculate the result based on the general accessibility
-- level routine.
-- We would like to use Associated_Node_For_Itype here instead,
-- but in some cases it is not fine grained enough ???
if Present (Assoc_Ent) then
return Static_Accessibility_Level
(Assoc_Ent, Object_Decl_Level);
end if;
-- Otherwise take the context of the anonymous access type into
-- account.
-- Obtain the defining entity for the internally generated
-- anonymous access type.
Def_Ent := Defining_Entity_Or_Empty
(Associated_Node_For_Itype (Typ));
if Present (Def_Ent) then
-- When the defining entity is a subprogram then we know the
-- anonymous access type Typ has been generated to either
-- describe an anonymous access type formal or an anonymous
-- access result type.
-- Since we are only interested in the formal case, avoid
-- the anonymous access result type.
if Is_Subprogram (Def_Ent)
and then not (Ekind (Def_Ent) = E_Function
and then Etype (Def_Ent) = Typ)
then
-- When the type comes from an anonymous access
-- parameter, the level is that of the subprogram
-- declaration.
return Scope_Depth (Def_Ent);
-- When the type is an access discriminant, the level is
-- that of the type.
elsif Ekind (Def_Ent) = E_Discriminant then
return Scope_Depth (Scope (Def_Ent));
end if;
end if;
-- If the type is a nonlocal anonymous access type (such as for
-- an access parameter) we treat it as being declared at the
-- library level to ensure that names such as X.all'access don't
-- fail static accessibility checks.
elsif not Is_Local_Anonymous_Access (Typ) then
return Scope_Depth (Standard_Standard);
-- If this is a return object, the accessibility level is that of
-- the result subtype of the enclosing function. The test here is
-- little complicated, because we have to account for extended
-- return statements that have been rewritten as blocks, in which
-- case we have to find and the Is_Return_Object attribute of the
-- itype's associated object. It would be nice to find a way to
-- simplify this test, but it doesn't seem worthwhile to add a new
-- flag just for purposes of this test. ???
elsif Ekind (Scope (Btyp)) = E_Return_Statement
or else
(Is_Itype (Btyp)
and then Nkind (Associated_Node_For_Itype (Btyp)) =
N_Object_Declaration
and then Is_Return_Object
(Defining_Identifier
(Associated_Node_For_Itype (Btyp))))
then
declare
Scop : Entity_Id;
begin
Scop := Scope (Scope (Btyp));
while Present (Scop) loop
exit when Ekind (Scop) = E_Function;
Scop := Scope (Scop);
end loop;
-- Treat the return object's type as having the level of the
-- function's result subtype (as per RM05-6.5(5.3/2)).
return Type_Access_Level (Etype (Scop), Allow_Alt_Model);
end;
end if;
end if;
Btyp := Root_Type (Btyp);
-- The accessibility level of anonymous access types associated with
-- discriminants is that of the current instance of the type, and
-- that's deeper than the type itself (AARM 3.10.2 (12.3.21)).
-- AI-402: access discriminants have accessibility based on the
-- object rather than the type in Ada 2005, so the above paragraph
-- doesn't apply.
-- ??? Needs completion with rules from AI-416
if Ada_Version <= Ada_95
and then Ekind (Typ) = E_Anonymous_Access_Type
and then Present (Associated_Node_For_Itype (Typ))
and then Nkind (Associated_Node_For_Itype (Typ)) =
N_Discriminant_Specification
then
return Scope_Depth (Enclosing_Dynamic_Scope (Btyp)) + 1;
end if;
end if;
-- Return library level for a generic formal type. This is done because
-- RM(10.3.2) says that "The statically deeper relationship does not
-- apply to ... a descendant of a generic formal type". Rather than
-- checking at each point where a static accessibility check is
-- performed to see if we are dealing with a formal type, this rule is
-- implemented by having Type_Access_Level and Deepest_Type_Access_Level
-- return extreme values for a formal type; Deepest_Type_Access_Level
-- returns Int'Last. By calling the appropriate function from among the
-- two, we ensure that the static accessibility check will pass if we
-- happen to run into a formal type. More specifically, we should call
-- Deepest_Type_Access_Level instead of Type_Access_Level whenever the
-- call occurs as part of a static accessibility check and the error
-- case is the case where the type's level is too shallow (as opposed
-- to too deep).
if Is_Generic_Type (Root_Type (Btyp)) then
return Scope_Depth (Standard_Standard);
end if;
return Scope_Depth (Enclosing_Dynamic_Scope (Btyp));
end Type_Access_Level;
end Accessibility;