
2014-07-30 Pat Rogers <rogers@adacore.com> * gnat_rm.texi: Minor word error. 2014-07-30 Ed Schonberg <schonberg@adacore.com> * exp_prag.adb (Expand_Old): Insert declarationss of temporaries created to capture the value of the prefix of 'Old at the beginning of the current declarative part, to prevent data flow anomalies in the postcondition procedure that will follow. 2014-07-30 Yannick Moy <moy@adacore.com> * debug.adb: Retire debug flag -gnatdQ. * inline.adb (Can_Be_Inlined_In_GNATprove_Mode): Check SPARK_Mode on decl, not on body. Ignore predicate functions. * sem_ch6.adb (Analyze_Subprogram_Body_Helper): Remove use of debug flag -gnatdQ. Correctly analyze SPARK_Mode on decl and body when generating a decl for a body on which SPARK_Mode aspect is given. * sem_prag.adb (Analyze_Pragma|SPARK_Mode): Reorder tests for attaching pragma to entity, to account for declaration not coming from source. * sem_res.adb (Resolve_Call): Issue warning and flag subprogram as not always inlined in GNATprove mode, when called in an assertion context. From-SVN: r213271
12680 lines
454 KiB
Ada
12680 lines
454 KiB
Ada
------------------------------------------------------------------------------
|
|
-- --
|
|
-- GNAT COMPILER COMPONENTS --
|
|
-- --
|
|
-- S E M _ C H 1 3 --
|
|
-- --
|
|
-- B o d y --
|
|
-- --
|
|
-- Copyright (C) 1992-2014, 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 Aspects; use Aspects;
|
|
with Atree; use Atree;
|
|
with Checks; use Checks;
|
|
with Debug; use Debug;
|
|
with Einfo; use Einfo;
|
|
with Elists; use Elists;
|
|
with Errout; use Errout;
|
|
with Exp_Disp; use Exp_Disp;
|
|
with Exp_Tss; use Exp_Tss;
|
|
with Exp_Util; use Exp_Util;
|
|
with Freeze; use Freeze;
|
|
with Lib; use Lib;
|
|
with Lib.Xref; use Lib.Xref;
|
|
with Namet; use Namet;
|
|
with Nlists; use Nlists;
|
|
with Nmake; use Nmake;
|
|
with Opt; use Opt;
|
|
with Restrict; use Restrict;
|
|
with Rident; use Rident;
|
|
with Rtsfind; use Rtsfind;
|
|
with Sem; use Sem;
|
|
with Sem_Aux; use Sem_Aux;
|
|
with Sem_Case; use Sem_Case;
|
|
with Sem_Ch3; use Sem_Ch3;
|
|
with Sem_Ch6; use Sem_Ch6;
|
|
with Sem_Ch8; use Sem_Ch8;
|
|
with Sem_Dim; use Sem_Dim;
|
|
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_Type; use Sem_Type;
|
|
with Sem_Util; use Sem_Util;
|
|
with Sem_Warn; use Sem_Warn;
|
|
with Sinput; use Sinput;
|
|
with Snames; use Snames;
|
|
with Stand; use Stand;
|
|
with Sinfo; use Sinfo;
|
|
with Stringt; use Stringt;
|
|
with Targparm; use Targparm;
|
|
with Ttypes; use Ttypes;
|
|
with Tbuild; use Tbuild;
|
|
with Urealp; use Urealp;
|
|
with Warnsw; use Warnsw;
|
|
|
|
with GNAT.Heap_Sort_G;
|
|
|
|
package body Sem_Ch13 is
|
|
|
|
SSU : constant Pos := System_Storage_Unit;
|
|
-- Convenient short hand for commonly used constant
|
|
|
|
-----------------------
|
|
-- Local Subprograms --
|
|
-----------------------
|
|
|
|
procedure Alignment_Check_For_Size_Change (Typ : Entity_Id; Size : Uint);
|
|
-- This routine is called after setting one of the sizes of type entity
|
|
-- Typ to Size. The purpose is to deal with the situation of a derived
|
|
-- type whose inherited alignment is no longer appropriate for the new
|
|
-- size value. In this case, we reset the Alignment to unknown.
|
|
|
|
procedure Build_Discrete_Static_Predicate
|
|
(Typ : Entity_Id;
|
|
Expr : Node_Id;
|
|
Nam : Name_Id);
|
|
-- Given a predicated type Typ, where Typ is a discrete static subtype,
|
|
-- whose predicate expression is Expr, tests if Expr is a static predicate,
|
|
-- and if so, builds the predicate range list. Nam is the name of the one
|
|
-- argument to the predicate function. Occurrences of the type name in the
|
|
-- predicate expression have been replaced by identifier references to this
|
|
-- name, which is unique, so any identifier with Chars matching Nam must be
|
|
-- a reference to the type. If the predicate is non-static, this procedure
|
|
-- returns doing nothing. If the predicate is static, then the predicate
|
|
-- list is stored in Static_Discrete_Predicate (Typ), and the Expr is
|
|
-- rewritten as a canonicalized membership operation.
|
|
|
|
procedure Build_Predicate_Functions (Typ : Entity_Id; N : Node_Id);
|
|
-- If Typ has predicates (indicated by Has_Predicates being set for Typ),
|
|
-- then either there are pragma Predicate entries on the rep chain for the
|
|
-- type (note that Predicate aspects are converted to pragma Predicate), or
|
|
-- there are inherited aspects from a parent type, or ancestor subtypes.
|
|
-- This procedure builds the spec and body for the Predicate function that
|
|
-- tests these predicates. N is the freeze node for the type. The spec of
|
|
-- the function is inserted before the freeze node, and the body of the
|
|
-- function is inserted after the freeze node. If the predicate expression
|
|
-- has at least one Raise_Expression, then this procedure also builds the
|
|
-- M version of the predicate function for use in membership tests.
|
|
|
|
procedure Check_Pool_Size_Clash (Ent : Entity_Id; SP, SS : Node_Id);
|
|
-- Called if both Storage_Pool and Storage_Size attribute definition
|
|
-- clauses (SP and SS) are present for entity Ent. Issue error message.
|
|
|
|
procedure Freeze_Entity_Checks (N : Node_Id);
|
|
-- Called from Analyze_Freeze_Entity and Analyze_Generic_Freeze Entity
|
|
-- to generate appropriate semantic checks that are delayed until this
|
|
-- point (they had to be delayed this long for cases of delayed aspects,
|
|
-- e.g. analysis of statically predicated subtypes in choices, for which
|
|
-- we have to be sure the subtypes in question are frozen before checking.
|
|
|
|
function Get_Alignment_Value (Expr : Node_Id) return Uint;
|
|
-- Given the expression for an alignment value, returns the corresponding
|
|
-- Uint value. If the value is inappropriate, then error messages are
|
|
-- posted as required, and a value of No_Uint is returned.
|
|
|
|
function Is_Operational_Item (N : Node_Id) return Boolean;
|
|
-- A specification for a stream attribute is allowed before the full type
|
|
-- is declared, as explained in AI-00137 and the corrigendum. Attributes
|
|
-- that do not specify a representation characteristic are operational
|
|
-- attributes.
|
|
|
|
function Is_Predicate_Static
|
|
(Expr : Node_Id;
|
|
Nam : Name_Id) return Boolean;
|
|
-- Given predicate expression Expr, tests if Expr is predicate-static in
|
|
-- the sense of the rules in (RM 3.2.4 (15-24)). Occurrences of the type
|
|
-- name in the predicate expression have been replaced by references to
|
|
-- an identifier whose Chars field is Nam. This name is unique, so any
|
|
-- identifier with Chars matching Nam must be a reference to the type.
|
|
-- Returns True if the expression is predicate-static and False otherwise,
|
|
-- but is not in the business of setting flags or issuing error messages.
|
|
--
|
|
-- Only scalar types can have static predicates, so False is always
|
|
-- returned for non-scalar types.
|
|
--
|
|
-- Note: the RM seems to suggest that string types can also have static
|
|
-- predicates. But that really makes lttle sense as very few useful
|
|
-- predicates can be constructed for strings. Remember that:
|
|
--
|
|
-- "ABC" < "DEF"
|
|
--
|
|
-- is not a static expression. So even though the clearly faulty RM wording
|
|
-- allows the following:
|
|
--
|
|
-- subtype S is String with Static_Predicate => S < "DEF"
|
|
--
|
|
-- We can't allow this, otherwise we have predicate-static applying to a
|
|
-- larger class than static expressions, which was never intended.
|
|
|
|
procedure New_Stream_Subprogram
|
|
(N : Node_Id;
|
|
Ent : Entity_Id;
|
|
Subp : Entity_Id;
|
|
Nam : TSS_Name_Type);
|
|
-- Create a subprogram renaming of a given stream attribute to the
|
|
-- designated subprogram and then in the tagged case, provide this as a
|
|
-- primitive operation, or in the non-tagged case make an appropriate TSS
|
|
-- entry. This is more properly an expansion activity than just semantics,
|
|
-- but the presence of user-defined stream functions for limited types is a
|
|
-- legality check, which is why this takes place here rather than in
|
|
-- exp_ch13, where it was previously. Nam indicates the name of the TSS
|
|
-- function to be generated.
|
|
--
|
|
-- To avoid elaboration anomalies with freeze nodes, for untagged types
|
|
-- we generate both a subprogram declaration and a subprogram renaming
|
|
-- declaration, so that the attribute specification is handled as a
|
|
-- renaming_as_body. For tagged types, the specification is one of the
|
|
-- primitive specs.
|
|
|
|
generic
|
|
with procedure Replace_Type_Reference (N : Node_Id);
|
|
procedure Replace_Type_References_Generic (N : Node_Id; T : Entity_Id);
|
|
-- This is used to scan an expression for a predicate or invariant aspect
|
|
-- replacing occurrences of the name of the subtype to which the aspect
|
|
-- applies with appropriate references to the parameter of the predicate
|
|
-- function or invariant procedure. The procedure passed as a generic
|
|
-- parameter does the actual replacement of node N, which is either a
|
|
-- simple direct reference to T, or a selected component that represents
|
|
-- an appropriately qualified occurrence of T.
|
|
|
|
procedure Resolve_Iterable_Operation
|
|
(N : Node_Id;
|
|
Cursor : Entity_Id;
|
|
Typ : Entity_Id;
|
|
Nam : Name_Id);
|
|
-- If the name of a primitive operation for an Iterable aspect is
|
|
-- overloaded, resolve according to required signature.
|
|
|
|
procedure Set_Biased
|
|
(E : Entity_Id;
|
|
N : Node_Id;
|
|
Msg : String;
|
|
Biased : Boolean := True);
|
|
-- If Biased is True, sets Has_Biased_Representation flag for E, and
|
|
-- outputs a warning message at node N if Warn_On_Biased_Representation is
|
|
-- is True. This warning inserts the string Msg to describe the construct
|
|
-- causing biasing.
|
|
|
|
----------------------------------------------
|
|
-- Table for Validate_Unchecked_Conversions --
|
|
----------------------------------------------
|
|
|
|
-- The following table collects unchecked conversions for validation.
|
|
-- Entries are made by Validate_Unchecked_Conversion and then the call
|
|
-- to Validate_Unchecked_Conversions does the actual error checking and
|
|
-- posting of warnings. The reason for this delayed processing is to take
|
|
-- advantage of back-annotations of size and alignment values performed by
|
|
-- the back end.
|
|
|
|
-- Note: the reason we store a Source_Ptr value instead of a Node_Id is
|
|
-- that by the time Validate_Unchecked_Conversions is called, Sprint will
|
|
-- already have modified all Sloc values if the -gnatD option is set.
|
|
|
|
type UC_Entry is record
|
|
Eloc : Source_Ptr; -- node used for posting warnings
|
|
Source : Entity_Id; -- source type for unchecked conversion
|
|
Target : Entity_Id; -- target type for unchecked conversion
|
|
Act_Unit : Entity_Id; -- actual function instantiated
|
|
end record;
|
|
|
|
package Unchecked_Conversions is new Table.Table (
|
|
Table_Component_Type => UC_Entry,
|
|
Table_Index_Type => Int,
|
|
Table_Low_Bound => 1,
|
|
Table_Initial => 50,
|
|
Table_Increment => 200,
|
|
Table_Name => "Unchecked_Conversions");
|
|
|
|
----------------------------------------
|
|
-- Table for Validate_Address_Clauses --
|
|
----------------------------------------
|
|
|
|
-- If an address clause has the form
|
|
|
|
-- for X'Address use Expr
|
|
|
|
-- where Expr is of the form Y'Address or recursively is a reference to a
|
|
-- constant of either of these forms, and X and Y are entities of objects,
|
|
-- then if Y has a smaller alignment than X, that merits a warning about
|
|
-- possible bad alignment. The following table collects address clauses of
|
|
-- this kind. We put these in a table so that they can be checked after the
|
|
-- back end has completed annotation of the alignments of objects, since we
|
|
-- can catch more cases that way.
|
|
|
|
type Address_Clause_Check_Record is record
|
|
N : Node_Id;
|
|
-- The address clause
|
|
|
|
X : Entity_Id;
|
|
-- The entity of the object overlaying Y
|
|
|
|
Y : Entity_Id;
|
|
-- The entity of the object being overlaid
|
|
|
|
Off : Boolean;
|
|
-- Whether the address is offset within Y
|
|
end record;
|
|
|
|
package Address_Clause_Checks is new Table.Table (
|
|
Table_Component_Type => Address_Clause_Check_Record,
|
|
Table_Index_Type => Int,
|
|
Table_Low_Bound => 1,
|
|
Table_Initial => 20,
|
|
Table_Increment => 200,
|
|
Table_Name => "Address_Clause_Checks");
|
|
|
|
-----------------------------------------
|
|
-- Adjust_Record_For_Reverse_Bit_Order --
|
|
-----------------------------------------
|
|
|
|
procedure Adjust_Record_For_Reverse_Bit_Order (R : Entity_Id) is
|
|
Comp : Node_Id;
|
|
CC : Node_Id;
|
|
|
|
begin
|
|
-- Processing depends on version of Ada
|
|
|
|
-- For Ada 95, we just renumber bits within a storage unit. We do the
|
|
-- same for Ada 83 mode, since we recognize the Bit_Order attribute in
|
|
-- Ada 83, and are free to add this extension.
|
|
|
|
if Ada_Version < Ada_2005 then
|
|
Comp := First_Component_Or_Discriminant (R);
|
|
while Present (Comp) loop
|
|
CC := Component_Clause (Comp);
|
|
|
|
-- If component clause is present, then deal with the non-default
|
|
-- bit order case for Ada 95 mode.
|
|
|
|
-- We only do this processing for the base type, and in fact that
|
|
-- is important, since otherwise if there are record subtypes, we
|
|
-- could reverse the bits once for each subtype, which is wrong.
|
|
|
|
if Present (CC) and then Ekind (R) = E_Record_Type then
|
|
declare
|
|
CFB : constant Uint := Component_Bit_Offset (Comp);
|
|
CSZ : constant Uint := Esize (Comp);
|
|
CLC : constant Node_Id := Component_Clause (Comp);
|
|
Pos : constant Node_Id := Position (CLC);
|
|
FB : constant Node_Id := First_Bit (CLC);
|
|
|
|
Storage_Unit_Offset : constant Uint :=
|
|
CFB / System_Storage_Unit;
|
|
|
|
Start_Bit : constant Uint :=
|
|
CFB mod System_Storage_Unit;
|
|
|
|
begin
|
|
-- Cases where field goes over storage unit boundary
|
|
|
|
if Start_Bit + CSZ > System_Storage_Unit then
|
|
|
|
-- Allow multi-byte field but generate warning
|
|
|
|
if Start_Bit mod System_Storage_Unit = 0
|
|
and then CSZ mod System_Storage_Unit = 0
|
|
then
|
|
Error_Msg_N
|
|
("info: multi-byte field specified with "
|
|
& "non-standard Bit_Order?V?", CLC);
|
|
|
|
if Bytes_Big_Endian then
|
|
Error_Msg_N
|
|
("\bytes are not reversed "
|
|
& "(component is big-endian)?V?", CLC);
|
|
else
|
|
Error_Msg_N
|
|
("\bytes are not reversed "
|
|
& "(component is little-endian)?V?", CLC);
|
|
end if;
|
|
|
|
-- Do not allow non-contiguous field
|
|
|
|
else
|
|
Error_Msg_N
|
|
("attempt to specify non-contiguous field "
|
|
& "not permitted", CLC);
|
|
Error_Msg_N
|
|
("\caused by non-standard Bit_Order "
|
|
& "specified", CLC);
|
|
Error_Msg_N
|
|
("\consider possibility of using "
|
|
& "Ada 2005 mode here", CLC);
|
|
end if;
|
|
|
|
-- Case where field fits in one storage unit
|
|
|
|
else
|
|
-- Give warning if suspicious component clause
|
|
|
|
if Intval (FB) >= System_Storage_Unit
|
|
and then Warn_On_Reverse_Bit_Order
|
|
then
|
|
Error_Msg_N
|
|
("info: Bit_Order clause does not affect " &
|
|
"byte ordering?V?", Pos);
|
|
Error_Msg_Uint_1 :=
|
|
Intval (Pos) + Intval (FB) /
|
|
System_Storage_Unit;
|
|
Error_Msg_N
|
|
("info: position normalized to ^ before bit " &
|
|
"order interpreted?V?", Pos);
|
|
end if;
|
|
|
|
-- Here is where we fix up the Component_Bit_Offset value
|
|
-- to account for the reverse bit order. Some examples of
|
|
-- what needs to be done are:
|
|
|
|
-- First_Bit .. Last_Bit Component_Bit_Offset
|
|
-- old new old new
|
|
|
|
-- 0 .. 0 7 .. 7 0 7
|
|
-- 0 .. 1 6 .. 7 0 6
|
|
-- 0 .. 2 5 .. 7 0 5
|
|
-- 0 .. 7 0 .. 7 0 4
|
|
|
|
-- 1 .. 1 6 .. 6 1 6
|
|
-- 1 .. 4 3 .. 6 1 3
|
|
-- 4 .. 7 0 .. 3 4 0
|
|
|
|
-- The rule is that the first bit is is obtained by
|
|
-- subtracting the old ending bit from storage_unit - 1.
|
|
|
|
Set_Component_Bit_Offset
|
|
(Comp,
|
|
(Storage_Unit_Offset * System_Storage_Unit) +
|
|
(System_Storage_Unit - 1) -
|
|
(Start_Bit + CSZ - 1));
|
|
|
|
Set_Normalized_First_Bit
|
|
(Comp,
|
|
Component_Bit_Offset (Comp) mod
|
|
System_Storage_Unit);
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
Next_Component_Or_Discriminant (Comp);
|
|
end loop;
|
|
|
|
-- For Ada 2005, we do machine scalar processing, as fully described In
|
|
-- AI-133. This involves gathering all components which start at the
|
|
-- same byte offset and processing them together. Same approach is still
|
|
-- valid in later versions including Ada 2012.
|
|
|
|
else
|
|
declare
|
|
Max_Machine_Scalar_Size : constant Uint :=
|
|
UI_From_Int
|
|
(Standard_Long_Long_Integer_Size);
|
|
-- We use this as the maximum machine scalar size
|
|
|
|
Num_CC : Natural;
|
|
SSU : constant Uint := UI_From_Int (System_Storage_Unit);
|
|
|
|
begin
|
|
-- This first loop through components does two things. First it
|
|
-- deals with the case of components with component clauses whose
|
|
-- length is greater than the maximum machine scalar size (either
|
|
-- accepting them or rejecting as needed). Second, it counts the
|
|
-- number of components with component clauses whose length does
|
|
-- not exceed this maximum for later processing.
|
|
|
|
Num_CC := 0;
|
|
Comp := First_Component_Or_Discriminant (R);
|
|
while Present (Comp) loop
|
|
CC := Component_Clause (Comp);
|
|
|
|
if Present (CC) then
|
|
declare
|
|
Fbit : constant Uint := Static_Integer (First_Bit (CC));
|
|
Lbit : constant Uint := Static_Integer (Last_Bit (CC));
|
|
|
|
begin
|
|
-- Case of component with last bit >= max machine scalar
|
|
|
|
if Lbit >= Max_Machine_Scalar_Size then
|
|
|
|
-- This is allowed only if first bit is zero, and
|
|
-- last bit + 1 is a multiple of storage unit size.
|
|
|
|
if Fbit = 0 and then (Lbit + 1) mod SSU = 0 then
|
|
|
|
-- This is the case to give a warning if enabled
|
|
|
|
if Warn_On_Reverse_Bit_Order then
|
|
Error_Msg_N
|
|
("info: multi-byte field specified with "
|
|
& " non-standard Bit_Order?V?", CC);
|
|
|
|
if Bytes_Big_Endian then
|
|
Error_Msg_N
|
|
("\bytes are not reversed "
|
|
& "(component is big-endian)?V?", CC);
|
|
else
|
|
Error_Msg_N
|
|
("\bytes are not reversed "
|
|
& "(component is little-endian)?V?", CC);
|
|
end if;
|
|
end if;
|
|
|
|
-- Give error message for RM 13.5.1(10) violation
|
|
|
|
else
|
|
Error_Msg_FE
|
|
("machine scalar rules not followed for&",
|
|
First_Bit (CC), Comp);
|
|
|
|
Error_Msg_Uint_1 := Lbit;
|
|
Error_Msg_Uint_2 := Max_Machine_Scalar_Size;
|
|
Error_Msg_F
|
|
("\last bit (^) exceeds maximum machine "
|
|
& "scalar size (^)",
|
|
First_Bit (CC));
|
|
|
|
if (Lbit + 1) mod SSU /= 0 then
|
|
Error_Msg_Uint_1 := SSU;
|
|
Error_Msg_F
|
|
("\and is not a multiple of Storage_Unit (^) "
|
|
& "(RM 13.4.1(10))",
|
|
First_Bit (CC));
|
|
|
|
else
|
|
Error_Msg_Uint_1 := Fbit;
|
|
Error_Msg_F
|
|
("\and first bit (^) is non-zero "
|
|
& "(RM 13.4.1(10))",
|
|
First_Bit (CC));
|
|
end if;
|
|
end if;
|
|
|
|
-- OK case of machine scalar related component clause,
|
|
-- For now, just count them.
|
|
|
|
else
|
|
Num_CC := Num_CC + 1;
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
Next_Component_Or_Discriminant (Comp);
|
|
end loop;
|
|
|
|
-- We need to sort the component clauses on the basis of the
|
|
-- Position values in the clause, so we can group clauses with
|
|
-- the same Position together to determine the relevant machine
|
|
-- scalar size.
|
|
|
|
Sort_CC : declare
|
|
Comps : array (0 .. Num_CC) of Entity_Id;
|
|
-- Array to collect component and discriminant entities. The
|
|
-- data starts at index 1, the 0'th entry is for the sort
|
|
-- routine.
|
|
|
|
function CP_Lt (Op1, Op2 : Natural) return Boolean;
|
|
-- Compare routine for Sort
|
|
|
|
procedure CP_Move (From : Natural; To : Natural);
|
|
-- Move routine for Sort
|
|
|
|
package Sorting is new GNAT.Heap_Sort_G (CP_Move, CP_Lt);
|
|
|
|
Start : Natural;
|
|
Stop : Natural;
|
|
-- Start and stop positions in the component list of the set of
|
|
-- components with the same starting position (that constitute
|
|
-- components in a single machine scalar).
|
|
|
|
MaxL : Uint;
|
|
-- Maximum last bit value of any component in this set
|
|
|
|
MSS : Uint;
|
|
-- Corresponding machine scalar size
|
|
|
|
-----------
|
|
-- CP_Lt --
|
|
-----------
|
|
|
|
function CP_Lt (Op1, Op2 : Natural) return Boolean is
|
|
begin
|
|
return Position (Component_Clause (Comps (Op1))) <
|
|
Position (Component_Clause (Comps (Op2)));
|
|
end CP_Lt;
|
|
|
|
-------------
|
|
-- CP_Move --
|
|
-------------
|
|
|
|
procedure CP_Move (From : Natural; To : Natural) is
|
|
begin
|
|
Comps (To) := Comps (From);
|
|
end CP_Move;
|
|
|
|
-- Start of processing for Sort_CC
|
|
|
|
begin
|
|
-- Collect the machine scalar relevant component clauses
|
|
|
|
Num_CC := 0;
|
|
Comp := First_Component_Or_Discriminant (R);
|
|
while Present (Comp) loop
|
|
declare
|
|
CC : constant Node_Id := Component_Clause (Comp);
|
|
|
|
begin
|
|
-- Collect only component clauses whose last bit is less
|
|
-- than machine scalar size. Any component clause whose
|
|
-- last bit exceeds this value does not take part in
|
|
-- machine scalar layout considerations. The test for
|
|
-- Error_Posted makes sure we exclude component clauses
|
|
-- for which we already posted an error.
|
|
|
|
if Present (CC)
|
|
and then not Error_Posted (Last_Bit (CC))
|
|
and then Static_Integer (Last_Bit (CC)) <
|
|
Max_Machine_Scalar_Size
|
|
then
|
|
Num_CC := Num_CC + 1;
|
|
Comps (Num_CC) := Comp;
|
|
end if;
|
|
end;
|
|
|
|
Next_Component_Or_Discriminant (Comp);
|
|
end loop;
|
|
|
|
-- Sort by ascending position number
|
|
|
|
Sorting.Sort (Num_CC);
|
|
|
|
-- We now have all the components whose size does not exceed
|
|
-- the max machine scalar value, sorted by starting position.
|
|
-- In this loop we gather groups of clauses starting at the
|
|
-- same position, to process them in accordance with AI-133.
|
|
|
|
Stop := 0;
|
|
while Stop < Num_CC loop
|
|
Start := Stop + 1;
|
|
Stop := Start;
|
|
MaxL :=
|
|
Static_Integer
|
|
(Last_Bit (Component_Clause (Comps (Start))));
|
|
while Stop < Num_CC loop
|
|
if Static_Integer
|
|
(Position (Component_Clause (Comps (Stop + 1)))) =
|
|
Static_Integer
|
|
(Position (Component_Clause (Comps (Stop))))
|
|
then
|
|
Stop := Stop + 1;
|
|
MaxL :=
|
|
UI_Max
|
|
(MaxL,
|
|
Static_Integer
|
|
(Last_Bit
|
|
(Component_Clause (Comps (Stop)))));
|
|
else
|
|
exit;
|
|
end if;
|
|
end loop;
|
|
|
|
-- Now we have a group of component clauses from Start to
|
|
-- Stop whose positions are identical, and MaxL is the
|
|
-- maximum last bit value of any of these components.
|
|
|
|
-- We need to determine the corresponding machine scalar
|
|
-- size. This loop assumes that machine scalar sizes are
|
|
-- even, and that each possible machine scalar has twice
|
|
-- as many bits as the next smaller one.
|
|
|
|
MSS := Max_Machine_Scalar_Size;
|
|
while MSS mod 2 = 0
|
|
and then (MSS / 2) >= SSU
|
|
and then (MSS / 2) > MaxL
|
|
loop
|
|
MSS := MSS / 2;
|
|
end loop;
|
|
|
|
-- Here is where we fix up the Component_Bit_Offset value
|
|
-- to account for the reverse bit order. Some examples of
|
|
-- what needs to be done for the case of a machine scalar
|
|
-- size of 8 are:
|
|
|
|
-- First_Bit .. Last_Bit Component_Bit_Offset
|
|
-- old new old new
|
|
|
|
-- 0 .. 0 7 .. 7 0 7
|
|
-- 0 .. 1 6 .. 7 0 6
|
|
-- 0 .. 2 5 .. 7 0 5
|
|
-- 0 .. 7 0 .. 7 0 4
|
|
|
|
-- 1 .. 1 6 .. 6 1 6
|
|
-- 1 .. 4 3 .. 6 1 3
|
|
-- 4 .. 7 0 .. 3 4 0
|
|
|
|
-- The rule is that the first bit is obtained by subtracting
|
|
-- the old ending bit from machine scalar size - 1.
|
|
|
|
for C in Start .. Stop loop
|
|
declare
|
|
Comp : constant Entity_Id := Comps (C);
|
|
CC : constant Node_Id := Component_Clause (Comp);
|
|
|
|
LB : constant Uint := Static_Integer (Last_Bit (CC));
|
|
NFB : constant Uint := MSS - Uint_1 - LB;
|
|
NLB : constant Uint := NFB + Esize (Comp) - 1;
|
|
Pos : constant Uint := Static_Integer (Position (CC));
|
|
|
|
begin
|
|
if Warn_On_Reverse_Bit_Order then
|
|
Error_Msg_Uint_1 := MSS;
|
|
Error_Msg_N
|
|
("info: reverse bit order in machine " &
|
|
"scalar of length^?V?", First_Bit (CC));
|
|
Error_Msg_Uint_1 := NFB;
|
|
Error_Msg_Uint_2 := NLB;
|
|
|
|
if Bytes_Big_Endian then
|
|
Error_Msg_NE
|
|
("\big-endian range for component "
|
|
& "& is ^ .. ^?V?", First_Bit (CC), Comp);
|
|
else
|
|
Error_Msg_NE
|
|
("\little-endian range for component"
|
|
& "& is ^ .. ^?V?", First_Bit (CC), Comp);
|
|
end if;
|
|
end if;
|
|
|
|
Set_Component_Bit_Offset (Comp, Pos * SSU + NFB);
|
|
Set_Normalized_First_Bit (Comp, NFB mod SSU);
|
|
end;
|
|
end loop;
|
|
end loop;
|
|
end Sort_CC;
|
|
end;
|
|
end if;
|
|
end Adjust_Record_For_Reverse_Bit_Order;
|
|
|
|
-------------------------------------
|
|
-- Alignment_Check_For_Size_Change --
|
|
-------------------------------------
|
|
|
|
procedure Alignment_Check_For_Size_Change (Typ : Entity_Id; Size : Uint) is
|
|
begin
|
|
-- If the alignment is known, and not set by a rep clause, and is
|
|
-- inconsistent with the size being set, then reset it to unknown,
|
|
-- we assume in this case that the size overrides the inherited
|
|
-- alignment, and that the alignment must be recomputed.
|
|
|
|
if Known_Alignment (Typ)
|
|
and then not Has_Alignment_Clause (Typ)
|
|
and then Size mod (Alignment (Typ) * SSU) /= 0
|
|
then
|
|
Init_Alignment (Typ);
|
|
end if;
|
|
end Alignment_Check_For_Size_Change;
|
|
|
|
-------------------------------------
|
|
-- Analyze_Aspects_At_Freeze_Point --
|
|
-------------------------------------
|
|
|
|
procedure Analyze_Aspects_At_Freeze_Point (E : Entity_Id) is
|
|
ASN : Node_Id;
|
|
A_Id : Aspect_Id;
|
|
Ritem : Node_Id;
|
|
|
|
procedure Analyze_Aspect_Default_Value (ASN : Node_Id);
|
|
-- This routine analyzes an Aspect_Default_[Component_]Value denoted by
|
|
-- the aspect specification node ASN.
|
|
|
|
procedure Inherit_Delayed_Rep_Aspects (ASN : Node_Id);
|
|
-- As discussed in the spec of Aspects (see Aspect_Delay declaration),
|
|
-- a derived type can inherit aspects from its parent which have been
|
|
-- specified at the time of the derivation using an aspect, as in:
|
|
--
|
|
-- type A is range 1 .. 10
|
|
-- with Size => Not_Defined_Yet;
|
|
-- ..
|
|
-- type B is new A;
|
|
-- ..
|
|
-- Not_Defined_Yet : constant := 64;
|
|
--
|
|
-- In this example, the Size of A is considered to be specified prior
|
|
-- to the derivation, and thus inherited, even though the value is not
|
|
-- known at the time of derivation. To deal with this, we use two entity
|
|
-- flags. The flag Has_Derived_Rep_Aspects is set in the parent type (A
|
|
-- here), and then the flag May_Inherit_Delayed_Rep_Aspects is set in
|
|
-- the derived type (B here). If this flag is set when the derived type
|
|
-- is frozen, then this procedure is called to ensure proper inheritance
|
|
-- of all delayed aspects from the parent type. The derived type is E,
|
|
-- the argument to Analyze_Aspects_At_Freeze_Point. ASN is the first
|
|
-- aspect specification node in the Rep_Item chain for the parent type.
|
|
|
|
procedure Make_Pragma_From_Boolean_Aspect (ASN : Node_Id);
|
|
-- Given an aspect specification node ASN whose expression is an
|
|
-- optional Boolean, this routines creates the corresponding pragma
|
|
-- at the freezing point.
|
|
|
|
----------------------------------
|
|
-- Analyze_Aspect_Default_Value --
|
|
----------------------------------
|
|
|
|
procedure Analyze_Aspect_Default_Value (ASN : Node_Id) is
|
|
Ent : constant Entity_Id := Entity (ASN);
|
|
Expr : constant Node_Id := Expression (ASN);
|
|
Id : constant Node_Id := Identifier (ASN);
|
|
|
|
begin
|
|
Error_Msg_Name_1 := Chars (Id);
|
|
|
|
if not Is_Type (Ent) then
|
|
Error_Msg_N ("aspect% can only apply to a type", Id);
|
|
return;
|
|
|
|
elsif not Is_First_Subtype (Ent) then
|
|
Error_Msg_N ("aspect% cannot apply to subtype", Id);
|
|
return;
|
|
|
|
elsif A_Id = Aspect_Default_Value
|
|
and then not Is_Scalar_Type (Ent)
|
|
then
|
|
Error_Msg_N ("aspect% can only be applied to scalar type", Id);
|
|
return;
|
|
|
|
elsif A_Id = Aspect_Default_Component_Value then
|
|
if not Is_Array_Type (Ent) then
|
|
Error_Msg_N ("aspect% can only be applied to array type", Id);
|
|
return;
|
|
|
|
elsif not Is_Scalar_Type (Component_Type (Ent)) then
|
|
Error_Msg_N ("aspect% requires scalar components", Id);
|
|
return;
|
|
end if;
|
|
end if;
|
|
|
|
Set_Has_Default_Aspect (Base_Type (Ent));
|
|
|
|
if Is_Scalar_Type (Ent) then
|
|
Set_Default_Aspect_Value (Base_Type (Ent), Expr);
|
|
else
|
|
Set_Default_Aspect_Component_Value (Base_Type (Ent), Expr);
|
|
end if;
|
|
end Analyze_Aspect_Default_Value;
|
|
|
|
---------------------------------
|
|
-- Inherit_Delayed_Rep_Aspects --
|
|
---------------------------------
|
|
|
|
procedure Inherit_Delayed_Rep_Aspects (ASN : Node_Id) is
|
|
P : constant Entity_Id := Entity (ASN);
|
|
-- Entithy for parent type
|
|
|
|
N : Node_Id;
|
|
-- Item from Rep_Item chain
|
|
|
|
A : Aspect_Id;
|
|
|
|
begin
|
|
-- Loop through delayed aspects for the parent type
|
|
|
|
N := ASN;
|
|
while Present (N) loop
|
|
if Nkind (N) = N_Aspect_Specification then
|
|
exit when Entity (N) /= P;
|
|
|
|
if Is_Delayed_Aspect (N) then
|
|
A := Get_Aspect_Id (Chars (Identifier (N)));
|
|
|
|
-- Process delayed rep aspect. For Boolean attributes it is
|
|
-- not possible to cancel an attribute once set (the attempt
|
|
-- to use an aspect with xxx => False is an error) for a
|
|
-- derived type. So for those cases, we do not have to check
|
|
-- if a clause has been given for the derived type, since it
|
|
-- is harmless to set it again if it is already set.
|
|
|
|
case A is
|
|
|
|
-- Alignment
|
|
|
|
when Aspect_Alignment =>
|
|
if not Has_Alignment_Clause (E) then
|
|
Set_Alignment (E, Alignment (P));
|
|
end if;
|
|
|
|
-- Atomic
|
|
|
|
when Aspect_Atomic =>
|
|
if Is_Atomic (P) then
|
|
Set_Is_Atomic (E);
|
|
end if;
|
|
|
|
-- Atomic_Components
|
|
|
|
when Aspect_Atomic_Components =>
|
|
if Has_Atomic_Components (P) then
|
|
Set_Has_Atomic_Components (Base_Type (E));
|
|
end if;
|
|
|
|
-- Bit_Order
|
|
|
|
when Aspect_Bit_Order =>
|
|
if Is_Record_Type (E)
|
|
and then No (Get_Attribute_Definition_Clause
|
|
(E, Attribute_Bit_Order))
|
|
and then Reverse_Bit_Order (P)
|
|
then
|
|
Set_Reverse_Bit_Order (Base_Type (E));
|
|
end if;
|
|
|
|
-- Component_Size
|
|
|
|
when Aspect_Component_Size =>
|
|
if Is_Array_Type (E)
|
|
and then not Has_Component_Size_Clause (E)
|
|
then
|
|
Set_Component_Size
|
|
(Base_Type (E), Component_Size (P));
|
|
end if;
|
|
|
|
-- Machine_Radix
|
|
|
|
when Aspect_Machine_Radix =>
|
|
if Is_Decimal_Fixed_Point_Type (E)
|
|
and then not Has_Machine_Radix_Clause (E)
|
|
then
|
|
Set_Machine_Radix_10 (E, Machine_Radix_10 (P));
|
|
end if;
|
|
|
|
-- Object_Size (also Size which also sets Object_Size)
|
|
|
|
when Aspect_Object_Size | Aspect_Size =>
|
|
if not Has_Size_Clause (E)
|
|
and then
|
|
No (Get_Attribute_Definition_Clause
|
|
(E, Attribute_Object_Size))
|
|
then
|
|
Set_Esize (E, Esize (P));
|
|
end if;
|
|
|
|
-- Pack
|
|
|
|
when Aspect_Pack =>
|
|
if not Is_Packed (E) then
|
|
Set_Is_Packed (Base_Type (E));
|
|
|
|
if Is_Bit_Packed_Array (P) then
|
|
Set_Is_Bit_Packed_Array (Base_Type (E));
|
|
Set_Packed_Array_Impl_Type
|
|
(E, Packed_Array_Impl_Type (P));
|
|
end if;
|
|
end if;
|
|
|
|
-- Scalar_Storage_Order
|
|
|
|
when Aspect_Scalar_Storage_Order =>
|
|
if (Is_Record_Type (E) or else Is_Array_Type (E))
|
|
and then No (Get_Attribute_Definition_Clause
|
|
(E, Attribute_Scalar_Storage_Order))
|
|
and then Reverse_Storage_Order (P)
|
|
then
|
|
Set_Reverse_Storage_Order (Base_Type (E));
|
|
|
|
-- Clear default SSO indications, since the aspect
|
|
-- overrides the default.
|
|
|
|
Set_SSO_Set_Low_By_Default (Base_Type (E), False);
|
|
Set_SSO_Set_High_By_Default (Base_Type (E), False);
|
|
end if;
|
|
|
|
-- Small
|
|
|
|
when Aspect_Small =>
|
|
if Is_Fixed_Point_Type (E)
|
|
and then not Has_Small_Clause (E)
|
|
then
|
|
Set_Small_Value (E, Small_Value (P));
|
|
end if;
|
|
|
|
-- Storage_Size
|
|
|
|
when Aspect_Storage_Size =>
|
|
if (Is_Access_Type (E) or else Is_Task_Type (E))
|
|
and then not Has_Storage_Size_Clause (E)
|
|
then
|
|
Set_Storage_Size_Variable
|
|
(Base_Type (E), Storage_Size_Variable (P));
|
|
end if;
|
|
|
|
-- Value_Size
|
|
|
|
when Aspect_Value_Size =>
|
|
|
|
-- Value_Size is never inherited, it is either set by
|
|
-- default, or it is explicitly set for the derived
|
|
-- type. So nothing to do here.
|
|
|
|
null;
|
|
|
|
-- Volatile
|
|
|
|
when Aspect_Volatile =>
|
|
if Is_Volatile (P) then
|
|
Set_Is_Volatile (E);
|
|
end if;
|
|
|
|
-- Volatile_Components
|
|
|
|
when Aspect_Volatile_Components =>
|
|
if Has_Volatile_Components (P) then
|
|
Set_Has_Volatile_Components (Base_Type (E));
|
|
end if;
|
|
|
|
-- That should be all the Rep Aspects
|
|
|
|
when others =>
|
|
pragma Assert (Aspect_Delay (A_Id) /= Rep_Aspect);
|
|
null;
|
|
|
|
end case;
|
|
end if;
|
|
end if;
|
|
|
|
N := Next_Rep_Item (N);
|
|
end loop;
|
|
end Inherit_Delayed_Rep_Aspects;
|
|
|
|
-------------------------------------
|
|
-- Make_Pragma_From_Boolean_Aspect --
|
|
-------------------------------------
|
|
|
|
procedure Make_Pragma_From_Boolean_Aspect (ASN : Node_Id) is
|
|
Ident : constant Node_Id := Identifier (ASN);
|
|
A_Name : constant Name_Id := Chars (Ident);
|
|
A_Id : constant Aspect_Id := Get_Aspect_Id (A_Name);
|
|
Ent : constant Entity_Id := Entity (ASN);
|
|
Expr : constant Node_Id := Expression (ASN);
|
|
Loc : constant Source_Ptr := Sloc (ASN);
|
|
|
|
Prag : Node_Id;
|
|
|
|
procedure Check_False_Aspect_For_Derived_Type;
|
|
-- This procedure checks for the case of a false aspect for a derived
|
|
-- type, which improperly tries to cancel an aspect inherited from
|
|
-- the parent.
|
|
|
|
-----------------------------------------
|
|
-- Check_False_Aspect_For_Derived_Type --
|
|
-----------------------------------------
|
|
|
|
procedure Check_False_Aspect_For_Derived_Type is
|
|
Par : Node_Id;
|
|
|
|
begin
|
|
-- We are only checking derived types
|
|
|
|
if not Is_Derived_Type (E) then
|
|
return;
|
|
end if;
|
|
|
|
Par := Nearest_Ancestor (E);
|
|
|
|
case A_Id is
|
|
when Aspect_Atomic | Aspect_Shared =>
|
|
if not Is_Atomic (Par) then
|
|
return;
|
|
end if;
|
|
|
|
when Aspect_Atomic_Components =>
|
|
if not Has_Atomic_Components (Par) then
|
|
return;
|
|
end if;
|
|
|
|
when Aspect_Discard_Names =>
|
|
if not Discard_Names (Par) then
|
|
return;
|
|
end if;
|
|
|
|
when Aspect_Pack =>
|
|
if not Is_Packed (Par) then
|
|
return;
|
|
end if;
|
|
|
|
when Aspect_Unchecked_Union =>
|
|
if not Is_Unchecked_Union (Par) then
|
|
return;
|
|
end if;
|
|
|
|
when Aspect_Volatile =>
|
|
if not Is_Volatile (Par) then
|
|
return;
|
|
end if;
|
|
|
|
when Aspect_Volatile_Components =>
|
|
if not Has_Volatile_Components (Par) then
|
|
return;
|
|
end if;
|
|
|
|
when others =>
|
|
return;
|
|
end case;
|
|
|
|
-- Fall through means we are canceling an inherited aspect
|
|
|
|
Error_Msg_Name_1 := A_Name;
|
|
Error_Msg_NE
|
|
("derived type& inherits aspect%, cannot cancel", Expr, E);
|
|
|
|
end Check_False_Aspect_For_Derived_Type;
|
|
|
|
-- Start of processing for Make_Pragma_From_Boolean_Aspect
|
|
|
|
begin
|
|
-- Note that we know Expr is present, because for a missing Expr
|
|
-- argument, we knew it was True and did not need to delay the
|
|
-- evaluation to the freeze point.
|
|
|
|
if Is_False (Static_Boolean (Expr)) then
|
|
Check_False_Aspect_For_Derived_Type;
|
|
|
|
else
|
|
Prag :=
|
|
Make_Pragma (Loc,
|
|
Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Ident),
|
|
Expression => New_Occurrence_Of (Ent, Sloc (Ident)))),
|
|
|
|
Pragma_Identifier =>
|
|
Make_Identifier (Sloc (Ident), Chars (Ident)));
|
|
|
|
Set_From_Aspect_Specification (Prag, True);
|
|
Set_Corresponding_Aspect (Prag, ASN);
|
|
Set_Aspect_Rep_Item (ASN, Prag);
|
|
Set_Is_Delayed_Aspect (Prag);
|
|
Set_Parent (Prag, ASN);
|
|
end if;
|
|
end Make_Pragma_From_Boolean_Aspect;
|
|
|
|
-- Start of processing for Analyze_Aspects_At_Freeze_Point
|
|
|
|
begin
|
|
-- Must be visible in current scope
|
|
|
|
if not Scope_Within_Or_Same (Current_Scope, Scope (E)) then
|
|
return;
|
|
end if;
|
|
|
|
-- Look for aspect specification entries for this entity
|
|
|
|
ASN := First_Rep_Item (E);
|
|
while Present (ASN) loop
|
|
if Nkind (ASN) = N_Aspect_Specification then
|
|
exit when Entity (ASN) /= E;
|
|
|
|
if Is_Delayed_Aspect (ASN) then
|
|
A_Id := Get_Aspect_Id (ASN);
|
|
|
|
case A_Id is
|
|
|
|
-- For aspects whose expression is an optional Boolean, make
|
|
-- the corresponding pragma at the freezing point.
|
|
|
|
when Boolean_Aspects |
|
|
Library_Unit_Aspects =>
|
|
Make_Pragma_From_Boolean_Aspect (ASN);
|
|
|
|
-- Special handling for aspects that don't correspond to
|
|
-- pragmas/attributes.
|
|
|
|
when Aspect_Default_Value |
|
|
Aspect_Default_Component_Value =>
|
|
Analyze_Aspect_Default_Value (ASN);
|
|
|
|
-- Ditto for iterator aspects, because the corresponding
|
|
-- attributes may not have been analyzed yet.
|
|
|
|
when Aspect_Constant_Indexing |
|
|
Aspect_Variable_Indexing |
|
|
Aspect_Default_Iterator |
|
|
Aspect_Iterator_Element =>
|
|
Analyze (Expression (ASN));
|
|
|
|
when Aspect_Iterable =>
|
|
Validate_Iterable_Aspect (E, ASN);
|
|
|
|
when others =>
|
|
null;
|
|
end case;
|
|
|
|
Ritem := Aspect_Rep_Item (ASN);
|
|
|
|
if Present (Ritem) then
|
|
Analyze (Ritem);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
Next_Rep_Item (ASN);
|
|
end loop;
|
|
|
|
-- This is where we inherit delayed rep aspects from our parent. Note
|
|
-- that if we fell out of the above loop with ASN non-empty, it means
|
|
-- we hit an aspect for an entity other than E, and it must be the
|
|
-- type from which we were derived.
|
|
|
|
if May_Inherit_Delayed_Rep_Aspects (E) then
|
|
Inherit_Delayed_Rep_Aspects (ASN);
|
|
end if;
|
|
end Analyze_Aspects_At_Freeze_Point;
|
|
|
|
-----------------------------------
|
|
-- Analyze_Aspect_Specifications --
|
|
-----------------------------------
|
|
|
|
procedure Analyze_Aspect_Specifications (N : Node_Id; E : Entity_Id) is
|
|
procedure Decorate (Asp : Node_Id; Prag : Node_Id);
|
|
-- Establish linkages between an aspect and its corresponding
|
|
-- pragma.
|
|
|
|
procedure Insert_After_SPARK_Mode
|
|
(Prag : Node_Id;
|
|
Ins_Nod : Node_Id;
|
|
Decls : List_Id);
|
|
-- Subsidiary to the analysis of aspects Abstract_State, Initializes,
|
|
-- Initial_Condition and Refined_State. Insert node Prag before node
|
|
-- Ins_Nod. If Ins_Nod is for pragma SPARK_Mode, then skip SPARK_Mode.
|
|
-- Decls is the associated declarative list where Prag is to reside.
|
|
|
|
procedure Insert_Pragma (Prag : Node_Id);
|
|
-- Subsidiary to the analysis of aspects Attach_Handler, Contract_Cases,
|
|
-- Depends, Global, Post, Pre, Refined_Depends and Refined_Global.
|
|
-- Insert pragma Prag such that it mimics the placement of a source
|
|
-- pragma of the same kind.
|
|
--
|
|
-- procedure Proc (Formal : ...) with Global => ...;
|
|
--
|
|
-- procedure Proc (Formal : ...);
|
|
-- pragma Global (...);
|
|
|
|
--------------
|
|
-- Decorate --
|
|
--------------
|
|
|
|
procedure Decorate (Asp : Node_Id; Prag : Node_Id) is
|
|
begin
|
|
Set_Aspect_Rep_Item (Asp, Prag);
|
|
Set_Corresponding_Aspect (Prag, Asp);
|
|
Set_From_Aspect_Specification (Prag);
|
|
Set_Parent (Prag, Asp);
|
|
end Decorate;
|
|
|
|
-----------------------------
|
|
-- Insert_After_SPARK_Mode --
|
|
-----------------------------
|
|
|
|
procedure Insert_After_SPARK_Mode
|
|
(Prag : Node_Id;
|
|
Ins_Nod : Node_Id;
|
|
Decls : List_Id)
|
|
is
|
|
Decl : Node_Id := Ins_Nod;
|
|
|
|
begin
|
|
-- Skip SPARK_Mode
|
|
|
|
if Present (Decl)
|
|
and then Nkind (Decl) = N_Pragma
|
|
and then Pragma_Name (Decl) = Name_SPARK_Mode
|
|
then
|
|
Decl := Next (Decl);
|
|
end if;
|
|
|
|
if Present (Decl) then
|
|
Insert_Before (Decl, Prag);
|
|
|
|
-- Aitem acts as the last declaration
|
|
|
|
else
|
|
Append_To (Decls, Prag);
|
|
end if;
|
|
end Insert_After_SPARK_Mode;
|
|
|
|
-------------------
|
|
-- Insert_Pragma --
|
|
-------------------
|
|
|
|
procedure Insert_Pragma (Prag : Node_Id) is
|
|
Aux : Node_Id;
|
|
Decl : Node_Id;
|
|
|
|
begin
|
|
-- When the context is a library unit, the pragma is added to the
|
|
-- Pragmas_After list.
|
|
|
|
if Nkind (Parent (N)) = N_Compilation_Unit then
|
|
Aux := Aux_Decls_Node (Parent (N));
|
|
|
|
if No (Pragmas_After (Aux)) then
|
|
Set_Pragmas_After (Aux, New_List);
|
|
end if;
|
|
|
|
Prepend (Prag, Pragmas_After (Aux));
|
|
|
|
-- Pragmas associated with subprogram bodies are inserted in the
|
|
-- declarative part.
|
|
|
|
elsif Nkind (N) = N_Subprogram_Body then
|
|
if Present (Declarations (N)) then
|
|
|
|
-- Skip other internally generated pragmas from aspects to find
|
|
-- the proper insertion point. As a result the order of pragmas
|
|
-- is the same as the order of aspects.
|
|
|
|
Decl := First (Declarations (N));
|
|
while Present (Decl) loop
|
|
if Nkind (Decl) = N_Pragma
|
|
and then From_Aspect_Specification (Decl)
|
|
then
|
|
Next (Decl);
|
|
else
|
|
exit;
|
|
end if;
|
|
end loop;
|
|
|
|
if Present (Decl) then
|
|
Insert_Before (Decl, Prag);
|
|
else
|
|
Append (Prag, Declarations (N));
|
|
end if;
|
|
else
|
|
Set_Declarations (N, New_List (Prag));
|
|
end if;
|
|
|
|
-- Default
|
|
|
|
else
|
|
Insert_After (N, Prag);
|
|
end if;
|
|
end Insert_Pragma;
|
|
|
|
-- Local variables
|
|
|
|
Aspect : Node_Id;
|
|
Aitem : Node_Id;
|
|
Ent : Node_Id;
|
|
|
|
L : constant List_Id := Aspect_Specifications (N);
|
|
|
|
Ins_Node : Node_Id := N;
|
|
-- Insert pragmas/attribute definition clause after this node when no
|
|
-- delayed analysis is required.
|
|
|
|
-- Start of processing for Analyze_Aspect_Specifications
|
|
|
|
-- The general processing involves building an attribute definition
|
|
-- clause or a pragma node that corresponds to the aspect. Then in order
|
|
-- to delay the evaluation of this aspect to the freeze point, we attach
|
|
-- the corresponding pragma/attribute definition clause to the aspect
|
|
-- specification node, which is then placed in the Rep Item chain. In
|
|
-- this case we mark the entity by setting the flag Has_Delayed_Aspects
|
|
-- and we evaluate the rep item at the freeze point. When the aspect
|
|
-- doesn't have a corresponding pragma/attribute definition clause, then
|
|
-- its analysis is simply delayed at the freeze point.
|
|
|
|
-- Some special cases don't require delay analysis, thus the aspect is
|
|
-- analyzed right now.
|
|
|
|
-- Note that there is a special handling for Pre, Post, Test_Case,
|
|
-- Contract_Cases aspects. In these cases, we do not have to worry
|
|
-- about delay issues, since the pragmas themselves deal with delay
|
|
-- of visibility for the expression analysis. Thus, we just insert
|
|
-- the pragma after the node N.
|
|
|
|
begin
|
|
pragma Assert (Present (L));
|
|
|
|
-- Loop through aspects
|
|
|
|
Aspect := First (L);
|
|
Aspect_Loop : while Present (Aspect) loop
|
|
Analyze_One_Aspect : declare
|
|
Expr : constant Node_Id := Expression (Aspect);
|
|
Id : constant Node_Id := Identifier (Aspect);
|
|
Loc : constant Source_Ptr := Sloc (Aspect);
|
|
Nam : constant Name_Id := Chars (Id);
|
|
A_Id : constant Aspect_Id := Get_Aspect_Id (Nam);
|
|
Anod : Node_Id;
|
|
|
|
Delay_Required : Boolean;
|
|
-- Set False if delay is not required
|
|
|
|
Eloc : Source_Ptr := No_Location;
|
|
-- Source location of expression, modified when we split PPC's. It
|
|
-- is set below when Expr is present.
|
|
|
|
procedure Analyze_Aspect_External_Or_Link_Name;
|
|
-- Perform analysis of the External_Name or Link_Name aspects
|
|
|
|
procedure Analyze_Aspect_Implicit_Dereference;
|
|
-- Perform analysis of the Implicit_Dereference aspects
|
|
|
|
procedure Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations : List_Id;
|
|
Pragma_Name : Name_Id);
|
|
-- This is a wrapper for Make_Pragma used for converting aspects
|
|
-- to pragmas. It takes care of Sloc (set from Loc) and building
|
|
-- the pragma identifier from the given name. In addition the
|
|
-- flags Class_Present and Split_PPC are set from the aspect
|
|
-- node, as well as Is_Ignored. This routine also sets the
|
|
-- From_Aspect_Specification in the resulting pragma node to
|
|
-- True, and sets Corresponding_Aspect to point to the aspect.
|
|
-- The resulting pragma is assigned to Aitem.
|
|
|
|
------------------------------------------
|
|
-- Analyze_Aspect_External_Or_Link_Name --
|
|
------------------------------------------
|
|
|
|
procedure Analyze_Aspect_External_Or_Link_Name is
|
|
begin
|
|
-- Verify that there is an Import/Export aspect defined for the
|
|
-- entity. The processing of that aspect in turn checks that
|
|
-- there is a Convention aspect declared. The pragma is
|
|
-- constructed when processing the Convention aspect.
|
|
|
|
declare
|
|
A : Node_Id;
|
|
|
|
begin
|
|
A := First (L);
|
|
while Present (A) loop
|
|
exit when Nam_In (Chars (Identifier (A)), Name_Export,
|
|
Name_Import);
|
|
Next (A);
|
|
end loop;
|
|
|
|
if No (A) then
|
|
Error_Msg_N
|
|
("missing Import/Export for Link/External name",
|
|
Aspect);
|
|
end if;
|
|
end;
|
|
end Analyze_Aspect_External_Or_Link_Name;
|
|
|
|
-----------------------------------------
|
|
-- Analyze_Aspect_Implicit_Dereference --
|
|
-----------------------------------------
|
|
|
|
procedure Analyze_Aspect_Implicit_Dereference is
|
|
begin
|
|
if not Is_Type (E) or else not Has_Discriminants (E) then
|
|
Error_Msg_N
|
|
("aspect must apply to a type with discriminants", N);
|
|
|
|
else
|
|
declare
|
|
Disc : Entity_Id;
|
|
|
|
begin
|
|
Disc := First_Discriminant (E);
|
|
while Present (Disc) loop
|
|
if Chars (Expr) = Chars (Disc)
|
|
and then Ekind (Etype (Disc)) =
|
|
E_Anonymous_Access_Type
|
|
then
|
|
Set_Has_Implicit_Dereference (E);
|
|
Set_Has_Implicit_Dereference (Disc);
|
|
return;
|
|
end if;
|
|
|
|
Next_Discriminant (Disc);
|
|
end loop;
|
|
|
|
-- Error if no proper access discriminant.
|
|
|
|
Error_Msg_NE
|
|
("not an access discriminant of&", Expr, E);
|
|
end;
|
|
end if;
|
|
end Analyze_Aspect_Implicit_Dereference;
|
|
|
|
-----------------------
|
|
-- Make_Aitem_Pragma --
|
|
-----------------------
|
|
|
|
procedure Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations : List_Id;
|
|
Pragma_Name : Name_Id)
|
|
is
|
|
Args : List_Id := Pragma_Argument_Associations;
|
|
|
|
begin
|
|
-- We should never get here if aspect was disabled
|
|
|
|
pragma Assert (not Is_Disabled (Aspect));
|
|
|
|
-- Certain aspects allow for an optional name or expression. Do
|
|
-- not generate a pragma with empty argument association list.
|
|
|
|
if No (Args) or else No (Expression (First (Args))) then
|
|
Args := No_List;
|
|
end if;
|
|
|
|
-- Build the pragma
|
|
|
|
Aitem :=
|
|
Make_Pragma (Loc,
|
|
Pragma_Argument_Associations => Args,
|
|
Pragma_Identifier =>
|
|
Make_Identifier (Sloc (Id), Pragma_Name),
|
|
Class_Present => Class_Present (Aspect),
|
|
Split_PPC => Split_PPC (Aspect));
|
|
|
|
-- Set additional semantic fields
|
|
|
|
if Is_Ignored (Aspect) then
|
|
Set_Is_Ignored (Aitem);
|
|
elsif Is_Checked (Aspect) then
|
|
Set_Is_Checked (Aitem);
|
|
end if;
|
|
|
|
Set_Corresponding_Aspect (Aitem, Aspect);
|
|
Set_From_Aspect_Specification (Aitem, True);
|
|
end Make_Aitem_Pragma;
|
|
|
|
-- Start of processing for Analyze_One_Aspect
|
|
|
|
begin
|
|
-- Skip aspect if already analyzed, to avoid looping in some cases
|
|
|
|
if Analyzed (Aspect) then
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Skip looking at aspect if it is totally disabled. Just mark it
|
|
-- as such for later reference in the tree. This also sets the
|
|
-- Is_Ignored and Is_Checked flags appropriately.
|
|
|
|
Check_Applicable_Policy (Aspect);
|
|
|
|
if Is_Disabled (Aspect) then
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Set the source location of expression, used in the case of
|
|
-- a failed precondition/postcondition or invariant. Note that
|
|
-- the source location of the expression is not usually the best
|
|
-- choice here. For example, it gets located on the last AND
|
|
-- keyword in a chain of boolean expressiond AND'ed together.
|
|
-- It is best to put the message on the first character of the
|
|
-- assertion, which is the effect of the First_Node call here.
|
|
|
|
if Present (Expr) then
|
|
Eloc := Sloc (First_Node (Expr));
|
|
end if;
|
|
|
|
-- Check restriction No_Implementation_Aspect_Specifications
|
|
|
|
if Implementation_Defined_Aspect (A_Id) then
|
|
Check_Restriction
|
|
(No_Implementation_Aspect_Specifications, Aspect);
|
|
end if;
|
|
|
|
-- Check restriction No_Specification_Of_Aspect
|
|
|
|
Check_Restriction_No_Specification_Of_Aspect (Aspect);
|
|
|
|
-- Mark aspect analyzed (actual analysis is delayed till later)
|
|
|
|
Set_Analyzed (Aspect);
|
|
Set_Entity (Aspect, E);
|
|
Ent := New_Occurrence_Of (E, Sloc (Id));
|
|
|
|
-- Check for duplicate aspect. Note that the Comes_From_Source
|
|
-- test allows duplicate Pre/Post's that we generate internally
|
|
-- to escape being flagged here.
|
|
|
|
if No_Duplicates_Allowed (A_Id) then
|
|
Anod := First (L);
|
|
while Anod /= Aspect loop
|
|
if Comes_From_Source (Aspect)
|
|
and then Same_Aspect (A_Id, Get_Aspect_Id (Anod))
|
|
then
|
|
Error_Msg_Name_1 := Nam;
|
|
Error_Msg_Sloc := Sloc (Anod);
|
|
|
|
-- Case of same aspect specified twice
|
|
|
|
if Class_Present (Anod) = Class_Present (Aspect) then
|
|
if not Class_Present (Anod) then
|
|
Error_Msg_NE
|
|
("aspect% for & previously given#",
|
|
Id, E);
|
|
else
|
|
Error_Msg_NE
|
|
("aspect `%''Class` for & previously given#",
|
|
Id, E);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
Next (Anod);
|
|
end loop;
|
|
end if;
|
|
|
|
-- Check some general restrictions on language defined aspects
|
|
|
|
if not Implementation_Defined_Aspect (A_Id) then
|
|
Error_Msg_Name_1 := Nam;
|
|
|
|
-- Not allowed for renaming declarations
|
|
|
|
if Nkind (N) in N_Renaming_Declaration then
|
|
Error_Msg_N
|
|
("aspect % not allowed for renaming declaration",
|
|
Aspect);
|
|
end if;
|
|
|
|
-- Not allowed for formal type declarations
|
|
|
|
if Nkind (N) = N_Formal_Type_Declaration then
|
|
Error_Msg_N
|
|
("aspect % not allowed for formal type declaration",
|
|
Aspect);
|
|
end if;
|
|
end if;
|
|
|
|
-- Copy expression for later processing by the procedures
|
|
-- Check_Aspect_At_[Freeze_Point | End_Of_Declarations]
|
|
|
|
Set_Entity (Id, New_Copy_Tree (Expr));
|
|
|
|
-- Set Delay_Required as appropriate to aspect
|
|
|
|
case Aspect_Delay (A_Id) is
|
|
when Always_Delay =>
|
|
Delay_Required := True;
|
|
|
|
when Never_Delay =>
|
|
Delay_Required := False;
|
|
|
|
when Rep_Aspect =>
|
|
|
|
-- If expression has the form of an integer literal, then
|
|
-- do not delay, since we know the value cannot change.
|
|
-- This optimization catches most rep clause cases.
|
|
|
|
if (Present (Expr) and then Nkind (Expr) = N_Integer_Literal)
|
|
or else (A_Id in Boolean_Aspects and then No (Expr))
|
|
then
|
|
Delay_Required := False;
|
|
else
|
|
Delay_Required := True;
|
|
Set_Has_Delayed_Rep_Aspects (E);
|
|
end if;
|
|
end case;
|
|
|
|
-- Processing based on specific aspect
|
|
|
|
case A_Id is
|
|
|
|
-- No_Aspect should be impossible
|
|
|
|
when No_Aspect =>
|
|
raise Program_Error;
|
|
|
|
-- Case 1: Aspects corresponding to attribute definition
|
|
-- clauses.
|
|
|
|
when Aspect_Address |
|
|
Aspect_Alignment |
|
|
Aspect_Bit_Order |
|
|
Aspect_Component_Size |
|
|
Aspect_Constant_Indexing |
|
|
Aspect_Default_Iterator |
|
|
Aspect_Dispatching_Domain |
|
|
Aspect_External_Tag |
|
|
Aspect_Input |
|
|
Aspect_Iterable |
|
|
Aspect_Iterator_Element |
|
|
Aspect_Machine_Radix |
|
|
Aspect_Object_Size |
|
|
Aspect_Output |
|
|
Aspect_Read |
|
|
Aspect_Scalar_Storage_Order |
|
|
Aspect_Size |
|
|
Aspect_Small |
|
|
Aspect_Simple_Storage_Pool |
|
|
Aspect_Storage_Pool |
|
|
Aspect_Stream_Size |
|
|
Aspect_Value_Size |
|
|
Aspect_Variable_Indexing |
|
|
Aspect_Write =>
|
|
|
|
-- Indexing aspects apply only to tagged type
|
|
|
|
if (A_Id = Aspect_Constant_Indexing
|
|
or else
|
|
A_Id = Aspect_Variable_Indexing)
|
|
and then not (Is_Type (E)
|
|
and then Is_Tagged_Type (E))
|
|
then
|
|
Error_Msg_N ("indexing applies to a tagged type", N);
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- For the case of aspect Address, we don't consider that we
|
|
-- know the entity is never set in the source, since it is
|
|
-- is likely aliasing is occurring.
|
|
|
|
-- Note: one might think that the analysis of the resulting
|
|
-- attribute definition clause would take care of that, but
|
|
-- that's not the case since it won't be from source.
|
|
|
|
if A_Id = Aspect_Address then
|
|
Set_Never_Set_In_Source (E, False);
|
|
end if;
|
|
|
|
-- Correctness of the profile of a stream operation is
|
|
-- verified at the freeze point, but we must detect the
|
|
-- illegal specification of this aspect for a subtype now,
|
|
-- to prevent malformed rep_item chains.
|
|
|
|
if (A_Id = Aspect_Input or else
|
|
A_Id = Aspect_Output or else
|
|
A_Id = Aspect_Read or else
|
|
A_Id = Aspect_Write)
|
|
and not Is_First_Subtype (E)
|
|
then
|
|
Error_Msg_N
|
|
("local name must be a first subtype", Aspect);
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Construct the attribute definition clause
|
|
|
|
Aitem :=
|
|
Make_Attribute_Definition_Clause (Loc,
|
|
Name => Ent,
|
|
Chars => Chars (Id),
|
|
Expression => Relocate_Node (Expr));
|
|
|
|
-- If the address is specified, then we treat the entity as
|
|
-- referenced, to avoid spurious warnings. This is analogous
|
|
-- to what is done with an attribute definition clause, but
|
|
-- here we don't want to generate a reference because this
|
|
-- is the point of definition of the entity.
|
|
|
|
if A_Id = Aspect_Address then
|
|
Set_Referenced (E);
|
|
end if;
|
|
|
|
-- Case 2: Aspects corresponding to pragmas
|
|
|
|
-- Case 2a: Aspects corresponding to pragmas with two
|
|
-- arguments, where the first argument is a local name
|
|
-- referring to the entity, and the second argument is the
|
|
-- aspect definition expression.
|
|
|
|
-- Linker_Section/Suppress/Unsuppress
|
|
|
|
when Aspect_Linker_Section |
|
|
Aspect_Suppress |
|
|
Aspect_Unsuppress =>
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => New_Occurrence_Of (E, Loc)),
|
|
Make_Pragma_Argument_Association (Sloc (Expr),
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Chars (Id));
|
|
|
|
-- Synchronization
|
|
|
|
-- Corresponds to pragma Implemented, construct the pragma
|
|
|
|
when Aspect_Synchronization =>
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => New_Occurrence_Of (E, Loc)),
|
|
Make_Pragma_Argument_Association (Sloc (Expr),
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Implemented);
|
|
|
|
-- Attach_Handler
|
|
|
|
when Aspect_Attach_Handler =>
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Ent),
|
|
Expression => Ent),
|
|
Make_Pragma_Argument_Association (Sloc (Expr),
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Attach_Handler);
|
|
|
|
-- We need to insert this pragma into the tree to get proper
|
|
-- processing and to look valid from a placement viewpoint.
|
|
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Dynamic_Predicate, Predicate, Static_Predicate
|
|
|
|
when Aspect_Dynamic_Predicate |
|
|
Aspect_Predicate |
|
|
Aspect_Static_Predicate =>
|
|
|
|
-- These aspects apply only to subtypes
|
|
|
|
if not Is_Type (E) then
|
|
Error_Msg_N
|
|
("predicate can only be specified for a subtype",
|
|
Aspect);
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Construct the pragma (always a pragma Predicate, with
|
|
-- flags recording whether it is static/dynamic). We also
|
|
-- set flags recording this in the type itself.
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Ent),
|
|
Expression => Ent),
|
|
Make_Pragma_Argument_Association (Sloc (Expr),
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Predicate);
|
|
|
|
-- Mark type has predicates, and remember what kind of
|
|
-- aspect lead to this predicate (we need this to access
|
|
-- the right set of check policies later on).
|
|
|
|
Set_Has_Predicates (E);
|
|
|
|
if A_Id = Aspect_Dynamic_Predicate then
|
|
Set_Has_Dynamic_Predicate_Aspect (E);
|
|
elsif A_Id = Aspect_Static_Predicate then
|
|
Set_Has_Static_Predicate_Aspect (E);
|
|
end if;
|
|
|
|
-- If the type is private, indicate that its completion
|
|
-- has a freeze node, because that is the one that will
|
|
-- be visible at freeze time.
|
|
|
|
if Is_Private_Type (E) and then Present (Full_View (E)) then
|
|
Set_Has_Predicates (Full_View (E));
|
|
|
|
if A_Id = Aspect_Dynamic_Predicate then
|
|
Set_Has_Dynamic_Predicate_Aspect (Full_View (E));
|
|
elsif A_Id = Aspect_Static_Predicate then
|
|
Set_Has_Static_Predicate_Aspect (Full_View (E));
|
|
end if;
|
|
|
|
Set_Has_Delayed_Aspects (Full_View (E));
|
|
Ensure_Freeze_Node (Full_View (E));
|
|
end if;
|
|
|
|
-- Case 2b: Aspects corresponding to pragmas with two
|
|
-- arguments, where the second argument is a local name
|
|
-- referring to the entity, and the first argument is the
|
|
-- aspect definition expression.
|
|
|
|
-- Convention
|
|
|
|
when Aspect_Convention =>
|
|
|
|
-- The aspect may be part of the specification of an import
|
|
-- or export pragma. Scan the aspect list to gather the
|
|
-- other components, if any. The name of the generated
|
|
-- pragma is one of Convention/Import/Export.
|
|
|
|
declare
|
|
P_Name : Name_Id;
|
|
A_Name : Name_Id;
|
|
A : Node_Id;
|
|
Arg_List : List_Id;
|
|
Found : Boolean;
|
|
L_Assoc : Node_Id;
|
|
E_Assoc : Node_Id;
|
|
|
|
begin
|
|
P_Name := Chars (Id);
|
|
Found := False;
|
|
Arg_List := New_List;
|
|
L_Assoc := Empty;
|
|
E_Assoc := Empty;
|
|
|
|
A := First (L);
|
|
while Present (A) loop
|
|
A_Name := Chars (Identifier (A));
|
|
|
|
if Nam_In (A_Name, Name_Import, Name_Export) then
|
|
if Found then
|
|
Error_Msg_N ("conflicting", A);
|
|
else
|
|
Found := True;
|
|
end if;
|
|
|
|
P_Name := A_Name;
|
|
|
|
elsif A_Name = Name_Link_Name then
|
|
L_Assoc :=
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Chars => A_Name,
|
|
Expression => Relocate_Node (Expression (A)));
|
|
|
|
elsif A_Name = Name_External_Name then
|
|
E_Assoc :=
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Chars => A_Name,
|
|
Expression => Relocate_Node (Expression (A)));
|
|
end if;
|
|
|
|
Next (A);
|
|
end loop;
|
|
|
|
Arg_List := New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Expr),
|
|
Expression => Relocate_Node (Expr)),
|
|
Make_Pragma_Argument_Association (Sloc (Ent),
|
|
Expression => Ent));
|
|
|
|
if Present (L_Assoc) then
|
|
Append_To (Arg_List, L_Assoc);
|
|
end if;
|
|
|
|
if Present (E_Assoc) then
|
|
Append_To (Arg_List, E_Assoc);
|
|
end if;
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => Arg_List,
|
|
Pragma_Name => P_Name);
|
|
end;
|
|
|
|
-- CPU, Interrupt_Priority, Priority
|
|
|
|
-- These three aspects can be specified for a subprogram spec
|
|
-- or body, in which case we analyze the expression and export
|
|
-- the value of the aspect.
|
|
|
|
-- Previously, we generated an equivalent pragma for bodies
|
|
-- (note that the specs cannot contain these pragmas). The
|
|
-- pragma was inserted ahead of local declarations, rather than
|
|
-- after the body. This leads to a certain duplication between
|
|
-- the processing performed for the aspect and the pragma, but
|
|
-- given the straightforward handling required it is simpler
|
|
-- to duplicate than to translate the aspect in the spec into
|
|
-- a pragma in the declarative part of the body.
|
|
|
|
when Aspect_CPU |
|
|
Aspect_Interrupt_Priority |
|
|
Aspect_Priority =>
|
|
|
|
if Nkind_In (N, N_Subprogram_Body,
|
|
N_Subprogram_Declaration)
|
|
then
|
|
-- Analyze the aspect expression
|
|
|
|
Analyze_And_Resolve (Expr, Standard_Integer);
|
|
|
|
-- Interrupt_Priority aspect not allowed for main
|
|
-- subprograms. ARM D.1 does not forbid this explicitly,
|
|
-- but ARM J.15.11 (6/3) does not permit pragma
|
|
-- Interrupt_Priority for subprograms.
|
|
|
|
if A_Id = Aspect_Interrupt_Priority then
|
|
Error_Msg_N
|
|
("Interrupt_Priority aspect cannot apply to "
|
|
& "subprogram", Expr);
|
|
|
|
-- The expression must be static
|
|
|
|
elsif not Is_OK_Static_Expression (Expr) then
|
|
Flag_Non_Static_Expr
|
|
("aspect requires static expression!", Expr);
|
|
|
|
-- Check whether this is the main subprogram. Issue a
|
|
-- warning only if it is obviously not a main program
|
|
-- (when it has parameters or when the subprogram is
|
|
-- within a package).
|
|
|
|
elsif Present (Parameter_Specifications
|
|
(Specification (N)))
|
|
or else not Is_Compilation_Unit (Defining_Entity (N))
|
|
then
|
|
-- See ARM D.1 (14/3) and D.16 (12/3)
|
|
|
|
Error_Msg_N
|
|
("aspect applied to subprogram other than the "
|
|
& "main subprogram has no effect??", Expr);
|
|
|
|
-- Otherwise check in range and export the value
|
|
|
|
-- For the CPU aspect
|
|
|
|
elsif A_Id = Aspect_CPU then
|
|
if Is_In_Range (Expr, RTE (RE_CPU_Range)) then
|
|
|
|
-- Value is correct so we export the value to make
|
|
-- it available at execution time.
|
|
|
|
Set_Main_CPU
|
|
(Main_Unit, UI_To_Int (Expr_Value (Expr)));
|
|
|
|
else
|
|
Error_Msg_N
|
|
("main subprogram CPU is out of range", Expr);
|
|
end if;
|
|
|
|
-- For the Priority aspect
|
|
|
|
elsif A_Id = Aspect_Priority then
|
|
if Is_In_Range (Expr, RTE (RE_Priority)) then
|
|
|
|
-- Value is correct so we export the value to make
|
|
-- it available at execution time.
|
|
|
|
Set_Main_Priority
|
|
(Main_Unit, UI_To_Int (Expr_Value (Expr)));
|
|
|
|
-- Ignore pragma if Relaxed_RM_Semantics to support
|
|
-- other targets/non GNAT compilers.
|
|
|
|
elsif not Relaxed_RM_Semantics then
|
|
Error_Msg_N
|
|
("main subprogram priority is out of range",
|
|
Expr);
|
|
end if;
|
|
end if;
|
|
|
|
-- Load an arbitrary entity from System.Tasking.Stages
|
|
-- or System.Tasking.Restricted.Stages (depending on
|
|
-- the supported profile) to make sure that one of these
|
|
-- packages is implicitly with'ed, since we need to have
|
|
-- the tasking run time active for the pragma Priority to
|
|
-- have any effect. Previously we with'ed the package
|
|
-- System.Tasking, but this package does not trigger the
|
|
-- required initialization of the run-time library.
|
|
|
|
declare
|
|
Discard : Entity_Id;
|
|
begin
|
|
if Restricted_Profile then
|
|
Discard := RTE (RE_Activate_Restricted_Tasks);
|
|
else
|
|
Discard := RTE (RE_Activate_Tasks);
|
|
end if;
|
|
end;
|
|
|
|
-- Handling for these Aspects in subprograms is complete
|
|
|
|
goto Continue;
|
|
|
|
-- For tasks
|
|
|
|
else
|
|
-- Pass the aspect as an attribute
|
|
|
|
Aitem :=
|
|
Make_Attribute_Definition_Clause (Loc,
|
|
Name => Ent,
|
|
Chars => Chars (Id),
|
|
Expression => Relocate_Node (Expr));
|
|
end if;
|
|
|
|
-- Warnings
|
|
|
|
when Aspect_Warnings =>
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Expr),
|
|
Expression => Relocate_Node (Expr)),
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => New_Occurrence_Of (E, Loc))),
|
|
Pragma_Name => Chars (Id));
|
|
|
|
-- Case 2c: Aspects corresponding to pragmas with three
|
|
-- arguments.
|
|
|
|
-- Invariant aspects have a first argument that references the
|
|
-- entity, a second argument that is the expression and a third
|
|
-- argument that is an appropriate message.
|
|
|
|
-- Invariant, Type_Invariant
|
|
|
|
when Aspect_Invariant |
|
|
Aspect_Type_Invariant =>
|
|
|
|
-- Analysis of the pragma will verify placement legality:
|
|
-- an invariant must apply to a private type, or appear in
|
|
-- the private part of a spec and apply to a completion.
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Ent),
|
|
Expression => Ent),
|
|
Make_Pragma_Argument_Association (Sloc (Expr),
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Invariant);
|
|
|
|
-- Add message unless exception messages are suppressed
|
|
|
|
if not Opt.Exception_Locations_Suppressed then
|
|
Append_To (Pragma_Argument_Associations (Aitem),
|
|
Make_Pragma_Argument_Association (Eloc,
|
|
Chars => Name_Message,
|
|
Expression =>
|
|
Make_String_Literal (Eloc,
|
|
Strval => "failed invariant from "
|
|
& Build_Location_String (Eloc))));
|
|
end if;
|
|
|
|
-- For Invariant case, insert immediately after the entity
|
|
-- declaration. We do not have to worry about delay issues
|
|
-- since the pragma processing takes care of this.
|
|
|
|
Delay_Required := False;
|
|
|
|
-- Case 2d : Aspects that correspond to a pragma with one
|
|
-- argument.
|
|
|
|
-- Abstract_State
|
|
|
|
-- Aspect Abstract_State introduces implicit declarations for
|
|
-- all state abstraction entities it defines. To emulate this
|
|
-- behavior, insert the pragma at the beginning of the visible
|
|
-- declarations of the related package so that it is analyzed
|
|
-- immediately.
|
|
|
|
when Aspect_Abstract_State => Abstract_State : declare
|
|
Context : Node_Id := N;
|
|
Decl : Node_Id;
|
|
Decls : List_Id;
|
|
|
|
begin
|
|
-- When aspect Abstract_State appears on a generic package,
|
|
-- it is propageted to the package instance. The context in
|
|
-- this case is the instance spec.
|
|
|
|
if Nkind (Context) = N_Package_Instantiation then
|
|
Context := Instance_Spec (Context);
|
|
end if;
|
|
|
|
if Nkind_In (Context, N_Generic_Package_Declaration,
|
|
N_Package_Declaration)
|
|
then
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Abstract_State);
|
|
Decorate (Aspect, Aitem);
|
|
|
|
Decls := Visible_Declarations (Specification (Context));
|
|
|
|
-- In general pragma Abstract_State must be at the top
|
|
-- of the existing visible declarations to emulate its
|
|
-- source counterpart. The only exception to this is a
|
|
-- generic instance in which case the pragma must be
|
|
-- inserted after the association renamings.
|
|
|
|
if Present (Decls) then
|
|
Decl := First (Decls);
|
|
|
|
-- The visible declarations of a generic instance have
|
|
-- the following structure:
|
|
|
|
-- <renamings of generic formals>
|
|
-- <renamings of internally-generated spec and body>
|
|
-- <first source declaration>
|
|
|
|
-- The pragma must be inserted before the first source
|
|
-- declaration, skip the instance "header".
|
|
|
|
if Is_Generic_Instance (Defining_Entity (Context)) then
|
|
while Present (Decl)
|
|
and then not Comes_From_Source (Decl)
|
|
loop
|
|
Decl := Next (Decl);
|
|
end loop;
|
|
end if;
|
|
|
|
-- Pragma Abstract_State must be inserted after pragma
|
|
-- SPARK_Mode in the tree. This ensures that any error
|
|
-- messages dependent on SPARK_Mode will be properly
|
|
-- enabled/suppressed.
|
|
|
|
Insert_After_SPARK_Mode
|
|
(Prag => Aitem,
|
|
Ins_Nod => Decl,
|
|
Decls => Decls);
|
|
|
|
-- Otherwise the pragma forms a new declarative list
|
|
|
|
else
|
|
Set_Visible_Declarations
|
|
(Specification (Context), New_List (Aitem));
|
|
end if;
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("aspect & must apply to a package declaration",
|
|
Aspect, Id);
|
|
end if;
|
|
|
|
goto Continue;
|
|
end Abstract_State;
|
|
|
|
-- Depends
|
|
|
|
-- Aspect Depends is never delayed because it is equivalent to
|
|
-- a source pragma which appears after the related subprogram.
|
|
-- To deal with forward references, the generated pragma is
|
|
-- stored in the contract of the related subprogram and later
|
|
-- analyzed at the end of the declarative region. See routine
|
|
-- Analyze_Depends_In_Decl_Part for details.
|
|
|
|
when Aspect_Depends =>
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Depends);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Global
|
|
|
|
-- Aspect Global is never delayed because it is equivalent to
|
|
-- a source pragma which appears after the related subprogram.
|
|
-- To deal with forward references, the generated pragma is
|
|
-- stored in the contract of the related subprogram and later
|
|
-- analyzed at the end of the declarative region. See routine
|
|
-- Analyze_Global_In_Decl_Part for details.
|
|
|
|
when Aspect_Global =>
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Global);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Initial_Condition
|
|
|
|
-- Aspect Initial_Condition is never delayed because it is
|
|
-- equivalent to a source pragma which appears after the
|
|
-- related package. To deal with forward references, the
|
|
-- generated pragma is stored in the contract of the related
|
|
-- package and later analyzed at the end of the declarative
|
|
-- region. See routine Analyze_Initial_Condition_In_Decl_Part
|
|
-- for details.
|
|
|
|
when Aspect_Initial_Condition => Initial_Condition : declare
|
|
Context : Node_Id := N;
|
|
Decls : List_Id;
|
|
|
|
begin
|
|
-- When aspect Initial_Condition appears on a generic
|
|
-- package, it is propageted to the package instance. The
|
|
-- context in this case is the instance spec.
|
|
|
|
if Nkind (Context) = N_Package_Instantiation then
|
|
Context := Instance_Spec (Context);
|
|
end if;
|
|
|
|
if Nkind_In (Context, N_Generic_Package_Declaration,
|
|
N_Package_Declaration)
|
|
then
|
|
Decls := Visible_Declarations (Specification (Context));
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name =>
|
|
Name_Initial_Condition);
|
|
Decorate (Aspect, Aitem);
|
|
|
|
if No (Decls) then
|
|
Decls := New_List;
|
|
Set_Visible_Declarations (Context, Decls);
|
|
end if;
|
|
|
|
-- When aspects Abstract_State, Initial_Condition and
|
|
-- Initializes are out of order, ensure that pragma
|
|
-- SPARK_Mode is always at the top of the declarative
|
|
-- list to properly enable/suppress errors.
|
|
|
|
Insert_After_SPARK_Mode
|
|
(Prag => Aitem,
|
|
Ins_Nod => First (Decls),
|
|
Decls => Decls);
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("aspect & must apply to a package declaration",
|
|
Aspect, Id);
|
|
end if;
|
|
|
|
goto Continue;
|
|
end Initial_Condition;
|
|
|
|
-- Initializes
|
|
|
|
-- Aspect Initializes is never delayed because it is equivalent
|
|
-- to a source pragma appearing after the related package. To
|
|
-- deal with forward references, the generated pragma is stored
|
|
-- in the contract of the related package and later analyzed at
|
|
-- the end of the declarative region. For details, see routine
|
|
-- Analyze_Initializes_In_Decl_Part.
|
|
|
|
when Aspect_Initializes => Initializes : declare
|
|
Context : Node_Id := N;
|
|
Decls : List_Id;
|
|
|
|
begin
|
|
-- When aspect Initializes appears on a generic package,
|
|
-- it is propageted to the package instance. The context
|
|
-- in this case is the instance spec.
|
|
|
|
if Nkind (Context) = N_Package_Instantiation then
|
|
Context := Instance_Spec (Context);
|
|
end if;
|
|
|
|
if Nkind_In (Context, N_Generic_Package_Declaration,
|
|
N_Package_Declaration)
|
|
then
|
|
Decls := Visible_Declarations (Specification (Context));
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Initializes);
|
|
Decorate (Aspect, Aitem);
|
|
|
|
if No (Decls) then
|
|
Decls := New_List;
|
|
Set_Visible_Declarations (Context, Decls);
|
|
end if;
|
|
|
|
-- When aspects Abstract_State, Initial_Condition and
|
|
-- Initializes are out of order, ensure that pragma
|
|
-- SPARK_Mode is always at the top of the declarative
|
|
-- list to properly enable/suppress errors.
|
|
|
|
Insert_After_SPARK_Mode
|
|
(Prag => Aitem,
|
|
Ins_Nod => First (Decls),
|
|
Decls => Decls);
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("aspect & must apply to a package declaration",
|
|
Aspect, Id);
|
|
end if;
|
|
|
|
goto Continue;
|
|
end Initializes;
|
|
|
|
-- Part_Of
|
|
|
|
when Aspect_Part_Of =>
|
|
if Nkind_In (N, N_Object_Declaration,
|
|
N_Package_Instantiation)
|
|
then
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Part_Of);
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("aspect & must apply to a variable or package "
|
|
& "instantiation", Aspect, Id);
|
|
end if;
|
|
|
|
-- SPARK_Mode
|
|
|
|
when Aspect_SPARK_Mode => SPARK_Mode : declare
|
|
Decls : List_Id;
|
|
|
|
begin
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_SPARK_Mode);
|
|
|
|
-- When the aspect appears on a package body, insert the
|
|
-- generated pragma at the top of the body declarations to
|
|
-- emulate the behavior of a source pragma.
|
|
|
|
if Nkind (N) = N_Package_Body then
|
|
Decorate (Aspect, Aitem);
|
|
|
|
Decls := Declarations (N);
|
|
|
|
if No (Decls) then
|
|
Decls := New_List;
|
|
Set_Declarations (N, Decls);
|
|
end if;
|
|
|
|
Prepend_To (Decls, Aitem);
|
|
goto Continue;
|
|
|
|
-- When the aspect is associated with package declaration,
|
|
-- insert the generated pragma at the top of the visible
|
|
-- declarations to emulate the behavior of a source pragma.
|
|
|
|
elsif Nkind (N) = N_Package_Declaration then
|
|
Decorate (Aspect, Aitem);
|
|
|
|
Decls := Visible_Declarations (Specification (N));
|
|
|
|
if No (Decls) then
|
|
Decls := New_List;
|
|
Set_Visible_Declarations (Specification (N), Decls);
|
|
end if;
|
|
|
|
Prepend_To (Decls, Aitem);
|
|
goto Continue;
|
|
end if;
|
|
end SPARK_Mode;
|
|
|
|
-- Refined_Depends
|
|
|
|
-- Aspect Refined_Depends is never delayed because it is
|
|
-- equivalent to a source pragma which appears in the
|
|
-- declarations of the related subprogram body. To deal with
|
|
-- forward references, the generated pragma is stored in the
|
|
-- contract of the related subprogram body and later analyzed
|
|
-- at the end of the declarative region. For details, see
|
|
-- routine Analyze_Refined_Depends_In_Decl_Part.
|
|
|
|
when Aspect_Refined_Depends =>
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Refined_Depends);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Refined_Global
|
|
|
|
-- Aspect Refined_Global is never delayed because it is
|
|
-- equivalent to a source pragma which appears in the
|
|
-- declarations of the related subprogram body. To deal with
|
|
-- forward references, the generated pragma is stored in the
|
|
-- contract of the related subprogram body and later analyzed
|
|
-- at the end of the declarative region. For details, see
|
|
-- routine Analyze_Refined_Global_In_Decl_Part.
|
|
|
|
when Aspect_Refined_Global =>
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Refined_Global);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Refined_Post
|
|
|
|
when Aspect_Refined_Post =>
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Refined_Post);
|
|
|
|
-- Refined_State
|
|
|
|
when Aspect_Refined_State => Refined_State : declare
|
|
Decls : List_Id;
|
|
|
|
begin
|
|
-- The corresponding pragma for Refined_State is inserted in
|
|
-- the declarations of the related package body. This action
|
|
-- synchronizes both the source and from-aspect versions of
|
|
-- the pragma.
|
|
|
|
if Nkind (N) = N_Package_Body then
|
|
Decls := Declarations (N);
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Refined_State);
|
|
Decorate (Aspect, Aitem);
|
|
|
|
if No (Decls) then
|
|
Decls := New_List;
|
|
Set_Declarations (N, Decls);
|
|
end if;
|
|
|
|
-- Pragma Refined_State must be inserted after pragma
|
|
-- SPARK_Mode in the tree. This ensures that any error
|
|
-- messages dependent on SPARK_Mode will be properly
|
|
-- enabled/suppressed.
|
|
|
|
Insert_After_SPARK_Mode
|
|
(Prag => Aitem,
|
|
Ins_Nod => First (Decls),
|
|
Decls => Decls);
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("aspect & must apply to a package body", Aspect, Id);
|
|
end if;
|
|
|
|
goto Continue;
|
|
end Refined_State;
|
|
|
|
-- Relative_Deadline
|
|
|
|
when Aspect_Relative_Deadline =>
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Relative_Deadline);
|
|
|
|
-- If the aspect applies to a task, the corresponding pragma
|
|
-- must appear within its declarations, not after.
|
|
|
|
if Nkind (N) = N_Task_Type_Declaration then
|
|
declare
|
|
Def : Node_Id;
|
|
V : List_Id;
|
|
|
|
begin
|
|
if No (Task_Definition (N)) then
|
|
Set_Task_Definition (N,
|
|
Make_Task_Definition (Loc,
|
|
Visible_Declarations => New_List,
|
|
End_Label => Empty));
|
|
end if;
|
|
|
|
Def := Task_Definition (N);
|
|
V := Visible_Declarations (Def);
|
|
if not Is_Empty_List (V) then
|
|
Insert_Before (First (V), Aitem);
|
|
|
|
else
|
|
Set_Visible_Declarations (Def, New_List (Aitem));
|
|
end if;
|
|
|
|
goto Continue;
|
|
end;
|
|
end if;
|
|
|
|
-- Case 2e: Annotate aspect
|
|
|
|
when Aspect_Annotate =>
|
|
declare
|
|
Args : List_Id;
|
|
Pargs : List_Id;
|
|
Arg : Node_Id;
|
|
|
|
begin
|
|
-- The argument can be a single identifier
|
|
|
|
if Nkind (Expr) = N_Identifier then
|
|
|
|
-- One level of parens is allowed
|
|
|
|
if Paren_Count (Expr) > 1 then
|
|
Error_Msg_F ("extra parentheses ignored", Expr);
|
|
end if;
|
|
|
|
Set_Paren_Count (Expr, 0);
|
|
|
|
-- Add the single item to the list
|
|
|
|
Args := New_List (Expr);
|
|
|
|
-- Otherwise we must have an aggregate
|
|
|
|
elsif Nkind (Expr) = N_Aggregate then
|
|
|
|
-- Must be positional
|
|
|
|
if Present (Component_Associations (Expr)) then
|
|
Error_Msg_F
|
|
("purely positional aggregate required", Expr);
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Must not be parenthesized
|
|
|
|
if Paren_Count (Expr) /= 0 then
|
|
Error_Msg_F ("extra parentheses ignored", Expr);
|
|
end if;
|
|
|
|
-- List of arguments is list of aggregate expressions
|
|
|
|
Args := Expressions (Expr);
|
|
|
|
-- Anything else is illegal
|
|
|
|
else
|
|
Error_Msg_F ("wrong form for Annotate aspect", Expr);
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Prepare pragma arguments
|
|
|
|
Pargs := New_List;
|
|
Arg := First (Args);
|
|
while Present (Arg) loop
|
|
Append_To (Pargs,
|
|
Make_Pragma_Argument_Association (Sloc (Arg),
|
|
Expression => Relocate_Node (Arg)));
|
|
Next (Arg);
|
|
end loop;
|
|
|
|
Append_To (Pargs,
|
|
Make_Pragma_Argument_Association (Sloc (Ent),
|
|
Chars => Name_Entity,
|
|
Expression => Ent));
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => Pargs,
|
|
Pragma_Name => Name_Annotate);
|
|
end;
|
|
|
|
-- Case 3 : Aspects that don't correspond to pragma/attribute
|
|
-- definition clause.
|
|
|
|
-- Case 3a: The aspects listed below don't correspond to
|
|
-- pragmas/attributes but do require delayed analysis.
|
|
|
|
-- Default_Value, Default_Component_Value
|
|
|
|
when Aspect_Default_Value |
|
|
Aspect_Default_Component_Value =>
|
|
Aitem := Empty;
|
|
|
|
-- Case 3b: The aspects listed below don't correspond to
|
|
-- pragmas/attributes and don't need delayed analysis.
|
|
|
|
-- Implicit_Dereference
|
|
|
|
-- For Implicit_Dereference, External_Name and Link_Name, only
|
|
-- the legality checks are done during the analysis, thus no
|
|
-- delay is required.
|
|
|
|
when Aspect_Implicit_Dereference =>
|
|
Analyze_Aspect_Implicit_Dereference;
|
|
goto Continue;
|
|
|
|
-- External_Name, Link_Name
|
|
|
|
when Aspect_External_Name |
|
|
Aspect_Link_Name =>
|
|
Analyze_Aspect_External_Or_Link_Name;
|
|
goto Continue;
|
|
|
|
-- Dimension
|
|
|
|
when Aspect_Dimension =>
|
|
Analyze_Aspect_Dimension (N, Id, Expr);
|
|
goto Continue;
|
|
|
|
-- Dimension_System
|
|
|
|
when Aspect_Dimension_System =>
|
|
Analyze_Aspect_Dimension_System (N, Id, Expr);
|
|
goto Continue;
|
|
|
|
-- Case 4: Aspects requiring special handling
|
|
|
|
-- Pre/Post/Test_Case/Contract_Cases whose corresponding
|
|
-- pragmas take care of the delay.
|
|
|
|
-- Pre/Post
|
|
|
|
-- Aspects Pre/Post generate Precondition/Postcondition pragmas
|
|
-- with a first argument that is the expression, and a second
|
|
-- argument that is an informative message if the test fails.
|
|
-- This is inserted right after the declaration, to get the
|
|
-- required pragma placement. The processing for the pragmas
|
|
-- takes care of the required delay.
|
|
|
|
when Pre_Post_Aspects => Pre_Post : declare
|
|
Pname : Name_Id;
|
|
|
|
begin
|
|
if A_Id = Aspect_Pre or else A_Id = Aspect_Precondition then
|
|
Pname := Name_Precondition;
|
|
else
|
|
Pname := Name_Postcondition;
|
|
end if;
|
|
|
|
-- If the expressions is of the form A and then B, then
|
|
-- we generate separate Pre/Post aspects for the separate
|
|
-- clauses. Since we allow multiple pragmas, there is no
|
|
-- problem in allowing multiple Pre/Post aspects internally.
|
|
-- These should be treated in reverse order (B first and
|
|
-- A second) since they are later inserted just after N in
|
|
-- the order they are treated. This way, the pragma for A
|
|
-- ends up preceding the pragma for B, which may have an
|
|
-- importance for the error raised (either constraint error
|
|
-- or precondition error).
|
|
|
|
-- We do not do this for Pre'Class, since we have to put
|
|
-- these conditions together in a complex OR expression
|
|
|
|
-- We do not do this in ASIS mode, as ASIS relies on the
|
|
-- original node representing the complete expression, when
|
|
-- retrieving it through the source aspect table.
|
|
|
|
if not ASIS_Mode
|
|
and then (Pname = Name_Postcondition
|
|
or else not Class_Present (Aspect))
|
|
then
|
|
while Nkind (Expr) = N_And_Then loop
|
|
Insert_After (Aspect,
|
|
Make_Aspect_Specification (Sloc (Left_Opnd (Expr)),
|
|
Identifier => Identifier (Aspect),
|
|
Expression => Relocate_Node (Left_Opnd (Expr)),
|
|
Class_Present => Class_Present (Aspect),
|
|
Split_PPC => True));
|
|
Rewrite (Expr, Relocate_Node (Right_Opnd (Expr)));
|
|
Eloc := Sloc (Expr);
|
|
end loop;
|
|
end if;
|
|
|
|
-- Build the precondition/postcondition pragma
|
|
|
|
-- Add note about why we do NOT need Copy_Tree here ???
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Eloc,
|
|
Chars => Name_Check,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Pname);
|
|
|
|
-- Add message unless exception messages are suppressed
|
|
|
|
if not Opt.Exception_Locations_Suppressed then
|
|
Append_To (Pragma_Argument_Associations (Aitem),
|
|
Make_Pragma_Argument_Association (Eloc,
|
|
Chars => Name_Message,
|
|
Expression =>
|
|
Make_String_Literal (Eloc,
|
|
Strval => "failed "
|
|
& Get_Name_String (Pname)
|
|
& " from "
|
|
& Build_Location_String (Eloc))));
|
|
end if;
|
|
|
|
Set_Is_Delayed_Aspect (Aspect);
|
|
|
|
-- For Pre/Post cases, insert immediately after the entity
|
|
-- declaration, since that is the required pragma placement.
|
|
-- Note that for these aspects, we do not have to worry
|
|
-- about delay issues, since the pragmas themselves deal
|
|
-- with delay of visibility for the expression analysis.
|
|
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
end Pre_Post;
|
|
|
|
-- Test_Case
|
|
|
|
when Aspect_Test_Case => Test_Case : declare
|
|
Args : List_Id;
|
|
Comp_Expr : Node_Id;
|
|
Comp_Assn : Node_Id;
|
|
New_Expr : Node_Id;
|
|
|
|
begin
|
|
Args := New_List;
|
|
|
|
if Nkind (Parent (N)) = N_Compilation_Unit then
|
|
Error_Msg_Name_1 := Nam;
|
|
Error_Msg_N ("incorrect placement of aspect `%`", E);
|
|
goto Continue;
|
|
end if;
|
|
|
|
if Nkind (Expr) /= N_Aggregate then
|
|
Error_Msg_Name_1 := Nam;
|
|
Error_Msg_NE
|
|
("wrong syntax for aspect `%` for &", Id, E);
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Make pragma expressions refer to the original aspect
|
|
-- expressions through the Original_Node link. This is
|
|
-- used in semantic analysis for ASIS mode, so that the
|
|
-- original expression also gets analyzed.
|
|
|
|
Comp_Expr := First (Expressions (Expr));
|
|
while Present (Comp_Expr) loop
|
|
New_Expr := Relocate_Node (Comp_Expr);
|
|
Set_Original_Node (New_Expr, Comp_Expr);
|
|
Append_To (Args,
|
|
Make_Pragma_Argument_Association (Sloc (Comp_Expr),
|
|
Expression => New_Expr));
|
|
Next (Comp_Expr);
|
|
end loop;
|
|
|
|
Comp_Assn := First (Component_Associations (Expr));
|
|
while Present (Comp_Assn) loop
|
|
if List_Length (Choices (Comp_Assn)) /= 1
|
|
or else
|
|
Nkind (First (Choices (Comp_Assn))) /= N_Identifier
|
|
then
|
|
Error_Msg_Name_1 := Nam;
|
|
Error_Msg_NE
|
|
("wrong syntax for aspect `%` for &", Id, E);
|
|
goto Continue;
|
|
end if;
|
|
|
|
New_Expr := Relocate_Node (Expression (Comp_Assn));
|
|
Set_Original_Node (New_Expr, Expression (Comp_Assn));
|
|
Append_To (Args,
|
|
Make_Pragma_Argument_Association (Sloc (Comp_Assn),
|
|
Chars => Chars (First (Choices (Comp_Assn))),
|
|
Expression => New_Expr));
|
|
Next (Comp_Assn);
|
|
end loop;
|
|
|
|
-- Build the test-case pragma
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => Args,
|
|
Pragma_Name => Nam);
|
|
end Test_Case;
|
|
|
|
-- Contract_Cases
|
|
|
|
when Aspect_Contract_Cases =>
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Nam);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Case 5: Special handling for aspects with an optional
|
|
-- boolean argument.
|
|
|
|
-- In the general case, the corresponding pragma cannot be
|
|
-- generated yet because the evaluation of the boolean needs
|
|
-- to be delayed till the freeze point.
|
|
|
|
when Boolean_Aspects |
|
|
Library_Unit_Aspects =>
|
|
|
|
Set_Is_Boolean_Aspect (Aspect);
|
|
|
|
-- Lock_Free aspect only apply to protected objects
|
|
|
|
if A_Id = Aspect_Lock_Free then
|
|
if Ekind (E) /= E_Protected_Type then
|
|
Error_Msg_Name_1 := Nam;
|
|
Error_Msg_N
|
|
("aspect % only applies to a protected object",
|
|
Aspect);
|
|
|
|
else
|
|
-- Set the Uses_Lock_Free flag to True if there is no
|
|
-- expression or if the expression is True. The
|
|
-- evaluation of this aspect should be delayed to the
|
|
-- freeze point (why???)
|
|
|
|
if No (Expr)
|
|
or else Is_True (Static_Boolean (Expr))
|
|
then
|
|
Set_Uses_Lock_Free (E);
|
|
end if;
|
|
|
|
Record_Rep_Item (E, Aspect);
|
|
end if;
|
|
|
|
goto Continue;
|
|
|
|
elsif A_Id = Aspect_Import or else A_Id = Aspect_Export then
|
|
|
|
-- For the case of aspects Import and Export, we don't
|
|
-- consider that we know the entity is never set in the
|
|
-- source, since it is is likely modified outside the
|
|
-- program.
|
|
|
|
-- Note: one might think that the analysis of the
|
|
-- resulting pragma would take care of that, but
|
|
-- that's not the case since it won't be from source.
|
|
|
|
if Ekind (E) = E_Variable then
|
|
Set_Never_Set_In_Source (E, False);
|
|
end if;
|
|
|
|
-- In older versions of Ada the corresponding pragmas
|
|
-- specified a Convention. In Ada 2012 the convention
|
|
-- is specified as a separate aspect, and it is optional,
|
|
-- given that it defaults to Convention_Ada. The code
|
|
-- that verifed that there was a matching convention
|
|
-- is now obsolete.
|
|
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Library unit aspects require special handling in the case
|
|
-- of a package declaration, the pragma needs to be inserted
|
|
-- in the list of declarations for the associated package.
|
|
-- There is no issue of visibility delay for these aspects.
|
|
|
|
if A_Id in Library_Unit_Aspects
|
|
and then
|
|
Nkind_In (N, N_Package_Declaration,
|
|
N_Generic_Package_Declaration)
|
|
and then Nkind (Parent (N)) /= N_Compilation_Unit
|
|
then
|
|
Error_Msg_N
|
|
("incorrect context for library unit aspect&", Id);
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- External property aspects are Boolean by nature, but
|
|
-- their pragmas must contain two arguments, the second
|
|
-- being the optional Boolean expression.
|
|
|
|
if A_Id = Aspect_Async_Readers or else
|
|
A_Id = Aspect_Async_Writers or else
|
|
A_Id = Aspect_Effective_Reads or else
|
|
A_Id = Aspect_Effective_Writes
|
|
then
|
|
declare
|
|
Args : List_Id;
|
|
|
|
begin
|
|
-- The first argument of the external property pragma
|
|
-- is the related object.
|
|
|
|
Args :=
|
|
New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Ent),
|
|
Expression => Ent));
|
|
|
|
-- The second argument is the optional Boolean
|
|
-- expression which must be propagated even if it
|
|
-- evaluates to False as this has special semantic
|
|
-- meaning.
|
|
|
|
if Present (Expr) then
|
|
Append_To (Args,
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr)));
|
|
end if;
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => Args,
|
|
Pragma_Name => Nam);
|
|
end;
|
|
|
|
-- Cases where we do not delay, includes all cases where
|
|
-- the expression is missing other than the above cases.
|
|
|
|
elsif not Delay_Required or else No (Expr) then
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Ent),
|
|
Expression => Ent)),
|
|
Pragma_Name => Chars (Id));
|
|
Delay_Required := False;
|
|
|
|
-- In general cases, the corresponding pragma/attribute
|
|
-- definition clause will be inserted later at the freezing
|
|
-- point, and we do not need to build it now.
|
|
|
|
else
|
|
Aitem := Empty;
|
|
end if;
|
|
|
|
-- Storage_Size
|
|
|
|
-- This is special because for access types we need to generate
|
|
-- an attribute definition clause. This also works for single
|
|
-- task declarations, but it does not work for task type
|
|
-- declarations, because we have the case where the expression
|
|
-- references a discriminant of the task type. That can't use
|
|
-- an attribute definition clause because we would not have
|
|
-- visibility on the discriminant. For that case we must
|
|
-- generate a pragma in the task definition.
|
|
|
|
when Aspect_Storage_Size =>
|
|
|
|
-- Task type case
|
|
|
|
if Ekind (E) = E_Task_Type then
|
|
declare
|
|
Decl : constant Node_Id := Declaration_Node (E);
|
|
|
|
begin
|
|
pragma Assert (Nkind (Decl) = N_Task_Type_Declaration);
|
|
|
|
-- If no task definition, create one
|
|
|
|
if No (Task_Definition (Decl)) then
|
|
Set_Task_Definition (Decl,
|
|
Make_Task_Definition (Loc,
|
|
Visible_Declarations => Empty_List,
|
|
End_Label => Empty));
|
|
end if;
|
|
|
|
-- Create a pragma and put it at the start of the
|
|
-- task definition for the task type declaration.
|
|
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Storage_Size);
|
|
|
|
Prepend
|
|
(Aitem,
|
|
Visible_Declarations (Task_Definition (Decl)));
|
|
goto Continue;
|
|
end;
|
|
|
|
-- All other cases, generate attribute definition
|
|
|
|
else
|
|
Aitem :=
|
|
Make_Attribute_Definition_Clause (Loc,
|
|
Name => Ent,
|
|
Chars => Chars (Id),
|
|
Expression => Relocate_Node (Expr));
|
|
end if;
|
|
end case;
|
|
|
|
-- Attach the corresponding pragma/attribute definition clause to
|
|
-- the aspect specification node.
|
|
|
|
if Present (Aitem) then
|
|
Set_From_Aspect_Specification (Aitem);
|
|
end if;
|
|
|
|
-- In the context of a compilation unit, we directly put the
|
|
-- pragma in the Pragmas_After list of the N_Compilation_Unit_Aux
|
|
-- node (no delay is required here) except for aspects on a
|
|
-- subprogram body (see below) and a generic package, for which
|
|
-- we need to introduce the pragma before building the generic
|
|
-- copy (see sem_ch12), and for package instantiations, where
|
|
-- the library unit pragmas are better handled early.
|
|
|
|
if Nkind (Parent (N)) = N_Compilation_Unit
|
|
and then (Present (Aitem) or else Is_Boolean_Aspect (Aspect))
|
|
then
|
|
declare
|
|
Aux : constant Node_Id := Aux_Decls_Node (Parent (N));
|
|
|
|
begin
|
|
pragma Assert (Nkind (Aux) = N_Compilation_Unit_Aux);
|
|
|
|
-- For a Boolean aspect, create the corresponding pragma if
|
|
-- no expression or if the value is True.
|
|
|
|
if Is_Boolean_Aspect (Aspect) and then No (Aitem) then
|
|
if Is_True (Static_Boolean (Expr)) then
|
|
Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Ent),
|
|
Expression => Ent)),
|
|
Pragma_Name => Chars (Id));
|
|
|
|
Set_From_Aspect_Specification (Aitem, True);
|
|
Set_Corresponding_Aspect (Aitem, Aspect);
|
|
|
|
else
|
|
goto Continue;
|
|
end if;
|
|
end if;
|
|
|
|
-- If the aspect is on a subprogram body (relevant aspect
|
|
-- is Inline), add the pragma in front of the declarations.
|
|
|
|
if Nkind (N) = N_Subprogram_Body then
|
|
if No (Declarations (N)) then
|
|
Set_Declarations (N, New_List);
|
|
end if;
|
|
|
|
Prepend (Aitem, Declarations (N));
|
|
|
|
elsif Nkind (N) = N_Generic_Package_Declaration then
|
|
if No (Visible_Declarations (Specification (N))) then
|
|
Set_Visible_Declarations (Specification (N), New_List);
|
|
end if;
|
|
|
|
Prepend (Aitem,
|
|
Visible_Declarations (Specification (N)));
|
|
|
|
elsif Nkind (N) = N_Package_Instantiation then
|
|
declare
|
|
Spec : constant Node_Id :=
|
|
Specification (Instance_Spec (N));
|
|
begin
|
|
if No (Visible_Declarations (Spec)) then
|
|
Set_Visible_Declarations (Spec, New_List);
|
|
end if;
|
|
|
|
Prepend (Aitem, Visible_Declarations (Spec));
|
|
end;
|
|
|
|
else
|
|
if No (Pragmas_After (Aux)) then
|
|
Set_Pragmas_After (Aux, New_List);
|
|
end if;
|
|
|
|
Append (Aitem, Pragmas_After (Aux));
|
|
end if;
|
|
|
|
goto Continue;
|
|
end;
|
|
end if;
|
|
|
|
-- The evaluation of the aspect is delayed to the freezing point.
|
|
-- The pragma or attribute clause if there is one is then attached
|
|
-- to the aspect specification which is put in the rep item list.
|
|
|
|
if Delay_Required then
|
|
if Present (Aitem) then
|
|
Set_Is_Delayed_Aspect (Aitem);
|
|
Set_Aspect_Rep_Item (Aspect, Aitem);
|
|
Set_Parent (Aitem, Aspect);
|
|
end if;
|
|
|
|
Set_Is_Delayed_Aspect (Aspect);
|
|
|
|
-- In the case of Default_Value, link the aspect to base type
|
|
-- as well, even though it appears on a first subtype. This is
|
|
-- mandated by the semantics of the aspect. Do not establish
|
|
-- the link when processing the base type itself as this leads
|
|
-- to a rep item circularity. Verify that we are dealing with
|
|
-- a scalar type to prevent cascaded errors.
|
|
|
|
if A_Id = Aspect_Default_Value
|
|
and then Is_Scalar_Type (E)
|
|
and then Base_Type (E) /= E
|
|
then
|
|
Set_Has_Delayed_Aspects (Base_Type (E));
|
|
Record_Rep_Item (Base_Type (E), Aspect);
|
|
end if;
|
|
|
|
Set_Has_Delayed_Aspects (E);
|
|
Record_Rep_Item (E, Aspect);
|
|
|
|
-- When delay is not required and the context is a package or a
|
|
-- subprogram body, insert the pragma in the body declarations.
|
|
|
|
elsif Nkind_In (N, N_Package_Body, N_Subprogram_Body) then
|
|
if No (Declarations (N)) then
|
|
Set_Declarations (N, New_List);
|
|
end if;
|
|
|
|
-- The pragma is added before source declarations
|
|
|
|
Prepend_To (Declarations (N), Aitem);
|
|
|
|
-- When delay is not required and the context is not a compilation
|
|
-- unit, we simply insert the pragma/attribute definition clause
|
|
-- in sequence.
|
|
|
|
else
|
|
Insert_After (Ins_Node, Aitem);
|
|
Ins_Node := Aitem;
|
|
end if;
|
|
end Analyze_One_Aspect;
|
|
|
|
<<Continue>>
|
|
Next (Aspect);
|
|
end loop Aspect_Loop;
|
|
|
|
if Has_Delayed_Aspects (E) then
|
|
Ensure_Freeze_Node (E);
|
|
end if;
|
|
end Analyze_Aspect_Specifications;
|
|
|
|
-----------------------
|
|
-- Analyze_At_Clause --
|
|
-----------------------
|
|
|
|
-- An at clause is replaced by the corresponding Address attribute
|
|
-- definition clause that is the preferred approach in Ada 95.
|
|
|
|
procedure Analyze_At_Clause (N : Node_Id) is
|
|
CS : constant Boolean := Comes_From_Source (N);
|
|
|
|
begin
|
|
-- This is an obsolescent feature
|
|
|
|
Check_Restriction (No_Obsolescent_Features, N);
|
|
|
|
if Warn_On_Obsolescent_Feature then
|
|
Error_Msg_N
|
|
("?j?at clause is an obsolescent feature (RM J.7(2))", N);
|
|
Error_Msg_N
|
|
("\?j?use address attribute definition clause instead", N);
|
|
end if;
|
|
|
|
-- Rewrite as address clause
|
|
|
|
Rewrite (N,
|
|
Make_Attribute_Definition_Clause (Sloc (N),
|
|
Name => Identifier (N),
|
|
Chars => Name_Address,
|
|
Expression => Expression (N)));
|
|
|
|
-- We preserve Comes_From_Source, since logically the clause still comes
|
|
-- from the source program even though it is changed in form.
|
|
|
|
Set_Comes_From_Source (N, CS);
|
|
|
|
-- Analyze rewritten clause
|
|
|
|
Analyze_Attribute_Definition_Clause (N);
|
|
end Analyze_At_Clause;
|
|
|
|
-----------------------------------------
|
|
-- Analyze_Attribute_Definition_Clause --
|
|
-----------------------------------------
|
|
|
|
procedure Analyze_Attribute_Definition_Clause (N : Node_Id) is
|
|
Loc : constant Source_Ptr := Sloc (N);
|
|
Nam : constant Node_Id := Name (N);
|
|
Attr : constant Name_Id := Chars (N);
|
|
Expr : constant Node_Id := Expression (N);
|
|
Id : constant Attribute_Id := Get_Attribute_Id (Attr);
|
|
|
|
Ent : Entity_Id;
|
|
-- The entity of Nam after it is analyzed. In the case of an incomplete
|
|
-- type, this is the underlying type.
|
|
|
|
U_Ent : Entity_Id;
|
|
-- The underlying entity to which the attribute applies. Generally this
|
|
-- is the Underlying_Type of Ent, except in the case where the clause
|
|
-- applies to full view of incomplete type or private type in which case
|
|
-- U_Ent is just a copy of Ent.
|
|
|
|
FOnly : Boolean := False;
|
|
-- Reset to True for subtype specific attribute (Alignment, Size)
|
|
-- and for stream attributes, i.e. those cases where in the call
|
|
-- to Rep_Item_Too_Late, FOnly is set True so that only the freezing
|
|
-- rules are checked. Note that the case of stream attributes is not
|
|
-- clear from the RM, but see AI95-00137. Also, the RM seems to
|
|
-- disallow Storage_Size for derived task types, but that is also
|
|
-- clearly unintentional.
|
|
|
|
procedure Analyze_Stream_TSS_Definition (TSS_Nam : TSS_Name_Type);
|
|
-- Common processing for 'Read, 'Write, 'Input and 'Output attribute
|
|
-- definition clauses.
|
|
|
|
function Duplicate_Clause return Boolean;
|
|
-- This routine checks if the aspect for U_Ent being given by attribute
|
|
-- definition clause N is for an aspect that has already been specified,
|
|
-- and if so gives an error message. If there is a duplicate, True is
|
|
-- returned, otherwise if there is no error, False is returned.
|
|
|
|
procedure Check_Indexing_Functions;
|
|
-- Check that the function in Constant_Indexing or Variable_Indexing
|
|
-- attribute has the proper type structure. If the name is overloaded,
|
|
-- check that some interpretation is legal.
|
|
|
|
procedure Check_Iterator_Functions;
|
|
-- Check that there is a single function in Default_Iterator attribute
|
|
-- has the proper type structure.
|
|
|
|
function Check_Primitive_Function (Subp : Entity_Id) return Boolean;
|
|
-- Common legality check for the previous two
|
|
|
|
-----------------------------------
|
|
-- Analyze_Stream_TSS_Definition --
|
|
-----------------------------------
|
|
|
|
procedure Analyze_Stream_TSS_Definition (TSS_Nam : TSS_Name_Type) is
|
|
Subp : Entity_Id := Empty;
|
|
I : Interp_Index;
|
|
It : Interp;
|
|
Pnam : Entity_Id;
|
|
|
|
Is_Read : constant Boolean := (TSS_Nam = TSS_Stream_Read);
|
|
-- True for Read attribute, false for other attributes
|
|
|
|
function Has_Good_Profile (Subp : Entity_Id) return Boolean;
|
|
-- Return true if the entity is a subprogram with an appropriate
|
|
-- profile for the attribute being defined.
|
|
|
|
----------------------
|
|
-- Has_Good_Profile --
|
|
----------------------
|
|
|
|
function Has_Good_Profile (Subp : Entity_Id) return Boolean is
|
|
F : Entity_Id;
|
|
Is_Function : constant Boolean := (TSS_Nam = TSS_Stream_Input);
|
|
Expected_Ekind : constant array (Boolean) of Entity_Kind :=
|
|
(False => E_Procedure, True => E_Function);
|
|
Typ : Entity_Id;
|
|
|
|
begin
|
|
if Ekind (Subp) /= Expected_Ekind (Is_Function) then
|
|
return False;
|
|
end if;
|
|
|
|
F := First_Formal (Subp);
|
|
|
|
if No (F)
|
|
or else Ekind (Etype (F)) /= E_Anonymous_Access_Type
|
|
or else Designated_Type (Etype (F)) /=
|
|
Class_Wide_Type (RTE (RE_Root_Stream_Type))
|
|
then
|
|
return False;
|
|
end if;
|
|
|
|
if not Is_Function then
|
|
Next_Formal (F);
|
|
|
|
declare
|
|
Expected_Mode : constant array (Boolean) of Entity_Kind :=
|
|
(False => E_In_Parameter,
|
|
True => E_Out_Parameter);
|
|
begin
|
|
if Parameter_Mode (F) /= Expected_Mode (Is_Read) then
|
|
return False;
|
|
end if;
|
|
end;
|
|
|
|
Typ := Etype (F);
|
|
|
|
-- If the attribute specification comes from an aspect
|
|
-- specification for a class-wide stream, the parameter
|
|
-- must be a class-wide type of the entity to which the
|
|
-- aspect applies.
|
|
|
|
if From_Aspect_Specification (N)
|
|
and then Class_Present (Parent (N))
|
|
and then Is_Class_Wide_Type (Typ)
|
|
then
|
|
Typ := Etype (Typ);
|
|
end if;
|
|
|
|
else
|
|
Typ := Etype (Subp);
|
|
end if;
|
|
|
|
-- Verify that the prefix of the attribute and the local name
|
|
-- for the type of the formal match.
|
|
|
|
if Base_Type (Typ) /= Base_Type (Ent)
|
|
or else Present ((Next_Formal (F)))
|
|
then
|
|
return False;
|
|
|
|
elsif not Is_Scalar_Type (Typ)
|
|
and then not Is_First_Subtype (Typ)
|
|
and then not Is_Class_Wide_Type (Typ)
|
|
then
|
|
return False;
|
|
|
|
else
|
|
return True;
|
|
end if;
|
|
end Has_Good_Profile;
|
|
|
|
-- Start of processing for Analyze_Stream_TSS_Definition
|
|
|
|
begin
|
|
FOnly := True;
|
|
|
|
if not Is_Type (U_Ent) then
|
|
Error_Msg_N ("local name must be a subtype", Nam);
|
|
return;
|
|
|
|
elsif not Is_First_Subtype (U_Ent) then
|
|
Error_Msg_N ("local name must be a first subtype", Nam);
|
|
return;
|
|
end if;
|
|
|
|
Pnam := TSS (Base_Type (U_Ent), TSS_Nam);
|
|
|
|
-- If Pnam is present, it can be either inherited from an ancestor
|
|
-- type (in which case it is legal to redefine it for this type), or
|
|
-- be a previous definition of the attribute for the same type (in
|
|
-- which case it is illegal).
|
|
|
|
-- In the first case, it will have been analyzed already, and we
|
|
-- can check that its profile does not match the expected profile
|
|
-- for a stream attribute of U_Ent. In the second case, either Pnam
|
|
-- has been analyzed (and has the expected profile), or it has not
|
|
-- been analyzed yet (case of a type that has not been frozen yet
|
|
-- and for which the stream attribute has been set using Set_TSS).
|
|
|
|
if Present (Pnam)
|
|
and then (No (First_Entity (Pnam)) or else Has_Good_Profile (Pnam))
|
|
then
|
|
Error_Msg_Sloc := Sloc (Pnam);
|
|
Error_Msg_Name_1 := Attr;
|
|
Error_Msg_N ("% attribute already defined #", Nam);
|
|
return;
|
|
end if;
|
|
|
|
Analyze (Expr);
|
|
|
|
if Is_Entity_Name (Expr) then
|
|
if not Is_Overloaded (Expr) then
|
|
if Has_Good_Profile (Entity (Expr)) then
|
|
Subp := Entity (Expr);
|
|
end if;
|
|
|
|
else
|
|
Get_First_Interp (Expr, I, It);
|
|
while Present (It.Nam) loop
|
|
if Has_Good_Profile (It.Nam) then
|
|
Subp := It.Nam;
|
|
exit;
|
|
end if;
|
|
|
|
Get_Next_Interp (I, It);
|
|
end loop;
|
|
end if;
|
|
end if;
|
|
|
|
if Present (Subp) then
|
|
if Is_Abstract_Subprogram (Subp) then
|
|
Error_Msg_N ("stream subprogram must not be abstract", Expr);
|
|
return;
|
|
|
|
-- Test for stream subprogram for interface type being non-null
|
|
|
|
elsif Is_Interface (U_Ent)
|
|
and then not Inside_A_Generic
|
|
and then Ekind (Subp) = E_Procedure
|
|
and then
|
|
not Null_Present
|
|
(Specification
|
|
(Unit_Declaration_Node (Ultimate_Alias (Subp))))
|
|
then
|
|
Error_Msg_N
|
|
("stream subprogram for interface type "
|
|
& "must be null procedure", Expr);
|
|
end if;
|
|
|
|
Set_Entity (Expr, Subp);
|
|
Set_Etype (Expr, Etype (Subp));
|
|
|
|
New_Stream_Subprogram (N, U_Ent, Subp, TSS_Nam);
|
|
|
|
else
|
|
Error_Msg_Name_1 := Attr;
|
|
Error_Msg_N ("incorrect expression for% attribute", Expr);
|
|
end if;
|
|
end Analyze_Stream_TSS_Definition;
|
|
|
|
------------------------------
|
|
-- Check_Indexing_Functions --
|
|
------------------------------
|
|
|
|
procedure Check_Indexing_Functions is
|
|
Indexing_Found : Boolean;
|
|
|
|
procedure Check_One_Function (Subp : Entity_Id);
|
|
-- Check one possible interpretation. Sets Indexing_Found True if an
|
|
-- indexing function is found.
|
|
|
|
------------------------
|
|
-- Check_One_Function --
|
|
------------------------
|
|
|
|
procedure Check_One_Function (Subp : Entity_Id) is
|
|
Default_Element : constant Node_Id :=
|
|
Find_Value_Of_Aspect
|
|
(Etype (First_Formal (Subp)),
|
|
Aspect_Iterator_Element);
|
|
|
|
begin
|
|
if not Check_Primitive_Function (Subp)
|
|
and then not Is_Overloaded (Expr)
|
|
then
|
|
Error_Msg_NE
|
|
("aspect Indexing requires a function that applies to type&",
|
|
Subp, Ent);
|
|
end if;
|
|
|
|
-- An indexing function must return either the default element of
|
|
-- the container, or a reference type. For variable indexing it
|
|
-- must be the latter.
|
|
|
|
if Present (Default_Element) then
|
|
Analyze (Default_Element);
|
|
|
|
if Is_Entity_Name (Default_Element)
|
|
and then Covers (Entity (Default_Element), Etype (Subp))
|
|
then
|
|
Indexing_Found := True;
|
|
return;
|
|
end if;
|
|
end if;
|
|
|
|
-- For variable_indexing the return type must be a reference type
|
|
|
|
if Attr = Name_Variable_Indexing
|
|
and then not Has_Implicit_Dereference (Etype (Subp))
|
|
then
|
|
Error_Msg_N
|
|
("function for indexing must return a reference type", Subp);
|
|
|
|
else
|
|
Indexing_Found := True;
|
|
end if;
|
|
end Check_One_Function;
|
|
|
|
-- Start of processing for Check_Indexing_Functions
|
|
|
|
begin
|
|
if In_Instance then
|
|
return;
|
|
end if;
|
|
|
|
Analyze (Expr);
|
|
|
|
if not Is_Overloaded (Expr) then
|
|
Check_One_Function (Entity (Expr));
|
|
|
|
else
|
|
declare
|
|
I : Interp_Index;
|
|
It : Interp;
|
|
|
|
begin
|
|
Indexing_Found := False;
|
|
Get_First_Interp (Expr, I, It);
|
|
while Present (It.Nam) loop
|
|
|
|
-- Note that analysis will have added the interpretation
|
|
-- that corresponds to the dereference. We only check the
|
|
-- subprogram itself.
|
|
|
|
if Is_Overloadable (It.Nam) then
|
|
Check_One_Function (It.Nam);
|
|
end if;
|
|
|
|
Get_Next_Interp (I, It);
|
|
end loop;
|
|
|
|
if not Indexing_Found then
|
|
Error_Msg_NE
|
|
("aspect Indexing requires a function that "
|
|
& "applies to type&", Expr, Ent);
|
|
end if;
|
|
end;
|
|
end if;
|
|
end Check_Indexing_Functions;
|
|
|
|
------------------------------
|
|
-- Check_Iterator_Functions --
|
|
------------------------------
|
|
|
|
procedure Check_Iterator_Functions is
|
|
Default : Entity_Id;
|
|
|
|
function Valid_Default_Iterator (Subp : Entity_Id) return Boolean;
|
|
-- Check one possible interpretation for validity
|
|
|
|
----------------------------
|
|
-- Valid_Default_Iterator --
|
|
----------------------------
|
|
|
|
function Valid_Default_Iterator (Subp : Entity_Id) return Boolean is
|
|
Formal : Entity_Id;
|
|
|
|
begin
|
|
if not Check_Primitive_Function (Subp) then
|
|
return False;
|
|
else
|
|
Formal := First_Formal (Subp);
|
|
end if;
|
|
|
|
-- False if any subsequent formal has no default expression
|
|
|
|
Formal := Next_Formal (Formal);
|
|
while Present (Formal) loop
|
|
if No (Expression (Parent (Formal))) then
|
|
return False;
|
|
end if;
|
|
|
|
Next_Formal (Formal);
|
|
end loop;
|
|
|
|
-- True if all subsequent formals have default expressions
|
|
|
|
return True;
|
|
end Valid_Default_Iterator;
|
|
|
|
-- Start of processing for Check_Iterator_Functions
|
|
|
|
begin
|
|
Analyze (Expr);
|
|
|
|
if not Is_Entity_Name (Expr) then
|
|
Error_Msg_N ("aspect Iterator must be a function name", Expr);
|
|
end if;
|
|
|
|
if not Is_Overloaded (Expr) then
|
|
if not Check_Primitive_Function (Entity (Expr)) then
|
|
Error_Msg_NE
|
|
("aspect Indexing requires a function that applies to type&",
|
|
Entity (Expr), Ent);
|
|
end if;
|
|
|
|
if not Valid_Default_Iterator (Entity (Expr)) then
|
|
Error_Msg_N ("improper function for default iterator", Expr);
|
|
end if;
|
|
|
|
else
|
|
Default := Empty;
|
|
declare
|
|
I : Interp_Index;
|
|
It : Interp;
|
|
|
|
begin
|
|
Get_First_Interp (Expr, I, It);
|
|
while Present (It.Nam) loop
|
|
if not Check_Primitive_Function (It.Nam)
|
|
or else not Valid_Default_Iterator (It.Nam)
|
|
then
|
|
Remove_Interp (I);
|
|
|
|
elsif Present (Default) then
|
|
Error_Msg_N ("default iterator must be unique", Expr);
|
|
|
|
else
|
|
Default := It.Nam;
|
|
end if;
|
|
|
|
Get_Next_Interp (I, It);
|
|
end loop;
|
|
end;
|
|
|
|
if Present (Default) then
|
|
Set_Entity (Expr, Default);
|
|
Set_Is_Overloaded (Expr, False);
|
|
end if;
|
|
end if;
|
|
end Check_Iterator_Functions;
|
|
|
|
-------------------------------
|
|
-- Check_Primitive_Function --
|
|
-------------------------------
|
|
|
|
function Check_Primitive_Function (Subp : Entity_Id) return Boolean is
|
|
Ctrl : Entity_Id;
|
|
|
|
begin
|
|
if Ekind (Subp) /= E_Function then
|
|
return False;
|
|
end if;
|
|
|
|
if No (First_Formal (Subp)) then
|
|
return False;
|
|
else
|
|
Ctrl := Etype (First_Formal (Subp));
|
|
end if;
|
|
|
|
if Ctrl = Ent
|
|
or else Ctrl = Class_Wide_Type (Ent)
|
|
or else
|
|
(Ekind (Ctrl) = E_Anonymous_Access_Type
|
|
and then
|
|
(Designated_Type (Ctrl) = Ent
|
|
or else Designated_Type (Ctrl) = Class_Wide_Type (Ent)))
|
|
then
|
|
null;
|
|
|
|
else
|
|
return False;
|
|
end if;
|
|
|
|
return True;
|
|
end Check_Primitive_Function;
|
|
|
|
----------------------
|
|
-- Duplicate_Clause --
|
|
----------------------
|
|
|
|
function Duplicate_Clause return Boolean is
|
|
A : Node_Id;
|
|
|
|
begin
|
|
-- Nothing to do if this attribute definition clause comes from
|
|
-- an aspect specification, since we could not be duplicating an
|
|
-- explicit clause, and we dealt with the case of duplicated aspects
|
|
-- in Analyze_Aspect_Specifications.
|
|
|
|
if From_Aspect_Specification (N) then
|
|
return False;
|
|
end if;
|
|
|
|
-- Otherwise current clause may duplicate previous clause, or a
|
|
-- previously given pragma or aspect specification for the same
|
|
-- aspect.
|
|
|
|
A := Get_Rep_Item (U_Ent, Chars (N), Check_Parents => False);
|
|
|
|
if Present (A) then
|
|
Error_Msg_Name_1 := Chars (N);
|
|
Error_Msg_Sloc := Sloc (A);
|
|
|
|
Error_Msg_NE ("aspect% for & previously given#", N, U_Ent);
|
|
return True;
|
|
end if;
|
|
|
|
return False;
|
|
end Duplicate_Clause;
|
|
|
|
-- Start of processing for Analyze_Attribute_Definition_Clause
|
|
|
|
begin
|
|
-- The following code is a defense against recursion. Not clear that
|
|
-- this can happen legitimately, but perhaps some error situations
|
|
-- can cause it, and we did see this recursion during testing.
|
|
|
|
if Analyzed (N) then
|
|
return;
|
|
else
|
|
Set_Analyzed (N, True);
|
|
end if;
|
|
|
|
-- Ignore some selected attributes in CodePeer mode since they are not
|
|
-- relevant in this context.
|
|
|
|
if CodePeer_Mode then
|
|
case Id is
|
|
|
|
-- Ignore Component_Size in CodePeer mode, to avoid changing the
|
|
-- internal representation of types by implicitly packing them.
|
|
|
|
when Attribute_Component_Size =>
|
|
Rewrite (N, Make_Null_Statement (Sloc (N)));
|
|
return;
|
|
|
|
when others =>
|
|
null;
|
|
end case;
|
|
end if;
|
|
|
|
-- Process Ignore_Rep_Clauses option
|
|
|
|
if Ignore_Rep_Clauses then
|
|
case Id is
|
|
|
|
-- The following should be ignored. They do not affect legality
|
|
-- and may be target dependent. The basic idea of -gnatI is to
|
|
-- ignore any rep clauses that may be target dependent but do not
|
|
-- affect legality (except possibly to be rejected because they
|
|
-- are incompatible with the compilation target).
|
|
|
|
when Attribute_Alignment |
|
|
Attribute_Bit_Order |
|
|
Attribute_Component_Size |
|
|
Attribute_Machine_Radix |
|
|
Attribute_Object_Size |
|
|
Attribute_Size |
|
|
Attribute_Small |
|
|
Attribute_Stream_Size |
|
|
Attribute_Value_Size =>
|
|
Kill_Rep_Clause (N);
|
|
return;
|
|
|
|
-- The following should not be ignored, because in the first place
|
|
-- they are reasonably portable, and should not cause problems in
|
|
-- compiling code from another target, and also they do affect
|
|
-- legality, e.g. failing to provide a stream attribute for a
|
|
-- type may make a program illegal.
|
|
|
|
when Attribute_External_Tag |
|
|
Attribute_Input |
|
|
Attribute_Output |
|
|
Attribute_Read |
|
|
Attribute_Simple_Storage_Pool |
|
|
Attribute_Storage_Pool |
|
|
Attribute_Storage_Size |
|
|
Attribute_Write =>
|
|
null;
|
|
|
|
-- We do not do anything here with address clauses, they will be
|
|
-- removed by Freeze later on, but for now, it works better to
|
|
-- keep then in the tree.
|
|
|
|
when Attribute_Address =>
|
|
null;
|
|
|
|
-- Other cases are errors ("attribute& cannot be set with
|
|
-- definition clause"), which will be caught below.
|
|
|
|
when others =>
|
|
null;
|
|
end case;
|
|
end if;
|
|
|
|
Analyze (Nam);
|
|
Ent := Entity (Nam);
|
|
|
|
if Rep_Item_Too_Early (Ent, N) then
|
|
return;
|
|
end if;
|
|
|
|
-- Rep clause applies to full view of incomplete type or private type if
|
|
-- we have one (if not, this is a premature use of the type). However,
|
|
-- certain semantic checks need to be done on the specified entity (i.e.
|
|
-- the private view), so we save it in Ent.
|
|
|
|
if Is_Private_Type (Ent)
|
|
and then Is_Derived_Type (Ent)
|
|
and then not Is_Tagged_Type (Ent)
|
|
and then No (Full_View (Ent))
|
|
then
|
|
-- If this is a private type whose completion is a derivation from
|
|
-- another private type, there is no full view, and the attribute
|
|
-- belongs to the type itself, not its underlying parent.
|
|
|
|
U_Ent := Ent;
|
|
|
|
elsif Ekind (Ent) = E_Incomplete_Type then
|
|
|
|
-- The attribute applies to the full view, set the entity of the
|
|
-- attribute definition accordingly.
|
|
|
|
Ent := Underlying_Type (Ent);
|
|
U_Ent := Ent;
|
|
Set_Entity (Nam, Ent);
|
|
|
|
else
|
|
U_Ent := Underlying_Type (Ent);
|
|
end if;
|
|
|
|
-- Avoid cascaded error
|
|
|
|
if Etype (Nam) = Any_Type then
|
|
return;
|
|
|
|
-- Must be declared in current scope or in case of an aspect
|
|
-- specification, must be visible in current scope.
|
|
|
|
elsif Scope (Ent) /= Current_Scope
|
|
and then
|
|
not (From_Aspect_Specification (N)
|
|
and then Scope_Within_Or_Same (Current_Scope, Scope (Ent)))
|
|
then
|
|
Error_Msg_N ("entity must be declared in this scope", Nam);
|
|
return;
|
|
|
|
-- Must not be a source renaming (we do have some cases where the
|
|
-- expander generates a renaming, and those cases are OK, in such
|
|
-- cases any attribute applies to the renamed object as well).
|
|
|
|
elsif Is_Object (Ent)
|
|
and then Present (Renamed_Object (Ent))
|
|
then
|
|
-- Case of renamed object from source, this is an error
|
|
|
|
if Comes_From_Source (Renamed_Object (Ent)) then
|
|
Get_Name_String (Chars (N));
|
|
Error_Msg_Strlen := Name_Len;
|
|
Error_Msg_String (1 .. Name_Len) := Name_Buffer (1 .. Name_Len);
|
|
Error_Msg_N
|
|
("~ clause not allowed for a renaming declaration "
|
|
& "(RM 13.1(6))", Nam);
|
|
return;
|
|
|
|
-- For the case of a compiler generated renaming, the attribute
|
|
-- definition clause applies to the renamed object created by the
|
|
-- expander. The easiest general way to handle this is to create a
|
|
-- copy of the attribute definition clause for this object.
|
|
|
|
elsif Is_Entity_Name (Renamed_Object (Ent)) then
|
|
Insert_Action (N,
|
|
Make_Attribute_Definition_Clause (Loc,
|
|
Name =>
|
|
New_Occurrence_Of (Entity (Renamed_Object (Ent)), Loc),
|
|
Chars => Chars (N),
|
|
Expression => Duplicate_Subexpr (Expression (N))));
|
|
|
|
-- If the renamed object is not an entity, it must be a dereference
|
|
-- of an unconstrained function call, and we must introduce a new
|
|
-- declaration to capture the expression. This is needed in the case
|
|
-- of 'Alignment, where the original declaration must be rewritten.
|
|
|
|
else
|
|
pragma Assert
|
|
(Nkind (Renamed_Object (Ent)) = N_Explicit_Dereference);
|
|
null;
|
|
end if;
|
|
|
|
-- If no underlying entity, use entity itself, applies to some
|
|
-- previously detected error cases ???
|
|
|
|
elsif No (U_Ent) then
|
|
U_Ent := Ent;
|
|
|
|
-- Cannot specify for a subtype (exception Object/Value_Size)
|
|
|
|
elsif Is_Type (U_Ent)
|
|
and then not Is_First_Subtype (U_Ent)
|
|
and then Id /= Attribute_Object_Size
|
|
and then Id /= Attribute_Value_Size
|
|
and then not From_At_Mod (N)
|
|
then
|
|
Error_Msg_N ("cannot specify attribute for subtype", Nam);
|
|
return;
|
|
end if;
|
|
|
|
Set_Entity (N, U_Ent);
|
|
Check_Restriction_No_Use_Of_Attribute (N);
|
|
|
|
-- Switch on particular attribute
|
|
|
|
case Id is
|
|
|
|
-------------
|
|
-- Address --
|
|
-------------
|
|
|
|
-- Address attribute definition clause
|
|
|
|
when Attribute_Address => Address : begin
|
|
|
|
-- A little error check, catch for X'Address use X'Address;
|
|
|
|
if Nkind (Nam) = N_Identifier
|
|
and then Nkind (Expr) = N_Attribute_Reference
|
|
and then Attribute_Name (Expr) = Name_Address
|
|
and then Nkind (Prefix (Expr)) = N_Identifier
|
|
and then Chars (Nam) = Chars (Prefix (Expr))
|
|
then
|
|
Error_Msg_NE
|
|
("address for & is self-referencing", Prefix (Expr), Ent);
|
|
return;
|
|
end if;
|
|
|
|
-- Not that special case, carry on with analysis of expression
|
|
|
|
Analyze_And_Resolve (Expr, RTE (RE_Address));
|
|
|
|
-- Even when ignoring rep clauses we need to indicate that the
|
|
-- entity has an address clause and thus it is legal to declare
|
|
-- it imported. Freeze will get rid of the address clause later.
|
|
|
|
if Ignore_Rep_Clauses then
|
|
if Ekind_In (U_Ent, E_Variable, E_Constant) then
|
|
Record_Rep_Item (U_Ent, N);
|
|
end if;
|
|
|
|
return;
|
|
end if;
|
|
|
|
if Duplicate_Clause then
|
|
null;
|
|
|
|
-- Case of address clause for subprogram
|
|
|
|
elsif Is_Subprogram (U_Ent) then
|
|
if Has_Homonym (U_Ent) then
|
|
Error_Msg_N
|
|
("address clause cannot be given " &
|
|
"for overloaded subprogram",
|
|
Nam);
|
|
return;
|
|
end if;
|
|
|
|
-- For subprograms, all address clauses are permitted, and we
|
|
-- mark the subprogram as having a deferred freeze so that Gigi
|
|
-- will not elaborate it too soon.
|
|
|
|
-- Above needs more comments, what is too soon about???
|
|
|
|
Set_Has_Delayed_Freeze (U_Ent);
|
|
|
|
-- Case of address clause for entry
|
|
|
|
elsif Ekind (U_Ent) = E_Entry then
|
|
if Nkind (Parent (N)) = N_Task_Body then
|
|
Error_Msg_N
|
|
("entry address must be specified in task spec", Nam);
|
|
return;
|
|
end if;
|
|
|
|
-- For entries, we require a constant address
|
|
|
|
Check_Constant_Address_Clause (Expr, U_Ent);
|
|
|
|
-- Special checks for task types
|
|
|
|
if Is_Task_Type (Scope (U_Ent))
|
|
and then Comes_From_Source (Scope (U_Ent))
|
|
then
|
|
Error_Msg_N
|
|
("??entry address declared for entry in task type", N);
|
|
Error_Msg_N
|
|
("\??only one task can be declared of this type", N);
|
|
end if;
|
|
|
|
-- Entry address clauses are obsolescent
|
|
|
|
Check_Restriction (No_Obsolescent_Features, N);
|
|
|
|
if Warn_On_Obsolescent_Feature then
|
|
Error_Msg_N
|
|
("?j?attaching interrupt to task entry is an " &
|
|
"obsolescent feature (RM J.7.1)", N);
|
|
Error_Msg_N
|
|
("\?j?use interrupt procedure instead", N);
|
|
end if;
|
|
|
|
-- Case of an address clause for a controlled object which we
|
|
-- consider to be erroneous.
|
|
|
|
elsif Is_Controlled (Etype (U_Ent))
|
|
or else Has_Controlled_Component (Etype (U_Ent))
|
|
then
|
|
Error_Msg_NE
|
|
("??controlled object& must not be overlaid", Nam, U_Ent);
|
|
Error_Msg_N
|
|
("\??Program_Error will be raised at run time", Nam);
|
|
Insert_Action (Declaration_Node (U_Ent),
|
|
Make_Raise_Program_Error (Loc,
|
|
Reason => PE_Overlaid_Controlled_Object));
|
|
return;
|
|
|
|
-- Case of address clause for a (non-controlled) object
|
|
|
|
elsif
|
|
Ekind (U_Ent) = E_Variable
|
|
or else
|
|
Ekind (U_Ent) = E_Constant
|
|
then
|
|
declare
|
|
Expr : constant Node_Id := Expression (N);
|
|
O_Ent : Entity_Id;
|
|
Off : Boolean;
|
|
|
|
begin
|
|
-- Exported variables cannot have an address clause, because
|
|
-- this cancels the effect of the pragma Export.
|
|
|
|
if Is_Exported (U_Ent) then
|
|
Error_Msg_N
|
|
("cannot export object with address clause", Nam);
|
|
return;
|
|
end if;
|
|
|
|
Find_Overlaid_Entity (N, O_Ent, Off);
|
|
|
|
-- Overlaying controlled objects is erroneous
|
|
|
|
if Present (O_Ent)
|
|
and then (Has_Controlled_Component (Etype (O_Ent))
|
|
or else Is_Controlled (Etype (O_Ent)))
|
|
then
|
|
Error_Msg_N
|
|
("??cannot overlay with controlled object", Expr);
|
|
Error_Msg_N
|
|
("\??Program_Error will be raised at run time", Expr);
|
|
Insert_Action (Declaration_Node (U_Ent),
|
|
Make_Raise_Program_Error (Loc,
|
|
Reason => PE_Overlaid_Controlled_Object));
|
|
return;
|
|
|
|
elsif Present (O_Ent)
|
|
and then Ekind (U_Ent) = E_Constant
|
|
and then not Is_Constant_Object (O_Ent)
|
|
then
|
|
Error_Msg_N ("??constant overlays a variable", Expr);
|
|
|
|
-- Imported variables can have an address clause, but then
|
|
-- the import is pretty meaningless except to suppress
|
|
-- initializations, so we do not need such variables to
|
|
-- be statically allocated (and in fact it causes trouble
|
|
-- if the address clause is a local value).
|
|
|
|
elsif Is_Imported (U_Ent) then
|
|
Set_Is_Statically_Allocated (U_Ent, False);
|
|
end if;
|
|
|
|
-- We mark a possible modification of a variable with an
|
|
-- address clause, since it is likely aliasing is occurring.
|
|
|
|
Note_Possible_Modification (Nam, Sure => False);
|
|
|
|
-- Here we are checking for explicit overlap of one variable
|
|
-- by another, and if we find this then mark the overlapped
|
|
-- variable as also being volatile to prevent unwanted
|
|
-- optimizations. This is a significant pessimization so
|
|
-- avoid it when there is an offset, i.e. when the object
|
|
-- is composite; they cannot be optimized easily anyway.
|
|
|
|
if Present (O_Ent)
|
|
and then Is_Object (O_Ent)
|
|
and then not Off
|
|
|
|
-- The following test is an expedient solution to what
|
|
-- is really a problem in CodePeer. Suppressing the
|
|
-- Set_Treat_As_Volatile call here prevents later
|
|
-- generation (in some cases) of trees that CodePeer
|
|
-- should, but currently does not, handle correctly.
|
|
-- This test should probably be removed when CodePeer
|
|
-- is improved, just because we want the tree CodePeer
|
|
-- analyzes to match the tree for which we generate code
|
|
-- as closely as is practical. ???
|
|
|
|
and then not CodePeer_Mode
|
|
then
|
|
-- ??? O_Ent might not be in current unit
|
|
|
|
Set_Treat_As_Volatile (O_Ent);
|
|
end if;
|
|
|
|
-- Legality checks on the address clause for initialized
|
|
-- objects is deferred until the freeze point, because
|
|
-- a subsequent pragma might indicate that the object
|
|
-- is imported and thus not initialized. Also, the address
|
|
-- clause might involve entities that have yet to be
|
|
-- elaborated.
|
|
|
|
Set_Has_Delayed_Freeze (U_Ent);
|
|
|
|
-- If an initialization call has been generated for this
|
|
-- object, it needs to be deferred to after the freeze node
|
|
-- we have just now added, otherwise GIGI will see a
|
|
-- reference to the variable (as actual to the IP call)
|
|
-- before its definition.
|
|
|
|
declare
|
|
Init_Call : constant Node_Id :=
|
|
Remove_Init_Call (U_Ent, N);
|
|
|
|
begin
|
|
if Present (Init_Call) then
|
|
Append_Freeze_Action (U_Ent, Init_Call);
|
|
|
|
-- Reset Initialization_Statements pointer so that
|
|
-- if there is a pragma Import further down, it can
|
|
-- clear any default initialization.
|
|
|
|
Set_Initialization_Statements (U_Ent, Init_Call);
|
|
end if;
|
|
end;
|
|
|
|
if Is_Exported (U_Ent) then
|
|
Error_Msg_N
|
|
("& cannot be exported if an address clause is given",
|
|
Nam);
|
|
Error_Msg_N
|
|
("\define and export a variable "
|
|
& "that holds its address instead", Nam);
|
|
end if;
|
|
|
|
-- Entity has delayed freeze, so we will generate an
|
|
-- alignment check at the freeze point unless suppressed.
|
|
|
|
if not Range_Checks_Suppressed (U_Ent)
|
|
and then not Alignment_Checks_Suppressed (U_Ent)
|
|
then
|
|
Set_Check_Address_Alignment (N);
|
|
end if;
|
|
|
|
-- Kill the size check code, since we are not allocating
|
|
-- the variable, it is somewhere else.
|
|
|
|
Kill_Size_Check_Code (U_Ent);
|
|
|
|
-- If the address clause is of the form:
|
|
|
|
-- for Y'Address use X'Address
|
|
|
|
-- or
|
|
|
|
-- Const : constant Address := X'Address;
|
|
-- ...
|
|
-- for Y'Address use Const;
|
|
|
|
-- then we make an entry in the table for checking the size
|
|
-- and alignment of the overlaying variable. We defer this
|
|
-- check till after code generation to take full advantage
|
|
-- of the annotation done by the back end.
|
|
|
|
-- If the entity has a generic type, the check will be
|
|
-- performed in the instance if the actual type justifies
|
|
-- it, and we do not insert the clause in the table to
|
|
-- prevent spurious warnings.
|
|
|
|
-- Note: we used to test Comes_From_Source and only give
|
|
-- this warning for source entities, but we have removed
|
|
-- this test. It really seems bogus to generate overlays
|
|
-- that would trigger this warning in generated code.
|
|
-- Furthermore, by removing the test, we handle the
|
|
-- aspect case properly.
|
|
|
|
if Address_Clause_Overlay_Warnings
|
|
and then Present (O_Ent)
|
|
and then Is_Object (O_Ent)
|
|
then
|
|
if not Is_Generic_Type (Etype (U_Ent)) then
|
|
Address_Clause_Checks.Append ((N, U_Ent, O_Ent, Off));
|
|
end if;
|
|
|
|
-- If variable overlays a constant view, and we are
|
|
-- warning on overlays, then mark the variable as
|
|
-- overlaying a constant (we will give warnings later
|
|
-- if this variable is assigned).
|
|
|
|
if Is_Constant_Object (O_Ent)
|
|
and then Ekind (U_Ent) = E_Variable
|
|
then
|
|
Set_Overlays_Constant (U_Ent);
|
|
end if;
|
|
end if;
|
|
end;
|
|
|
|
-- Not a valid entity for an address clause
|
|
|
|
else
|
|
Error_Msg_N ("address cannot be given for &", Nam);
|
|
end if;
|
|
end Address;
|
|
|
|
---------------
|
|
-- Alignment --
|
|
---------------
|
|
|
|
-- Alignment attribute definition clause
|
|
|
|
when Attribute_Alignment => Alignment : declare
|
|
Align : constant Uint := Get_Alignment_Value (Expr);
|
|
Max_Align : constant Uint := UI_From_Int (Maximum_Alignment);
|
|
|
|
begin
|
|
FOnly := True;
|
|
|
|
if not Is_Type (U_Ent)
|
|
and then Ekind (U_Ent) /= E_Variable
|
|
and then Ekind (U_Ent) /= E_Constant
|
|
then
|
|
Error_Msg_N ("alignment cannot be given for &", Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
null;
|
|
|
|
elsif Align /= No_Uint then
|
|
Set_Has_Alignment_Clause (U_Ent);
|
|
|
|
-- Tagged type case, check for attempt to set alignment to a
|
|
-- value greater than Max_Align, and reset if so.
|
|
|
|
if Is_Tagged_Type (U_Ent) and then Align > Max_Align then
|
|
Error_Msg_N
|
|
("alignment for & set to Maximum_Aligment??", Nam);
|
|
Set_Alignment (U_Ent, Max_Align);
|
|
|
|
-- All other cases
|
|
|
|
else
|
|
Set_Alignment (U_Ent, Align);
|
|
end if;
|
|
|
|
-- For an array type, U_Ent is the first subtype. In that case,
|
|
-- also set the alignment of the anonymous base type so that
|
|
-- other subtypes (such as the itypes for aggregates of the
|
|
-- type) also receive the expected alignment.
|
|
|
|
if Is_Array_Type (U_Ent) then
|
|
Set_Alignment (Base_Type (U_Ent), Align);
|
|
end if;
|
|
end if;
|
|
end Alignment;
|
|
|
|
---------------
|
|
-- Bit_Order --
|
|
---------------
|
|
|
|
-- Bit_Order attribute definition clause
|
|
|
|
when Attribute_Bit_Order => Bit_Order : declare
|
|
begin
|
|
if not Is_Record_Type (U_Ent) then
|
|
Error_Msg_N
|
|
("Bit_Order can only be defined for record type", Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
null;
|
|
|
|
else
|
|
Analyze_And_Resolve (Expr, RTE (RE_Bit_Order));
|
|
|
|
if Etype (Expr) = Any_Type then
|
|
return;
|
|
|
|
elsif not Is_OK_Static_Expression (Expr) then
|
|
Flag_Non_Static_Expr
|
|
("Bit_Order requires static expression!", Expr);
|
|
|
|
else
|
|
if (Expr_Value (Expr) = 0) /= Bytes_Big_Endian then
|
|
Set_Reverse_Bit_Order (Base_Type (U_Ent), True);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end Bit_Order;
|
|
|
|
--------------------
|
|
-- Component_Size --
|
|
--------------------
|
|
|
|
-- Component_Size attribute definition clause
|
|
|
|
when Attribute_Component_Size => Component_Size_Case : declare
|
|
Csize : constant Uint := Static_Integer (Expr);
|
|
Ctyp : Entity_Id;
|
|
Btype : Entity_Id;
|
|
Biased : Boolean;
|
|
New_Ctyp : Entity_Id;
|
|
Decl : Node_Id;
|
|
|
|
begin
|
|
if not Is_Array_Type (U_Ent) then
|
|
Error_Msg_N ("component size requires array type", Nam);
|
|
return;
|
|
end if;
|
|
|
|
Btype := Base_Type (U_Ent);
|
|
Ctyp := Component_Type (Btype);
|
|
|
|
if Duplicate_Clause then
|
|
null;
|
|
|
|
elsif Rep_Item_Too_Early (Btype, N) then
|
|
null;
|
|
|
|
elsif Csize /= No_Uint then
|
|
Check_Size (Expr, Ctyp, Csize, Biased);
|
|
|
|
-- For the biased case, build a declaration for a subtype that
|
|
-- will be used to represent the biased subtype that reflects
|
|
-- the biased representation of components. We need the subtype
|
|
-- to get proper conversions on referencing elements of the
|
|
-- array. Note: component size clauses are ignored in VM mode.
|
|
|
|
if VM_Target = No_VM then
|
|
if Biased then
|
|
New_Ctyp :=
|
|
Make_Defining_Identifier (Loc,
|
|
Chars =>
|
|
New_External_Name (Chars (U_Ent), 'C', 0, 'T'));
|
|
|
|
Decl :=
|
|
Make_Subtype_Declaration (Loc,
|
|
Defining_Identifier => New_Ctyp,
|
|
Subtype_Indication =>
|
|
New_Occurrence_Of (Component_Type (Btype), Loc));
|
|
|
|
Set_Parent (Decl, N);
|
|
Analyze (Decl, Suppress => All_Checks);
|
|
|
|
Set_Has_Delayed_Freeze (New_Ctyp, False);
|
|
Set_Esize (New_Ctyp, Csize);
|
|
Set_RM_Size (New_Ctyp, Csize);
|
|
Init_Alignment (New_Ctyp);
|
|
Set_Is_Itype (New_Ctyp, True);
|
|
Set_Associated_Node_For_Itype (New_Ctyp, U_Ent);
|
|
|
|
Set_Component_Type (Btype, New_Ctyp);
|
|
Set_Biased (New_Ctyp, N, "component size clause");
|
|
end if;
|
|
|
|
Set_Component_Size (Btype, Csize);
|
|
|
|
-- For VM case, we ignore component size clauses
|
|
|
|
else
|
|
-- Give a warning unless we are in GNAT mode, in which case
|
|
-- the warning is suppressed since it is not useful.
|
|
|
|
if not GNAT_Mode then
|
|
Error_Msg_N
|
|
("component size ignored in this configuration??", N);
|
|
end if;
|
|
end if;
|
|
|
|
-- Deal with warning on overridden size
|
|
|
|
if Warn_On_Overridden_Size
|
|
and then Has_Size_Clause (Ctyp)
|
|
and then RM_Size (Ctyp) /= Csize
|
|
then
|
|
Error_Msg_NE
|
|
("component size overrides size clause for&?S?", N, Ctyp);
|
|
end if;
|
|
|
|
Set_Has_Component_Size_Clause (Btype, True);
|
|
Set_Has_Non_Standard_Rep (Btype, True);
|
|
end if;
|
|
end Component_Size_Case;
|
|
|
|
-----------------------
|
|
-- Constant_Indexing --
|
|
-----------------------
|
|
|
|
when Attribute_Constant_Indexing =>
|
|
Check_Indexing_Functions;
|
|
|
|
---------
|
|
-- CPU --
|
|
---------
|
|
|
|
when Attribute_CPU => CPU :
|
|
begin
|
|
-- CPU attribute definition clause not allowed except from aspect
|
|
-- specification.
|
|
|
|
if From_Aspect_Specification (N) then
|
|
if not Is_Task_Type (U_Ent) then
|
|
Error_Msg_N ("CPU can only be defined for task", Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
null;
|
|
|
|
else
|
|
-- The expression must be analyzed in the special manner
|
|
-- described in "Handling of Default and Per-Object
|
|
-- Expressions" in sem.ads.
|
|
|
|
-- The visibility to the discriminants must be restored
|
|
|
|
Push_Scope_And_Install_Discriminants (U_Ent);
|
|
Preanalyze_Spec_Expression (Expr, RTE (RE_CPU_Range));
|
|
Uninstall_Discriminants_And_Pop_Scope (U_Ent);
|
|
|
|
if not Is_OK_Static_Expression (Expr) then
|
|
Check_Restriction (Static_Priorities, Expr);
|
|
end if;
|
|
end if;
|
|
|
|
else
|
|
Error_Msg_N
|
|
("attribute& cannot be set with definition clause", N);
|
|
end if;
|
|
end CPU;
|
|
|
|
----------------------
|
|
-- Default_Iterator --
|
|
----------------------
|
|
|
|
when Attribute_Default_Iterator => Default_Iterator : declare
|
|
Func : Entity_Id;
|
|
|
|
begin
|
|
if not Is_Tagged_Type (U_Ent) then
|
|
Error_Msg_N
|
|
("aspect Default_Iterator applies to tagged type", Nam);
|
|
end if;
|
|
|
|
Check_Iterator_Functions;
|
|
|
|
Analyze (Expr);
|
|
|
|
if not Is_Entity_Name (Expr)
|
|
or else Ekind (Entity (Expr)) /= E_Function
|
|
then
|
|
Error_Msg_N ("aspect Iterator must be a function", Expr);
|
|
else
|
|
Func := Entity (Expr);
|
|
end if;
|
|
|
|
if No (First_Formal (Func))
|
|
or else Etype (First_Formal (Func)) /= U_Ent
|
|
then
|
|
Error_Msg_NE
|
|
("Default Iterator must be a primitive of&", Func, U_Ent);
|
|
end if;
|
|
end Default_Iterator;
|
|
|
|
------------------------
|
|
-- Dispatching_Domain --
|
|
------------------------
|
|
|
|
when Attribute_Dispatching_Domain => Dispatching_Domain :
|
|
begin
|
|
-- Dispatching_Domain attribute definition clause not allowed
|
|
-- except from aspect specification.
|
|
|
|
if From_Aspect_Specification (N) then
|
|
if not Is_Task_Type (U_Ent) then
|
|
Error_Msg_N ("Dispatching_Domain can only be defined" &
|
|
"for task",
|
|
Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
null;
|
|
|
|
else
|
|
-- The expression must be analyzed in the special manner
|
|
-- described in "Handling of Default and Per-Object
|
|
-- Expressions" in sem.ads.
|
|
|
|
-- The visibility to the discriminants must be restored
|
|
|
|
Push_Scope_And_Install_Discriminants (U_Ent);
|
|
|
|
Preanalyze_Spec_Expression
|
|
(Expr, RTE (RE_Dispatching_Domain));
|
|
|
|
Uninstall_Discriminants_And_Pop_Scope (U_Ent);
|
|
end if;
|
|
|
|
else
|
|
Error_Msg_N
|
|
("attribute& cannot be set with definition clause", N);
|
|
end if;
|
|
end Dispatching_Domain;
|
|
|
|
------------------
|
|
-- External_Tag --
|
|
------------------
|
|
|
|
when Attribute_External_Tag => External_Tag :
|
|
begin
|
|
if not Is_Tagged_Type (U_Ent) then
|
|
Error_Msg_N ("should be a tagged type", Nam);
|
|
end if;
|
|
|
|
if Duplicate_Clause then
|
|
null;
|
|
|
|
else
|
|
Analyze_And_Resolve (Expr, Standard_String);
|
|
|
|
if not Is_OK_Static_Expression (Expr) then
|
|
Flag_Non_Static_Expr
|
|
("static string required for tag name!", Nam);
|
|
end if;
|
|
|
|
if VM_Target /= No_VM then
|
|
Error_Msg_Name_1 := Attr;
|
|
Error_Msg_N
|
|
("% attribute unsupported in this configuration", Nam);
|
|
end if;
|
|
|
|
if not Is_Library_Level_Entity (U_Ent) then
|
|
Error_Msg_NE
|
|
("??non-unique external tag supplied for &", N, U_Ent);
|
|
Error_Msg_N
|
|
("\??same external tag applies to all "
|
|
& "subprogram calls", N);
|
|
Error_Msg_N
|
|
("\??corresponding internal tag cannot be obtained", N);
|
|
end if;
|
|
end if;
|
|
end External_Tag;
|
|
|
|
--------------------------
|
|
-- Implicit_Dereference --
|
|
--------------------------
|
|
|
|
when Attribute_Implicit_Dereference =>
|
|
|
|
-- Legality checks already performed at the point of the type
|
|
-- declaration, aspect is not delayed.
|
|
|
|
null;
|
|
|
|
-----------
|
|
-- Input --
|
|
-----------
|
|
|
|
when Attribute_Input =>
|
|
Analyze_Stream_TSS_Definition (TSS_Stream_Input);
|
|
Set_Has_Specified_Stream_Input (Ent);
|
|
|
|
------------------------
|
|
-- Interrupt_Priority --
|
|
------------------------
|
|
|
|
when Attribute_Interrupt_Priority => Interrupt_Priority :
|
|
begin
|
|
-- Interrupt_Priority attribute definition clause not allowed
|
|
-- except from aspect specification.
|
|
|
|
if From_Aspect_Specification (N) then
|
|
if not (Is_Protected_Type (U_Ent)
|
|
or else Is_Task_Type (U_Ent))
|
|
then
|
|
Error_Msg_N
|
|
("Interrupt_Priority can only be defined for task" &
|
|
"and protected object",
|
|
Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
null;
|
|
|
|
else
|
|
-- The expression must be analyzed in the special manner
|
|
-- described in "Handling of Default and Per-Object
|
|
-- Expressions" in sem.ads.
|
|
|
|
-- The visibility to the discriminants must be restored
|
|
|
|
Push_Scope_And_Install_Discriminants (U_Ent);
|
|
|
|
Preanalyze_Spec_Expression
|
|
(Expr, RTE (RE_Interrupt_Priority));
|
|
|
|
Uninstall_Discriminants_And_Pop_Scope (U_Ent);
|
|
end if;
|
|
|
|
else
|
|
Error_Msg_N
|
|
("attribute& cannot be set with definition clause", N);
|
|
end if;
|
|
end Interrupt_Priority;
|
|
|
|
--------------
|
|
-- Iterable --
|
|
--------------
|
|
|
|
when Attribute_Iterable =>
|
|
Analyze (Expr);
|
|
|
|
if Nkind (Expr) /= N_Aggregate then
|
|
Error_Msg_N ("aspect Iterable must be an aggregate", Expr);
|
|
end if;
|
|
|
|
declare
|
|
Assoc : Node_Id;
|
|
|
|
begin
|
|
Assoc := First (Component_Associations (Expr));
|
|
while Present (Assoc) loop
|
|
if not Is_Entity_Name (Expression (Assoc)) then
|
|
Error_Msg_N ("value must be a function", Assoc);
|
|
end if;
|
|
|
|
Next (Assoc);
|
|
end loop;
|
|
end;
|
|
|
|
----------------------
|
|
-- Iterator_Element --
|
|
----------------------
|
|
|
|
when Attribute_Iterator_Element =>
|
|
Analyze (Expr);
|
|
|
|
if not Is_Entity_Name (Expr)
|
|
or else not Is_Type (Entity (Expr))
|
|
then
|
|
Error_Msg_N ("aspect Iterator_Element must be a type", Expr);
|
|
end if;
|
|
|
|
-------------------
|
|
-- Machine_Radix --
|
|
-------------------
|
|
|
|
-- Machine radix attribute definition clause
|
|
|
|
when Attribute_Machine_Radix => Machine_Radix : declare
|
|
Radix : constant Uint := Static_Integer (Expr);
|
|
|
|
begin
|
|
if not Is_Decimal_Fixed_Point_Type (U_Ent) then
|
|
Error_Msg_N ("decimal fixed-point type expected for &", Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
null;
|
|
|
|
elsif Radix /= No_Uint then
|
|
Set_Has_Machine_Radix_Clause (U_Ent);
|
|
Set_Has_Non_Standard_Rep (Base_Type (U_Ent));
|
|
|
|
if Radix = 2 then
|
|
null;
|
|
elsif Radix = 10 then
|
|
Set_Machine_Radix_10 (U_Ent);
|
|
else
|
|
Error_Msg_N ("machine radix value must be 2 or 10", Expr);
|
|
end if;
|
|
end if;
|
|
end Machine_Radix;
|
|
|
|
-----------------
|
|
-- Object_Size --
|
|
-----------------
|
|
|
|
-- Object_Size attribute definition clause
|
|
|
|
when Attribute_Object_Size => Object_Size : declare
|
|
Size : constant Uint := Static_Integer (Expr);
|
|
|
|
Biased : Boolean;
|
|
pragma Warnings (Off, Biased);
|
|
|
|
begin
|
|
if not Is_Type (U_Ent) then
|
|
Error_Msg_N ("Object_Size cannot be given for &", Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
null;
|
|
|
|
else
|
|
Check_Size (Expr, U_Ent, Size, Biased);
|
|
|
|
if Is_Scalar_Type (U_Ent) then
|
|
if Size /= 8 and then Size /= 16 and then Size /= 32
|
|
and then UI_Mod (Size, 64) /= 0
|
|
then
|
|
Error_Msg_N
|
|
("Object_Size must be 8, 16, 32, or multiple of 64",
|
|
Expr);
|
|
end if;
|
|
|
|
elsif Size mod 8 /= 0 then
|
|
Error_Msg_N ("Object_Size must be a multiple of 8", Expr);
|
|
end if;
|
|
|
|
Set_Esize (U_Ent, Size);
|
|
Set_Has_Object_Size_Clause (U_Ent);
|
|
Alignment_Check_For_Size_Change (U_Ent, Size);
|
|
end if;
|
|
end Object_Size;
|
|
|
|
------------
|
|
-- Output --
|
|
------------
|
|
|
|
when Attribute_Output =>
|
|
Analyze_Stream_TSS_Definition (TSS_Stream_Output);
|
|
Set_Has_Specified_Stream_Output (Ent);
|
|
|
|
--------------
|
|
-- Priority --
|
|
--------------
|
|
|
|
when Attribute_Priority => Priority :
|
|
begin
|
|
-- Priority attribute definition clause not allowed except from
|
|
-- aspect specification.
|
|
|
|
if From_Aspect_Specification (N) then
|
|
if not (Is_Protected_Type (U_Ent)
|
|
or else Is_Task_Type (U_Ent)
|
|
or else Ekind (U_Ent) = E_Procedure)
|
|
then
|
|
Error_Msg_N
|
|
("Priority can only be defined for task and protected " &
|
|
"object",
|
|
Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
null;
|
|
|
|
else
|
|
-- The expression must be analyzed in the special manner
|
|
-- described in "Handling of Default and Per-Object
|
|
-- Expressions" in sem.ads.
|
|
|
|
-- The visibility to the discriminants must be restored
|
|
|
|
Push_Scope_And_Install_Discriminants (U_Ent);
|
|
Preanalyze_Spec_Expression (Expr, Standard_Integer);
|
|
Uninstall_Discriminants_And_Pop_Scope (U_Ent);
|
|
|
|
if not Is_OK_Static_Expression (Expr) then
|
|
Check_Restriction (Static_Priorities, Expr);
|
|
end if;
|
|
end if;
|
|
|
|
else
|
|
Error_Msg_N
|
|
("attribute& cannot be set with definition clause", N);
|
|
end if;
|
|
end Priority;
|
|
|
|
----------
|
|
-- Read --
|
|
----------
|
|
|
|
when Attribute_Read =>
|
|
Analyze_Stream_TSS_Definition (TSS_Stream_Read);
|
|
Set_Has_Specified_Stream_Read (Ent);
|
|
|
|
--------------------------
|
|
-- Scalar_Storage_Order --
|
|
--------------------------
|
|
|
|
-- Scalar_Storage_Order attribute definition clause
|
|
|
|
when Attribute_Scalar_Storage_Order => Scalar_Storage_Order : declare
|
|
begin
|
|
if not (Is_Record_Type (U_Ent) or else Is_Array_Type (U_Ent)) then
|
|
Error_Msg_N
|
|
("Scalar_Storage_Order can only be defined for "
|
|
& "record or array type", Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
null;
|
|
|
|
else
|
|
Analyze_And_Resolve (Expr, RTE (RE_Bit_Order));
|
|
|
|
if Etype (Expr) = Any_Type then
|
|
return;
|
|
|
|
elsif not Is_OK_Static_Expression (Expr) then
|
|
Flag_Non_Static_Expr
|
|
("Scalar_Storage_Order requires static expression!", Expr);
|
|
|
|
elsif (Expr_Value (Expr) = 0) /= Bytes_Big_Endian then
|
|
|
|
-- Here for the case of a non-default (i.e. non-confirming)
|
|
-- Scalar_Storage_Order attribute definition.
|
|
|
|
if Support_Nondefault_SSO_On_Target then
|
|
Set_Reverse_Storage_Order (Base_Type (U_Ent), True);
|
|
else
|
|
Error_Msg_N
|
|
("non-default Scalar_Storage_Order "
|
|
& "not supported on target", Expr);
|
|
end if;
|
|
end if;
|
|
|
|
-- Clear SSO default indications since explicit setting of the
|
|
-- order overrides the defaults.
|
|
|
|
Set_SSO_Set_Low_By_Default (Base_Type (U_Ent), False);
|
|
Set_SSO_Set_High_By_Default (Base_Type (U_Ent), False);
|
|
end if;
|
|
end Scalar_Storage_Order;
|
|
|
|
----------
|
|
-- Size --
|
|
----------
|
|
|
|
-- Size attribute definition clause
|
|
|
|
when Attribute_Size => Size : declare
|
|
Size : constant Uint := Static_Integer (Expr);
|
|
Etyp : Entity_Id;
|
|
Biased : Boolean;
|
|
|
|
begin
|
|
FOnly := True;
|
|
|
|
if Duplicate_Clause then
|
|
null;
|
|
|
|
elsif not Is_Type (U_Ent)
|
|
and then Ekind (U_Ent) /= E_Variable
|
|
and then Ekind (U_Ent) /= E_Constant
|
|
then
|
|
Error_Msg_N ("size cannot be given for &", Nam);
|
|
|
|
elsif Is_Array_Type (U_Ent)
|
|
and then not Is_Constrained (U_Ent)
|
|
then
|
|
Error_Msg_N
|
|
("size cannot be given for unconstrained array", Nam);
|
|
|
|
elsif Size /= No_Uint then
|
|
if VM_Target /= No_VM and then not GNAT_Mode then
|
|
|
|
-- Size clause is not handled properly on VM targets.
|
|
-- Display a warning unless we are in GNAT mode, in which
|
|
-- case this is useless.
|
|
|
|
Error_Msg_N
|
|
("size clauses are ignored in this configuration??", N);
|
|
end if;
|
|
|
|
if Is_Type (U_Ent) then
|
|
Etyp := U_Ent;
|
|
else
|
|
Etyp := Etype (U_Ent);
|
|
end if;
|
|
|
|
-- Check size, note that Gigi is in charge of checking that the
|
|
-- size of an array or record type is OK. Also we do not check
|
|
-- the size in the ordinary fixed-point case, since it is too
|
|
-- early to do so (there may be subsequent small clause that
|
|
-- affects the size). We can check the size if a small clause
|
|
-- has already been given.
|
|
|
|
if not Is_Ordinary_Fixed_Point_Type (U_Ent)
|
|
or else Has_Small_Clause (U_Ent)
|
|
then
|
|
Check_Size (Expr, Etyp, Size, Biased);
|
|
Set_Biased (U_Ent, N, "size clause", Biased);
|
|
end if;
|
|
|
|
-- For types set RM_Size and Esize if possible
|
|
|
|
if Is_Type (U_Ent) then
|
|
Set_RM_Size (U_Ent, Size);
|
|
|
|
-- For elementary types, increase Object_Size to power of 2,
|
|
-- but not less than a storage unit in any case (normally
|
|
-- this means it will be byte addressable).
|
|
|
|
-- For all other types, nothing else to do, we leave Esize
|
|
-- (object size) unset, the back end will set it from the
|
|
-- size and alignment in an appropriate manner.
|
|
|
|
-- In both cases, we check whether the alignment must be
|
|
-- reset in the wake of the size change.
|
|
|
|
if Is_Elementary_Type (U_Ent) then
|
|
if Size <= System_Storage_Unit then
|
|
Init_Esize (U_Ent, System_Storage_Unit);
|
|
elsif Size <= 16 then
|
|
Init_Esize (U_Ent, 16);
|
|
elsif Size <= 32 then
|
|
Init_Esize (U_Ent, 32);
|
|
else
|
|
Set_Esize (U_Ent, (Size + 63) / 64 * 64);
|
|
end if;
|
|
|
|
Alignment_Check_For_Size_Change (U_Ent, Esize (U_Ent));
|
|
else
|
|
Alignment_Check_For_Size_Change (U_Ent, Size);
|
|
end if;
|
|
|
|
-- For objects, set Esize only
|
|
|
|
else
|
|
if Is_Elementary_Type (Etyp) then
|
|
if Size /= System_Storage_Unit
|
|
and then
|
|
Size /= System_Storage_Unit * 2
|
|
and then
|
|
Size /= System_Storage_Unit * 4
|
|
and then
|
|
Size /= System_Storage_Unit * 8
|
|
then
|
|
Error_Msg_Uint_1 := UI_From_Int (System_Storage_Unit);
|
|
Error_Msg_Uint_2 := Error_Msg_Uint_1 * 8;
|
|
Error_Msg_N
|
|
("size for primitive object must be a power of 2"
|
|
& " in the range ^-^", N);
|
|
end if;
|
|
end if;
|
|
|
|
Set_Esize (U_Ent, Size);
|
|
end if;
|
|
|
|
Set_Has_Size_Clause (U_Ent);
|
|
end if;
|
|
end Size;
|
|
|
|
-----------
|
|
-- Small --
|
|
-----------
|
|
|
|
-- Small attribute definition clause
|
|
|
|
when Attribute_Small => Small : declare
|
|
Implicit_Base : constant Entity_Id := Base_Type (U_Ent);
|
|
Small : Ureal;
|
|
|
|
begin
|
|
Analyze_And_Resolve (Expr, Any_Real);
|
|
|
|
if Etype (Expr) = Any_Type then
|
|
return;
|
|
|
|
elsif not Is_OK_Static_Expression (Expr) then
|
|
Flag_Non_Static_Expr
|
|
("small requires static expression!", Expr);
|
|
return;
|
|
|
|
else
|
|
Small := Expr_Value_R (Expr);
|
|
|
|
if Small <= Ureal_0 then
|
|
Error_Msg_N ("small value must be greater than zero", Expr);
|
|
return;
|
|
end if;
|
|
|
|
end if;
|
|
|
|
if not Is_Ordinary_Fixed_Point_Type (U_Ent) then
|
|
Error_Msg_N
|
|
("small requires an ordinary fixed point type", Nam);
|
|
|
|
elsif Has_Small_Clause (U_Ent) then
|
|
Error_Msg_N ("small already given for &", Nam);
|
|
|
|
elsif Small > Delta_Value (U_Ent) then
|
|
Error_Msg_N
|
|
("small value must not be greater than delta value", Nam);
|
|
|
|
else
|
|
Set_Small_Value (U_Ent, Small);
|
|
Set_Small_Value (Implicit_Base, Small);
|
|
Set_Has_Small_Clause (U_Ent);
|
|
Set_Has_Small_Clause (Implicit_Base);
|
|
Set_Has_Non_Standard_Rep (Implicit_Base);
|
|
end if;
|
|
end Small;
|
|
|
|
------------------
|
|
-- Storage_Pool --
|
|
------------------
|
|
|
|
-- Storage_Pool attribute definition clause
|
|
|
|
when Attribute_Storage_Pool | Attribute_Simple_Storage_Pool => declare
|
|
Pool : Entity_Id;
|
|
T : Entity_Id;
|
|
|
|
begin
|
|
if Ekind (U_Ent) = E_Access_Subprogram_Type then
|
|
Error_Msg_N
|
|
("storage pool cannot be given for access-to-subprogram type",
|
|
Nam);
|
|
return;
|
|
|
|
elsif not
|
|
Ekind_In (U_Ent, E_Access_Type, E_General_Access_Type)
|
|
then
|
|
Error_Msg_N
|
|
("storage pool can only be given for access types", Nam);
|
|
return;
|
|
|
|
elsif Is_Derived_Type (U_Ent) then
|
|
Error_Msg_N
|
|
("storage pool cannot be given for a derived access type",
|
|
Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
return;
|
|
|
|
elsif Present (Associated_Storage_Pool (U_Ent)) then
|
|
Error_Msg_N ("storage pool already given for &", Nam);
|
|
return;
|
|
end if;
|
|
|
|
-- Check for Storage_Size previously given
|
|
|
|
declare
|
|
SS : constant Node_Id :=
|
|
Get_Attribute_Definition_Clause
|
|
(U_Ent, Attribute_Storage_Size);
|
|
begin
|
|
if Present (SS) then
|
|
Check_Pool_Size_Clash (U_Ent, N, SS);
|
|
end if;
|
|
end;
|
|
|
|
-- Storage_Pool case
|
|
|
|
if Id = Attribute_Storage_Pool then
|
|
Analyze_And_Resolve
|
|
(Expr, Class_Wide_Type (RTE (RE_Root_Storage_Pool)));
|
|
|
|
-- In the Simple_Storage_Pool case, we allow a variable of any
|
|
-- simple storage pool type, so we Resolve without imposing an
|
|
-- expected type.
|
|
|
|
else
|
|
Analyze_And_Resolve (Expr);
|
|
|
|
if not Present (Get_Rep_Pragma
|
|
(Etype (Expr), Name_Simple_Storage_Pool_Type))
|
|
then
|
|
Error_Msg_N
|
|
("expression must be of a simple storage pool type", Expr);
|
|
end if;
|
|
end if;
|
|
|
|
if not Denotes_Variable (Expr) then
|
|
Error_Msg_N ("storage pool must be a variable", Expr);
|
|
return;
|
|
end if;
|
|
|
|
if Nkind (Expr) = N_Type_Conversion then
|
|
T := Etype (Expression (Expr));
|
|
else
|
|
T := Etype (Expr);
|
|
end if;
|
|
|
|
-- The Stack_Bounded_Pool is used internally for implementing
|
|
-- access types with a Storage_Size. Since it only work properly
|
|
-- when used on one specific type, we need to check that it is not
|
|
-- hijacked improperly:
|
|
|
|
-- type T is access Integer;
|
|
-- for T'Storage_Size use n;
|
|
-- type Q is access Float;
|
|
-- for Q'Storage_Size use T'Storage_Size; -- incorrect
|
|
|
|
if RTE_Available (RE_Stack_Bounded_Pool)
|
|
and then Base_Type (T) = RTE (RE_Stack_Bounded_Pool)
|
|
then
|
|
Error_Msg_N ("non-shareable internal Pool", Expr);
|
|
return;
|
|
end if;
|
|
|
|
-- If the argument is a name that is not an entity name, then
|
|
-- we construct a renaming operation to define an entity of
|
|
-- type storage pool.
|
|
|
|
if not Is_Entity_Name (Expr)
|
|
and then Is_Object_Reference (Expr)
|
|
then
|
|
Pool := Make_Temporary (Loc, 'P', Expr);
|
|
|
|
declare
|
|
Rnode : constant Node_Id :=
|
|
Make_Object_Renaming_Declaration (Loc,
|
|
Defining_Identifier => Pool,
|
|
Subtype_Mark =>
|
|
New_Occurrence_Of (Etype (Expr), Loc),
|
|
Name => Expr);
|
|
|
|
begin
|
|
-- If the attribute definition clause comes from an aspect
|
|
-- clause, then insert the renaming before the associated
|
|
-- entity's declaration, since the attribute clause has
|
|
-- not yet been appended to the declaration list.
|
|
|
|
if From_Aspect_Specification (N) then
|
|
Insert_Before (Parent (Entity (N)), Rnode);
|
|
else
|
|
Insert_Before (N, Rnode);
|
|
end if;
|
|
|
|
Analyze (Rnode);
|
|
Set_Associated_Storage_Pool (U_Ent, Pool);
|
|
end;
|
|
|
|
elsif Is_Entity_Name (Expr) then
|
|
Pool := Entity (Expr);
|
|
|
|
-- If pool is a renamed object, get original one. This can
|
|
-- happen with an explicit renaming, and within instances.
|
|
|
|
while Present (Renamed_Object (Pool))
|
|
and then Is_Entity_Name (Renamed_Object (Pool))
|
|
loop
|
|
Pool := Entity (Renamed_Object (Pool));
|
|
end loop;
|
|
|
|
if Present (Renamed_Object (Pool))
|
|
and then Nkind (Renamed_Object (Pool)) = N_Type_Conversion
|
|
and then Is_Entity_Name (Expression (Renamed_Object (Pool)))
|
|
then
|
|
Pool := Entity (Expression (Renamed_Object (Pool)));
|
|
end if;
|
|
|
|
Set_Associated_Storage_Pool (U_Ent, Pool);
|
|
|
|
elsif Nkind (Expr) = N_Type_Conversion
|
|
and then Is_Entity_Name (Expression (Expr))
|
|
and then Nkind (Original_Node (Expr)) = N_Attribute_Reference
|
|
then
|
|
Pool := Entity (Expression (Expr));
|
|
Set_Associated_Storage_Pool (U_Ent, Pool);
|
|
|
|
else
|
|
Error_Msg_N ("incorrect reference to a Storage Pool", Expr);
|
|
return;
|
|
end if;
|
|
end;
|
|
|
|
------------------
|
|
-- Storage_Size --
|
|
------------------
|
|
|
|
-- Storage_Size attribute definition clause
|
|
|
|
when Attribute_Storage_Size => Storage_Size : declare
|
|
Btype : constant Entity_Id := Base_Type (U_Ent);
|
|
|
|
begin
|
|
if Is_Task_Type (U_Ent) then
|
|
|
|
-- Check obsolescent (but never obsolescent if from aspect)
|
|
|
|
if not From_Aspect_Specification (N) then
|
|
Check_Restriction (No_Obsolescent_Features, N);
|
|
|
|
if Warn_On_Obsolescent_Feature then
|
|
Error_Msg_N
|
|
("?j?storage size clause for task is an " &
|
|
"obsolescent feature (RM J.9)", N);
|
|
Error_Msg_N ("\?j?use Storage_Size pragma instead", N);
|
|
end if;
|
|
end if;
|
|
|
|
FOnly := True;
|
|
end if;
|
|
|
|
if not Is_Access_Type (U_Ent)
|
|
and then Ekind (U_Ent) /= E_Task_Type
|
|
then
|
|
Error_Msg_N ("storage size cannot be given for &", Nam);
|
|
|
|
elsif Is_Access_Type (U_Ent) and Is_Derived_Type (U_Ent) then
|
|
Error_Msg_N
|
|
("storage size cannot be given for a derived access type",
|
|
Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
null;
|
|
|
|
else
|
|
Analyze_And_Resolve (Expr, Any_Integer);
|
|
|
|
if Is_Access_Type (U_Ent) then
|
|
|
|
-- Check for Storage_Pool previously given
|
|
|
|
declare
|
|
SP : constant Node_Id :=
|
|
Get_Attribute_Definition_Clause
|
|
(U_Ent, Attribute_Storage_Pool);
|
|
|
|
begin
|
|
if Present (SP) then
|
|
Check_Pool_Size_Clash (U_Ent, SP, N);
|
|
end if;
|
|
end;
|
|
|
|
-- Special case of for x'Storage_Size use 0
|
|
|
|
if Is_OK_Static_Expression (Expr)
|
|
and then Expr_Value (Expr) = 0
|
|
then
|
|
Set_No_Pool_Assigned (Btype);
|
|
end if;
|
|
end if;
|
|
|
|
Set_Has_Storage_Size_Clause (Btype);
|
|
end if;
|
|
end Storage_Size;
|
|
|
|
-----------------
|
|
-- Stream_Size --
|
|
-----------------
|
|
|
|
when Attribute_Stream_Size => Stream_Size : declare
|
|
Size : constant Uint := Static_Integer (Expr);
|
|
|
|
begin
|
|
if Ada_Version <= Ada_95 then
|
|
Check_Restriction (No_Implementation_Attributes, N);
|
|
end if;
|
|
|
|
if Duplicate_Clause then
|
|
null;
|
|
|
|
elsif Is_Elementary_Type (U_Ent) then
|
|
if Size /= System_Storage_Unit
|
|
and then
|
|
Size /= System_Storage_Unit * 2
|
|
and then
|
|
Size /= System_Storage_Unit * 4
|
|
and then
|
|
Size /= System_Storage_Unit * 8
|
|
then
|
|
Error_Msg_Uint_1 := UI_From_Int (System_Storage_Unit);
|
|
Error_Msg_N
|
|
("stream size for elementary type must be a"
|
|
& " power of 2 and at least ^", N);
|
|
|
|
elsif RM_Size (U_Ent) > Size then
|
|
Error_Msg_Uint_1 := RM_Size (U_Ent);
|
|
Error_Msg_N
|
|
("stream size for elementary type must be a"
|
|
& " power of 2 and at least ^", N);
|
|
end if;
|
|
|
|
Set_Has_Stream_Size_Clause (U_Ent);
|
|
|
|
else
|
|
Error_Msg_N ("Stream_Size cannot be given for &", Nam);
|
|
end if;
|
|
end Stream_Size;
|
|
|
|
----------------
|
|
-- Value_Size --
|
|
----------------
|
|
|
|
-- Value_Size attribute definition clause
|
|
|
|
when Attribute_Value_Size => Value_Size : declare
|
|
Size : constant Uint := Static_Integer (Expr);
|
|
Biased : Boolean;
|
|
|
|
begin
|
|
if not Is_Type (U_Ent) then
|
|
Error_Msg_N ("Value_Size cannot be given for &", Nam);
|
|
|
|
elsif Duplicate_Clause then
|
|
null;
|
|
|
|
elsif Is_Array_Type (U_Ent)
|
|
and then not Is_Constrained (U_Ent)
|
|
then
|
|
Error_Msg_N
|
|
("Value_Size cannot be given for unconstrained array", Nam);
|
|
|
|
else
|
|
if Is_Elementary_Type (U_Ent) then
|
|
Check_Size (Expr, U_Ent, Size, Biased);
|
|
Set_Biased (U_Ent, N, "value size clause", Biased);
|
|
end if;
|
|
|
|
Set_RM_Size (U_Ent, Size);
|
|
end if;
|
|
end Value_Size;
|
|
|
|
-----------------------
|
|
-- Variable_Indexing --
|
|
-----------------------
|
|
|
|
when Attribute_Variable_Indexing =>
|
|
Check_Indexing_Functions;
|
|
|
|
-----------
|
|
-- Write --
|
|
-----------
|
|
|
|
when Attribute_Write =>
|
|
Analyze_Stream_TSS_Definition (TSS_Stream_Write);
|
|
Set_Has_Specified_Stream_Write (Ent);
|
|
|
|
-- All other attributes cannot be set
|
|
|
|
when others =>
|
|
Error_Msg_N
|
|
("attribute& cannot be set with definition clause", N);
|
|
end case;
|
|
|
|
-- The test for the type being frozen must be performed after any
|
|
-- expression the clause has been analyzed since the expression itself
|
|
-- might cause freezing that makes the clause illegal.
|
|
|
|
if Rep_Item_Too_Late (U_Ent, N, FOnly) then
|
|
return;
|
|
end if;
|
|
end Analyze_Attribute_Definition_Clause;
|
|
|
|
----------------------------
|
|
-- Analyze_Code_Statement --
|
|
----------------------------
|
|
|
|
procedure Analyze_Code_Statement (N : Node_Id) is
|
|
HSS : constant Node_Id := Parent (N);
|
|
SBody : constant Node_Id := Parent (HSS);
|
|
Subp : constant Entity_Id := Current_Scope;
|
|
Stmt : Node_Id;
|
|
Decl : Node_Id;
|
|
StmtO : Node_Id;
|
|
DeclO : Node_Id;
|
|
|
|
begin
|
|
-- Analyze and check we get right type, note that this implements the
|
|
-- requirement (RM 13.8(1)) that Machine_Code be with'ed, since that
|
|
-- is the only way that Asm_Insn could possibly be visible.
|
|
|
|
Analyze_And_Resolve (Expression (N));
|
|
|
|
if Etype (Expression (N)) = Any_Type then
|
|
return;
|
|
elsif Etype (Expression (N)) /= RTE (RE_Asm_Insn) then
|
|
Error_Msg_N ("incorrect type for code statement", N);
|
|
return;
|
|
end if;
|
|
|
|
Check_Code_Statement (N);
|
|
|
|
-- Make sure we appear in the handled statement sequence of a
|
|
-- subprogram (RM 13.8(3)).
|
|
|
|
if Nkind (HSS) /= N_Handled_Sequence_Of_Statements
|
|
or else Nkind (SBody) /= N_Subprogram_Body
|
|
then
|
|
Error_Msg_N
|
|
("code statement can only appear in body of subprogram", N);
|
|
return;
|
|
end if;
|
|
|
|
-- Do remaining checks (RM 13.8(3)) if not already done
|
|
|
|
if not Is_Machine_Code_Subprogram (Subp) then
|
|
Set_Is_Machine_Code_Subprogram (Subp);
|
|
|
|
-- No exception handlers allowed
|
|
|
|
if Present (Exception_Handlers (HSS)) then
|
|
Error_Msg_N
|
|
("exception handlers not permitted in machine code subprogram",
|
|
First (Exception_Handlers (HSS)));
|
|
end if;
|
|
|
|
-- No declarations other than use clauses and pragmas (we allow
|
|
-- certain internally generated declarations as well).
|
|
|
|
Decl := First (Declarations (SBody));
|
|
while Present (Decl) loop
|
|
DeclO := Original_Node (Decl);
|
|
if Comes_From_Source (DeclO)
|
|
and not Nkind_In (DeclO, N_Pragma,
|
|
N_Use_Package_Clause,
|
|
N_Use_Type_Clause,
|
|
N_Implicit_Label_Declaration)
|
|
then
|
|
Error_Msg_N
|
|
("this declaration not allowed in machine code subprogram",
|
|
DeclO);
|
|
end if;
|
|
|
|
Next (Decl);
|
|
end loop;
|
|
|
|
-- No statements other than code statements, pragmas, and labels.
|
|
-- Again we allow certain internally generated statements.
|
|
|
|
-- In Ada 2012, qualified expressions are names, and the code
|
|
-- statement is initially parsed as a procedure call.
|
|
|
|
Stmt := First (Statements (HSS));
|
|
while Present (Stmt) loop
|
|
StmtO := Original_Node (Stmt);
|
|
|
|
-- A procedure call transformed into a code statement is OK.
|
|
|
|
if Ada_Version >= Ada_2012
|
|
and then Nkind (StmtO) = N_Procedure_Call_Statement
|
|
and then Nkind (Name (StmtO)) = N_Qualified_Expression
|
|
then
|
|
null;
|
|
|
|
elsif Comes_From_Source (StmtO)
|
|
and then not Nkind_In (StmtO, N_Pragma,
|
|
N_Label,
|
|
N_Code_Statement)
|
|
then
|
|
Error_Msg_N
|
|
("this statement is not allowed in machine code subprogram",
|
|
StmtO);
|
|
end if;
|
|
|
|
Next (Stmt);
|
|
end loop;
|
|
end if;
|
|
end Analyze_Code_Statement;
|
|
|
|
-----------------------------------------------
|
|
-- Analyze_Enumeration_Representation_Clause --
|
|
-----------------------------------------------
|
|
|
|
procedure Analyze_Enumeration_Representation_Clause (N : Node_Id) is
|
|
Ident : constant Node_Id := Identifier (N);
|
|
Aggr : constant Node_Id := Array_Aggregate (N);
|
|
Enumtype : Entity_Id;
|
|
Elit : Entity_Id;
|
|
Expr : Node_Id;
|
|
Assoc : Node_Id;
|
|
Choice : Node_Id;
|
|
Val : Uint;
|
|
|
|
Err : Boolean := False;
|
|
-- Set True to avoid cascade errors and crashes on incorrect source code
|
|
|
|
Lo : constant Uint := Expr_Value (Type_Low_Bound (Universal_Integer));
|
|
Hi : constant Uint := Expr_Value (Type_High_Bound (Universal_Integer));
|
|
-- Allowed range of universal integer (= allowed range of enum lit vals)
|
|
|
|
Min : Uint;
|
|
Max : Uint;
|
|
-- Minimum and maximum values of entries
|
|
|
|
Max_Node : Node_Id;
|
|
-- Pointer to node for literal providing max value
|
|
|
|
begin
|
|
if Ignore_Rep_Clauses then
|
|
Kill_Rep_Clause (N);
|
|
return;
|
|
end if;
|
|
|
|
-- Ignore enumeration rep clauses by default in CodePeer mode,
|
|
-- unless -gnatd.I is specified, as a work around for potential false
|
|
-- positive messages.
|
|
|
|
if CodePeer_Mode and not Debug_Flag_Dot_II then
|
|
return;
|
|
end if;
|
|
|
|
-- First some basic error checks
|
|
|
|
Find_Type (Ident);
|
|
Enumtype := Entity (Ident);
|
|
|
|
if Enumtype = Any_Type
|
|
or else Rep_Item_Too_Early (Enumtype, N)
|
|
then
|
|
return;
|
|
else
|
|
Enumtype := Underlying_Type (Enumtype);
|
|
end if;
|
|
|
|
if not Is_Enumeration_Type (Enumtype) then
|
|
Error_Msg_NE
|
|
("enumeration type required, found}",
|
|
Ident, First_Subtype (Enumtype));
|
|
return;
|
|
end if;
|
|
|
|
-- Ignore rep clause on generic actual type. This will already have
|
|
-- been flagged on the template as an error, and this is the safest
|
|
-- way to ensure we don't get a junk cascaded message in the instance.
|
|
|
|
if Is_Generic_Actual_Type (Enumtype) then
|
|
return;
|
|
|
|
-- Type must be in current scope
|
|
|
|
elsif Scope (Enumtype) /= Current_Scope then
|
|
Error_Msg_N ("type must be declared in this scope", Ident);
|
|
return;
|
|
|
|
-- Type must be a first subtype
|
|
|
|
elsif not Is_First_Subtype (Enumtype) then
|
|
Error_Msg_N ("cannot give enumeration rep clause for subtype", N);
|
|
return;
|
|
|
|
-- Ignore duplicate rep clause
|
|
|
|
elsif Has_Enumeration_Rep_Clause (Enumtype) then
|
|
Error_Msg_N ("duplicate enumeration rep clause ignored", N);
|
|
return;
|
|
|
|
-- Don't allow rep clause for standard [wide_[wide_]]character
|
|
|
|
elsif Is_Standard_Character_Type (Enumtype) then
|
|
Error_Msg_N ("enumeration rep clause not allowed for this type", N);
|
|
return;
|
|
|
|
-- Check that the expression is a proper aggregate (no parentheses)
|
|
|
|
elsif Paren_Count (Aggr) /= 0 then
|
|
Error_Msg
|
|
("extra parentheses surrounding aggregate not allowed",
|
|
First_Sloc (Aggr));
|
|
return;
|
|
|
|
-- All tests passed, so set rep clause in place
|
|
|
|
else
|
|
Set_Has_Enumeration_Rep_Clause (Enumtype);
|
|
Set_Has_Enumeration_Rep_Clause (Base_Type (Enumtype));
|
|
end if;
|
|
|
|
-- Now we process the aggregate. Note that we don't use the normal
|
|
-- aggregate code for this purpose, because we don't want any of the
|
|
-- normal expansion activities, and a number of special semantic
|
|
-- rules apply (including the component type being any integer type)
|
|
|
|
Elit := First_Literal (Enumtype);
|
|
|
|
-- First the positional entries if any
|
|
|
|
if Present (Expressions (Aggr)) then
|
|
Expr := First (Expressions (Aggr));
|
|
while Present (Expr) loop
|
|
if No (Elit) then
|
|
Error_Msg_N ("too many entries in aggregate", Expr);
|
|
return;
|
|
end if;
|
|
|
|
Val := Static_Integer (Expr);
|
|
|
|
-- Err signals that we found some incorrect entries processing
|
|
-- the list. The final checks for completeness and ordering are
|
|
-- skipped in this case.
|
|
|
|
if Val = No_Uint then
|
|
Err := True;
|
|
elsif Val < Lo or else Hi < Val then
|
|
Error_Msg_N ("value outside permitted range", Expr);
|
|
Err := True;
|
|
end if;
|
|
|
|
Set_Enumeration_Rep (Elit, Val);
|
|
Set_Enumeration_Rep_Expr (Elit, Expr);
|
|
Next (Expr);
|
|
Next (Elit);
|
|
end loop;
|
|
end if;
|
|
|
|
-- Now process the named entries if present
|
|
|
|
if Present (Component_Associations (Aggr)) then
|
|
Assoc := First (Component_Associations (Aggr));
|
|
while Present (Assoc) loop
|
|
Choice := First (Choices (Assoc));
|
|
|
|
if Present (Next (Choice)) then
|
|
Error_Msg_N
|
|
("multiple choice not allowed here", Next (Choice));
|
|
Err := True;
|
|
end if;
|
|
|
|
if Nkind (Choice) = N_Others_Choice then
|
|
Error_Msg_N ("others choice not allowed here", Choice);
|
|
Err := True;
|
|
|
|
elsif Nkind (Choice) = N_Range then
|
|
|
|
-- ??? should allow zero/one element range here
|
|
|
|
Error_Msg_N ("range not allowed here", Choice);
|
|
Err := True;
|
|
|
|
else
|
|
Analyze_And_Resolve (Choice, Enumtype);
|
|
|
|
if Error_Posted (Choice) then
|
|
Err := True;
|
|
end if;
|
|
|
|
if not Err then
|
|
if Is_Entity_Name (Choice)
|
|
and then Is_Type (Entity (Choice))
|
|
then
|
|
Error_Msg_N ("subtype name not allowed here", Choice);
|
|
Err := True;
|
|
|
|
-- ??? should allow static subtype with zero/one entry
|
|
|
|
elsif Etype (Choice) = Base_Type (Enumtype) then
|
|
if not Is_OK_Static_Expression (Choice) then
|
|
Flag_Non_Static_Expr
|
|
("non-static expression used for choice!", Choice);
|
|
Err := True;
|
|
|
|
else
|
|
Elit := Expr_Value_E (Choice);
|
|
|
|
if Present (Enumeration_Rep_Expr (Elit)) then
|
|
Error_Msg_Sloc :=
|
|
Sloc (Enumeration_Rep_Expr (Elit));
|
|
Error_Msg_NE
|
|
("representation for& previously given#",
|
|
Choice, Elit);
|
|
Err := True;
|
|
end if;
|
|
|
|
Set_Enumeration_Rep_Expr (Elit, Expression (Assoc));
|
|
|
|
Expr := Expression (Assoc);
|
|
Val := Static_Integer (Expr);
|
|
|
|
if Val = No_Uint then
|
|
Err := True;
|
|
|
|
elsif Val < Lo or else Hi < Val then
|
|
Error_Msg_N ("value outside permitted range", Expr);
|
|
Err := True;
|
|
end if;
|
|
|
|
Set_Enumeration_Rep (Elit, Val);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
Next (Assoc);
|
|
end loop;
|
|
end if;
|
|
|
|
-- Aggregate is fully processed. Now we check that a full set of
|
|
-- representations was given, and that they are in range and in order.
|
|
-- These checks are only done if no other errors occurred.
|
|
|
|
if not Err then
|
|
Min := No_Uint;
|
|
Max := No_Uint;
|
|
|
|
Elit := First_Literal (Enumtype);
|
|
while Present (Elit) loop
|
|
if No (Enumeration_Rep_Expr (Elit)) then
|
|
Error_Msg_NE ("missing representation for&!", N, Elit);
|
|
|
|
else
|
|
Val := Enumeration_Rep (Elit);
|
|
|
|
if Min = No_Uint then
|
|
Min := Val;
|
|
end if;
|
|
|
|
if Val /= No_Uint then
|
|
if Max /= No_Uint and then Val <= Max then
|
|
Error_Msg_NE
|
|
("enumeration value for& not ordered!",
|
|
Enumeration_Rep_Expr (Elit), Elit);
|
|
end if;
|
|
|
|
Max_Node := Enumeration_Rep_Expr (Elit);
|
|
Max := Val;
|
|
end if;
|
|
|
|
-- If there is at least one literal whose representation is not
|
|
-- equal to the Pos value, then note that this enumeration type
|
|
-- has a non-standard representation.
|
|
|
|
if Val /= Enumeration_Pos (Elit) then
|
|
Set_Has_Non_Standard_Rep (Base_Type (Enumtype));
|
|
end if;
|
|
end if;
|
|
|
|
Next (Elit);
|
|
end loop;
|
|
|
|
-- Now set proper size information
|
|
|
|
declare
|
|
Minsize : Uint := UI_From_Int (Minimum_Size (Enumtype));
|
|
|
|
begin
|
|
if Has_Size_Clause (Enumtype) then
|
|
|
|
-- All OK, if size is OK now
|
|
|
|
if RM_Size (Enumtype) >= Minsize then
|
|
null;
|
|
|
|
else
|
|
-- Try if we can get by with biasing
|
|
|
|
Minsize :=
|
|
UI_From_Int (Minimum_Size (Enumtype, Biased => True));
|
|
|
|
-- Error message if even biasing does not work
|
|
|
|
if RM_Size (Enumtype) < Minsize then
|
|
Error_Msg_Uint_1 := RM_Size (Enumtype);
|
|
Error_Msg_Uint_2 := Max;
|
|
Error_Msg_N
|
|
("previously given size (^) is too small "
|
|
& "for this value (^)", Max_Node);
|
|
|
|
-- If biasing worked, indicate that we now have biased rep
|
|
|
|
else
|
|
Set_Biased
|
|
(Enumtype, Size_Clause (Enumtype), "size clause");
|
|
end if;
|
|
end if;
|
|
|
|
else
|
|
Set_RM_Size (Enumtype, Minsize);
|
|
Set_Enum_Esize (Enumtype);
|
|
end if;
|
|
|
|
Set_RM_Size (Base_Type (Enumtype), RM_Size (Enumtype));
|
|
Set_Esize (Base_Type (Enumtype), Esize (Enumtype));
|
|
Set_Alignment (Base_Type (Enumtype), Alignment (Enumtype));
|
|
end;
|
|
end if;
|
|
|
|
-- We repeat the too late test in case it froze itself
|
|
|
|
if Rep_Item_Too_Late (Enumtype, N) then
|
|
null;
|
|
end if;
|
|
end Analyze_Enumeration_Representation_Clause;
|
|
|
|
----------------------------
|
|
-- Analyze_Free_Statement --
|
|
----------------------------
|
|
|
|
procedure Analyze_Free_Statement (N : Node_Id) is
|
|
begin
|
|
Analyze (Expression (N));
|
|
end Analyze_Free_Statement;
|
|
|
|
---------------------------
|
|
-- Analyze_Freeze_Entity --
|
|
---------------------------
|
|
|
|
procedure Analyze_Freeze_Entity (N : Node_Id) is
|
|
begin
|
|
Freeze_Entity_Checks (N);
|
|
end Analyze_Freeze_Entity;
|
|
|
|
-----------------------------------
|
|
-- Analyze_Freeze_Generic_Entity --
|
|
-----------------------------------
|
|
|
|
procedure Analyze_Freeze_Generic_Entity (N : Node_Id) is
|
|
begin
|
|
Freeze_Entity_Checks (N);
|
|
end Analyze_Freeze_Generic_Entity;
|
|
|
|
------------------------------------------
|
|
-- Analyze_Record_Representation_Clause --
|
|
------------------------------------------
|
|
|
|
-- Note: we check as much as we can here, but we can't do any checks
|
|
-- based on the position values (e.g. overlap checks) until freeze time
|
|
-- because especially in Ada 2005 (machine scalar mode), the processing
|
|
-- for non-standard bit order can substantially change the positions.
|
|
-- See procedure Check_Record_Representation_Clause (called from Freeze)
|
|
-- for the remainder of this processing.
|
|
|
|
procedure Analyze_Record_Representation_Clause (N : Node_Id) is
|
|
Ident : constant Node_Id := Identifier (N);
|
|
Biased : Boolean;
|
|
CC : Node_Id;
|
|
Comp : Entity_Id;
|
|
Fbit : Uint;
|
|
Hbit : Uint := Uint_0;
|
|
Lbit : Uint;
|
|
Ocomp : Entity_Id;
|
|
Posit : Uint;
|
|
Rectype : Entity_Id;
|
|
Recdef : Node_Id;
|
|
|
|
function Is_Inherited (Comp : Entity_Id) return Boolean;
|
|
-- True if Comp is an inherited component in a record extension
|
|
|
|
------------------
|
|
-- Is_Inherited --
|
|
------------------
|
|
|
|
function Is_Inherited (Comp : Entity_Id) return Boolean is
|
|
Comp_Base : Entity_Id;
|
|
|
|
begin
|
|
if Ekind (Rectype) = E_Record_Subtype then
|
|
Comp_Base := Original_Record_Component (Comp);
|
|
else
|
|
Comp_Base := Comp;
|
|
end if;
|
|
|
|
return Comp_Base /= Original_Record_Component (Comp_Base);
|
|
end Is_Inherited;
|
|
|
|
-- Local variables
|
|
|
|
Is_Record_Extension : Boolean;
|
|
-- True if Rectype is a record extension
|
|
|
|
CR_Pragma : Node_Id := Empty;
|
|
-- Points to N_Pragma node if Complete_Representation pragma present
|
|
|
|
-- Start of processing for Analyze_Record_Representation_Clause
|
|
|
|
begin
|
|
if Ignore_Rep_Clauses then
|
|
Kill_Rep_Clause (N);
|
|
return;
|
|
end if;
|
|
|
|
Find_Type (Ident);
|
|
Rectype := Entity (Ident);
|
|
|
|
if Rectype = Any_Type or else Rep_Item_Too_Early (Rectype, N) then
|
|
return;
|
|
else
|
|
Rectype := Underlying_Type (Rectype);
|
|
end if;
|
|
|
|
-- First some basic error checks
|
|
|
|
if not Is_Record_Type (Rectype) then
|
|
Error_Msg_NE
|
|
("record type required, found}", Ident, First_Subtype (Rectype));
|
|
return;
|
|
|
|
elsif Scope (Rectype) /= Current_Scope then
|
|
Error_Msg_N ("type must be declared in this scope", N);
|
|
return;
|
|
|
|
elsif not Is_First_Subtype (Rectype) then
|
|
Error_Msg_N ("cannot give record rep clause for subtype", N);
|
|
return;
|
|
|
|
elsif Has_Record_Rep_Clause (Rectype) then
|
|
Error_Msg_N ("duplicate record rep clause ignored", N);
|
|
return;
|
|
|
|
elsif Rep_Item_Too_Late (Rectype, N) then
|
|
return;
|
|
end if;
|
|
|
|
-- We know we have a first subtype, now possibly go the the anonymous
|
|
-- base type to determine whether Rectype is a record extension.
|
|
|
|
Recdef := Type_Definition (Declaration_Node (Base_Type (Rectype)));
|
|
Is_Record_Extension :=
|
|
Nkind (Recdef) = N_Derived_Type_Definition
|
|
and then Present (Record_Extension_Part (Recdef));
|
|
|
|
if Present (Mod_Clause (N)) then
|
|
declare
|
|
Loc : constant Source_Ptr := Sloc (N);
|
|
M : constant Node_Id := Mod_Clause (N);
|
|
P : constant List_Id := Pragmas_Before (M);
|
|
AtM_Nod : Node_Id;
|
|
|
|
Mod_Val : Uint;
|
|
pragma Warnings (Off, Mod_Val);
|
|
|
|
begin
|
|
Check_Restriction (No_Obsolescent_Features, Mod_Clause (N));
|
|
|
|
if Warn_On_Obsolescent_Feature then
|
|
Error_Msg_N
|
|
("?j?mod clause is an obsolescent feature (RM J.8)", N);
|
|
Error_Msg_N
|
|
("\?j?use alignment attribute definition clause instead", N);
|
|
end if;
|
|
|
|
if Present (P) then
|
|
Analyze_List (P);
|
|
end if;
|
|
|
|
-- In ASIS_Mode mode, expansion is disabled, but we must convert
|
|
-- the Mod clause into an alignment clause anyway, so that the
|
|
-- back-end can compute and back-annotate properly the size and
|
|
-- alignment of types that may include this record.
|
|
|
|
-- This seems dubious, this destroys the source tree in a manner
|
|
-- not detectable by ASIS ???
|
|
|
|
if Operating_Mode = Check_Semantics and then ASIS_Mode then
|
|
AtM_Nod :=
|
|
Make_Attribute_Definition_Clause (Loc,
|
|
Name => New_Occurrence_Of (Base_Type (Rectype), Loc),
|
|
Chars => Name_Alignment,
|
|
Expression => Relocate_Node (Expression (M)));
|
|
|
|
Set_From_At_Mod (AtM_Nod);
|
|
Insert_After (N, AtM_Nod);
|
|
Mod_Val := Get_Alignment_Value (Expression (AtM_Nod));
|
|
Set_Mod_Clause (N, Empty);
|
|
|
|
else
|
|
-- Get the alignment value to perform error checking
|
|
|
|
Mod_Val := Get_Alignment_Value (Expression (M));
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
-- For untagged types, clear any existing component clauses for the
|
|
-- type. If the type is derived, this is what allows us to override
|
|
-- a rep clause for the parent. For type extensions, the representation
|
|
-- of the inherited components is inherited, so we want to keep previous
|
|
-- component clauses for completeness.
|
|
|
|
if not Is_Tagged_Type (Rectype) then
|
|
Comp := First_Component_Or_Discriminant (Rectype);
|
|
while Present (Comp) loop
|
|
Set_Component_Clause (Comp, Empty);
|
|
Next_Component_Or_Discriminant (Comp);
|
|
end loop;
|
|
end if;
|
|
|
|
-- All done if no component clauses
|
|
|
|
CC := First (Component_Clauses (N));
|
|
|
|
if No (CC) then
|
|
return;
|
|
end if;
|
|
|
|
-- A representation like this applies to the base type
|
|
|
|
Set_Has_Record_Rep_Clause (Base_Type (Rectype));
|
|
Set_Has_Non_Standard_Rep (Base_Type (Rectype));
|
|
Set_Has_Specified_Layout (Base_Type (Rectype));
|
|
|
|
-- Process the component clauses
|
|
|
|
while Present (CC) loop
|
|
|
|
-- Pragma
|
|
|
|
if Nkind (CC) = N_Pragma then
|
|
Analyze (CC);
|
|
|
|
-- The only pragma of interest is Complete_Representation
|
|
|
|
if Pragma_Name (CC) = Name_Complete_Representation then
|
|
CR_Pragma := CC;
|
|
end if;
|
|
|
|
-- Processing for real component clause
|
|
|
|
else
|
|
Posit := Static_Integer (Position (CC));
|
|
Fbit := Static_Integer (First_Bit (CC));
|
|
Lbit := Static_Integer (Last_Bit (CC));
|
|
|
|
if Posit /= No_Uint
|
|
and then Fbit /= No_Uint
|
|
and then Lbit /= No_Uint
|
|
then
|
|
if Posit < 0 then
|
|
Error_Msg_N
|
|
("position cannot be negative", Position (CC));
|
|
|
|
elsif Fbit < 0 then
|
|
Error_Msg_N
|
|
("first bit cannot be negative", First_Bit (CC));
|
|
|
|
-- The Last_Bit specified in a component clause must not be
|
|
-- less than the First_Bit minus one (RM-13.5.1(10)).
|
|
|
|
elsif Lbit < Fbit - 1 then
|
|
Error_Msg_N
|
|
("last bit cannot be less than first bit minus one",
|
|
Last_Bit (CC));
|
|
|
|
-- Values look OK, so find the corresponding record component
|
|
-- Even though the syntax allows an attribute reference for
|
|
-- implementation-defined components, GNAT does not allow the
|
|
-- tag to get an explicit position.
|
|
|
|
elsif Nkind (Component_Name (CC)) = N_Attribute_Reference then
|
|
if Attribute_Name (Component_Name (CC)) = Name_Tag then
|
|
Error_Msg_N ("position of tag cannot be specified", CC);
|
|
else
|
|
Error_Msg_N ("illegal component name", CC);
|
|
end if;
|
|
|
|
else
|
|
Comp := First_Entity (Rectype);
|
|
while Present (Comp) loop
|
|
exit when Chars (Comp) = Chars (Component_Name (CC));
|
|
Next_Entity (Comp);
|
|
end loop;
|
|
|
|
if No (Comp) then
|
|
|
|
-- Maybe component of base type that is absent from
|
|
-- statically constrained first subtype.
|
|
|
|
Comp := First_Entity (Base_Type (Rectype));
|
|
while Present (Comp) loop
|
|
exit when Chars (Comp) = Chars (Component_Name (CC));
|
|
Next_Entity (Comp);
|
|
end loop;
|
|
end if;
|
|
|
|
if No (Comp) then
|
|
Error_Msg_N
|
|
("component clause is for non-existent field", CC);
|
|
|
|
-- Ada 2012 (AI05-0026): Any name that denotes a
|
|
-- discriminant of an object of an unchecked union type
|
|
-- shall not occur within a record_representation_clause.
|
|
|
|
-- The general restriction of using record rep clauses on
|
|
-- Unchecked_Union types has now been lifted. Since it is
|
|
-- possible to introduce a record rep clause which mentions
|
|
-- the discriminant of an Unchecked_Union in non-Ada 2012
|
|
-- code, this check is applied to all versions of the
|
|
-- language.
|
|
|
|
elsif Ekind (Comp) = E_Discriminant
|
|
and then Is_Unchecked_Union (Rectype)
|
|
then
|
|
Error_Msg_N
|
|
("cannot reference discriminant of unchecked union",
|
|
Component_Name (CC));
|
|
|
|
elsif Is_Record_Extension and then Is_Inherited (Comp) then
|
|
Error_Msg_NE
|
|
("component clause not allowed for inherited "
|
|
& "component&", CC, Comp);
|
|
|
|
elsif Present (Component_Clause (Comp)) then
|
|
|
|
-- Diagnose duplicate rep clause, or check consistency
|
|
-- if this is an inherited component. In a double fault,
|
|
-- there may be a duplicate inconsistent clause for an
|
|
-- inherited component.
|
|
|
|
if Scope (Original_Record_Component (Comp)) = Rectype
|
|
or else Parent (Component_Clause (Comp)) = N
|
|
then
|
|
Error_Msg_Sloc := Sloc (Component_Clause (Comp));
|
|
Error_Msg_N ("component clause previously given#", CC);
|
|
|
|
else
|
|
declare
|
|
Rep1 : constant Node_Id := Component_Clause (Comp);
|
|
begin
|
|
if Intval (Position (Rep1)) /=
|
|
Intval (Position (CC))
|
|
or else Intval (First_Bit (Rep1)) /=
|
|
Intval (First_Bit (CC))
|
|
or else Intval (Last_Bit (Rep1)) /=
|
|
Intval (Last_Bit (CC))
|
|
then
|
|
Error_Msg_N
|
|
("component clause inconsistent "
|
|
& "with representation of ancestor", CC);
|
|
|
|
elsif Warn_On_Redundant_Constructs then
|
|
Error_Msg_N
|
|
("?r?redundant confirming component clause "
|
|
& "for component!", CC);
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
-- Normal case where this is the first component clause we
|
|
-- have seen for this entity, so set it up properly.
|
|
|
|
else
|
|
-- Make reference for field in record rep clause and set
|
|
-- appropriate entity field in the field identifier.
|
|
|
|
Generate_Reference
|
|
(Comp, Component_Name (CC), Set_Ref => False);
|
|
Set_Entity (Component_Name (CC), Comp);
|
|
|
|
-- Update Fbit and Lbit to the actual bit number
|
|
|
|
Fbit := Fbit + UI_From_Int (SSU) * Posit;
|
|
Lbit := Lbit + UI_From_Int (SSU) * Posit;
|
|
|
|
if Has_Size_Clause (Rectype)
|
|
and then RM_Size (Rectype) <= Lbit
|
|
then
|
|
Error_Msg_N
|
|
("bit number out of range of specified size",
|
|
Last_Bit (CC));
|
|
else
|
|
Set_Component_Clause (Comp, CC);
|
|
Set_Component_Bit_Offset (Comp, Fbit);
|
|
Set_Esize (Comp, 1 + (Lbit - Fbit));
|
|
Set_Normalized_First_Bit (Comp, Fbit mod SSU);
|
|
Set_Normalized_Position (Comp, Fbit / SSU);
|
|
|
|
if Warn_On_Overridden_Size
|
|
and then Has_Size_Clause (Etype (Comp))
|
|
and then RM_Size (Etype (Comp)) /= Esize (Comp)
|
|
then
|
|
Error_Msg_NE
|
|
("?S?component size overrides size clause for&",
|
|
Component_Name (CC), Etype (Comp));
|
|
end if;
|
|
|
|
-- This information is also set in the corresponding
|
|
-- component of the base type, found by accessing the
|
|
-- Original_Record_Component link if it is present.
|
|
|
|
Ocomp := Original_Record_Component (Comp);
|
|
|
|
if Hbit < Lbit then
|
|
Hbit := Lbit;
|
|
end if;
|
|
|
|
Check_Size
|
|
(Component_Name (CC),
|
|
Etype (Comp),
|
|
Esize (Comp),
|
|
Biased);
|
|
|
|
Set_Biased
|
|
(Comp, First_Node (CC), "component clause", Biased);
|
|
|
|
if Present (Ocomp) then
|
|
Set_Component_Clause (Ocomp, CC);
|
|
Set_Component_Bit_Offset (Ocomp, Fbit);
|
|
Set_Normalized_First_Bit (Ocomp, Fbit mod SSU);
|
|
Set_Normalized_Position (Ocomp, Fbit / SSU);
|
|
Set_Esize (Ocomp, 1 + (Lbit - Fbit));
|
|
|
|
Set_Normalized_Position_Max
|
|
(Ocomp, Normalized_Position (Ocomp));
|
|
|
|
-- Note: we don't use Set_Biased here, because we
|
|
-- already gave a warning above if needed, and we
|
|
-- would get a duplicate for the same name here.
|
|
|
|
Set_Has_Biased_Representation
|
|
(Ocomp, Has_Biased_Representation (Comp));
|
|
end if;
|
|
|
|
if Esize (Comp) < 0 then
|
|
Error_Msg_N ("component size is negative", CC);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
Next (CC);
|
|
end loop;
|
|
|
|
-- Check missing components if Complete_Representation pragma appeared
|
|
|
|
if Present (CR_Pragma) then
|
|
Comp := First_Component_Or_Discriminant (Rectype);
|
|
while Present (Comp) loop
|
|
if No (Component_Clause (Comp)) then
|
|
Error_Msg_NE
|
|
("missing component clause for &", CR_Pragma, Comp);
|
|
end if;
|
|
|
|
Next_Component_Or_Discriminant (Comp);
|
|
end loop;
|
|
|
|
-- Give missing components warning if required
|
|
|
|
elsif Warn_On_Unrepped_Components then
|
|
declare
|
|
Num_Repped_Components : Nat := 0;
|
|
Num_Unrepped_Components : Nat := 0;
|
|
|
|
begin
|
|
-- First count number of repped and unrepped components
|
|
|
|
Comp := First_Component_Or_Discriminant (Rectype);
|
|
while Present (Comp) loop
|
|
if Present (Component_Clause (Comp)) then
|
|
Num_Repped_Components := Num_Repped_Components + 1;
|
|
else
|
|
Num_Unrepped_Components := Num_Unrepped_Components + 1;
|
|
end if;
|
|
|
|
Next_Component_Or_Discriminant (Comp);
|
|
end loop;
|
|
|
|
-- We are only interested in the case where there is at least one
|
|
-- unrepped component, and at least half the components have rep
|
|
-- clauses. We figure that if less than half have them, then the
|
|
-- partial rep clause is really intentional. If the component
|
|
-- type has no underlying type set at this point (as for a generic
|
|
-- formal type), we don't know enough to give a warning on the
|
|
-- component.
|
|
|
|
if Num_Unrepped_Components > 0
|
|
and then Num_Unrepped_Components < Num_Repped_Components
|
|
then
|
|
Comp := First_Component_Or_Discriminant (Rectype);
|
|
while Present (Comp) loop
|
|
if No (Component_Clause (Comp))
|
|
and then Comes_From_Source (Comp)
|
|
and then Present (Underlying_Type (Etype (Comp)))
|
|
and then (Is_Scalar_Type (Underlying_Type (Etype (Comp)))
|
|
or else Size_Known_At_Compile_Time
|
|
(Underlying_Type (Etype (Comp))))
|
|
and then not Has_Warnings_Off (Rectype)
|
|
then
|
|
Error_Msg_Sloc := Sloc (Comp);
|
|
Error_Msg_NE
|
|
("?C?no component clause given for & declared #",
|
|
N, Comp);
|
|
end if;
|
|
|
|
Next_Component_Or_Discriminant (Comp);
|
|
end loop;
|
|
end if;
|
|
end;
|
|
end if;
|
|
end Analyze_Record_Representation_Clause;
|
|
|
|
-------------------------------------
|
|
-- Build_Discrete_Static_Predicate --
|
|
-------------------------------------
|
|
|
|
procedure Build_Discrete_Static_Predicate
|
|
(Typ : Entity_Id;
|
|
Expr : Node_Id;
|
|
Nam : Name_Id)
|
|
is
|
|
Loc : constant Source_Ptr := Sloc (Expr);
|
|
|
|
Non_Static : exception;
|
|
-- Raised if something non-static is found
|
|
|
|
Btyp : constant Entity_Id := Base_Type (Typ);
|
|
|
|
BLo : constant Uint := Expr_Value (Type_Low_Bound (Btyp));
|
|
BHi : constant Uint := Expr_Value (Type_High_Bound (Btyp));
|
|
-- Low bound and high bound value of base type of Typ
|
|
|
|
TLo : constant Uint := Expr_Value (Type_Low_Bound (Typ));
|
|
THi : constant Uint := Expr_Value (Type_High_Bound (Typ));
|
|
-- Low bound and high bound values of static subtype Typ
|
|
|
|
type REnt is record
|
|
Lo, Hi : Uint;
|
|
end record;
|
|
-- One entry in a Rlist value, a single REnt (range entry) value denotes
|
|
-- one range from Lo to Hi. To represent a single value range Lo = Hi =
|
|
-- value.
|
|
|
|
type RList is array (Nat range <>) of REnt;
|
|
-- A list of ranges. The ranges are sorted in increasing order, and are
|
|
-- disjoint (there is a gap of at least one value between each range in
|
|
-- the table). A value is in the set of ranges in Rlist if it lies
|
|
-- within one of these ranges.
|
|
|
|
False_Range : constant RList :=
|
|
RList'(1 .. 0 => REnt'(No_Uint, No_Uint));
|
|
-- An empty set of ranges represents a range list that can never be
|
|
-- satisfied, since there are no ranges in which the value could lie,
|
|
-- so it does not lie in any of them. False_Range is a canonical value
|
|
-- for this empty set, but general processing should test for an Rlist
|
|
-- with length zero (see Is_False predicate), since other null ranges
|
|
-- may appear which must be treated as False.
|
|
|
|
True_Range : constant RList := RList'(1 => REnt'(BLo, BHi));
|
|
-- Range representing True, value must be in the base range
|
|
|
|
function "and" (Left : RList; Right : RList) return RList;
|
|
-- And's together two range lists, returning a range list. This is a set
|
|
-- intersection operation.
|
|
|
|
function "or" (Left : RList; Right : RList) return RList;
|
|
-- Or's together two range lists, returning a range list. This is a set
|
|
-- union operation.
|
|
|
|
function "not" (Right : RList) return RList;
|
|
-- Returns complement of a given range list, i.e. a range list
|
|
-- representing all the values in TLo .. THi that are not in the input
|
|
-- operand Right.
|
|
|
|
function Build_Val (V : Uint) return Node_Id;
|
|
-- Return an analyzed N_Identifier node referencing this value, suitable
|
|
-- for use as an entry in the Static_Discrte_Predicate list. This node
|
|
-- is typed with the base type.
|
|
|
|
function Build_Range (Lo : Uint; Hi : Uint) return Node_Id;
|
|
-- Return an analyzed N_Range node referencing this range, suitable for
|
|
-- use as an entry in the Static_Discrete_Predicate list. This node is
|
|
-- typed with the base type.
|
|
|
|
function Get_RList (Exp : Node_Id) return RList;
|
|
-- This is a recursive routine that converts the given expression into a
|
|
-- list of ranges, suitable for use in building the static predicate.
|
|
|
|
function Is_False (R : RList) return Boolean;
|
|
pragma Inline (Is_False);
|
|
-- Returns True if the given range list is empty, and thus represents a
|
|
-- False list of ranges that can never be satisfied.
|
|
|
|
function Is_True (R : RList) return Boolean;
|
|
-- Returns True if R trivially represents the True predicate by having a
|
|
-- single range from BLo to BHi.
|
|
|
|
function Is_Type_Ref (N : Node_Id) return Boolean;
|
|
pragma Inline (Is_Type_Ref);
|
|
-- Returns if True if N is a reference to the type for the predicate in
|
|
-- the expression (i.e. if it is an identifier whose Chars field matches
|
|
-- the Nam given in the call). N must not be parenthesized, if the type
|
|
-- name appears in parens, this routine will return False.
|
|
|
|
function Lo_Val (N : Node_Id) return Uint;
|
|
-- Given an entry from a Static_Discrete_Predicate list that is either
|
|
-- a static expression or static range, gets either the expression value
|
|
-- or the low bound of the range.
|
|
|
|
function Hi_Val (N : Node_Id) return Uint;
|
|
-- Given an entry from a Static_Discrete_Predicate list that is either
|
|
-- a static expression or static range, gets either the expression value
|
|
-- or the high bound of the range.
|
|
|
|
function Membership_Entry (N : Node_Id) return RList;
|
|
-- Given a single membership entry (range, value, or subtype), returns
|
|
-- the corresponding range list. Raises Static_Error if not static.
|
|
|
|
function Membership_Entries (N : Node_Id) return RList;
|
|
-- Given an element on an alternatives list of a membership operation,
|
|
-- returns the range list corresponding to this entry and all following
|
|
-- entries (i.e. returns the "or" of this list of values).
|
|
|
|
function Stat_Pred (Typ : Entity_Id) return RList;
|
|
-- Given a type, if it has a static predicate, then return the predicate
|
|
-- as a range list, otherwise raise Non_Static.
|
|
|
|
-----------
|
|
-- "and" --
|
|
-----------
|
|
|
|
function "and" (Left : RList; Right : RList) return RList is
|
|
FEnt : REnt;
|
|
-- First range of result
|
|
|
|
SLeft : Nat := Left'First;
|
|
-- Start of rest of left entries
|
|
|
|
SRight : Nat := Right'First;
|
|
-- Start of rest of right entries
|
|
|
|
begin
|
|
-- If either range is True, return the other
|
|
|
|
if Is_True (Left) then
|
|
return Right;
|
|
elsif Is_True (Right) then
|
|
return Left;
|
|
end if;
|
|
|
|
-- If either range is False, return False
|
|
|
|
if Is_False (Left) or else Is_False (Right) then
|
|
return False_Range;
|
|
end if;
|
|
|
|
-- Loop to remove entries at start that are disjoint, and thus just
|
|
-- get discarded from the result entirely.
|
|
|
|
loop
|
|
-- If no operands left in either operand, result is false
|
|
|
|
if SLeft > Left'Last or else SRight > Right'Last then
|
|
return False_Range;
|
|
|
|
-- Discard first left operand entry if disjoint with right
|
|
|
|
elsif Left (SLeft).Hi < Right (SRight).Lo then
|
|
SLeft := SLeft + 1;
|
|
|
|
-- Discard first right operand entry if disjoint with left
|
|
|
|
elsif Right (SRight).Hi < Left (SLeft).Lo then
|
|
SRight := SRight + 1;
|
|
|
|
-- Otherwise we have an overlapping entry
|
|
|
|
else
|
|
exit;
|
|
end if;
|
|
end loop;
|
|
|
|
-- Now we have two non-null operands, and first entries overlap. The
|
|
-- first entry in the result will be the overlapping part of these
|
|
-- two entries.
|
|
|
|
FEnt := REnt'(Lo => UI_Max (Left (SLeft).Lo, Right (SRight).Lo),
|
|
Hi => UI_Min (Left (SLeft).Hi, Right (SRight).Hi));
|
|
|
|
-- Now we can remove the entry that ended at a lower value, since its
|
|
-- contribution is entirely contained in Fent.
|
|
|
|
if Left (SLeft).Hi <= Right (SRight).Hi then
|
|
SLeft := SLeft + 1;
|
|
else
|
|
SRight := SRight + 1;
|
|
end if;
|
|
|
|
-- Compute result by concatenating this first entry with the "and" of
|
|
-- the remaining parts of the left and right operands. Note that if
|
|
-- either of these is empty, "and" will yield empty, so that we will
|
|
-- end up with just Fent, which is what we want in that case.
|
|
|
|
return
|
|
FEnt & (Left (SLeft .. Left'Last) and Right (SRight .. Right'Last));
|
|
end "and";
|
|
|
|
-----------
|
|
-- "not" --
|
|
-----------
|
|
|
|
function "not" (Right : RList) return RList is
|
|
begin
|
|
-- Return True if False range
|
|
|
|
if Is_False (Right) then
|
|
return True_Range;
|
|
end if;
|
|
|
|
-- Return False if True range
|
|
|
|
if Is_True (Right) then
|
|
return False_Range;
|
|
end if;
|
|
|
|
-- Here if not trivial case
|
|
|
|
declare
|
|
Result : RList (1 .. Right'Length + 1);
|
|
-- May need one more entry for gap at beginning and end
|
|
|
|
Count : Nat := 0;
|
|
-- Number of entries stored in Result
|
|
|
|
begin
|
|
-- Gap at start
|
|
|
|
if Right (Right'First).Lo > TLo then
|
|
Count := Count + 1;
|
|
Result (Count) := REnt'(TLo, Right (Right'First).Lo - 1);
|
|
end if;
|
|
|
|
-- Gaps between ranges
|
|
|
|
for J in Right'First .. Right'Last - 1 loop
|
|
Count := Count + 1;
|
|
Result (Count) := REnt'(Right (J).Hi + 1, Right (J + 1).Lo - 1);
|
|
end loop;
|
|
|
|
-- Gap at end
|
|
|
|
if Right (Right'Last).Hi < THi then
|
|
Count := Count + 1;
|
|
Result (Count) := REnt'(Right (Right'Last).Hi + 1, THi);
|
|
end if;
|
|
|
|
return Result (1 .. Count);
|
|
end;
|
|
end "not";
|
|
|
|
----------
|
|
-- "or" --
|
|
----------
|
|
|
|
function "or" (Left : RList; Right : RList) return RList is
|
|
FEnt : REnt;
|
|
-- First range of result
|
|
|
|
SLeft : Nat := Left'First;
|
|
-- Start of rest of left entries
|
|
|
|
SRight : Nat := Right'First;
|
|
-- Start of rest of right entries
|
|
|
|
begin
|
|
-- If either range is True, return True
|
|
|
|
if Is_True (Left) or else Is_True (Right) then
|
|
return True_Range;
|
|
end if;
|
|
|
|
-- If either range is False (empty), return the other
|
|
|
|
if Is_False (Left) then
|
|
return Right;
|
|
elsif Is_False (Right) then
|
|
return Left;
|
|
end if;
|
|
|
|
-- Initialize result first entry from left or right operand depending
|
|
-- on which starts with the lower range.
|
|
|
|
if Left (SLeft).Lo < Right (SRight).Lo then
|
|
FEnt := Left (SLeft);
|
|
SLeft := SLeft + 1;
|
|
else
|
|
FEnt := Right (SRight);
|
|
SRight := SRight + 1;
|
|
end if;
|
|
|
|
-- This loop eats ranges from left and right operands that are
|
|
-- contiguous with the first range we are gathering.
|
|
|
|
loop
|
|
-- Eat first entry in left operand if contiguous or overlapped by
|
|
-- gathered first operand of result.
|
|
|
|
if SLeft <= Left'Last
|
|
and then Left (SLeft).Lo <= FEnt.Hi + 1
|
|
then
|
|
FEnt.Hi := UI_Max (FEnt.Hi, Left (SLeft).Hi);
|
|
SLeft := SLeft + 1;
|
|
|
|
-- Eat first entry in right operand if contiguous or overlapped by
|
|
-- gathered right operand of result.
|
|
|
|
elsif SRight <= Right'Last
|
|
and then Right (SRight).Lo <= FEnt.Hi + 1
|
|
then
|
|
FEnt.Hi := UI_Max (FEnt.Hi, Right (SRight).Hi);
|
|
SRight := SRight + 1;
|
|
|
|
-- All done if no more entries to eat
|
|
|
|
else
|
|
exit;
|
|
end if;
|
|
end loop;
|
|
|
|
-- Obtain result as the first entry we just computed, concatenated
|
|
-- to the "or" of the remaining results (if one operand is empty,
|
|
-- this will just concatenate with the other
|
|
|
|
return
|
|
FEnt & (Left (SLeft .. Left'Last) or Right (SRight .. Right'Last));
|
|
end "or";
|
|
|
|
-----------------
|
|
-- Build_Range --
|
|
-----------------
|
|
|
|
function Build_Range (Lo : Uint; Hi : Uint) return Node_Id is
|
|
Result : Node_Id;
|
|
begin
|
|
Result :=
|
|
Make_Range (Loc,
|
|
Low_Bound => Build_Val (Lo),
|
|
High_Bound => Build_Val (Hi));
|
|
Set_Etype (Result, Btyp);
|
|
Set_Analyzed (Result);
|
|
return Result;
|
|
end Build_Range;
|
|
|
|
---------------
|
|
-- Build_Val --
|
|
---------------
|
|
|
|
function Build_Val (V : Uint) return Node_Id is
|
|
Result : Node_Id;
|
|
|
|
begin
|
|
if Is_Enumeration_Type (Typ) then
|
|
Result := Get_Enum_Lit_From_Pos (Typ, V, Loc);
|
|
else
|
|
Result := Make_Integer_Literal (Loc, V);
|
|
end if;
|
|
|
|
Set_Etype (Result, Btyp);
|
|
Set_Is_Static_Expression (Result);
|
|
Set_Analyzed (Result);
|
|
return Result;
|
|
end Build_Val;
|
|
|
|
---------------
|
|
-- Get_RList --
|
|
---------------
|
|
|
|
function Get_RList (Exp : Node_Id) return RList is
|
|
Op : Node_Kind;
|
|
Val : Uint;
|
|
|
|
begin
|
|
-- Static expression can only be true or false
|
|
|
|
if Is_OK_Static_Expression (Exp) then
|
|
if Expr_Value (Exp) = 0 then
|
|
return False_Range;
|
|
else
|
|
return True_Range;
|
|
end if;
|
|
end if;
|
|
|
|
-- Otherwise test node type
|
|
|
|
Op := Nkind (Exp);
|
|
|
|
case Op is
|
|
|
|
-- And
|
|
|
|
when N_Op_And | N_And_Then =>
|
|
return Get_RList (Left_Opnd (Exp))
|
|
and
|
|
Get_RList (Right_Opnd (Exp));
|
|
|
|
-- Or
|
|
|
|
when N_Op_Or | N_Or_Else =>
|
|
return Get_RList (Left_Opnd (Exp))
|
|
or
|
|
Get_RList (Right_Opnd (Exp));
|
|
|
|
-- Not
|
|
|
|
when N_Op_Not =>
|
|
return not Get_RList (Right_Opnd (Exp));
|
|
|
|
-- Comparisons of type with static value
|
|
|
|
when N_Op_Compare =>
|
|
|
|
-- Type is left operand
|
|
|
|
if Is_Type_Ref (Left_Opnd (Exp))
|
|
and then Is_OK_Static_Expression (Right_Opnd (Exp))
|
|
then
|
|
Val := Expr_Value (Right_Opnd (Exp));
|
|
|
|
-- Typ is right operand
|
|
|
|
elsif Is_Type_Ref (Right_Opnd (Exp))
|
|
and then Is_OK_Static_Expression (Left_Opnd (Exp))
|
|
then
|
|
Val := Expr_Value (Left_Opnd (Exp));
|
|
|
|
-- Invert sense of comparison
|
|
|
|
case Op is
|
|
when N_Op_Gt => Op := N_Op_Lt;
|
|
when N_Op_Lt => Op := N_Op_Gt;
|
|
when N_Op_Ge => Op := N_Op_Le;
|
|
when N_Op_Le => Op := N_Op_Ge;
|
|
when others => null;
|
|
end case;
|
|
|
|
-- Other cases are non-static
|
|
|
|
else
|
|
raise Non_Static;
|
|
end if;
|
|
|
|
-- Construct range according to comparison operation
|
|
|
|
case Op is
|
|
when N_Op_Eq =>
|
|
return RList'(1 => REnt'(Val, Val));
|
|
|
|
when N_Op_Ge =>
|
|
return RList'(1 => REnt'(Val, BHi));
|
|
|
|
when N_Op_Gt =>
|
|
return RList'(1 => REnt'(Val + 1, BHi));
|
|
|
|
when N_Op_Le =>
|
|
return RList'(1 => REnt'(BLo, Val));
|
|
|
|
when N_Op_Lt =>
|
|
return RList'(1 => REnt'(BLo, Val - 1));
|
|
|
|
when N_Op_Ne =>
|
|
return RList'(REnt'(BLo, Val - 1), REnt'(Val + 1, BHi));
|
|
|
|
when others =>
|
|
raise Program_Error;
|
|
end case;
|
|
|
|
-- Membership (IN)
|
|
|
|
when N_In =>
|
|
if not Is_Type_Ref (Left_Opnd (Exp)) then
|
|
raise Non_Static;
|
|
end if;
|
|
|
|
if Present (Right_Opnd (Exp)) then
|
|
return Membership_Entry (Right_Opnd (Exp));
|
|
else
|
|
return Membership_Entries (First (Alternatives (Exp)));
|
|
end if;
|
|
|
|
-- Negative membership (NOT IN)
|
|
|
|
when N_Not_In =>
|
|
if not Is_Type_Ref (Left_Opnd (Exp)) then
|
|
raise Non_Static;
|
|
end if;
|
|
|
|
if Present (Right_Opnd (Exp)) then
|
|
return not Membership_Entry (Right_Opnd (Exp));
|
|
else
|
|
return not Membership_Entries (First (Alternatives (Exp)));
|
|
end if;
|
|
|
|
-- Function call, may be call to static predicate
|
|
|
|
when N_Function_Call =>
|
|
if Is_Entity_Name (Name (Exp)) then
|
|
declare
|
|
Ent : constant Entity_Id := Entity (Name (Exp));
|
|
begin
|
|
if Is_Predicate_Function (Ent)
|
|
or else
|
|
Is_Predicate_Function_M (Ent)
|
|
then
|
|
return Stat_Pred (Etype (First_Formal (Ent)));
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
-- Other function call cases are non-static
|
|
|
|
raise Non_Static;
|
|
|
|
-- Qualified expression, dig out the expression
|
|
|
|
when N_Qualified_Expression =>
|
|
return Get_RList (Expression (Exp));
|
|
|
|
when N_Case_Expression =>
|
|
declare
|
|
Alt : Node_Id;
|
|
Choices : List_Id;
|
|
Dep : Node_Id;
|
|
|
|
begin
|
|
if not Is_Entity_Name (Expression (Expr))
|
|
or else Etype (Expression (Expr)) /= Typ
|
|
then
|
|
Error_Msg_N
|
|
("expression must denaote subtype", Expression (Expr));
|
|
return False_Range;
|
|
end if;
|
|
|
|
-- Collect discrete choices in all True alternatives
|
|
|
|
Choices := New_List;
|
|
Alt := First (Alternatives (Exp));
|
|
while Present (Alt) loop
|
|
Dep := Expression (Alt);
|
|
|
|
if not Is_OK_Static_Expression (Dep) then
|
|
raise Non_Static;
|
|
|
|
elsif Is_True (Expr_Value (Dep)) then
|
|
Append_List_To (Choices,
|
|
New_Copy_List (Discrete_Choices (Alt)));
|
|
end if;
|
|
|
|
Next (Alt);
|
|
end loop;
|
|
|
|
return Membership_Entries (First (Choices));
|
|
end;
|
|
|
|
-- Expression with actions: if no actions, dig out expression
|
|
|
|
when N_Expression_With_Actions =>
|
|
if Is_Empty_List (Actions (Exp)) then
|
|
return Get_RList (Expression (Exp));
|
|
else
|
|
raise Non_Static;
|
|
end if;
|
|
|
|
-- Xor operator
|
|
|
|
when N_Op_Xor =>
|
|
return (Get_RList (Left_Opnd (Exp))
|
|
and not Get_RList (Right_Opnd (Exp)))
|
|
or (Get_RList (Right_Opnd (Exp))
|
|
and not Get_RList (Left_Opnd (Exp)));
|
|
|
|
-- Any other node type is non-static
|
|
|
|
when others =>
|
|
raise Non_Static;
|
|
end case;
|
|
end Get_RList;
|
|
|
|
------------
|
|
-- Hi_Val --
|
|
------------
|
|
|
|
function Hi_Val (N : Node_Id) return Uint is
|
|
begin
|
|
if Is_OK_Static_Expression (N) then
|
|
return Expr_Value (N);
|
|
else
|
|
pragma Assert (Nkind (N) = N_Range);
|
|
return Expr_Value (High_Bound (N));
|
|
end if;
|
|
end Hi_Val;
|
|
|
|
--------------
|
|
-- Is_False --
|
|
--------------
|
|
|
|
function Is_False (R : RList) return Boolean is
|
|
begin
|
|
return R'Length = 0;
|
|
end Is_False;
|
|
|
|
-------------
|
|
-- Is_True --
|
|
-------------
|
|
|
|
function Is_True (R : RList) return Boolean is
|
|
begin
|
|
return R'Length = 1
|
|
and then R (R'First).Lo = BLo
|
|
and then R (R'First).Hi = BHi;
|
|
end Is_True;
|
|
|
|
-----------------
|
|
-- Is_Type_Ref --
|
|
-----------------
|
|
|
|
function Is_Type_Ref (N : Node_Id) return Boolean is
|
|
begin
|
|
return Nkind (N) = N_Identifier
|
|
and then Chars (N) = Nam
|
|
and then Paren_Count (N) = 0;
|
|
end Is_Type_Ref;
|
|
|
|
------------
|
|
-- Lo_Val --
|
|
------------
|
|
|
|
function Lo_Val (N : Node_Id) return Uint is
|
|
begin
|
|
if Is_OK_Static_Expression (N) then
|
|
return Expr_Value (N);
|
|
else
|
|
pragma Assert (Nkind (N) = N_Range);
|
|
return Expr_Value (Low_Bound (N));
|
|
end if;
|
|
end Lo_Val;
|
|
|
|
------------------------
|
|
-- Membership_Entries --
|
|
------------------------
|
|
|
|
function Membership_Entries (N : Node_Id) return RList is
|
|
begin
|
|
if No (Next (N)) then
|
|
return Membership_Entry (N);
|
|
else
|
|
return Membership_Entry (N) or Membership_Entries (Next (N));
|
|
end if;
|
|
end Membership_Entries;
|
|
|
|
----------------------
|
|
-- Membership_Entry --
|
|
----------------------
|
|
|
|
function Membership_Entry (N : Node_Id) return RList is
|
|
Val : Uint;
|
|
SLo : Uint;
|
|
SHi : Uint;
|
|
|
|
begin
|
|
-- Range case
|
|
|
|
if Nkind (N) = N_Range then
|
|
if not Is_OK_Static_Expression (Low_Bound (N))
|
|
or else
|
|
not Is_OK_Static_Expression (High_Bound (N))
|
|
then
|
|
raise Non_Static;
|
|
else
|
|
SLo := Expr_Value (Low_Bound (N));
|
|
SHi := Expr_Value (High_Bound (N));
|
|
return RList'(1 => REnt'(SLo, SHi));
|
|
end if;
|
|
|
|
-- Static expression case
|
|
|
|
elsif Is_OK_Static_Expression (N) then
|
|
Val := Expr_Value (N);
|
|
return RList'(1 => REnt'(Val, Val));
|
|
|
|
-- Identifier (other than static expression) case
|
|
|
|
else pragma Assert (Nkind (N) = N_Identifier);
|
|
|
|
-- Type case
|
|
|
|
if Is_Type (Entity (N)) then
|
|
|
|
-- If type has predicates, process them
|
|
|
|
if Has_Predicates (Entity (N)) then
|
|
return Stat_Pred (Entity (N));
|
|
|
|
-- For static subtype without predicates, get range
|
|
|
|
elsif Is_OK_Static_Subtype (Entity (N)) then
|
|
SLo := Expr_Value (Type_Low_Bound (Entity (N)));
|
|
SHi := Expr_Value (Type_High_Bound (Entity (N)));
|
|
return RList'(1 => REnt'(SLo, SHi));
|
|
|
|
-- Any other type makes us non-static
|
|
|
|
else
|
|
raise Non_Static;
|
|
end if;
|
|
|
|
-- Any other kind of identifier in predicate (e.g. a non-static
|
|
-- expression value) means this is not a static predicate.
|
|
|
|
else
|
|
raise Non_Static;
|
|
end if;
|
|
end if;
|
|
end Membership_Entry;
|
|
|
|
---------------
|
|
-- Stat_Pred --
|
|
---------------
|
|
|
|
function Stat_Pred (Typ : Entity_Id) return RList is
|
|
begin
|
|
-- Not static if type does not have static predicates
|
|
|
|
if not Has_Static_Predicate (Typ) then
|
|
raise Non_Static;
|
|
end if;
|
|
|
|
-- Otherwise we convert the predicate list to a range list
|
|
|
|
declare
|
|
Spred : constant List_Id := Static_Discrete_Predicate (Typ);
|
|
Result : RList (1 .. List_Length (Spred));
|
|
P : Node_Id;
|
|
|
|
begin
|
|
P := First (Static_Discrete_Predicate (Typ));
|
|
for J in Result'Range loop
|
|
Result (J) := REnt'(Lo_Val (P), Hi_Val (P));
|
|
Next (P);
|
|
end loop;
|
|
|
|
return Result;
|
|
end;
|
|
end Stat_Pred;
|
|
|
|
-- Start of processing for Build_Discrete_Static_Predicate
|
|
|
|
begin
|
|
-- Analyze the expression to see if it is a static predicate
|
|
|
|
declare
|
|
Ranges : constant RList := Get_RList (Expr);
|
|
-- Range list from expression if it is static
|
|
|
|
Plist : List_Id;
|
|
|
|
begin
|
|
-- Convert range list into a form for the static predicate. In the
|
|
-- Ranges array, we just have raw ranges, these must be converted
|
|
-- to properly typed and analyzed static expressions or range nodes.
|
|
|
|
-- Note: here we limit ranges to the ranges of the subtype, so that
|
|
-- a predicate is always false for values outside the subtype. That
|
|
-- seems fine, such values are invalid anyway, and considering them
|
|
-- to fail the predicate seems allowed and friendly, and furthermore
|
|
-- simplifies processing for case statements and loops.
|
|
|
|
Plist := New_List;
|
|
|
|
for J in Ranges'Range loop
|
|
declare
|
|
Lo : Uint := Ranges (J).Lo;
|
|
Hi : Uint := Ranges (J).Hi;
|
|
|
|
begin
|
|
-- Ignore completely out of range entry
|
|
|
|
if Hi < TLo or else Lo > THi then
|
|
null;
|
|
|
|
-- Otherwise process entry
|
|
|
|
else
|
|
-- Adjust out of range value to subtype range
|
|
|
|
if Lo < TLo then
|
|
Lo := TLo;
|
|
end if;
|
|
|
|
if Hi > THi then
|
|
Hi := THi;
|
|
end if;
|
|
|
|
-- Convert range into required form
|
|
|
|
Append_To (Plist, Build_Range (Lo, Hi));
|
|
end if;
|
|
end;
|
|
end loop;
|
|
|
|
-- Processing was successful and all entries were static, so now we
|
|
-- can store the result as the predicate list.
|
|
|
|
Set_Static_Discrete_Predicate (Typ, Plist);
|
|
|
|
-- The processing for static predicates put the expression into
|
|
-- canonical form as a series of ranges. It also eliminated
|
|
-- duplicates and collapsed and combined ranges. We might as well
|
|
-- replace the alternatives list of the right operand of the
|
|
-- membership test with the static predicate list, which will
|
|
-- usually be more efficient.
|
|
|
|
declare
|
|
New_Alts : constant List_Id := New_List;
|
|
Old_Node : Node_Id;
|
|
New_Node : Node_Id;
|
|
|
|
begin
|
|
Old_Node := First (Plist);
|
|
while Present (Old_Node) loop
|
|
New_Node := New_Copy (Old_Node);
|
|
|
|
if Nkind (New_Node) = N_Range then
|
|
Set_Low_Bound (New_Node, New_Copy (Low_Bound (Old_Node)));
|
|
Set_High_Bound (New_Node, New_Copy (High_Bound (Old_Node)));
|
|
end if;
|
|
|
|
Append_To (New_Alts, New_Node);
|
|
Next (Old_Node);
|
|
end loop;
|
|
|
|
-- If empty list, replace by False
|
|
|
|
if Is_Empty_List (New_Alts) then
|
|
Rewrite (Expr, New_Occurrence_Of (Standard_False, Loc));
|
|
|
|
-- Else replace by set membership test
|
|
|
|
else
|
|
Rewrite (Expr,
|
|
Make_In (Loc,
|
|
Left_Opnd => Make_Identifier (Loc, Nam),
|
|
Right_Opnd => Empty,
|
|
Alternatives => New_Alts));
|
|
|
|
-- Resolve new expression in function context
|
|
|
|
Install_Formals (Predicate_Function (Typ));
|
|
Push_Scope (Predicate_Function (Typ));
|
|
Analyze_And_Resolve (Expr, Standard_Boolean);
|
|
Pop_Scope;
|
|
end if;
|
|
end;
|
|
end;
|
|
|
|
-- If non-static, return doing nothing
|
|
|
|
exception
|
|
when Non_Static =>
|
|
return;
|
|
end Build_Discrete_Static_Predicate;
|
|
|
|
-------------------------------------------
|
|
-- Build_Invariant_Procedure_Declaration --
|
|
-------------------------------------------
|
|
|
|
function Build_Invariant_Procedure_Declaration
|
|
(Typ : Entity_Id) return Node_Id
|
|
is
|
|
Loc : constant Source_Ptr := Sloc (Typ);
|
|
Object_Entity : constant Entity_Id :=
|
|
Make_Defining_Identifier (Loc, New_Internal_Name ('I'));
|
|
Spec : Node_Id;
|
|
SId : Entity_Id;
|
|
|
|
begin
|
|
Set_Etype (Object_Entity, Typ);
|
|
|
|
-- Check for duplicate definiations.
|
|
|
|
if Has_Invariants (Typ) and then Present (Invariant_Procedure (Typ)) then
|
|
return Empty;
|
|
end if;
|
|
|
|
SId :=
|
|
Make_Defining_Identifier (Loc,
|
|
Chars => New_External_Name (Chars (Typ), "Invariant"));
|
|
Set_Has_Invariants (Typ);
|
|
Set_Ekind (SId, E_Procedure);
|
|
Set_Is_Invariant_Procedure (SId);
|
|
Set_Invariant_Procedure (Typ, SId);
|
|
|
|
Spec :=
|
|
Make_Procedure_Specification (Loc,
|
|
Defining_Unit_Name => SId,
|
|
Parameter_Specifications => New_List (
|
|
Make_Parameter_Specification (Loc,
|
|
Defining_Identifier => Object_Entity,
|
|
Parameter_Type => New_Occurrence_Of (Typ, Loc))));
|
|
|
|
return Make_Subprogram_Declaration (Loc, Specification => Spec);
|
|
end Build_Invariant_Procedure_Declaration;
|
|
|
|
-------------------------------
|
|
-- Build_Invariant_Procedure --
|
|
-------------------------------
|
|
|
|
-- The procedure that is constructed here has the form
|
|
|
|
-- procedure typInvariant (Ixxx : typ) is
|
|
-- begin
|
|
-- pragma Check (Invariant, exp, "failed invariant from xxx");
|
|
-- pragma Check (Invariant, exp, "failed invariant from xxx");
|
|
-- ...
|
|
-- pragma Check (Invariant, exp, "failed inherited invariant from xxx");
|
|
-- ...
|
|
-- end typInvariant;
|
|
|
|
procedure Build_Invariant_Procedure (Typ : Entity_Id; N : Node_Id) is
|
|
Loc : constant Source_Ptr := Sloc (Typ);
|
|
Stmts : List_Id;
|
|
Spec : Node_Id;
|
|
SId : Entity_Id;
|
|
PDecl : Node_Id;
|
|
PBody : Node_Id;
|
|
|
|
Nam : Name_Id;
|
|
-- Name for Check pragma, usually Invariant, but might be Type_Invariant
|
|
-- if we come from a Type_Invariant aspect, we make sure to build the
|
|
-- Check pragma with the right name, so that Check_Policy works right.
|
|
|
|
Visible_Decls : constant List_Id := Visible_Declarations (N);
|
|
Private_Decls : constant List_Id := Private_Declarations (N);
|
|
|
|
procedure Add_Invariants (T : Entity_Id; Inherit : Boolean);
|
|
-- Appends statements to Stmts for any invariants in the rep item chain
|
|
-- of the given type. If Inherit is False, then we only process entries
|
|
-- on the chain for the type Typ. If Inherit is True, then we ignore any
|
|
-- Invariant aspects, but we process all Invariant'Class aspects, adding
|
|
-- "inherited" to the exception message and generating an informational
|
|
-- message about the inheritance of an invariant.
|
|
|
|
Object_Name : Name_Id;
|
|
-- Name for argument of invariant procedure
|
|
|
|
Object_Entity : Node_Id;
|
|
-- The entity of the formal for the procedure
|
|
|
|
--------------------
|
|
-- Add_Invariants --
|
|
--------------------
|
|
|
|
procedure Add_Invariants (T : Entity_Id; Inherit : Boolean) is
|
|
Ritem : Node_Id;
|
|
Arg1 : Node_Id;
|
|
Arg2 : Node_Id;
|
|
Arg3 : Node_Id;
|
|
Exp : Node_Id;
|
|
Loc : Source_Ptr;
|
|
Assoc : List_Id;
|
|
Str : String_Id;
|
|
|
|
procedure Replace_Type_Reference (N : Node_Id);
|
|
-- Replace a single occurrence N of the subtype name with a reference
|
|
-- to the formal of the predicate function. N can be an identifier
|
|
-- referencing the subtype, or a selected component, representing an
|
|
-- appropriately qualified occurrence of the subtype name.
|
|
|
|
procedure Replace_Type_References is
|
|
new Replace_Type_References_Generic (Replace_Type_Reference);
|
|
-- Traverse an expression replacing all occurrences of the subtype
|
|
-- name with appropriate references to the object that is the formal
|
|
-- parameter of the predicate function. Note that we must ensure
|
|
-- that the type and entity information is properly set in the
|
|
-- replacement node, since we will do a Preanalyze call of this
|
|
-- expression without proper visibility of the procedure argument.
|
|
|
|
----------------------------
|
|
-- Replace_Type_Reference --
|
|
----------------------------
|
|
|
|
-- Note: See comments in Add_Predicates.Replace_Type_Reference
|
|
-- regarding handling of Sloc and Comes_From_Source.
|
|
|
|
procedure Replace_Type_Reference (N : Node_Id) is
|
|
begin
|
|
|
|
-- Add semantic information to node to be rewritten, for ASIS
|
|
-- navigation needs.
|
|
|
|
if Nkind (N) = N_Identifier then
|
|
Set_Entity (N, T);
|
|
Set_Etype (N, T);
|
|
|
|
elsif Nkind (N) = N_Selected_Component then
|
|
Analyze (Prefix (N));
|
|
Set_Entity (Selector_Name (N), T);
|
|
Set_Etype (Selector_Name (N), T);
|
|
end if;
|
|
|
|
-- Invariant'Class, replace with T'Class (obj)
|
|
|
|
if Class_Present (Ritem) then
|
|
Rewrite (N,
|
|
Make_Type_Conversion (Sloc (N),
|
|
Subtype_Mark =>
|
|
Make_Attribute_Reference (Sloc (N),
|
|
Prefix => New_Occurrence_Of (T, Sloc (N)),
|
|
Attribute_Name => Name_Class),
|
|
Expression => Make_Identifier (Sloc (N), Object_Name)));
|
|
|
|
Set_Entity (Expression (N), Object_Entity);
|
|
Set_Etype (Expression (N), Typ);
|
|
|
|
-- Invariant, replace with obj
|
|
|
|
else
|
|
Rewrite (N, Make_Identifier (Sloc (N), Object_Name));
|
|
Set_Entity (N, Object_Entity);
|
|
Set_Etype (N, Typ);
|
|
end if;
|
|
|
|
Set_Comes_From_Source (N, True);
|
|
end Replace_Type_Reference;
|
|
|
|
-- Start of processing for Add_Invariants
|
|
|
|
begin
|
|
Ritem := First_Rep_Item (T);
|
|
while Present (Ritem) loop
|
|
if Nkind (Ritem) = N_Pragma
|
|
and then Pragma_Name (Ritem) = Name_Invariant
|
|
then
|
|
Arg1 := First (Pragma_Argument_Associations (Ritem));
|
|
Arg2 := Next (Arg1);
|
|
Arg3 := Next (Arg2);
|
|
|
|
Arg1 := Get_Pragma_Arg (Arg1);
|
|
Arg2 := Get_Pragma_Arg (Arg2);
|
|
|
|
-- For Inherit case, ignore Invariant, process only Class case
|
|
|
|
if Inherit then
|
|
if not Class_Present (Ritem) then
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- For Inherit false, process only item for right type
|
|
|
|
else
|
|
if Entity (Arg1) /= Typ then
|
|
goto Continue;
|
|
end if;
|
|
end if;
|
|
|
|
if No (Stmts) then
|
|
Stmts := Empty_List;
|
|
end if;
|
|
|
|
Exp := New_Copy_Tree (Arg2);
|
|
|
|
-- Preserve sloc of original pragma Invariant
|
|
|
|
Loc := Sloc (Ritem);
|
|
|
|
-- We need to replace any occurrences of the name of the type
|
|
-- with references to the object, converted to type'Class in
|
|
-- the case of Invariant'Class aspects.
|
|
|
|
Replace_Type_References (Exp, T);
|
|
|
|
-- If this invariant comes from an aspect, find the aspect
|
|
-- specification, and replace the saved expression because
|
|
-- we need the subtype references replaced for the calls to
|
|
-- Preanalyze_Spec_Expressin in Check_Aspect_At_Freeze_Point
|
|
-- and Check_Aspect_At_End_Of_Declarations.
|
|
|
|
if From_Aspect_Specification (Ritem) then
|
|
declare
|
|
Aitem : Node_Id;
|
|
|
|
begin
|
|
-- Loop to find corresponding aspect, note that this
|
|
-- must be present given the pragma is marked delayed.
|
|
|
|
-- Note: in practice Next_Rep_Item (Ritem) is Empty so
|
|
-- this loop does nothing. Furthermore, why isn't this
|
|
-- simply Corresponding_Aspect ???
|
|
|
|
Aitem := Next_Rep_Item (Ritem);
|
|
while Present (Aitem) loop
|
|
if Nkind (Aitem) = N_Aspect_Specification
|
|
and then Aspect_Rep_Item (Aitem) = Ritem
|
|
then
|
|
Set_Entity
|
|
(Identifier (Aitem), New_Copy_Tree (Exp));
|
|
exit;
|
|
end if;
|
|
|
|
Aitem := Next_Rep_Item (Aitem);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
|
|
-- Now we need to preanalyze the expression to properly capture
|
|
-- the visibility in the visible part. The expression will not
|
|
-- be analyzed for real until the body is analyzed, but that is
|
|
-- at the end of the private part and has the wrong visibility.
|
|
|
|
Set_Parent (Exp, N);
|
|
Preanalyze_Assert_Expression (Exp, Standard_Boolean);
|
|
|
|
-- In ASIS mode, even if assertions are not enabled, we must
|
|
-- analyze the original expression in the aspect specification
|
|
-- because it is part of the original tree.
|
|
|
|
if ASIS_Mode and then From_Aspect_Specification (Ritem) then
|
|
declare
|
|
Inv : constant Node_Id :=
|
|
Expression (Corresponding_Aspect (Ritem));
|
|
begin
|
|
Replace_Type_References (Inv, T);
|
|
Preanalyze_Assert_Expression (Inv, Standard_Boolean);
|
|
end;
|
|
end if;
|
|
|
|
-- Get name to be used for Check pragma
|
|
|
|
if not From_Aspect_Specification (Ritem) then
|
|
Nam := Name_Invariant;
|
|
else
|
|
Nam := Chars (Identifier (Corresponding_Aspect (Ritem)));
|
|
end if;
|
|
|
|
-- Build first two arguments for Check pragma
|
|
|
|
Assoc :=
|
|
New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Make_Identifier (Loc, Chars => Nam)),
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Exp));
|
|
|
|
-- Add message if present in Invariant pragma
|
|
|
|
if Present (Arg3) then
|
|
Str := Strval (Get_Pragma_Arg (Arg3));
|
|
|
|
-- If inherited case, and message starts "failed invariant",
|
|
-- change it to be "failed inherited invariant".
|
|
|
|
if Inherit then
|
|
String_To_Name_Buffer (Str);
|
|
|
|
if Name_Buffer (1 .. 16) = "failed invariant" then
|
|
Insert_Str_In_Name_Buffer ("inherited ", 8);
|
|
Str := String_From_Name_Buffer;
|
|
end if;
|
|
end if;
|
|
|
|
Append_To (Assoc,
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Make_String_Literal (Loc, Str)));
|
|
end if;
|
|
|
|
-- Add Check pragma to list of statements
|
|
|
|
Append_To (Stmts,
|
|
Make_Pragma (Loc,
|
|
Pragma_Identifier =>
|
|
Make_Identifier (Loc, Name_Check),
|
|
Pragma_Argument_Associations => Assoc));
|
|
|
|
-- If Inherited case and option enabled, output info msg. Note
|
|
-- that we know this is a case of Invariant'Class.
|
|
|
|
if Inherit and Opt.List_Inherited_Aspects then
|
|
Error_Msg_Sloc := Sloc (Ritem);
|
|
Error_Msg_N
|
|
("info: & inherits `Invariant''Class` aspect from #?L?",
|
|
Typ);
|
|
end if;
|
|
end if;
|
|
|
|
<<Continue>>
|
|
Next_Rep_Item (Ritem);
|
|
end loop;
|
|
end Add_Invariants;
|
|
|
|
-- Start of processing for Build_Invariant_Procedure
|
|
|
|
begin
|
|
Stmts := No_List;
|
|
PDecl := Empty;
|
|
PBody := Empty;
|
|
SId := Empty;
|
|
|
|
-- If the aspect specification exists for some view of the type, the
|
|
-- declaration for the procedure has been created.
|
|
|
|
if Has_Invariants (Typ) then
|
|
SId := Invariant_Procedure (Typ);
|
|
end if;
|
|
|
|
if Present (SId) then
|
|
PDecl := Unit_Declaration_Node (SId);
|
|
else
|
|
PDecl := Build_Invariant_Procedure_Declaration (Typ);
|
|
end if;
|
|
|
|
-- Recover formal of procedure, for use in the calls to invariant
|
|
-- functions (including inherited ones).
|
|
|
|
Object_Entity :=
|
|
Defining_Identifier
|
|
(First (Parameter_Specifications (Specification (PDecl))));
|
|
Object_Name := Chars (Object_Entity);
|
|
|
|
-- Add invariants for the current type
|
|
|
|
Add_Invariants (Typ, Inherit => False);
|
|
|
|
-- Add invariants for parent types
|
|
|
|
declare
|
|
Current_Typ : Entity_Id;
|
|
Parent_Typ : Entity_Id;
|
|
|
|
begin
|
|
Current_Typ := Typ;
|
|
loop
|
|
Parent_Typ := Etype (Current_Typ);
|
|
|
|
if Is_Private_Type (Parent_Typ)
|
|
and then Present (Full_View (Base_Type (Parent_Typ)))
|
|
then
|
|
Parent_Typ := Full_View (Base_Type (Parent_Typ));
|
|
end if;
|
|
|
|
exit when Parent_Typ = Current_Typ;
|
|
|
|
Current_Typ := Parent_Typ;
|
|
Add_Invariants (Current_Typ, Inherit => True);
|
|
end loop;
|
|
end;
|
|
|
|
-- Build the procedure if we generated at least one Check pragma
|
|
|
|
if Stmts /= No_List then
|
|
Spec := Copy_Separate_Tree (Specification (PDecl));
|
|
|
|
PBody :=
|
|
Make_Subprogram_Body (Loc,
|
|
Specification => Spec,
|
|
Declarations => Empty_List,
|
|
Handled_Statement_Sequence =>
|
|
Make_Handled_Sequence_Of_Statements (Loc,
|
|
Statements => Stmts));
|
|
|
|
-- Insert procedure declaration and spec at the appropriate points.
|
|
-- If declaration is already analyzed, it was processed by the
|
|
-- generated pragma.
|
|
|
|
if Present (Private_Decls) then
|
|
|
|
-- The spec goes at the end of visible declarations, but they have
|
|
-- already been analyzed, so we need to explicitly do the analyze.
|
|
|
|
if not Analyzed (PDecl) then
|
|
Append_To (Visible_Decls, PDecl);
|
|
Analyze (PDecl);
|
|
end if;
|
|
|
|
-- The body goes at the end of the private declarations, which we
|
|
-- have not analyzed yet, so we do not need to perform an explicit
|
|
-- analyze call. We skip this if there are no private declarations
|
|
-- (this is an error that will be caught elsewhere);
|
|
|
|
Append_To (Private_Decls, PBody);
|
|
|
|
-- If the invariant appears on the full view of a type, the
|
|
-- analysis of the private part is complete, and we must
|
|
-- analyze the new body explicitly.
|
|
|
|
if In_Private_Part (Current_Scope) then
|
|
Analyze (PBody);
|
|
end if;
|
|
|
|
-- If there are no private declarations this may be an error that
|
|
-- will be diagnosed elsewhere. However, if this is a non-private
|
|
-- type that inherits invariants, it needs no completion and there
|
|
-- may be no private part. In this case insert invariant procedure
|
|
-- at end of current declarative list, and analyze at once, given
|
|
-- that the type is about to be frozen.
|
|
|
|
elsif not Is_Private_Type (Typ) then
|
|
Append_To (Visible_Decls, PDecl);
|
|
Append_To (Visible_Decls, PBody);
|
|
Analyze (PDecl);
|
|
Analyze (PBody);
|
|
end if;
|
|
end if;
|
|
end Build_Invariant_Procedure;
|
|
|
|
-------------------------------
|
|
-- Build_Predicate_Functions --
|
|
-------------------------------
|
|
|
|
-- The procedures that are constructed here have the form:
|
|
|
|
-- function typPredicate (Ixxx : typ) return Boolean is
|
|
-- begin
|
|
-- return
|
|
-- exp1 and then exp2 and then ...
|
|
-- and then typ1Predicate (typ1 (Ixxx))
|
|
-- and then typ2Predicate (typ2 (Ixxx))
|
|
-- and then ...;
|
|
-- end typPredicate;
|
|
|
|
-- Here exp1, and exp2 are expressions from Predicate pragmas. Note that
|
|
-- this is the point at which these expressions get analyzed, providing the
|
|
-- required delay, and typ1, typ2, are entities from which predicates are
|
|
-- inherited. Note that we do NOT generate Check pragmas, that's because we
|
|
-- use this function even if checks are off, e.g. for membership tests.
|
|
|
|
-- If the expression has at least one Raise_Expression, then we also build
|
|
-- the typPredicateM version of the function, in which any occurrence of a
|
|
-- Raise_Expression is converted to "return False".
|
|
|
|
procedure Build_Predicate_Functions (Typ : Entity_Id; N : Node_Id) is
|
|
Loc : constant Source_Ptr := Sloc (Typ);
|
|
|
|
Expr : Node_Id;
|
|
-- This is the expression for the result of the function. It is
|
|
-- is build by connecting the component predicates with AND THEN.
|
|
|
|
Expr_M : Node_Id;
|
|
-- This is the corresponding return expression for the Predicate_M
|
|
-- function. It differs in that raise expressions are marked for
|
|
-- special expansion (see Process_REs).
|
|
|
|
Object_Name : constant Name_Id := New_Internal_Name ('I');
|
|
-- Name for argument of Predicate procedure. Note that we use the same
|
|
-- name for both predicate functions. That way the reference within the
|
|
-- predicate expression is the same in both functions.
|
|
|
|
Object_Entity : constant Entity_Id :=
|
|
Make_Defining_Identifier (Loc, Chars => Object_Name);
|
|
-- Entity for argument of Predicate procedure
|
|
|
|
Object_Entity_M : constant Entity_Id :=
|
|
Make_Defining_Identifier (Loc, Chars => Object_Name);
|
|
-- Entity for argument of Predicate_M procedure
|
|
|
|
Raise_Expression_Present : Boolean := False;
|
|
-- Set True if Expr has at least one Raise_Expression
|
|
|
|
procedure Add_Call (T : Entity_Id);
|
|
-- Includes a call to the predicate function for type T in Expr if T
|
|
-- has predicates and Predicate_Function (T) is non-empty.
|
|
|
|
procedure Add_Predicates;
|
|
-- Appends expressions for any Predicate pragmas in the rep item chain
|
|
-- Typ to Expr. Note that we look only at items for this exact entity.
|
|
-- Inheritance of predicates for the parent type is done by calling the
|
|
-- Predicate_Function of the parent type, using Add_Call above.
|
|
|
|
function Test_RE (N : Node_Id) return Traverse_Result;
|
|
-- Used in Test_REs, tests one node for being a raise expression, and if
|
|
-- so sets Raise_Expression_Present True.
|
|
|
|
procedure Test_REs is new Traverse_Proc (Test_RE);
|
|
-- Tests to see if Expr contains any raise expressions
|
|
|
|
function Process_RE (N : Node_Id) return Traverse_Result;
|
|
-- Used in Process REs, tests if node N is a raise expression, and if
|
|
-- so, marks it to be converted to return False.
|
|
|
|
procedure Process_REs is new Traverse_Proc (Process_RE);
|
|
-- Marks any raise expressions in Expr_M to return False
|
|
|
|
--------------
|
|
-- Add_Call --
|
|
--------------
|
|
|
|
procedure Add_Call (T : Entity_Id) is
|
|
Exp : Node_Id;
|
|
|
|
begin
|
|
if Present (T) and then Present (Predicate_Function (T)) then
|
|
Set_Has_Predicates (Typ);
|
|
|
|
-- Build the call to the predicate function of T
|
|
|
|
Exp :=
|
|
Make_Predicate_Call
|
|
(T, Convert_To (T, Make_Identifier (Loc, Object_Name)));
|
|
|
|
-- Add call to evolving expression, using AND THEN if needed
|
|
|
|
if No (Expr) then
|
|
Expr := Exp;
|
|
|
|
else
|
|
Expr :=
|
|
Make_And_Then (Sloc (Expr),
|
|
Left_Opnd => Relocate_Node (Expr),
|
|
Right_Opnd => Exp);
|
|
end if;
|
|
|
|
-- Output info message on inheritance if required. Note we do not
|
|
-- give this information for generic actual types, since it is
|
|
-- unwelcome noise in that case in instantiations. We also
|
|
-- generally suppress the message in instantiations, and also
|
|
-- if it involves internal names.
|
|
|
|
if Opt.List_Inherited_Aspects
|
|
and then not Is_Generic_Actual_Type (Typ)
|
|
and then Instantiation_Depth (Sloc (Typ)) = 0
|
|
and then not Is_Internal_Name (Chars (T))
|
|
and then not Is_Internal_Name (Chars (Typ))
|
|
then
|
|
Error_Msg_Sloc := Sloc (Predicate_Function (T));
|
|
Error_Msg_Node_2 := T;
|
|
Error_Msg_N ("info: & inherits predicate from & #?L?", Typ);
|
|
end if;
|
|
end if;
|
|
end Add_Call;
|
|
|
|
--------------------
|
|
-- Add_Predicates --
|
|
--------------------
|
|
|
|
procedure Add_Predicates is
|
|
Ritem : Node_Id;
|
|
Arg1 : Node_Id;
|
|
Arg2 : Node_Id;
|
|
|
|
procedure Replace_Type_Reference (N : Node_Id);
|
|
-- Replace a single occurrence N of the subtype name with a reference
|
|
-- to the formal of the predicate function. N can be an identifier
|
|
-- referencing the subtype, or a selected component, representing an
|
|
-- appropriately qualified occurrence of the subtype name.
|
|
|
|
procedure Replace_Type_References is
|
|
new Replace_Type_References_Generic (Replace_Type_Reference);
|
|
-- Traverse an expression changing every occurrence of an identifier
|
|
-- whose name matches the name of the subtype with a reference to
|
|
-- the formal parameter of the predicate function.
|
|
|
|
----------------------------
|
|
-- Replace_Type_Reference --
|
|
----------------------------
|
|
|
|
procedure Replace_Type_Reference (N : Node_Id) is
|
|
begin
|
|
Rewrite (N, Make_Identifier (Sloc (N), Object_Name));
|
|
-- Use the Sloc of the usage name, not the defining name
|
|
|
|
Set_Etype (N, Typ);
|
|
Set_Entity (N, Object_Entity);
|
|
|
|
-- We want to treat the node as if it comes from source, so that
|
|
-- ASIS will not ignore it
|
|
|
|
Set_Comes_From_Source (N, True);
|
|
end Replace_Type_Reference;
|
|
|
|
-- Start of processing for Add_Predicates
|
|
|
|
begin
|
|
Ritem := First_Rep_Item (Typ);
|
|
while Present (Ritem) loop
|
|
if Nkind (Ritem) = N_Pragma
|
|
and then Pragma_Name (Ritem) = Name_Predicate
|
|
then
|
|
-- Acquire arguments
|
|
|
|
Arg1 := First (Pragma_Argument_Associations (Ritem));
|
|
Arg2 := Next (Arg1);
|
|
|
|
Arg1 := Get_Pragma_Arg (Arg1);
|
|
Arg2 := Get_Pragma_Arg (Arg2);
|
|
|
|
-- See if this predicate pragma is for the current type or for
|
|
-- its full view. A predicate on a private completion is placed
|
|
-- on the partial view beause this is the visible entity that
|
|
-- is frozen.
|
|
|
|
if Entity (Arg1) = Typ
|
|
or else Full_View (Entity (Arg1)) = Typ
|
|
then
|
|
-- We have a match, this entry is for our subtype
|
|
|
|
-- We need to replace any occurrences of the name of the
|
|
-- type with references to the object.
|
|
|
|
Replace_Type_References (Arg2, Typ);
|
|
|
|
-- If this predicate comes from an aspect, find the aspect
|
|
-- specification, and replace the saved expression because
|
|
-- we need the subtype references replaced for the calls to
|
|
-- Preanalyze_Spec_Expressin in Check_Aspect_At_Freeze_Point
|
|
-- and Check_Aspect_At_End_Of_Declarations.
|
|
|
|
if From_Aspect_Specification (Ritem) then
|
|
declare
|
|
Aitem : Node_Id;
|
|
|
|
begin
|
|
-- Loop to find corresponding aspect, note that this
|
|
-- must be present given the pragma is marked delayed.
|
|
|
|
Aitem := Next_Rep_Item (Ritem);
|
|
loop
|
|
if Nkind (Aitem) = N_Aspect_Specification
|
|
and then Aspect_Rep_Item (Aitem) = Ritem
|
|
then
|
|
Set_Entity
|
|
(Identifier (Aitem), New_Copy_Tree (Arg2));
|
|
exit;
|
|
end if;
|
|
|
|
Aitem := Next_Rep_Item (Aitem);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
|
|
-- Now we can add the expression
|
|
|
|
if No (Expr) then
|
|
Expr := Relocate_Node (Arg2);
|
|
|
|
-- There already was a predicate, so add to it
|
|
|
|
else
|
|
Expr :=
|
|
Make_And_Then (Loc,
|
|
Left_Opnd => Relocate_Node (Expr),
|
|
Right_Opnd => Relocate_Node (Arg2));
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
Next_Rep_Item (Ritem);
|
|
end loop;
|
|
end Add_Predicates;
|
|
|
|
----------------
|
|
-- Process_RE --
|
|
----------------
|
|
|
|
function Process_RE (N : Node_Id) return Traverse_Result is
|
|
begin
|
|
if Nkind (N) = N_Raise_Expression then
|
|
Set_Convert_To_Return_False (N);
|
|
return Skip;
|
|
else
|
|
return OK;
|
|
end if;
|
|
end Process_RE;
|
|
|
|
-------------
|
|
-- Test_RE --
|
|
-------------
|
|
|
|
function Test_RE (N : Node_Id) return Traverse_Result is
|
|
begin
|
|
if Nkind (N) = N_Raise_Expression then
|
|
Raise_Expression_Present := True;
|
|
return Abandon;
|
|
else
|
|
return OK;
|
|
end if;
|
|
end Test_RE;
|
|
|
|
-- Start of processing for Build_Predicate_Functions
|
|
|
|
begin
|
|
-- Return if already built or if type does not have predicates
|
|
|
|
if not Has_Predicates (Typ)
|
|
or else Present (Predicate_Function (Typ))
|
|
then
|
|
return;
|
|
end if;
|
|
|
|
-- Prepare to construct predicate expression
|
|
|
|
Expr := Empty;
|
|
|
|
-- Add Predicates for the current type
|
|
|
|
Add_Predicates;
|
|
|
|
-- Add predicates for ancestor if present
|
|
|
|
declare
|
|
Atyp : constant Entity_Id := Nearest_Ancestor (Typ);
|
|
begin
|
|
if Present (Atyp) then
|
|
Add_Call (Atyp);
|
|
end if;
|
|
end;
|
|
|
|
-- Case where predicates are present
|
|
|
|
if Present (Expr) then
|
|
|
|
-- Test for raise expression present
|
|
|
|
Test_REs (Expr);
|
|
|
|
-- If raise expression is present, capture a copy of Expr for use
|
|
-- in building the predicateM function version later on. For this
|
|
-- copy we replace references to Object_Entity by Object_Entity_M.
|
|
|
|
if Raise_Expression_Present then
|
|
declare
|
|
Map : constant Elist_Id := New_Elmt_List;
|
|
begin
|
|
Append_Elmt (Object_Entity, Map);
|
|
Append_Elmt (Object_Entity_M, Map);
|
|
Expr_M := New_Copy_Tree (Expr, Map => Map);
|
|
end;
|
|
end if;
|
|
|
|
-- Build the main predicate function
|
|
|
|
declare
|
|
SId : constant Entity_Id :=
|
|
Make_Defining_Identifier (Loc,
|
|
Chars => New_External_Name (Chars (Typ), "Predicate"));
|
|
-- The entity for the the function spec
|
|
|
|
SIdB : constant Entity_Id :=
|
|
Make_Defining_Identifier (Loc,
|
|
Chars => New_External_Name (Chars (Typ), "Predicate"));
|
|
-- The entity for the function body
|
|
|
|
Spec : Node_Id;
|
|
FDecl : Node_Id;
|
|
FBody : Node_Id;
|
|
|
|
begin
|
|
-- Build function declaration
|
|
|
|
Set_Ekind (SId, E_Function);
|
|
Set_Is_Internal (SId);
|
|
Set_Is_Predicate_Function (SId);
|
|
Set_Predicate_Function (Typ, SId);
|
|
|
|
-- The predicate function is shared between views of a type
|
|
|
|
if Is_Private_Type (Typ) and then Present (Full_View (Typ)) then
|
|
Set_Predicate_Function (Full_View (Typ), SId);
|
|
end if;
|
|
|
|
Spec :=
|
|
Make_Function_Specification (Loc,
|
|
Defining_Unit_Name => SId,
|
|
Parameter_Specifications => New_List (
|
|
Make_Parameter_Specification (Loc,
|
|
Defining_Identifier => Object_Entity,
|
|
Parameter_Type => New_Occurrence_Of (Typ, Loc))),
|
|
Result_Definition =>
|
|
New_Occurrence_Of (Standard_Boolean, Loc));
|
|
|
|
FDecl :=
|
|
Make_Subprogram_Declaration (Loc,
|
|
Specification => Spec);
|
|
|
|
-- Build function body
|
|
|
|
Spec :=
|
|
Make_Function_Specification (Loc,
|
|
Defining_Unit_Name => SIdB,
|
|
Parameter_Specifications => New_List (
|
|
Make_Parameter_Specification (Loc,
|
|
Defining_Identifier =>
|
|
Make_Defining_Identifier (Loc, Object_Name),
|
|
Parameter_Type =>
|
|
New_Occurrence_Of (Typ, Loc))),
|
|
Result_Definition =>
|
|
New_Occurrence_Of (Standard_Boolean, Loc));
|
|
|
|
FBody :=
|
|
Make_Subprogram_Body (Loc,
|
|
Specification => Spec,
|
|
Declarations => Empty_List,
|
|
Handled_Statement_Sequence =>
|
|
Make_Handled_Sequence_Of_Statements (Loc,
|
|
Statements => New_List (
|
|
Make_Simple_Return_Statement (Loc,
|
|
Expression => Expr))));
|
|
|
|
-- Insert declaration before freeze node and body after
|
|
|
|
Insert_Before_And_Analyze (N, FDecl);
|
|
Insert_After_And_Analyze (N, FBody);
|
|
end;
|
|
|
|
-- Test for raise expressions present and if so build M version
|
|
|
|
if Raise_Expression_Present then
|
|
declare
|
|
SId : constant Entity_Id :=
|
|
Make_Defining_Identifier (Loc,
|
|
Chars => New_External_Name (Chars (Typ), "PredicateM"));
|
|
-- The entity for the the function spec
|
|
|
|
SIdB : constant Entity_Id :=
|
|
Make_Defining_Identifier (Loc,
|
|
Chars => New_External_Name (Chars (Typ), "PredicateM"));
|
|
-- The entity for the function body
|
|
|
|
Spec : Node_Id;
|
|
FDecl : Node_Id;
|
|
FBody : Node_Id;
|
|
BTemp : Entity_Id;
|
|
|
|
begin
|
|
-- Mark any raise expressions for special expansion
|
|
|
|
Process_REs (Expr_M);
|
|
|
|
-- Build function declaration
|
|
|
|
Set_Ekind (SId, E_Function);
|
|
Set_Is_Predicate_Function_M (SId);
|
|
Set_Predicate_Function_M (Typ, SId);
|
|
|
|
-- The predicate function is shared between views of a type
|
|
|
|
if Is_Private_Type (Typ) and then Present (Full_View (Typ)) then
|
|
Set_Predicate_Function_M (Full_View (Typ), SId);
|
|
end if;
|
|
|
|
Spec :=
|
|
Make_Function_Specification (Loc,
|
|
Defining_Unit_Name => SId,
|
|
Parameter_Specifications => New_List (
|
|
Make_Parameter_Specification (Loc,
|
|
Defining_Identifier => Object_Entity_M,
|
|
Parameter_Type => New_Occurrence_Of (Typ, Loc))),
|
|
Result_Definition =>
|
|
New_Occurrence_Of (Standard_Boolean, Loc));
|
|
|
|
FDecl :=
|
|
Make_Subprogram_Declaration (Loc,
|
|
Specification => Spec);
|
|
|
|
-- Build function body
|
|
|
|
Spec :=
|
|
Make_Function_Specification (Loc,
|
|
Defining_Unit_Name => SIdB,
|
|
Parameter_Specifications => New_List (
|
|
Make_Parameter_Specification (Loc,
|
|
Defining_Identifier =>
|
|
Make_Defining_Identifier (Loc, Object_Name),
|
|
Parameter_Type =>
|
|
New_Occurrence_Of (Typ, Loc))),
|
|
Result_Definition =>
|
|
New_Occurrence_Of (Standard_Boolean, Loc));
|
|
|
|
-- Build the body, we declare the boolean expression before
|
|
-- doing the return, because we are not really confident of
|
|
-- what happens if a return appears within a return.
|
|
|
|
BTemp :=
|
|
Make_Defining_Identifier (Loc,
|
|
Chars => New_Internal_Name ('B'));
|
|
|
|
FBody :=
|
|
Make_Subprogram_Body (Loc,
|
|
Specification => Spec,
|
|
|
|
Declarations => New_List (
|
|
Make_Object_Declaration (Loc,
|
|
Defining_Identifier => BTemp,
|
|
Constant_Present => True,
|
|
Object_Definition =>
|
|
New_Occurrence_Of (Standard_Boolean, Loc),
|
|
Expression => Expr_M)),
|
|
|
|
Handled_Statement_Sequence =>
|
|
Make_Handled_Sequence_Of_Statements (Loc,
|
|
Statements => New_List (
|
|
Make_Simple_Return_Statement (Loc,
|
|
Expression => New_Occurrence_Of (BTemp, Loc)))));
|
|
|
|
-- Insert declaration before freeze node and body after
|
|
|
|
Insert_Before_And_Analyze (N, FDecl);
|
|
Insert_After_And_Analyze (N, FBody);
|
|
end;
|
|
end if;
|
|
|
|
-- See if we have a static predicate. Note that the answer may be
|
|
-- yes even if we have an explicit Dynamic_Predicate present.
|
|
|
|
declare
|
|
PS : Boolean;
|
|
EN : Node_Id;
|
|
|
|
begin
|
|
if not Is_Scalar_Type (Typ) and then not Is_String_Type (Typ) then
|
|
PS := False;
|
|
else
|
|
PS := Is_Predicate_Static (Expr, Object_Name);
|
|
end if;
|
|
|
|
-- Case where we have a predicate-static aspect
|
|
|
|
if PS then
|
|
|
|
-- We don't set Has_Static_Predicate_Aspect, since we can have
|
|
-- any of the three cases (Predicate, Dynamic_Predicate, or
|
|
-- Static_Predicate) generating a predicate with an expression
|
|
-- that is predicate-static. We just indicate that we have a
|
|
-- predicate that can be treated as static.
|
|
|
|
Set_Has_Static_Predicate (Typ);
|
|
|
|
-- For discrete subtype, build the static predicate list
|
|
|
|
if Is_Discrete_Type (Typ) then
|
|
Build_Discrete_Static_Predicate (Typ, Expr, Object_Name);
|
|
|
|
-- If we don't get a static predicate list, it means that we
|
|
-- have a case where this is not possible, most typically in
|
|
-- the case where we inherit a dynamic predicate. We do not
|
|
-- consider this an error, we just leave the predicate as
|
|
-- dynamic. But if we do succeed in building the list, then
|
|
-- we mark the predicate as static.
|
|
|
|
if No (Static_Discrete_Predicate (Typ)) then
|
|
Set_Has_Static_Predicate (Typ, False);
|
|
end if;
|
|
|
|
-- For real or string subtype, save predicate expression
|
|
|
|
elsif Is_Real_Type (Typ) or else Is_String_Type (Typ) then
|
|
Set_Static_Real_Or_String_Predicate (Typ, Expr);
|
|
end if;
|
|
|
|
-- Case of dynamic predicate (expression is not predicate-static)
|
|
|
|
else
|
|
-- Again, we don't set Has_Dynamic_Predicate_Aspect, since that
|
|
-- is only set if we have an explicit Dynamic_Predicate aspect
|
|
-- given. Here we may simply have a Predicate aspect where the
|
|
-- expression happens not to be predicate-static.
|
|
|
|
-- Emit an error when the predicate is categorized as static
|
|
-- but its expression is not predicate-static.
|
|
|
|
-- First a little fiddling to get a nice location for the
|
|
-- message. If the expression is of the form (A and then B),
|
|
-- then use the left operand for the Sloc. This avoids getting
|
|
-- confused by a call to a higher-level predicate with a less
|
|
-- convenient source location.
|
|
|
|
EN := Expr;
|
|
while Nkind (EN) = N_And_Then loop
|
|
EN := Left_Opnd (EN);
|
|
end loop;
|
|
|
|
-- Now post appropriate message
|
|
|
|
if Has_Static_Predicate_Aspect (Typ) then
|
|
if Is_Scalar_Type (Typ) or else Is_String_Type (Typ) then
|
|
Error_Msg_F
|
|
("expression is not predicate-static (RM 3.2.4(16-22))",
|
|
EN);
|
|
else
|
|
Error_Msg_F
|
|
("static predicate requires scalar or string type", EN);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end;
|
|
end if;
|
|
end Build_Predicate_Functions;
|
|
|
|
-----------------------------------------
|
|
-- Check_Aspect_At_End_Of_Declarations --
|
|
-----------------------------------------
|
|
|
|
procedure Check_Aspect_At_End_Of_Declarations (ASN : Node_Id) is
|
|
Ent : constant Entity_Id := Entity (ASN);
|
|
Ident : constant Node_Id := Identifier (ASN);
|
|
A_Id : constant Aspect_Id := Get_Aspect_Id (Chars (Ident));
|
|
|
|
End_Decl_Expr : constant Node_Id := Entity (Ident);
|
|
-- Expression to be analyzed at end of declarations
|
|
|
|
Freeze_Expr : constant Node_Id := Expression (ASN);
|
|
-- Expression from call to Check_Aspect_At_Freeze_Point
|
|
|
|
T : constant Entity_Id := Etype (Freeze_Expr);
|
|
-- Type required for preanalyze call
|
|
|
|
Err : Boolean;
|
|
-- Set False if error
|
|
|
|
-- On entry to this procedure, Entity (Ident) contains a copy of the
|
|
-- original expression from the aspect, saved for this purpose, and
|
|
-- but Expression (Ident) is a preanalyzed copy of the expression,
|
|
-- preanalyzed just after the freeze point.
|
|
|
|
procedure Check_Overloaded_Name;
|
|
-- For aspects whose expression is simply a name, this routine checks if
|
|
-- the name is overloaded or not. If so, it verifies there is an
|
|
-- interpretation that matches the entity obtained at the freeze point,
|
|
-- otherwise the compiler complains.
|
|
|
|
---------------------------
|
|
-- Check_Overloaded_Name --
|
|
---------------------------
|
|
|
|
procedure Check_Overloaded_Name is
|
|
begin
|
|
if not Is_Overloaded (End_Decl_Expr) then
|
|
Err := not Is_Entity_Name (End_Decl_Expr)
|
|
or else Entity (End_Decl_Expr) /= Entity (Freeze_Expr);
|
|
|
|
else
|
|
Err := True;
|
|
|
|
declare
|
|
Index : Interp_Index;
|
|
It : Interp;
|
|
|
|
begin
|
|
Get_First_Interp (End_Decl_Expr, Index, It);
|
|
while Present (It.Typ) loop
|
|
if It.Nam = Entity (Freeze_Expr) then
|
|
Err := False;
|
|
exit;
|
|
end if;
|
|
|
|
Get_Next_Interp (Index, It);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
end Check_Overloaded_Name;
|
|
|
|
-- Start of processing for Check_Aspect_At_End_Of_Declarations
|
|
|
|
begin
|
|
-- Case of aspects Dimension, Dimension_System and Synchronization
|
|
|
|
if A_Id = Aspect_Synchronization then
|
|
return;
|
|
|
|
-- Case of stream attributes, just have to compare entities. However,
|
|
-- the expression is just a name (possibly overloaded), and there may
|
|
-- be stream operations declared for unrelated types, so we just need
|
|
-- to verify that one of these interpretations is the one available at
|
|
-- at the freeze point.
|
|
|
|
elsif A_Id = Aspect_Input or else
|
|
A_Id = Aspect_Output or else
|
|
A_Id = Aspect_Read or else
|
|
A_Id = Aspect_Write
|
|
then
|
|
Analyze (End_Decl_Expr);
|
|
Check_Overloaded_Name;
|
|
|
|
elsif A_Id = Aspect_Variable_Indexing or else
|
|
A_Id = Aspect_Constant_Indexing or else
|
|
A_Id = Aspect_Default_Iterator or else
|
|
A_Id = Aspect_Iterator_Element
|
|
then
|
|
-- Make type unfrozen before analysis, to prevent spurious errors
|
|
-- about late attributes.
|
|
|
|
Set_Is_Frozen (Ent, False);
|
|
Analyze (End_Decl_Expr);
|
|
Set_Is_Frozen (Ent, True);
|
|
|
|
-- If the end of declarations comes before any other freeze
|
|
-- point, the Freeze_Expr is not analyzed: no check needed.
|
|
|
|
if Analyzed (Freeze_Expr) and then not In_Instance then
|
|
Check_Overloaded_Name;
|
|
else
|
|
Err := False;
|
|
end if;
|
|
|
|
-- All other cases
|
|
|
|
else
|
|
-- Indicate that the expression comes from an aspect specification,
|
|
-- which is used in subsequent analysis even if expansion is off.
|
|
|
|
Set_Parent (End_Decl_Expr, ASN);
|
|
|
|
-- In a generic context the aspect expressions have not been
|
|
-- preanalyzed, so do it now. There are no conformance checks
|
|
-- to perform in this case.
|
|
|
|
if No (T) then
|
|
Check_Aspect_At_Freeze_Point (ASN);
|
|
return;
|
|
|
|
-- The default values attributes may be defined in the private part,
|
|
-- and the analysis of the expression may take place when only the
|
|
-- partial view is visible. The expression must be scalar, so use
|
|
-- the full view to resolve.
|
|
|
|
elsif (A_Id = Aspect_Default_Value
|
|
or else
|
|
A_Id = Aspect_Default_Component_Value)
|
|
and then Is_Private_Type (T)
|
|
then
|
|
Preanalyze_Spec_Expression (End_Decl_Expr, Full_View (T));
|
|
|
|
else
|
|
Preanalyze_Spec_Expression (End_Decl_Expr, T);
|
|
end if;
|
|
|
|
Err := not Fully_Conformant_Expressions (End_Decl_Expr, Freeze_Expr);
|
|
end if;
|
|
|
|
-- Output error message if error. Force error on aspect specification
|
|
-- even if there is an error on the expression itself.
|
|
|
|
if Err then
|
|
Error_Msg_NE
|
|
("!visibility of aspect for& changes after freeze point",
|
|
ASN, Ent);
|
|
Error_Msg_NE
|
|
("info: & is frozen here, aspects evaluated at this point??",
|
|
Freeze_Node (Ent), Ent);
|
|
end if;
|
|
end Check_Aspect_At_End_Of_Declarations;
|
|
|
|
----------------------------------
|
|
-- Check_Aspect_At_Freeze_Point --
|
|
----------------------------------
|
|
|
|
procedure Check_Aspect_At_Freeze_Point (ASN : Node_Id) is
|
|
Ident : constant Node_Id := Identifier (ASN);
|
|
-- Identifier (use Entity field to save expression)
|
|
|
|
A_Id : constant Aspect_Id := Get_Aspect_Id (Chars (Ident));
|
|
|
|
T : Entity_Id := Empty;
|
|
-- Type required for preanalyze call
|
|
|
|
begin
|
|
-- On entry to this procedure, Entity (Ident) contains a copy of the
|
|
-- original expression from the aspect, saved for this purpose.
|
|
|
|
-- On exit from this procedure Entity (Ident) is unchanged, still
|
|
-- containing that copy, but Expression (Ident) is a preanalyzed copy
|
|
-- of the expression, preanalyzed just after the freeze point.
|
|
|
|
-- Make a copy of the expression to be preanalyzed
|
|
|
|
Set_Expression (ASN, New_Copy_Tree (Entity (Ident)));
|
|
|
|
-- Find type for preanalyze call
|
|
|
|
case A_Id is
|
|
|
|
-- No_Aspect should be impossible
|
|
|
|
when No_Aspect =>
|
|
raise Program_Error;
|
|
|
|
-- Aspects taking an optional boolean argument
|
|
|
|
when Boolean_Aspects |
|
|
Library_Unit_Aspects =>
|
|
|
|
T := Standard_Boolean;
|
|
|
|
-- Aspects corresponding to attribute definition clauses
|
|
|
|
when Aspect_Address =>
|
|
T := RTE (RE_Address);
|
|
|
|
when Aspect_Attach_Handler =>
|
|
T := RTE (RE_Interrupt_ID);
|
|
|
|
when Aspect_Bit_Order | Aspect_Scalar_Storage_Order =>
|
|
T := RTE (RE_Bit_Order);
|
|
|
|
when Aspect_Convention =>
|
|
return;
|
|
|
|
when Aspect_CPU =>
|
|
T := RTE (RE_CPU_Range);
|
|
|
|
-- Default_Component_Value is resolved with the component type
|
|
|
|
when Aspect_Default_Component_Value =>
|
|
T := Component_Type (Entity (ASN));
|
|
|
|
-- Default_Value is resolved with the type entity in question
|
|
|
|
when Aspect_Default_Value =>
|
|
T := Entity (ASN);
|
|
|
|
-- Depends is a delayed aspect because it mentiones names first
|
|
-- introduced by aspect Global which is already delayed. There is
|
|
-- no action to be taken with respect to the aspect itself as the
|
|
-- analysis is done by the corresponding pragma.
|
|
|
|
when Aspect_Depends =>
|
|
return;
|
|
|
|
when Aspect_Dispatching_Domain =>
|
|
T := RTE (RE_Dispatching_Domain);
|
|
|
|
when Aspect_External_Tag =>
|
|
T := Standard_String;
|
|
|
|
when Aspect_External_Name =>
|
|
T := Standard_String;
|
|
|
|
-- Global is a delayed aspect because it may reference names that
|
|
-- have not been declared yet. There is no action to be taken with
|
|
-- respect to the aspect itself as the reference checking is done
|
|
-- on the corresponding pragma.
|
|
|
|
when Aspect_Global =>
|
|
return;
|
|
|
|
when Aspect_Link_Name =>
|
|
T := Standard_String;
|
|
|
|
when Aspect_Priority | Aspect_Interrupt_Priority =>
|
|
T := Standard_Integer;
|
|
|
|
when Aspect_Relative_Deadline =>
|
|
T := RTE (RE_Time_Span);
|
|
|
|
when Aspect_Small =>
|
|
T := Universal_Real;
|
|
|
|
-- For a simple storage pool, we have to retrieve the type of the
|
|
-- pool object associated with the aspect's corresponding attribute
|
|
-- definition clause.
|
|
|
|
when Aspect_Simple_Storage_Pool =>
|
|
T := Etype (Expression (Aspect_Rep_Item (ASN)));
|
|
|
|
when Aspect_Storage_Pool =>
|
|
T := Class_Wide_Type (RTE (RE_Root_Storage_Pool));
|
|
|
|
when Aspect_Alignment |
|
|
Aspect_Component_Size |
|
|
Aspect_Machine_Radix |
|
|
Aspect_Object_Size |
|
|
Aspect_Size |
|
|
Aspect_Storage_Size |
|
|
Aspect_Stream_Size |
|
|
Aspect_Value_Size =>
|
|
T := Any_Integer;
|
|
|
|
when Aspect_Linker_Section =>
|
|
T := Standard_String;
|
|
|
|
when Aspect_Synchronization =>
|
|
return;
|
|
|
|
-- Special case, the expression of these aspects is just an entity
|
|
-- that does not need any resolution, so just analyze.
|
|
|
|
when Aspect_Input |
|
|
Aspect_Output |
|
|
Aspect_Read |
|
|
Aspect_Suppress |
|
|
Aspect_Unsuppress |
|
|
Aspect_Warnings |
|
|
Aspect_Write =>
|
|
Analyze (Expression (ASN));
|
|
return;
|
|
|
|
-- Same for Iterator aspects, where the expression is a function
|
|
-- name. Legality rules are checked separately.
|
|
|
|
when Aspect_Constant_Indexing |
|
|
Aspect_Default_Iterator |
|
|
Aspect_Iterator_Element |
|
|
Aspect_Variable_Indexing =>
|
|
Analyze (Expression (ASN));
|
|
return;
|
|
|
|
-- Ditto for Iterable, legality checks in Validate_Iterable_Aspect.
|
|
|
|
when Aspect_Iterable =>
|
|
T := Entity (ASN);
|
|
|
|
declare
|
|
Cursor : constant Entity_Id := Get_Cursor_Type (ASN, T);
|
|
Assoc : Node_Id;
|
|
Expr : Node_Id;
|
|
|
|
begin
|
|
if Cursor = Any_Type then
|
|
return;
|
|
end if;
|
|
|
|
Assoc := First (Component_Associations (Expression (ASN)));
|
|
while Present (Assoc) loop
|
|
Expr := Expression (Assoc);
|
|
Analyze (Expr);
|
|
|
|
if not Error_Posted (Expr) then
|
|
Resolve_Iterable_Operation
|
|
(Expr, Cursor, T, Chars (First (Choices (Assoc))));
|
|
end if;
|
|
|
|
Next (Assoc);
|
|
end loop;
|
|
end;
|
|
|
|
return;
|
|
|
|
-- Invariant/Predicate take boolean expressions
|
|
|
|
when Aspect_Dynamic_Predicate |
|
|
Aspect_Invariant |
|
|
Aspect_Predicate |
|
|
Aspect_Static_Predicate |
|
|
Aspect_Type_Invariant =>
|
|
T := Standard_Boolean;
|
|
|
|
-- Here is the list of aspects that don't require delay analysis
|
|
|
|
when Aspect_Abstract_State |
|
|
Aspect_Annotate |
|
|
Aspect_Contract_Cases |
|
|
Aspect_Dimension |
|
|
Aspect_Dimension_System |
|
|
Aspect_Implicit_Dereference |
|
|
Aspect_Initial_Condition |
|
|
Aspect_Initializes |
|
|
Aspect_Part_Of |
|
|
Aspect_Post |
|
|
Aspect_Postcondition |
|
|
Aspect_Pre |
|
|
Aspect_Precondition |
|
|
Aspect_Refined_Depends |
|
|
Aspect_Refined_Global |
|
|
Aspect_Refined_Post |
|
|
Aspect_Refined_State |
|
|
Aspect_SPARK_Mode |
|
|
Aspect_Test_Case =>
|
|
raise Program_Error;
|
|
|
|
end case;
|
|
|
|
-- Do the preanalyze call
|
|
|
|
Preanalyze_Spec_Expression (Expression (ASN), T);
|
|
end Check_Aspect_At_Freeze_Point;
|
|
|
|
-----------------------------------
|
|
-- Check_Constant_Address_Clause --
|
|
-----------------------------------
|
|
|
|
procedure Check_Constant_Address_Clause
|
|
(Expr : Node_Id;
|
|
U_Ent : Entity_Id)
|
|
is
|
|
procedure Check_At_Constant_Address (Nod : Node_Id);
|
|
-- Checks that the given node N represents a name whose 'Address is
|
|
-- constant (in the same sense as OK_Constant_Address_Clause, i.e. the
|
|
-- address value is the same at the point of declaration of U_Ent and at
|
|
-- the time of elaboration of the address clause.
|
|
|
|
procedure Check_Expr_Constants (Nod : Node_Id);
|
|
-- Checks that Nod meets the requirements for a constant address clause
|
|
-- in the sense of the enclosing procedure.
|
|
|
|
procedure Check_List_Constants (Lst : List_Id);
|
|
-- Check that all elements of list Lst meet the requirements for a
|
|
-- constant address clause in the sense of the enclosing procedure.
|
|
|
|
-------------------------------
|
|
-- Check_At_Constant_Address --
|
|
-------------------------------
|
|
|
|
procedure Check_At_Constant_Address (Nod : Node_Id) is
|
|
begin
|
|
if Is_Entity_Name (Nod) then
|
|
if Present (Address_Clause (Entity ((Nod)))) then
|
|
Error_Msg_NE
|
|
("invalid address clause for initialized object &!",
|
|
Nod, U_Ent);
|
|
Error_Msg_NE
|
|
("address for& cannot" &
|
|
" depend on another address clause! (RM 13.1(22))!",
|
|
Nod, U_Ent);
|
|
|
|
elsif In_Same_Source_Unit (Entity (Nod), U_Ent)
|
|
and then Sloc (U_Ent) < Sloc (Entity (Nod))
|
|
then
|
|
Error_Msg_NE
|
|
("invalid address clause for initialized object &!",
|
|
Nod, U_Ent);
|
|
Error_Msg_Node_2 := U_Ent;
|
|
Error_Msg_NE
|
|
("\& must be defined before & (RM 13.1(22))!",
|
|
Nod, Entity (Nod));
|
|
end if;
|
|
|
|
elsif Nkind (Nod) = N_Selected_Component then
|
|
declare
|
|
T : constant Entity_Id := Etype (Prefix (Nod));
|
|
|
|
begin
|
|
if (Is_Record_Type (T)
|
|
and then Has_Discriminants (T))
|
|
or else
|
|
(Is_Access_Type (T)
|
|
and then Is_Record_Type (Designated_Type (T))
|
|
and then Has_Discriminants (Designated_Type (T)))
|
|
then
|
|
Error_Msg_NE
|
|
("invalid address clause for initialized object &!",
|
|
Nod, U_Ent);
|
|
Error_Msg_N
|
|
("\address cannot depend on component" &
|
|
" of discriminated record (RM 13.1(22))!",
|
|
Nod);
|
|
else
|
|
Check_At_Constant_Address (Prefix (Nod));
|
|
end if;
|
|
end;
|
|
|
|
elsif Nkind (Nod) = N_Indexed_Component then
|
|
Check_At_Constant_Address (Prefix (Nod));
|
|
Check_List_Constants (Expressions (Nod));
|
|
|
|
else
|
|
Check_Expr_Constants (Nod);
|
|
end if;
|
|
end Check_At_Constant_Address;
|
|
|
|
--------------------------
|
|
-- Check_Expr_Constants --
|
|
--------------------------
|
|
|
|
procedure Check_Expr_Constants (Nod : Node_Id) is
|
|
Loc_U_Ent : constant Source_Ptr := Sloc (U_Ent);
|
|
Ent : Entity_Id := Empty;
|
|
|
|
begin
|
|
if Nkind (Nod) in N_Has_Etype
|
|
and then Etype (Nod) = Any_Type
|
|
then
|
|
return;
|
|
end if;
|
|
|
|
case Nkind (Nod) is
|
|
when N_Empty | N_Error =>
|
|
return;
|
|
|
|
when N_Identifier | N_Expanded_Name =>
|
|
Ent := Entity (Nod);
|
|
|
|
-- We need to look at the original node if it is different
|
|
-- from the node, since we may have rewritten things and
|
|
-- substituted an identifier representing the rewrite.
|
|
|
|
if Original_Node (Nod) /= Nod then
|
|
Check_Expr_Constants (Original_Node (Nod));
|
|
|
|
-- If the node is an object declaration without initial
|
|
-- value, some code has been expanded, and the expression
|
|
-- is not constant, even if the constituents might be
|
|
-- acceptable, as in A'Address + offset.
|
|
|
|
if Ekind (Ent) = E_Variable
|
|
and then
|
|
Nkind (Declaration_Node (Ent)) = N_Object_Declaration
|
|
and then
|
|
No (Expression (Declaration_Node (Ent)))
|
|
then
|
|
Error_Msg_NE
|
|
("invalid address clause for initialized object &!",
|
|
Nod, U_Ent);
|
|
|
|
-- If entity is constant, it may be the result of expanding
|
|
-- a check. We must verify that its declaration appears
|
|
-- before the object in question, else we also reject the
|
|
-- address clause.
|
|
|
|
elsif Ekind (Ent) = E_Constant
|
|
and then In_Same_Source_Unit (Ent, U_Ent)
|
|
and then Sloc (Ent) > Loc_U_Ent
|
|
then
|
|
Error_Msg_NE
|
|
("invalid address clause for initialized object &!",
|
|
Nod, U_Ent);
|
|
end if;
|
|
|
|
return;
|
|
end if;
|
|
|
|
-- Otherwise look at the identifier and see if it is OK
|
|
|
|
if Ekind_In (Ent, E_Named_Integer, E_Named_Real)
|
|
or else Is_Type (Ent)
|
|
then
|
|
return;
|
|
|
|
elsif
|
|
Ekind (Ent) = E_Constant
|
|
or else
|
|
Ekind (Ent) = E_In_Parameter
|
|
then
|
|
-- This is the case where we must have Ent defined before
|
|
-- U_Ent. Clearly if they are in different units this
|
|
-- requirement is met since the unit containing Ent is
|
|
-- already processed.
|
|
|
|
if not In_Same_Source_Unit (Ent, U_Ent) then
|
|
return;
|
|
|
|
-- Otherwise location of Ent must be before the location
|
|
-- of U_Ent, that's what prior defined means.
|
|
|
|
elsif Sloc (Ent) < Loc_U_Ent then
|
|
return;
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("invalid address clause for initialized object &!",
|
|
Nod, U_Ent);
|
|
Error_Msg_Node_2 := U_Ent;
|
|
Error_Msg_NE
|
|
("\& must be defined before & (RM 13.1(22))!",
|
|
Nod, Ent);
|
|
end if;
|
|
|
|
elsif Nkind (Original_Node (Nod)) = N_Function_Call then
|
|
Check_Expr_Constants (Original_Node (Nod));
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("invalid address clause for initialized object &!",
|
|
Nod, U_Ent);
|
|
|
|
if Comes_From_Source (Ent) then
|
|
Error_Msg_NE
|
|
("\reference to variable& not allowed"
|
|
& " (RM 13.1(22))!", Nod, Ent);
|
|
else
|
|
Error_Msg_N
|
|
("non-static expression not allowed"
|
|
& " (RM 13.1(22))!", Nod);
|
|
end if;
|
|
end if;
|
|
|
|
when N_Integer_Literal =>
|
|
|
|
-- If this is a rewritten unchecked conversion, in a system
|
|
-- where Address is an integer type, always use the base type
|
|
-- for a literal value. This is user-friendly and prevents
|
|
-- order-of-elaboration issues with instances of unchecked
|
|
-- conversion.
|
|
|
|
if Nkind (Original_Node (Nod)) = N_Function_Call then
|
|
Set_Etype (Nod, Base_Type (Etype (Nod)));
|
|
end if;
|
|
|
|
when N_Real_Literal |
|
|
N_String_Literal |
|
|
N_Character_Literal =>
|
|
return;
|
|
|
|
when N_Range =>
|
|
Check_Expr_Constants (Low_Bound (Nod));
|
|
Check_Expr_Constants (High_Bound (Nod));
|
|
|
|
when N_Explicit_Dereference =>
|
|
Check_Expr_Constants (Prefix (Nod));
|
|
|
|
when N_Indexed_Component =>
|
|
Check_Expr_Constants (Prefix (Nod));
|
|
Check_List_Constants (Expressions (Nod));
|
|
|
|
when N_Slice =>
|
|
Check_Expr_Constants (Prefix (Nod));
|
|
Check_Expr_Constants (Discrete_Range (Nod));
|
|
|
|
when N_Selected_Component =>
|
|
Check_Expr_Constants (Prefix (Nod));
|
|
|
|
when N_Attribute_Reference =>
|
|
if Nam_In (Attribute_Name (Nod), Name_Address,
|
|
Name_Access,
|
|
Name_Unchecked_Access,
|
|
Name_Unrestricted_Access)
|
|
then
|
|
Check_At_Constant_Address (Prefix (Nod));
|
|
|
|
else
|
|
Check_Expr_Constants (Prefix (Nod));
|
|
Check_List_Constants (Expressions (Nod));
|
|
end if;
|
|
|
|
when N_Aggregate =>
|
|
Check_List_Constants (Component_Associations (Nod));
|
|
Check_List_Constants (Expressions (Nod));
|
|
|
|
when N_Component_Association =>
|
|
Check_Expr_Constants (Expression (Nod));
|
|
|
|
when N_Extension_Aggregate =>
|
|
Check_Expr_Constants (Ancestor_Part (Nod));
|
|
Check_List_Constants (Component_Associations (Nod));
|
|
Check_List_Constants (Expressions (Nod));
|
|
|
|
when N_Null =>
|
|
return;
|
|
|
|
when N_Binary_Op | N_Short_Circuit | N_Membership_Test =>
|
|
Check_Expr_Constants (Left_Opnd (Nod));
|
|
Check_Expr_Constants (Right_Opnd (Nod));
|
|
|
|
when N_Unary_Op =>
|
|
Check_Expr_Constants (Right_Opnd (Nod));
|
|
|
|
when N_Type_Conversion |
|
|
N_Qualified_Expression |
|
|
N_Allocator |
|
|
N_Unchecked_Type_Conversion =>
|
|
Check_Expr_Constants (Expression (Nod));
|
|
|
|
when N_Function_Call =>
|
|
if not Is_Pure (Entity (Name (Nod))) then
|
|
Error_Msg_NE
|
|
("invalid address clause for initialized object &!",
|
|
Nod, U_Ent);
|
|
|
|
Error_Msg_NE
|
|
("\function & is not pure (RM 13.1(22))!",
|
|
Nod, Entity (Name (Nod)));
|
|
|
|
else
|
|
Check_List_Constants (Parameter_Associations (Nod));
|
|
end if;
|
|
|
|
when N_Parameter_Association =>
|
|
Check_Expr_Constants (Explicit_Actual_Parameter (Nod));
|
|
|
|
when others =>
|
|
Error_Msg_NE
|
|
("invalid address clause for initialized object &!",
|
|
Nod, U_Ent);
|
|
Error_Msg_NE
|
|
("\must be constant defined before& (RM 13.1(22))!",
|
|
Nod, U_Ent);
|
|
end case;
|
|
end Check_Expr_Constants;
|
|
|
|
--------------------------
|
|
-- Check_List_Constants --
|
|
--------------------------
|
|
|
|
procedure Check_List_Constants (Lst : List_Id) is
|
|
Nod1 : Node_Id;
|
|
|
|
begin
|
|
if Present (Lst) then
|
|
Nod1 := First (Lst);
|
|
while Present (Nod1) loop
|
|
Check_Expr_Constants (Nod1);
|
|
Next (Nod1);
|
|
end loop;
|
|
end if;
|
|
end Check_List_Constants;
|
|
|
|
-- Start of processing for Check_Constant_Address_Clause
|
|
|
|
begin
|
|
-- If rep_clauses are to be ignored, no need for legality checks. In
|
|
-- particular, no need to pester user about rep clauses that violate
|
|
-- the rule on constant addresses, given that these clauses will be
|
|
-- removed by Freeze before they reach the back end.
|
|
|
|
if not Ignore_Rep_Clauses then
|
|
Check_Expr_Constants (Expr);
|
|
end if;
|
|
end Check_Constant_Address_Clause;
|
|
|
|
---------------------------
|
|
-- Check_Pool_Size_Clash --
|
|
---------------------------
|
|
|
|
procedure Check_Pool_Size_Clash (Ent : Entity_Id; SP, SS : Node_Id) is
|
|
Post : Node_Id;
|
|
|
|
begin
|
|
-- We need to find out which one came first. Note that in the case of
|
|
-- aspects mixed with pragmas there are cases where the processing order
|
|
-- is reversed, which is why we do the check here.
|
|
|
|
if Sloc (SP) < Sloc (SS) then
|
|
Error_Msg_Sloc := Sloc (SP);
|
|
Post := SS;
|
|
Error_Msg_NE ("Storage_Pool previously given for&#", Post, Ent);
|
|
|
|
else
|
|
Error_Msg_Sloc := Sloc (SS);
|
|
Post := SP;
|
|
Error_Msg_NE ("Storage_Size previously given for&#", Post, Ent);
|
|
end if;
|
|
|
|
Error_Msg_N
|
|
("\cannot have Storage_Size and Storage_Pool (RM 13.11(3))", Post);
|
|
end Check_Pool_Size_Clash;
|
|
|
|
----------------------------------------
|
|
-- Check_Record_Representation_Clause --
|
|
----------------------------------------
|
|
|
|
procedure Check_Record_Representation_Clause (N : Node_Id) is
|
|
Loc : constant Source_Ptr := Sloc (N);
|
|
Ident : constant Node_Id := Identifier (N);
|
|
Rectype : Entity_Id;
|
|
Fent : Entity_Id;
|
|
CC : Node_Id;
|
|
Fbit : Uint;
|
|
Lbit : Uint;
|
|
Hbit : Uint := Uint_0;
|
|
Comp : Entity_Id;
|
|
Pcomp : Entity_Id;
|
|
|
|
Max_Bit_So_Far : Uint;
|
|
-- Records the maximum bit position so far. If all field positions
|
|
-- are monotonically increasing, then we can skip the circuit for
|
|
-- checking for overlap, since no overlap is possible.
|
|
|
|
Tagged_Parent : Entity_Id := Empty;
|
|
-- This is set in the case of a derived tagged type for which we have
|
|
-- Is_Fully_Repped_Tagged_Type True (indicating that all components are
|
|
-- positioned by record representation clauses). In this case we must
|
|
-- check for overlap between components of this tagged type, and the
|
|
-- components of its parent. Tagged_Parent will point to this parent
|
|
-- type. For all other cases Tagged_Parent is left set to Empty.
|
|
|
|
Parent_Last_Bit : Uint;
|
|
-- Relevant only if Tagged_Parent is set, Parent_Last_Bit indicates the
|
|
-- last bit position for any field in the parent type. We only need to
|
|
-- check overlap for fields starting below this point.
|
|
|
|
Overlap_Check_Required : Boolean;
|
|
-- Used to keep track of whether or not an overlap check is required
|
|
|
|
Overlap_Detected : Boolean := False;
|
|
-- Set True if an overlap is detected
|
|
|
|
Ccount : Natural := 0;
|
|
-- Number of component clauses in record rep clause
|
|
|
|
procedure Check_Component_Overlap (C1_Ent, C2_Ent : Entity_Id);
|
|
-- Given two entities for record components or discriminants, checks
|
|
-- if they have overlapping component clauses and issues errors if so.
|
|
|
|
procedure Find_Component;
|
|
-- Finds component entity corresponding to current component clause (in
|
|
-- CC), and sets Comp to the entity, and Fbit/Lbit to the zero origin
|
|
-- start/stop bits for the field. If there is no matching component or
|
|
-- if the matching component does not have a component clause, then
|
|
-- that's an error and Comp is set to Empty, but no error message is
|
|
-- issued, since the message was already given. Comp is also set to
|
|
-- Empty if the current "component clause" is in fact a pragma.
|
|
|
|
-----------------------------
|
|
-- Check_Component_Overlap --
|
|
-----------------------------
|
|
|
|
procedure Check_Component_Overlap (C1_Ent, C2_Ent : Entity_Id) is
|
|
CC1 : constant Node_Id := Component_Clause (C1_Ent);
|
|
CC2 : constant Node_Id := Component_Clause (C2_Ent);
|
|
|
|
begin
|
|
if Present (CC1) and then Present (CC2) then
|
|
|
|
-- Exclude odd case where we have two tag components in the same
|
|
-- record, both at location zero. This seems a bit strange, but
|
|
-- it seems to happen in some circumstances, perhaps on an error.
|
|
|
|
if Nam_In (Chars (C1_Ent), Name_uTag, Name_uTag) then
|
|
return;
|
|
end if;
|
|
|
|
-- Here we check if the two fields overlap
|
|
|
|
declare
|
|
S1 : constant Uint := Component_Bit_Offset (C1_Ent);
|
|
S2 : constant Uint := Component_Bit_Offset (C2_Ent);
|
|
E1 : constant Uint := S1 + Esize (C1_Ent);
|
|
E2 : constant Uint := S2 + Esize (C2_Ent);
|
|
|
|
begin
|
|
if E2 <= S1 or else E1 <= S2 then
|
|
null;
|
|
else
|
|
Error_Msg_Node_2 := Component_Name (CC2);
|
|
Error_Msg_Sloc := Sloc (Error_Msg_Node_2);
|
|
Error_Msg_Node_1 := Component_Name (CC1);
|
|
Error_Msg_N
|
|
("component& overlaps & #", Component_Name (CC1));
|
|
Overlap_Detected := True;
|
|
end if;
|
|
end;
|
|
end if;
|
|
end Check_Component_Overlap;
|
|
|
|
--------------------
|
|
-- Find_Component --
|
|
--------------------
|
|
|
|
procedure Find_Component is
|
|
|
|
procedure Search_Component (R : Entity_Id);
|
|
-- Search components of R for a match. If found, Comp is set
|
|
|
|
----------------------
|
|
-- Search_Component --
|
|
----------------------
|
|
|
|
procedure Search_Component (R : Entity_Id) is
|
|
begin
|
|
Comp := First_Component_Or_Discriminant (R);
|
|
while Present (Comp) loop
|
|
|
|
-- Ignore error of attribute name for component name (we
|
|
-- already gave an error message for this, so no need to
|
|
-- complain here)
|
|
|
|
if Nkind (Component_Name (CC)) = N_Attribute_Reference then
|
|
null;
|
|
else
|
|
exit when Chars (Comp) = Chars (Component_Name (CC));
|
|
end if;
|
|
|
|
Next_Component_Or_Discriminant (Comp);
|
|
end loop;
|
|
end Search_Component;
|
|
|
|
-- Start of processing for Find_Component
|
|
|
|
begin
|
|
-- Return with Comp set to Empty if we have a pragma
|
|
|
|
if Nkind (CC) = N_Pragma then
|
|
Comp := Empty;
|
|
return;
|
|
end if;
|
|
|
|
-- Search current record for matching component
|
|
|
|
Search_Component (Rectype);
|
|
|
|
-- If not found, maybe component of base type discriminant that is
|
|
-- absent from statically constrained first subtype.
|
|
|
|
if No (Comp) then
|
|
Search_Component (Base_Type (Rectype));
|
|
end if;
|
|
|
|
-- If no component, or the component does not reference the component
|
|
-- clause in question, then there was some previous error for which
|
|
-- we already gave a message, so just return with Comp Empty.
|
|
|
|
if No (Comp) or else Component_Clause (Comp) /= CC then
|
|
Check_Error_Detected;
|
|
Comp := Empty;
|
|
|
|
-- Normal case where we have a component clause
|
|
|
|
else
|
|
Fbit := Component_Bit_Offset (Comp);
|
|
Lbit := Fbit + Esize (Comp) - 1;
|
|
end if;
|
|
end Find_Component;
|
|
|
|
-- Start of processing for Check_Record_Representation_Clause
|
|
|
|
begin
|
|
Find_Type (Ident);
|
|
Rectype := Entity (Ident);
|
|
|
|
if Rectype = Any_Type then
|
|
return;
|
|
else
|
|
Rectype := Underlying_Type (Rectype);
|
|
end if;
|
|
|
|
-- See if we have a fully repped derived tagged type
|
|
|
|
declare
|
|
PS : constant Entity_Id := Parent_Subtype (Rectype);
|
|
|
|
begin
|
|
if Present (PS) and then Is_Fully_Repped_Tagged_Type (PS) then
|
|
Tagged_Parent := PS;
|
|
|
|
-- Find maximum bit of any component of the parent type
|
|
|
|
Parent_Last_Bit := UI_From_Int (System_Address_Size - 1);
|
|
Pcomp := First_Entity (Tagged_Parent);
|
|
while Present (Pcomp) loop
|
|
if Ekind_In (Pcomp, E_Discriminant, E_Component) then
|
|
if Component_Bit_Offset (Pcomp) /= No_Uint
|
|
and then Known_Static_Esize (Pcomp)
|
|
then
|
|
Parent_Last_Bit :=
|
|
UI_Max
|
|
(Parent_Last_Bit,
|
|
Component_Bit_Offset (Pcomp) + Esize (Pcomp) - 1);
|
|
end if;
|
|
|
|
Next_Entity (Pcomp);
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
end;
|
|
|
|
-- All done if no component clauses
|
|
|
|
CC := First (Component_Clauses (N));
|
|
|
|
if No (CC) then
|
|
return;
|
|
end if;
|
|
|
|
-- If a tag is present, then create a component clause that places it
|
|
-- at the start of the record (otherwise gigi may place it after other
|
|
-- fields that have rep clauses).
|
|
|
|
Fent := First_Entity (Rectype);
|
|
|
|
if Nkind (Fent) = N_Defining_Identifier
|
|
and then Chars (Fent) = Name_uTag
|
|
then
|
|
Set_Component_Bit_Offset (Fent, Uint_0);
|
|
Set_Normalized_Position (Fent, Uint_0);
|
|
Set_Normalized_First_Bit (Fent, Uint_0);
|
|
Set_Normalized_Position_Max (Fent, Uint_0);
|
|
Init_Esize (Fent, System_Address_Size);
|
|
|
|
Set_Component_Clause (Fent,
|
|
Make_Component_Clause (Loc,
|
|
Component_Name => Make_Identifier (Loc, Name_uTag),
|
|
|
|
Position => Make_Integer_Literal (Loc, Uint_0),
|
|
First_Bit => Make_Integer_Literal (Loc, Uint_0),
|
|
Last_Bit =>
|
|
Make_Integer_Literal (Loc,
|
|
UI_From_Int (System_Address_Size))));
|
|
|
|
Ccount := Ccount + 1;
|
|
end if;
|
|
|
|
Max_Bit_So_Far := Uint_Minus_1;
|
|
Overlap_Check_Required := False;
|
|
|
|
-- Process the component clauses
|
|
|
|
while Present (CC) loop
|
|
Find_Component;
|
|
|
|
if Present (Comp) then
|
|
Ccount := Ccount + 1;
|
|
|
|
-- We need a full overlap check if record positions non-monotonic
|
|
|
|
if Fbit <= Max_Bit_So_Far then
|
|
Overlap_Check_Required := True;
|
|
end if;
|
|
|
|
Max_Bit_So_Far := Lbit;
|
|
|
|
-- Check bit position out of range of specified size
|
|
|
|
if Has_Size_Clause (Rectype)
|
|
and then RM_Size (Rectype) <= Lbit
|
|
then
|
|
Error_Msg_N
|
|
("bit number out of range of specified size",
|
|
Last_Bit (CC));
|
|
|
|
-- Check for overlap with tag component
|
|
|
|
else
|
|
if Is_Tagged_Type (Rectype)
|
|
and then Fbit < System_Address_Size
|
|
then
|
|
Error_Msg_NE
|
|
("component overlaps tag field of&",
|
|
Component_Name (CC), Rectype);
|
|
Overlap_Detected := True;
|
|
end if;
|
|
|
|
if Hbit < Lbit then
|
|
Hbit := Lbit;
|
|
end if;
|
|
end if;
|
|
|
|
-- Check parent overlap if component might overlap parent field
|
|
|
|
if Present (Tagged_Parent) and then Fbit <= Parent_Last_Bit then
|
|
Pcomp := First_Component_Or_Discriminant (Tagged_Parent);
|
|
while Present (Pcomp) loop
|
|
if not Is_Tag (Pcomp)
|
|
and then Chars (Pcomp) /= Name_uParent
|
|
then
|
|
Check_Component_Overlap (Comp, Pcomp);
|
|
end if;
|
|
|
|
Next_Component_Or_Discriminant (Pcomp);
|
|
end loop;
|
|
end if;
|
|
end if;
|
|
|
|
Next (CC);
|
|
end loop;
|
|
|
|
-- Now that we have processed all the component clauses, check for
|
|
-- overlap. We have to leave this till last, since the components can
|
|
-- appear in any arbitrary order in the representation clause.
|
|
|
|
-- We do not need this check if all specified ranges were monotonic,
|
|
-- as recorded by Overlap_Check_Required being False at this stage.
|
|
|
|
-- This first section checks if there are any overlapping entries at
|
|
-- all. It does this by sorting all entries and then seeing if there are
|
|
-- any overlaps. If there are none, then that is decisive, but if there
|
|
-- are overlaps, they may still be OK (they may result from fields in
|
|
-- different variants).
|
|
|
|
if Overlap_Check_Required then
|
|
Overlap_Check1 : declare
|
|
|
|
OC_Fbit : array (0 .. Ccount) of Uint;
|
|
-- First-bit values for component clauses, the value is the offset
|
|
-- of the first bit of the field from start of record. The zero
|
|
-- entry is for use in sorting.
|
|
|
|
OC_Lbit : array (0 .. Ccount) of Uint;
|
|
-- Last-bit values for component clauses, the value is the offset
|
|
-- of the last bit of the field from start of record. The zero
|
|
-- entry is for use in sorting.
|
|
|
|
OC_Count : Natural := 0;
|
|
-- Count of entries in OC_Fbit and OC_Lbit
|
|
|
|
function OC_Lt (Op1, Op2 : Natural) return Boolean;
|
|
-- Compare routine for Sort
|
|
|
|
procedure OC_Move (From : Natural; To : Natural);
|
|
-- Move routine for Sort
|
|
|
|
package Sorting is new GNAT.Heap_Sort_G (OC_Move, OC_Lt);
|
|
|
|
-----------
|
|
-- OC_Lt --
|
|
-----------
|
|
|
|
function OC_Lt (Op1, Op2 : Natural) return Boolean is
|
|
begin
|
|
return OC_Fbit (Op1) < OC_Fbit (Op2);
|
|
end OC_Lt;
|
|
|
|
-------------
|
|
-- OC_Move --
|
|
-------------
|
|
|
|
procedure OC_Move (From : Natural; To : Natural) is
|
|
begin
|
|
OC_Fbit (To) := OC_Fbit (From);
|
|
OC_Lbit (To) := OC_Lbit (From);
|
|
end OC_Move;
|
|
|
|
-- Start of processing for Overlap_Check
|
|
|
|
begin
|
|
CC := First (Component_Clauses (N));
|
|
while Present (CC) loop
|
|
|
|
-- Exclude component clause already marked in error
|
|
|
|
if not Error_Posted (CC) then
|
|
Find_Component;
|
|
|
|
if Present (Comp) then
|
|
OC_Count := OC_Count + 1;
|
|
OC_Fbit (OC_Count) := Fbit;
|
|
OC_Lbit (OC_Count) := Lbit;
|
|
end if;
|
|
end if;
|
|
|
|
Next (CC);
|
|
end loop;
|
|
|
|
Sorting.Sort (OC_Count);
|
|
|
|
Overlap_Check_Required := False;
|
|
for J in 1 .. OC_Count - 1 loop
|
|
if OC_Lbit (J) >= OC_Fbit (J + 1) then
|
|
Overlap_Check_Required := True;
|
|
exit;
|
|
end if;
|
|
end loop;
|
|
end Overlap_Check1;
|
|
end if;
|
|
|
|
-- If Overlap_Check_Required is still True, then we have to do the full
|
|
-- scale overlap check, since we have at least two fields that do
|
|
-- overlap, and we need to know if that is OK since they are in
|
|
-- different variant, or whether we have a definite problem.
|
|
|
|
if Overlap_Check_Required then
|
|
Overlap_Check2 : declare
|
|
C1_Ent, C2_Ent : Entity_Id;
|
|
-- Entities of components being checked for overlap
|
|
|
|
Clist : Node_Id;
|
|
-- Component_List node whose Component_Items are being checked
|
|
|
|
Citem : Node_Id;
|
|
-- Component declaration for component being checked
|
|
|
|
begin
|
|
C1_Ent := First_Entity (Base_Type (Rectype));
|
|
|
|
-- Loop through all components in record. For each component check
|
|
-- for overlap with any of the preceding elements on the component
|
|
-- list containing the component and also, if the component is in
|
|
-- a variant, check against components outside the case structure.
|
|
-- This latter test is repeated recursively up the variant tree.
|
|
|
|
Main_Component_Loop : while Present (C1_Ent) loop
|
|
if not Ekind_In (C1_Ent, E_Component, E_Discriminant) then
|
|
goto Continue_Main_Component_Loop;
|
|
end if;
|
|
|
|
-- Skip overlap check if entity has no declaration node. This
|
|
-- happens with discriminants in constrained derived types.
|
|
-- Possibly we are missing some checks as a result, but that
|
|
-- does not seem terribly serious.
|
|
|
|
if No (Declaration_Node (C1_Ent)) then
|
|
goto Continue_Main_Component_Loop;
|
|
end if;
|
|
|
|
Clist := Parent (List_Containing (Declaration_Node (C1_Ent)));
|
|
|
|
-- Loop through component lists that need checking. Check the
|
|
-- current component list and all lists in variants above us.
|
|
|
|
Component_List_Loop : loop
|
|
|
|
-- If derived type definition, go to full declaration
|
|
-- If at outer level, check discriminants if there are any.
|
|
|
|
if Nkind (Clist) = N_Derived_Type_Definition then
|
|
Clist := Parent (Clist);
|
|
end if;
|
|
|
|
-- Outer level of record definition, check discriminants
|
|
|
|
if Nkind_In (Clist, N_Full_Type_Declaration,
|
|
N_Private_Type_Declaration)
|
|
then
|
|
if Has_Discriminants (Defining_Identifier (Clist)) then
|
|
C2_Ent :=
|
|
First_Discriminant (Defining_Identifier (Clist));
|
|
while Present (C2_Ent) loop
|
|
exit when C1_Ent = C2_Ent;
|
|
Check_Component_Overlap (C1_Ent, C2_Ent);
|
|
Next_Discriminant (C2_Ent);
|
|
end loop;
|
|
end if;
|
|
|
|
-- Record extension case
|
|
|
|
elsif Nkind (Clist) = N_Derived_Type_Definition then
|
|
Clist := Empty;
|
|
|
|
-- Otherwise check one component list
|
|
|
|
else
|
|
Citem := First (Component_Items (Clist));
|
|
while Present (Citem) loop
|
|
if Nkind (Citem) = N_Component_Declaration then
|
|
C2_Ent := Defining_Identifier (Citem);
|
|
exit when C1_Ent = C2_Ent;
|
|
Check_Component_Overlap (C1_Ent, C2_Ent);
|
|
end if;
|
|
|
|
Next (Citem);
|
|
end loop;
|
|
end if;
|
|
|
|
-- Check for variants above us (the parent of the Clist can
|
|
-- be a variant, in which case its parent is a variant part,
|
|
-- and the parent of the variant part is a component list
|
|
-- whose components must all be checked against the current
|
|
-- component for overlap).
|
|
|
|
if Nkind (Parent (Clist)) = N_Variant then
|
|
Clist := Parent (Parent (Parent (Clist)));
|
|
|
|
-- Check for possible discriminant part in record, this
|
|
-- is treated essentially as another level in the
|
|
-- recursion. For this case the parent of the component
|
|
-- list is the record definition, and its parent is the
|
|
-- full type declaration containing the discriminant
|
|
-- specifications.
|
|
|
|
elsif Nkind (Parent (Clist)) = N_Record_Definition then
|
|
Clist := Parent (Parent ((Clist)));
|
|
|
|
-- If neither of these two cases, we are at the top of
|
|
-- the tree.
|
|
|
|
else
|
|
exit Component_List_Loop;
|
|
end if;
|
|
end loop Component_List_Loop;
|
|
|
|
<<Continue_Main_Component_Loop>>
|
|
Next_Entity (C1_Ent);
|
|
|
|
end loop Main_Component_Loop;
|
|
end Overlap_Check2;
|
|
end if;
|
|
|
|
-- The following circuit deals with warning on record holes (gaps). We
|
|
-- skip this check if overlap was detected, since it makes sense for the
|
|
-- programmer to fix this illegality before worrying about warnings.
|
|
|
|
if not Overlap_Detected and Warn_On_Record_Holes then
|
|
Record_Hole_Check : declare
|
|
Decl : constant Node_Id := Declaration_Node (Base_Type (Rectype));
|
|
-- Full declaration of record type
|
|
|
|
procedure Check_Component_List
|
|
(CL : Node_Id;
|
|
Sbit : Uint;
|
|
DS : List_Id);
|
|
-- Check component list CL for holes. The starting bit should be
|
|
-- Sbit. which is zero for the main record component list and set
|
|
-- appropriately for recursive calls for variants. DS is set to
|
|
-- a list of discriminant specifications to be included in the
|
|
-- consideration of components. It is No_List if none to consider.
|
|
|
|
--------------------------
|
|
-- Check_Component_List --
|
|
--------------------------
|
|
|
|
procedure Check_Component_List
|
|
(CL : Node_Id;
|
|
Sbit : Uint;
|
|
DS : List_Id)
|
|
is
|
|
Compl : Integer;
|
|
|
|
begin
|
|
Compl := Integer (List_Length (Component_Items (CL)));
|
|
|
|
if DS /= No_List then
|
|
Compl := Compl + Integer (List_Length (DS));
|
|
end if;
|
|
|
|
declare
|
|
Comps : array (Natural range 0 .. Compl) of Entity_Id;
|
|
-- Gather components (zero entry is for sort routine)
|
|
|
|
Ncomps : Natural := 0;
|
|
-- Number of entries stored in Comps (starting at Comps (1))
|
|
|
|
Citem : Node_Id;
|
|
-- One component item or discriminant specification
|
|
|
|
Nbit : Uint;
|
|
-- Starting bit for next component
|
|
|
|
CEnt : Entity_Id;
|
|
-- Component entity
|
|
|
|
Variant : Node_Id;
|
|
-- One variant
|
|
|
|
function Lt (Op1, Op2 : Natural) return Boolean;
|
|
-- Compare routine for Sort
|
|
|
|
procedure Move (From : Natural; To : Natural);
|
|
-- Move routine for Sort
|
|
|
|
package Sorting is new GNAT.Heap_Sort_G (Move, Lt);
|
|
|
|
--------
|
|
-- Lt --
|
|
--------
|
|
|
|
function Lt (Op1, Op2 : Natural) return Boolean is
|
|
begin
|
|
return Component_Bit_Offset (Comps (Op1))
|
|
<
|
|
Component_Bit_Offset (Comps (Op2));
|
|
end Lt;
|
|
|
|
----------
|
|
-- Move --
|
|
----------
|
|
|
|
procedure Move (From : Natural; To : Natural) is
|
|
begin
|
|
Comps (To) := Comps (From);
|
|
end Move;
|
|
|
|
begin
|
|
-- Gather discriminants into Comp
|
|
|
|
if DS /= No_List then
|
|
Citem := First (DS);
|
|
while Present (Citem) loop
|
|
if Nkind (Citem) = N_Discriminant_Specification then
|
|
declare
|
|
Ent : constant Entity_Id :=
|
|
Defining_Identifier (Citem);
|
|
begin
|
|
if Ekind (Ent) = E_Discriminant then
|
|
Ncomps := Ncomps + 1;
|
|
Comps (Ncomps) := Ent;
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
Next (Citem);
|
|
end loop;
|
|
end if;
|
|
|
|
-- Gather component entities into Comp
|
|
|
|
Citem := First (Component_Items (CL));
|
|
while Present (Citem) loop
|
|
if Nkind (Citem) = N_Component_Declaration then
|
|
Ncomps := Ncomps + 1;
|
|
Comps (Ncomps) := Defining_Identifier (Citem);
|
|
end if;
|
|
|
|
Next (Citem);
|
|
end loop;
|
|
|
|
-- Now sort the component entities based on the first bit.
|
|
-- Note we already know there are no overlapping components.
|
|
|
|
Sorting.Sort (Ncomps);
|
|
|
|
-- Loop through entries checking for holes
|
|
|
|
Nbit := Sbit;
|
|
for J in 1 .. Ncomps loop
|
|
CEnt := Comps (J);
|
|
Error_Msg_Uint_1 := Component_Bit_Offset (CEnt) - Nbit;
|
|
|
|
if Error_Msg_Uint_1 > 0 then
|
|
Error_Msg_NE
|
|
("?H?^-bit gap before component&",
|
|
Component_Name (Component_Clause (CEnt)), CEnt);
|
|
end if;
|
|
|
|
Nbit := Component_Bit_Offset (CEnt) + Esize (CEnt);
|
|
end loop;
|
|
|
|
-- Process variant parts recursively if present
|
|
|
|
if Present (Variant_Part (CL)) then
|
|
Variant := First (Variants (Variant_Part (CL)));
|
|
while Present (Variant) loop
|
|
Check_Component_List
|
|
(Component_List (Variant), Nbit, No_List);
|
|
Next (Variant);
|
|
end loop;
|
|
end if;
|
|
end;
|
|
end Check_Component_List;
|
|
|
|
-- Start of processing for Record_Hole_Check
|
|
|
|
begin
|
|
declare
|
|
Sbit : Uint;
|
|
|
|
begin
|
|
if Is_Tagged_Type (Rectype) then
|
|
Sbit := UI_From_Int (System_Address_Size);
|
|
else
|
|
Sbit := Uint_0;
|
|
end if;
|
|
|
|
if Nkind (Decl) = N_Full_Type_Declaration
|
|
and then Nkind (Type_Definition (Decl)) = N_Record_Definition
|
|
then
|
|
Check_Component_List
|
|
(Component_List (Type_Definition (Decl)),
|
|
Sbit,
|
|
Discriminant_Specifications (Decl));
|
|
end if;
|
|
end;
|
|
end Record_Hole_Check;
|
|
end if;
|
|
|
|
-- For records that have component clauses for all components, and whose
|
|
-- size is less than or equal to 32, we need to know the size in the
|
|
-- front end to activate possible packed array processing where the
|
|
-- component type is a record.
|
|
|
|
-- At this stage Hbit + 1 represents the first unused bit from all the
|
|
-- component clauses processed, so if the component clauses are
|
|
-- complete, then this is the length of the record.
|
|
|
|
-- For records longer than System.Storage_Unit, and for those where not
|
|
-- all components have component clauses, the back end determines the
|
|
-- length (it may for example be appropriate to round up the size
|
|
-- to some convenient boundary, based on alignment considerations, etc).
|
|
|
|
if Unknown_RM_Size (Rectype) and then Hbit + 1 <= 32 then
|
|
|
|
-- Nothing to do if at least one component has no component clause
|
|
|
|
Comp := First_Component_Or_Discriminant (Rectype);
|
|
while Present (Comp) loop
|
|
exit when No (Component_Clause (Comp));
|
|
Next_Component_Or_Discriminant (Comp);
|
|
end loop;
|
|
|
|
-- If we fall out of loop, all components have component clauses
|
|
-- and so we can set the size to the maximum value.
|
|
|
|
if No (Comp) then
|
|
Set_RM_Size (Rectype, Hbit + 1);
|
|
end if;
|
|
end if;
|
|
end Check_Record_Representation_Clause;
|
|
|
|
----------------
|
|
-- Check_Size --
|
|
----------------
|
|
|
|
procedure Check_Size
|
|
(N : Node_Id;
|
|
T : Entity_Id;
|
|
Siz : Uint;
|
|
Biased : out Boolean)
|
|
is
|
|
UT : constant Entity_Id := Underlying_Type (T);
|
|
M : Uint;
|
|
|
|
begin
|
|
Biased := False;
|
|
|
|
-- Reject patently improper size values.
|
|
|
|
if Is_Elementary_Type (T)
|
|
and then Siz > UI_From_Int (Int'Last)
|
|
then
|
|
Error_Msg_N ("Size value too large for elementary type", N);
|
|
|
|
if Nkind (Original_Node (N)) = N_Op_Expon then
|
|
Error_Msg_N
|
|
("\maybe '* was meant, rather than '*'*", Original_Node (N));
|
|
end if;
|
|
end if;
|
|
|
|
-- Dismiss generic types
|
|
|
|
if Is_Generic_Type (T)
|
|
or else
|
|
Is_Generic_Type (UT)
|
|
or else
|
|
Is_Generic_Type (Root_Type (UT))
|
|
then
|
|
return;
|
|
|
|
-- Guard against previous errors
|
|
|
|
elsif No (UT) or else UT = Any_Type then
|
|
Check_Error_Detected;
|
|
return;
|
|
|
|
-- Check case of bit packed array
|
|
|
|
elsif Is_Array_Type (UT)
|
|
and then Known_Static_Component_Size (UT)
|
|
and then Is_Bit_Packed_Array (UT)
|
|
then
|
|
declare
|
|
Asiz : Uint;
|
|
Indx : Node_Id;
|
|
Ityp : Entity_Id;
|
|
|
|
begin
|
|
Asiz := Component_Size (UT);
|
|
Indx := First_Index (UT);
|
|
loop
|
|
Ityp := Etype (Indx);
|
|
|
|
-- If non-static bound, then we are not in the business of
|
|
-- trying to check the length, and indeed an error will be
|
|
-- issued elsewhere, since sizes of non-static array types
|
|
-- cannot be set implicitly or explicitly.
|
|
|
|
if not Is_OK_Static_Subtype (Ityp) then
|
|
return;
|
|
end if;
|
|
|
|
-- Otherwise accumulate next dimension
|
|
|
|
Asiz := Asiz * (Expr_Value (Type_High_Bound (Ityp)) -
|
|
Expr_Value (Type_Low_Bound (Ityp)) +
|
|
Uint_1);
|
|
|
|
Next_Index (Indx);
|
|
exit when No (Indx);
|
|
end loop;
|
|
|
|
if Asiz <= Siz then
|
|
return;
|
|
|
|
else
|
|
Error_Msg_Uint_1 := Asiz;
|
|
Error_Msg_NE
|
|
("size for& too small, minimum allowed is ^", N, T);
|
|
Set_Esize (T, Asiz);
|
|
Set_RM_Size (T, Asiz);
|
|
end if;
|
|
end;
|
|
|
|
-- All other composite types are ignored
|
|
|
|
elsif Is_Composite_Type (UT) then
|
|
return;
|
|
|
|
-- For fixed-point types, don't check minimum if type is not frozen,
|
|
-- since we don't know all the characteristics of the type that can
|
|
-- affect the size (e.g. a specified small) till freeze time.
|
|
|
|
elsif Is_Fixed_Point_Type (UT)
|
|
and then not Is_Frozen (UT)
|
|
then
|
|
null;
|
|
|
|
-- Cases for which a minimum check is required
|
|
|
|
else
|
|
-- Ignore if specified size is correct for the type
|
|
|
|
if Known_Esize (UT) and then Siz = Esize (UT) then
|
|
return;
|
|
end if;
|
|
|
|
-- Otherwise get minimum size
|
|
|
|
M := UI_From_Int (Minimum_Size (UT));
|
|
|
|
if Siz < M then
|
|
|
|
-- Size is less than minimum size, but one possibility remains
|
|
-- that we can manage with the new size if we bias the type.
|
|
|
|
M := UI_From_Int (Minimum_Size (UT, Biased => True));
|
|
|
|
if Siz < M then
|
|
Error_Msg_Uint_1 := M;
|
|
Error_Msg_NE
|
|
("size for& too small, minimum allowed is ^", N, T);
|
|
Set_Esize (T, M);
|
|
Set_RM_Size (T, M);
|
|
else
|
|
Biased := True;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end Check_Size;
|
|
|
|
--------------------------
|
|
-- Freeze_Entity_Checks --
|
|
--------------------------
|
|
|
|
procedure Freeze_Entity_Checks (N : Node_Id) is
|
|
E : constant Entity_Id := Entity (N);
|
|
|
|
Non_Generic_Case : constant Boolean := Nkind (N) = N_Freeze_Entity;
|
|
-- True in non-generic case. Some of the processing here is skipped
|
|
-- for the generic case since it is not needed. Basically in the
|
|
-- generic case, we only need to do stuff that might generate error
|
|
-- messages or warnings.
|
|
begin
|
|
-- Remember that we are processing a freezing entity. Required to
|
|
-- ensure correct decoration of internal entities associated with
|
|
-- interfaces (see New_Overloaded_Entity).
|
|
|
|
Inside_Freezing_Actions := Inside_Freezing_Actions + 1;
|
|
|
|
-- For tagged types covering interfaces add internal entities that link
|
|
-- the primitives of the interfaces with the primitives that cover them.
|
|
-- Note: These entities were originally generated only when generating
|
|
-- code because their main purpose was to provide support to initialize
|
|
-- the secondary dispatch tables. They are now generated also when
|
|
-- compiling with no code generation to provide ASIS the relationship
|
|
-- between interface primitives and tagged type primitives. They are
|
|
-- also used to locate primitives covering interfaces when processing
|
|
-- generics (see Derive_Subprograms).
|
|
|
|
-- This is not needed in the generic case
|
|
|
|
if Ada_Version >= Ada_2005
|
|
and then Non_Generic_Case
|
|
and then Ekind (E) = E_Record_Type
|
|
and then Is_Tagged_Type (E)
|
|
and then not Is_Interface (E)
|
|
and then Has_Interfaces (E)
|
|
then
|
|
-- This would be a good common place to call the routine that checks
|
|
-- overriding of interface primitives (and thus factorize calls to
|
|
-- Check_Abstract_Overriding located at different contexts in the
|
|
-- compiler). However, this is not possible because it causes
|
|
-- spurious errors in case of late overriding.
|
|
|
|
Add_Internal_Interface_Entities (E);
|
|
end if;
|
|
|
|
-- Check CPP types
|
|
|
|
if Ekind (E) = E_Record_Type
|
|
and then Is_CPP_Class (E)
|
|
and then Is_Tagged_Type (E)
|
|
and then Tagged_Type_Expansion
|
|
then
|
|
if CPP_Num_Prims (E) = 0 then
|
|
|
|
-- If the CPP type has user defined components then it must import
|
|
-- primitives from C++. This is required because if the C++ class
|
|
-- has no primitives then the C++ compiler does not added the _tag
|
|
-- component to the type.
|
|
|
|
if First_Entity (E) /= Last_Entity (E) then
|
|
Error_Msg_N
|
|
("'C'P'P type must import at least one primitive from C++??",
|
|
E);
|
|
end if;
|
|
end if;
|
|
|
|
-- Check that all its primitives are abstract or imported from C++.
|
|
-- Check also availability of the C++ constructor.
|
|
|
|
declare
|
|
Has_Constructors : constant Boolean := Has_CPP_Constructors (E);
|
|
Elmt : Elmt_Id;
|
|
Error_Reported : Boolean := False;
|
|
Prim : Node_Id;
|
|
|
|
begin
|
|
Elmt := First_Elmt (Primitive_Operations (E));
|
|
while Present (Elmt) loop
|
|
Prim := Node (Elmt);
|
|
|
|
if Comes_From_Source (Prim) then
|
|
if Is_Abstract_Subprogram (Prim) then
|
|
null;
|
|
|
|
elsif not Is_Imported (Prim)
|
|
or else Convention (Prim) /= Convention_CPP
|
|
then
|
|
Error_Msg_N
|
|
("primitives of 'C'P'P types must be imported from C++ "
|
|
& "or abstract??", Prim);
|
|
|
|
elsif not Has_Constructors
|
|
and then not Error_Reported
|
|
then
|
|
Error_Msg_Name_1 := Chars (E);
|
|
Error_Msg_N
|
|
("??'C'P'P constructor required for type %", Prim);
|
|
Error_Reported := True;
|
|
end if;
|
|
end if;
|
|
|
|
Next_Elmt (Elmt);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
|
|
-- Check Ada derivation of CPP type
|
|
|
|
if Expander_Active -- why? losing errors in -gnatc mode???
|
|
and then Tagged_Type_Expansion
|
|
and then Ekind (E) = E_Record_Type
|
|
and then Etype (E) /= E
|
|
and then Is_CPP_Class (Etype (E))
|
|
and then CPP_Num_Prims (Etype (E)) > 0
|
|
and then not Is_CPP_Class (E)
|
|
and then not Has_CPP_Constructors (Etype (E))
|
|
then
|
|
-- If the parent has C++ primitives but it has no constructor then
|
|
-- check that all the primitives are overridden in this derivation;
|
|
-- otherwise the constructor of the parent is needed to build the
|
|
-- dispatch table.
|
|
|
|
declare
|
|
Elmt : Elmt_Id;
|
|
Prim : Node_Id;
|
|
|
|
begin
|
|
Elmt := First_Elmt (Primitive_Operations (E));
|
|
while Present (Elmt) loop
|
|
Prim := Node (Elmt);
|
|
|
|
if not Is_Abstract_Subprogram (Prim)
|
|
and then No (Interface_Alias (Prim))
|
|
and then Find_Dispatching_Type (Ultimate_Alias (Prim)) /= E
|
|
then
|
|
Error_Msg_Name_1 := Chars (Etype (E));
|
|
Error_Msg_N
|
|
("'C'P'P constructor required for parent type %", E);
|
|
exit;
|
|
end if;
|
|
|
|
Next_Elmt (Elmt);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
|
|
Inside_Freezing_Actions := Inside_Freezing_Actions - 1;
|
|
|
|
-- If we have a type with predicates, build predicate function. This
|
|
-- is not needed in the generic case, and is not needed within TSS
|
|
-- subprograms and other predefined primitives.
|
|
|
|
if Non_Generic_Case
|
|
and then Is_Type (E)
|
|
and then Has_Predicates (E)
|
|
and then not Within_Internal_Subprogram
|
|
then
|
|
Build_Predicate_Functions (E, N);
|
|
end if;
|
|
|
|
-- If type has delayed aspects, this is where we do the preanalysis at
|
|
-- the freeze point, as part of the consistent visibility check. Note
|
|
-- that this must be done after calling Build_Predicate_Functions or
|
|
-- Build_Invariant_Procedure since these subprograms fix occurrences of
|
|
-- the subtype name in the saved expression so that they will not cause
|
|
-- trouble in the preanalysis.
|
|
|
|
-- This is also not needed in the generic case
|
|
|
|
if Non_Generic_Case
|
|
and then Has_Delayed_Aspects (E)
|
|
and then Scope (E) = Current_Scope
|
|
then
|
|
-- Retrieve the visibility to the discriminants in order to properly
|
|
-- analyze the aspects.
|
|
|
|
Push_Scope_And_Install_Discriminants (E);
|
|
|
|
declare
|
|
Ritem : Node_Id;
|
|
|
|
begin
|
|
-- Look for aspect specification entries for this entity
|
|
|
|
Ritem := First_Rep_Item (E);
|
|
while Present (Ritem) loop
|
|
if Nkind (Ritem) = N_Aspect_Specification
|
|
and then Entity (Ritem) = E
|
|
and then Is_Delayed_Aspect (Ritem)
|
|
then
|
|
Check_Aspect_At_Freeze_Point (Ritem);
|
|
end if;
|
|
|
|
Next_Rep_Item (Ritem);
|
|
end loop;
|
|
end;
|
|
|
|
Uninstall_Discriminants_And_Pop_Scope (E);
|
|
end if;
|
|
|
|
-- For a record type, deal with variant parts. This has to be delayed
|
|
-- to this point, because of the issue of statically precicated
|
|
-- subtypes, which we have to ensure are frozen before checking
|
|
-- choices, since we need to have the static choice list set.
|
|
|
|
if Is_Record_Type (E) then
|
|
Check_Variant_Part : declare
|
|
D : constant Node_Id := Declaration_Node (E);
|
|
T : Node_Id;
|
|
C : Node_Id;
|
|
VP : Node_Id;
|
|
|
|
Others_Present : Boolean;
|
|
pragma Warnings (Off, Others_Present);
|
|
-- Indicates others present, not used in this case
|
|
|
|
procedure Non_Static_Choice_Error (Choice : Node_Id);
|
|
-- Error routine invoked by the generic instantiation below when
|
|
-- the variant part has a non static choice.
|
|
|
|
procedure Process_Declarations (Variant : Node_Id);
|
|
-- Processes declarations associated with a variant. We analyzed
|
|
-- the declarations earlier (in Sem_Ch3.Analyze_Variant_Part),
|
|
-- but we still need the recursive call to Check_Choices for any
|
|
-- nested variant to get its choices properly processed. This is
|
|
-- also where we expand out the choices if expansion is active.
|
|
|
|
package Variant_Choices_Processing is new
|
|
Generic_Check_Choices
|
|
(Process_Empty_Choice => No_OP,
|
|
Process_Non_Static_Choice => Non_Static_Choice_Error,
|
|
Process_Associated_Node => Process_Declarations);
|
|
use Variant_Choices_Processing;
|
|
|
|
-----------------------------
|
|
-- Non_Static_Choice_Error --
|
|
-----------------------------
|
|
|
|
procedure Non_Static_Choice_Error (Choice : Node_Id) is
|
|
begin
|
|
Flag_Non_Static_Expr
|
|
("choice given in variant part is not static!", Choice);
|
|
end Non_Static_Choice_Error;
|
|
|
|
--------------------------
|
|
-- Process_Declarations --
|
|
--------------------------
|
|
|
|
procedure Process_Declarations (Variant : Node_Id) is
|
|
CL : constant Node_Id := Component_List (Variant);
|
|
VP : Node_Id;
|
|
|
|
begin
|
|
-- Check for static predicate present in this variant
|
|
|
|
if Has_SP_Choice (Variant) then
|
|
|
|
-- Here we expand. You might expect to find this call in
|
|
-- Expand_N_Variant_Part, but that is called when we first
|
|
-- see the variant part, and we cannot do this expansion
|
|
-- earlier than the freeze point, since for statically
|
|
-- predicated subtypes, the predicate is not known till
|
|
-- the freeze point.
|
|
|
|
-- Furthermore, we do this expansion even if the expander
|
|
-- is not active, because other semantic processing, e.g.
|
|
-- for aggregates, requires the expanded list of choices.
|
|
|
|
-- If the expander is not active, then we can't just clobber
|
|
-- the list since it would invalidate the ASIS -gnatct tree.
|
|
-- So we have to rewrite the variant part with a Rewrite
|
|
-- call that replaces it with a copy and clobber the copy.
|
|
|
|
if not Expander_Active then
|
|
declare
|
|
NewV : constant Node_Id := New_Copy (Variant);
|
|
begin
|
|
Set_Discrete_Choices
|
|
(NewV, New_Copy_List (Discrete_Choices (Variant)));
|
|
Rewrite (Variant, NewV);
|
|
end;
|
|
end if;
|
|
|
|
Expand_Static_Predicates_In_Choices (Variant);
|
|
end if;
|
|
|
|
-- We don't need to worry about the declarations in the variant
|
|
-- (since they were analyzed by Analyze_Choices when we first
|
|
-- encountered the variant), but we do need to take care of
|
|
-- expansion of any nested variants.
|
|
|
|
if not Null_Present (CL) then
|
|
VP := Variant_Part (CL);
|
|
|
|
if Present (VP) then
|
|
Check_Choices
|
|
(VP, Variants (VP), Etype (Name (VP)), Others_Present);
|
|
end if;
|
|
end if;
|
|
end Process_Declarations;
|
|
|
|
-- Start of processing for Check_Variant_Part
|
|
|
|
begin
|
|
-- Find component list
|
|
|
|
C := Empty;
|
|
|
|
if Nkind (D) = N_Full_Type_Declaration then
|
|
T := Type_Definition (D);
|
|
|
|
if Nkind (T) = N_Record_Definition then
|
|
C := Component_List (T);
|
|
|
|
elsif Nkind (T) = N_Derived_Type_Definition
|
|
and then Present (Record_Extension_Part (T))
|
|
then
|
|
C := Component_List (Record_Extension_Part (T));
|
|
end if;
|
|
end if;
|
|
|
|
-- Case of variant part present
|
|
|
|
if Present (C) and then Present (Variant_Part (C)) then
|
|
VP := Variant_Part (C);
|
|
|
|
-- Check choices
|
|
|
|
Check_Choices
|
|
(VP, Variants (VP), Etype (Name (VP)), Others_Present);
|
|
|
|
-- If the last variant does not contain the Others choice,
|
|
-- replace it with an N_Others_Choice node since Gigi always
|
|
-- wants an Others. Note that we do not bother to call Analyze
|
|
-- on the modified variant part, since its only effect would be
|
|
-- to compute the Others_Discrete_Choices node laboriously, and
|
|
-- of course we already know the list of choices corresponding
|
|
-- to the others choice (it's the list we're replacing).
|
|
|
|
-- We only want to do this if the expander is active, since
|
|
-- we do not want to clobber the ASIS tree.
|
|
|
|
if Expander_Active then
|
|
declare
|
|
Last_Var : constant Node_Id :=
|
|
Last_Non_Pragma (Variants (VP));
|
|
|
|
Others_Node : Node_Id;
|
|
|
|
begin
|
|
if Nkind (First (Discrete_Choices (Last_Var))) /=
|
|
N_Others_Choice
|
|
then
|
|
Others_Node := Make_Others_Choice (Sloc (Last_Var));
|
|
Set_Others_Discrete_Choices
|
|
(Others_Node, Discrete_Choices (Last_Var));
|
|
Set_Discrete_Choices
|
|
(Last_Var, New_List (Others_Node));
|
|
end if;
|
|
end;
|
|
end if;
|
|
end if;
|
|
end Check_Variant_Part;
|
|
end if;
|
|
end Freeze_Entity_Checks;
|
|
|
|
-------------------------
|
|
-- Get_Alignment_Value --
|
|
-------------------------
|
|
|
|
function Get_Alignment_Value (Expr : Node_Id) return Uint is
|
|
Align : constant Uint := Static_Integer (Expr);
|
|
|
|
begin
|
|
if Align = No_Uint then
|
|
return No_Uint;
|
|
|
|
elsif Align <= 0 then
|
|
Error_Msg_N ("alignment value must be positive", Expr);
|
|
return No_Uint;
|
|
|
|
else
|
|
for J in Int range 0 .. 64 loop
|
|
declare
|
|
M : constant Uint := Uint_2 ** J;
|
|
|
|
begin
|
|
exit when M = Align;
|
|
|
|
if M > Align then
|
|
Error_Msg_N
|
|
("alignment value must be power of 2", Expr);
|
|
return No_Uint;
|
|
end if;
|
|
end;
|
|
end loop;
|
|
|
|
return Align;
|
|
end if;
|
|
end Get_Alignment_Value;
|
|
|
|
-------------------------------------
|
|
-- Inherit_Aspects_At_Freeze_Point --
|
|
-------------------------------------
|
|
|
|
procedure Inherit_Aspects_At_Freeze_Point (Typ : Entity_Id) is
|
|
function Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Rep_Item : Node_Id) return Boolean;
|
|
-- This routine checks if Rep_Item is either a pragma or an aspect
|
|
-- specification node whose correponding pragma (if any) is present in
|
|
-- the Rep Item chain of the entity it has been specified to.
|
|
|
|
--------------------------------------------------
|
|
-- Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item --
|
|
--------------------------------------------------
|
|
|
|
function Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Rep_Item : Node_Id) return Boolean
|
|
is
|
|
begin
|
|
return Nkind (Rep_Item) = N_Pragma
|
|
or else Present_In_Rep_Item
|
|
(Entity (Rep_Item), Aspect_Rep_Item (Rep_Item));
|
|
end Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item;
|
|
|
|
-- Start of processing for Inherit_Aspects_At_Freeze_Point
|
|
|
|
begin
|
|
-- A representation item is either subtype-specific (Size and Alignment
|
|
-- clauses) or type-related (all others). Subtype-specific aspects may
|
|
-- differ for different subtypes of the same type (RM 13.1.8).
|
|
|
|
-- A derived type inherits each type-related representation aspect of
|
|
-- its parent type that was directly specified before the declaration of
|
|
-- the derived type (RM 13.1.15).
|
|
|
|
-- A derived subtype inherits each subtype-specific representation
|
|
-- aspect of its parent subtype that was directly specified before the
|
|
-- declaration of the derived type (RM 13.1.15).
|
|
|
|
-- The general processing involves inheriting a representation aspect
|
|
-- from a parent type whenever the first rep item (aspect specification,
|
|
-- attribute definition clause, pragma) corresponding to the given
|
|
-- representation aspect in the rep item chain of Typ, if any, isn't
|
|
-- directly specified to Typ but to one of its parents.
|
|
|
|
-- ??? Note that, for now, just a limited number of representation
|
|
-- aspects have been inherited here so far. Many of them are
|
|
-- still inherited in Sem_Ch3. This will be fixed soon. Here is
|
|
-- a non- exhaustive list of aspects that likely also need to
|
|
-- be moved to this routine: Alignment, Component_Alignment,
|
|
-- Component_Size, Machine_Radix, Object_Size, Pack, Predicates,
|
|
-- Preelaborable_Initialization, RM_Size and Small.
|
|
|
|
if Nkind (Parent (Typ)) = N_Private_Extension_Declaration then
|
|
return;
|
|
end if;
|
|
|
|
-- Ada_05/Ada_2005
|
|
|
|
if not Has_Rep_Item (Typ, Name_Ada_05, Name_Ada_2005, False)
|
|
and then Has_Rep_Item (Typ, Name_Ada_05, Name_Ada_2005)
|
|
and then Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Get_Rep_Item (Typ, Name_Ada_05, Name_Ada_2005))
|
|
then
|
|
Set_Is_Ada_2005_Only (Typ);
|
|
end if;
|
|
|
|
-- Ada_12/Ada_2012
|
|
|
|
if not Has_Rep_Item (Typ, Name_Ada_12, Name_Ada_2012, False)
|
|
and then Has_Rep_Item (Typ, Name_Ada_12, Name_Ada_2012)
|
|
and then Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Get_Rep_Item (Typ, Name_Ada_12, Name_Ada_2012))
|
|
then
|
|
Set_Is_Ada_2012_Only (Typ);
|
|
end if;
|
|
|
|
-- Atomic/Shared
|
|
|
|
if not Has_Rep_Item (Typ, Name_Atomic, Name_Shared, False)
|
|
and then Has_Rep_Pragma (Typ, Name_Atomic, Name_Shared)
|
|
and then Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Get_Rep_Item (Typ, Name_Atomic, Name_Shared))
|
|
then
|
|
Set_Is_Atomic (Typ);
|
|
Set_Treat_As_Volatile (Typ);
|
|
Set_Is_Volatile (Typ);
|
|
end if;
|
|
|
|
-- Default_Component_Value
|
|
|
|
if Is_Array_Type (Typ)
|
|
and then Is_Base_Type (Typ)
|
|
and then Has_Rep_Item (Typ, Name_Default_Component_Value, False)
|
|
and then Has_Rep_Item (Typ, Name_Default_Component_Value)
|
|
then
|
|
Set_Default_Aspect_Component_Value (Typ,
|
|
Default_Aspect_Component_Value
|
|
(Entity (Get_Rep_Item (Typ, Name_Default_Component_Value))));
|
|
end if;
|
|
|
|
-- Default_Value
|
|
|
|
if Is_Scalar_Type (Typ)
|
|
and then Is_Base_Type (Typ)
|
|
and then Has_Rep_Item (Typ, Name_Default_Value, False)
|
|
and then Has_Rep_Item (Typ, Name_Default_Value)
|
|
then
|
|
Set_Default_Aspect_Value (Typ,
|
|
Default_Aspect_Value
|
|
(Entity (Get_Rep_Item (Typ, Name_Default_Value))));
|
|
end if;
|
|
|
|
-- Discard_Names
|
|
|
|
if not Has_Rep_Item (Typ, Name_Discard_Names, False)
|
|
and then Has_Rep_Item (Typ, Name_Discard_Names)
|
|
and then Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Get_Rep_Item (Typ, Name_Discard_Names))
|
|
then
|
|
Set_Discard_Names (Typ);
|
|
end if;
|
|
|
|
-- Invariants
|
|
|
|
if not Has_Rep_Item (Typ, Name_Invariant, False)
|
|
and then Has_Rep_Item (Typ, Name_Invariant)
|
|
and then Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Get_Rep_Item (Typ, Name_Invariant))
|
|
then
|
|
Set_Has_Invariants (Typ);
|
|
|
|
if Class_Present (Get_Rep_Item (Typ, Name_Invariant)) then
|
|
Set_Has_Inheritable_Invariants (Typ);
|
|
end if;
|
|
end if;
|
|
|
|
-- Volatile
|
|
|
|
if not Has_Rep_Item (Typ, Name_Volatile, False)
|
|
and then Has_Rep_Item (Typ, Name_Volatile)
|
|
and then Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Get_Rep_Item (Typ, Name_Volatile))
|
|
then
|
|
Set_Treat_As_Volatile (Typ);
|
|
Set_Is_Volatile (Typ);
|
|
end if;
|
|
|
|
-- Inheritance for derived types only
|
|
|
|
if Is_Derived_Type (Typ) then
|
|
declare
|
|
Bas_Typ : constant Entity_Id := Base_Type (Typ);
|
|
Imp_Bas_Typ : constant Entity_Id := Implementation_Base_Type (Typ);
|
|
|
|
begin
|
|
-- Atomic_Components
|
|
|
|
if not Has_Rep_Item (Typ, Name_Atomic_Components, False)
|
|
and then Has_Rep_Item (Typ, Name_Atomic_Components)
|
|
and then Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Get_Rep_Item (Typ, Name_Atomic_Components))
|
|
then
|
|
Set_Has_Atomic_Components (Imp_Bas_Typ);
|
|
end if;
|
|
|
|
-- Volatile_Components
|
|
|
|
if not Has_Rep_Item (Typ, Name_Volatile_Components, False)
|
|
and then Has_Rep_Item (Typ, Name_Volatile_Components)
|
|
and then Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Get_Rep_Item (Typ, Name_Volatile_Components))
|
|
then
|
|
Set_Has_Volatile_Components (Imp_Bas_Typ);
|
|
end if;
|
|
|
|
-- Finalize_Storage_Only.
|
|
|
|
if not Has_Rep_Pragma (Typ, Name_Finalize_Storage_Only, False)
|
|
and then Has_Rep_Pragma (Typ, Name_Finalize_Storage_Only)
|
|
then
|
|
Set_Finalize_Storage_Only (Bas_Typ);
|
|
end if;
|
|
|
|
-- Universal_Aliasing
|
|
|
|
if not Has_Rep_Item (Typ, Name_Universal_Aliasing, False)
|
|
and then Has_Rep_Item (Typ, Name_Universal_Aliasing)
|
|
and then Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Get_Rep_Item (Typ, Name_Universal_Aliasing))
|
|
then
|
|
Set_Universal_Aliasing (Imp_Bas_Typ);
|
|
end if;
|
|
|
|
-- Record type specific aspects
|
|
|
|
if Is_Record_Type (Typ) then
|
|
|
|
-- Bit_Order
|
|
|
|
if not Has_Rep_Item (Typ, Name_Bit_Order, False)
|
|
and then Has_Rep_Item (Typ, Name_Bit_Order)
|
|
then
|
|
Set_Reverse_Bit_Order (Bas_Typ,
|
|
Reverse_Bit_Order (Entity (Name
|
|
(Get_Rep_Item (Typ, Name_Bit_Order)))));
|
|
end if;
|
|
|
|
-- Scalar_Storage_Order
|
|
|
|
if not Has_Rep_Item (Typ, Name_Scalar_Storage_Order, False)
|
|
and then Has_Rep_Item (Typ, Name_Scalar_Storage_Order)
|
|
then
|
|
Set_Reverse_Storage_Order (Bas_Typ,
|
|
Reverse_Storage_Order (Entity (Name
|
|
(Get_Rep_Item (Typ, Name_Scalar_Storage_Order)))));
|
|
|
|
-- Clear default SSO indications, since the inherited aspect
|
|
-- which was set explicitly overrides the default.
|
|
|
|
Set_SSO_Set_Low_By_Default (Bas_Typ, False);
|
|
Set_SSO_Set_High_By_Default (Bas_Typ, False);
|
|
end if;
|
|
end if;
|
|
end;
|
|
end if;
|
|
end Inherit_Aspects_At_Freeze_Point;
|
|
|
|
----------------
|
|
-- Initialize --
|
|
----------------
|
|
|
|
procedure Initialize is
|
|
begin
|
|
Address_Clause_Checks.Init;
|
|
Independence_Checks.Init;
|
|
Unchecked_Conversions.Init;
|
|
end Initialize;
|
|
|
|
---------------------------
|
|
-- Install_Discriminants --
|
|
---------------------------
|
|
|
|
procedure Install_Discriminants (E : Entity_Id) is
|
|
Disc : Entity_Id;
|
|
Prev : Entity_Id;
|
|
begin
|
|
Disc := First_Discriminant (E);
|
|
while Present (Disc) loop
|
|
Prev := Current_Entity (Disc);
|
|
Set_Current_Entity (Disc);
|
|
Set_Is_Immediately_Visible (Disc);
|
|
Set_Homonym (Disc, Prev);
|
|
Next_Discriminant (Disc);
|
|
end loop;
|
|
end Install_Discriminants;
|
|
|
|
-------------------------
|
|
-- Is_Operational_Item --
|
|
-------------------------
|
|
|
|
function Is_Operational_Item (N : Node_Id) return Boolean is
|
|
begin
|
|
if Nkind (N) /= N_Attribute_Definition_Clause then
|
|
return False;
|
|
|
|
else
|
|
declare
|
|
Id : constant Attribute_Id := Get_Attribute_Id (Chars (N));
|
|
begin
|
|
return Id = Attribute_Input
|
|
or else Id = Attribute_Output
|
|
or else Id = Attribute_Read
|
|
or else Id = Attribute_Write
|
|
or else Id = Attribute_External_Tag;
|
|
end;
|
|
end if;
|
|
end Is_Operational_Item;
|
|
|
|
-------------------------
|
|
-- Is_Predicate_Static --
|
|
-------------------------
|
|
|
|
-- Note: the basic legality of the expression has already been checked, so
|
|
-- we don't need to worry about cases or ranges on strings for example.
|
|
|
|
function Is_Predicate_Static
|
|
(Expr : Node_Id;
|
|
Nam : Name_Id) return Boolean
|
|
is
|
|
function All_Static_Case_Alternatives (L : List_Id) return Boolean;
|
|
-- Given a list of case expression alternatives, returns True if
|
|
-- all the alternatives are static (have all static choices, and a
|
|
-- static expression).
|
|
|
|
function All_Static_Choices (L : List_Id) return Boolean;
|
|
-- Returns true if all elements of the list are OK static choices
|
|
-- as defined below for Is_Static_Choice. Used for case expression
|
|
-- alternatives and for the right operand of a membership test.
|
|
|
|
function Is_Static_Choice (N : Node_Id) return Boolean;
|
|
-- Returns True if N represents a static choice (static subtype, or
|
|
-- static subtype indication, or static expression, or static range).
|
|
--
|
|
-- Note that this is a bit more inclusive than we actually need
|
|
-- (in particular membership tests do not allow the use of subtype
|
|
-- indications). But that doesn't matter, we have already checked
|
|
-- that the construct is legal to get this far.
|
|
|
|
function Is_Type_Ref (N : Node_Id) return Boolean;
|
|
pragma Inline (Is_Type_Ref);
|
|
-- Returns True if N is a reference to the type for the predicate in
|
|
-- the expression (i.e. if it is an identifier whose Chars field matches
|
|
-- the Nam given in the call). N must not be parenthesized, if the type
|
|
-- name appears in parens, this routine will return False.
|
|
|
|
----------------------------------
|
|
-- All_Static_Case_Alternatives --
|
|
----------------------------------
|
|
|
|
function All_Static_Case_Alternatives (L : List_Id) return Boolean is
|
|
N : Node_Id;
|
|
|
|
begin
|
|
N := First (L);
|
|
while Present (N) loop
|
|
if not (All_Static_Choices (Discrete_Choices (N))
|
|
and then Is_OK_Static_Expression (Expression (N)))
|
|
then
|
|
return False;
|
|
end if;
|
|
|
|
Next (N);
|
|
end loop;
|
|
|
|
return True;
|
|
end All_Static_Case_Alternatives;
|
|
|
|
------------------------
|
|
-- All_Static_Choices --
|
|
------------------------
|
|
|
|
function All_Static_Choices (L : List_Id) return Boolean is
|
|
N : Node_Id;
|
|
|
|
begin
|
|
N := First (L);
|
|
while Present (N) loop
|
|
if not Is_Static_Choice (N) then
|
|
return False;
|
|
end if;
|
|
|
|
Next (N);
|
|
end loop;
|
|
|
|
return True;
|
|
end All_Static_Choices;
|
|
|
|
----------------------
|
|
-- Is_Static_Choice --
|
|
----------------------
|
|
|
|
function Is_Static_Choice (N : Node_Id) return Boolean is
|
|
begin
|
|
return Is_OK_Static_Expression (N)
|
|
or else (Is_Entity_Name (N) and then Is_Type (Entity (N))
|
|
and then Is_OK_Static_Subtype (Entity (N)))
|
|
or else (Nkind (N) = N_Subtype_Indication
|
|
and then Is_OK_Static_Subtype (Entity (N)))
|
|
or else (Nkind (N) = N_Range and then Is_OK_Static_Range (N));
|
|
end Is_Static_Choice;
|
|
|
|
-----------------
|
|
-- Is_Type_Ref --
|
|
-----------------
|
|
|
|
function Is_Type_Ref (N : Node_Id) return Boolean is
|
|
begin
|
|
return Nkind (N) = N_Identifier
|
|
and then Chars (N) = Nam
|
|
and then Paren_Count (N) = 0;
|
|
end Is_Type_Ref;
|
|
|
|
-- Start of processing for Is_Predicate_Static
|
|
|
|
begin
|
|
-- Predicate_Static means one of the following holds. Numbers are the
|
|
-- corresponding paragraph numbers in (RM 3.2.4(16-22)).
|
|
|
|
-- 16: A static expression
|
|
|
|
if Is_OK_Static_Expression (Expr) then
|
|
return True;
|
|
|
|
-- 17: A membership test whose simple_expression is the current
|
|
-- instance, and whose membership_choice_list meets the requirements
|
|
-- for a static membership test.
|
|
|
|
elsif Nkind (Expr) in N_Membership_Test
|
|
and then ((Present (Right_Opnd (Expr))
|
|
and then Is_Static_Choice (Right_Opnd (Expr)))
|
|
or else
|
|
(Present (Alternatives (Expr))
|
|
and then All_Static_Choices (Alternatives (Expr))))
|
|
then
|
|
return True;
|
|
|
|
-- 18. A case_expression whose selecting_expression is the current
|
|
-- instance, and whose dependent expressions are static expressions.
|
|
|
|
elsif Nkind (Expr) = N_Case_Expression
|
|
and then Is_Type_Ref (Expression (Expr))
|
|
and then All_Static_Case_Alternatives (Alternatives (Expr))
|
|
then
|
|
return True;
|
|
|
|
-- 19. A call to a predefined equality or ordering operator, where one
|
|
-- operand is the current instance, and the other is a static
|
|
-- expression.
|
|
|
|
-- Note: the RM is clearly wrong here in not excluding string types.
|
|
-- Without this exclusion, we would allow expressions like X > "ABC"
|
|
-- to be considered as predicate-static, which is clearly not intended,
|
|
-- since the idea is for predicate-static to be a subset of normal
|
|
-- static expressions (and "DEF" > "ABC" is not a static expression).
|
|
|
|
-- However, we do allow internally generated (not from source) equality
|
|
-- and inequality operations to be valid on strings (this helps deal
|
|
-- with cases where we transform A in "ABC" to A = "ABC).
|
|
|
|
elsif Nkind (Expr) in N_Op_Compare
|
|
and then ((not Is_String_Type (Etype (Left_Opnd (Expr))))
|
|
or else (Nkind_In (Expr, N_Op_Eq, N_Op_Ne)
|
|
and then not Comes_From_Source (Expr)))
|
|
and then ((Is_Type_Ref (Left_Opnd (Expr))
|
|
and then Is_OK_Static_Expression (Right_Opnd (Expr)))
|
|
or else
|
|
(Is_Type_Ref (Right_Opnd (Expr))
|
|
and then Is_OK_Static_Expression (Left_Opnd (Expr))))
|
|
then
|
|
return True;
|
|
|
|
-- 20. A call to a predefined boolean logical operator, where each
|
|
-- operand is predicate-static.
|
|
|
|
elsif (Nkind_In (Expr, N_Op_And, N_Op_Or, N_Op_Xor)
|
|
and then Is_Predicate_Static (Left_Opnd (Expr), Nam)
|
|
and then Is_Predicate_Static (Right_Opnd (Expr), Nam))
|
|
or else
|
|
(Nkind (Expr) = N_Op_Not
|
|
and then Is_Predicate_Static (Right_Opnd (Expr), Nam))
|
|
then
|
|
return True;
|
|
|
|
-- 21. A short-circuit control form where both operands are
|
|
-- predicate-static.
|
|
|
|
elsif Nkind (Expr) in N_Short_Circuit
|
|
and then Is_Predicate_Static (Left_Opnd (Expr), Nam)
|
|
and then Is_Predicate_Static (Right_Opnd (Expr), Nam)
|
|
then
|
|
return True;
|
|
|
|
-- 22. A parenthesized predicate-static expression. This does not
|
|
-- require any special test, since we just ignore paren levels in
|
|
-- all the cases above.
|
|
|
|
-- One more test that is an implementation artifact caused by the fact
|
|
-- that we are analyzing not the original expression, but the generated
|
|
-- expression in the body of the predicate function. This can include
|
|
-- references to inherited predicates, so that the expression we are
|
|
-- processing looks like:
|
|
|
|
-- expression and then xxPredicate (typ (Inns))
|
|
|
|
-- Where the call is to a Predicate function for an inherited predicate.
|
|
-- We simply ignore such a call (which could be to either a dynamic or
|
|
-- a static predicate, but remember that we can have a Static_Predicate
|
|
-- for a non-static subtype).
|
|
|
|
elsif Nkind (Expr) = N_Function_Call
|
|
and then Is_Predicate_Function (Entity (Name (Expr)))
|
|
then
|
|
return True;
|
|
|
|
-- That's an exhaustive list of tests, all other cases are not
|
|
-- predicate-static, so we return False.
|
|
|
|
else
|
|
return False;
|
|
end if;
|
|
end Is_Predicate_Static;
|
|
|
|
---------------------
|
|
-- Kill_Rep_Clause --
|
|
---------------------
|
|
|
|
procedure Kill_Rep_Clause (N : Node_Id) is
|
|
begin
|
|
pragma Assert (Ignore_Rep_Clauses);
|
|
|
|
-- Note: we use Replace rather than Rewrite, because we don't want
|
|
-- ASIS to be able to use Original_Node to dig out the (undecorated)
|
|
-- rep clause that is being replaced.
|
|
|
|
Replace (N, Make_Null_Statement (Sloc (N)));
|
|
|
|
-- The null statement must be marked as not coming from source. This is
|
|
-- so that ASIS ignores it, and also the back end does not expect bogus
|
|
-- "from source" null statements in weird places (e.g. in declarative
|
|
-- regions where such null statements are not allowed).
|
|
|
|
Set_Comes_From_Source (N, False);
|
|
end Kill_Rep_Clause;
|
|
|
|
------------------
|
|
-- Minimum_Size --
|
|
------------------
|
|
|
|
function Minimum_Size
|
|
(T : Entity_Id;
|
|
Biased : Boolean := False) return Nat
|
|
is
|
|
Lo : Uint := No_Uint;
|
|
Hi : Uint := No_Uint;
|
|
LoR : Ureal := No_Ureal;
|
|
HiR : Ureal := No_Ureal;
|
|
LoSet : Boolean := False;
|
|
HiSet : Boolean := False;
|
|
B : Uint;
|
|
S : Nat;
|
|
Ancest : Entity_Id;
|
|
R_Typ : constant Entity_Id := Root_Type (T);
|
|
|
|
begin
|
|
-- If bad type, return 0
|
|
|
|
if T = Any_Type then
|
|
return 0;
|
|
|
|
-- For generic types, just return zero. There cannot be any legitimate
|
|
-- need to know such a size, but this routine may be called with a
|
|
-- generic type as part of normal processing.
|
|
|
|
elsif Is_Generic_Type (R_Typ)
|
|
or else R_Typ = Any_Type
|
|
then
|
|
return 0;
|
|
|
|
-- Access types. Normally an access type cannot have a size smaller
|
|
-- than the size of System.Address. The exception is on VMS, where
|
|
-- we have short and long addresses, and it is possible for an access
|
|
-- type to have a short address size (and thus be less than the size
|
|
-- of System.Address itself). We simply skip the check for VMS, and
|
|
-- leave it to the back end to do the check.
|
|
|
|
elsif Is_Access_Type (T) then
|
|
if OpenVMS_On_Target then
|
|
return 0;
|
|
else
|
|
return System_Address_Size;
|
|
end if;
|
|
|
|
-- Floating-point types
|
|
|
|
elsif Is_Floating_Point_Type (T) then
|
|
return UI_To_Int (Esize (R_Typ));
|
|
|
|
-- Discrete types
|
|
|
|
elsif Is_Discrete_Type (T) then
|
|
|
|
-- The following loop is looking for the nearest compile time known
|
|
-- bounds following the ancestor subtype chain. The idea is to find
|
|
-- the most restrictive known bounds information.
|
|
|
|
Ancest := T;
|
|
loop
|
|
if Ancest = Any_Type or else Etype (Ancest) = Any_Type then
|
|
return 0;
|
|
end if;
|
|
|
|
if not LoSet then
|
|
if Compile_Time_Known_Value (Type_Low_Bound (Ancest)) then
|
|
Lo := Expr_Rep_Value (Type_Low_Bound (Ancest));
|
|
LoSet := True;
|
|
exit when HiSet;
|
|
end if;
|
|
end if;
|
|
|
|
if not HiSet then
|
|
if Compile_Time_Known_Value (Type_High_Bound (Ancest)) then
|
|
Hi := Expr_Rep_Value (Type_High_Bound (Ancest));
|
|
HiSet := True;
|
|
exit when LoSet;
|
|
end if;
|
|
end if;
|
|
|
|
Ancest := Ancestor_Subtype (Ancest);
|
|
|
|
if No (Ancest) then
|
|
Ancest := Base_Type (T);
|
|
|
|
if Is_Generic_Type (Ancest) then
|
|
return 0;
|
|
end if;
|
|
end if;
|
|
end loop;
|
|
|
|
-- Fixed-point types. We can't simply use Expr_Value to get the
|
|
-- Corresponding_Integer_Value values of the bounds, since these do not
|
|
-- get set till the type is frozen, and this routine can be called
|
|
-- before the type is frozen. Similarly the test for bounds being static
|
|
-- needs to include the case where we have unanalyzed real literals for
|
|
-- the same reason.
|
|
|
|
elsif Is_Fixed_Point_Type (T) then
|
|
|
|
-- The following loop is looking for the nearest compile time known
|
|
-- bounds following the ancestor subtype chain. The idea is to find
|
|
-- the most restrictive known bounds information.
|
|
|
|
Ancest := T;
|
|
loop
|
|
if Ancest = Any_Type or else Etype (Ancest) = Any_Type then
|
|
return 0;
|
|
end if;
|
|
|
|
-- Note: In the following two tests for LoSet and HiSet, it may
|
|
-- seem redundant to test for N_Real_Literal here since normally
|
|
-- one would assume that the test for the value being known at
|
|
-- compile time includes this case. However, there is a glitch.
|
|
-- If the real literal comes from folding a non-static expression,
|
|
-- then we don't consider any non- static expression to be known
|
|
-- at compile time if we are in configurable run time mode (needed
|
|
-- in some cases to give a clearer definition of what is and what
|
|
-- is not accepted). So the test is indeed needed. Without it, we
|
|
-- would set neither Lo_Set nor Hi_Set and get an infinite loop.
|
|
|
|
if not LoSet then
|
|
if Nkind (Type_Low_Bound (Ancest)) = N_Real_Literal
|
|
or else Compile_Time_Known_Value (Type_Low_Bound (Ancest))
|
|
then
|
|
LoR := Expr_Value_R (Type_Low_Bound (Ancest));
|
|
LoSet := True;
|
|
exit when HiSet;
|
|
end if;
|
|
end if;
|
|
|
|
if not HiSet then
|
|
if Nkind (Type_High_Bound (Ancest)) = N_Real_Literal
|
|
or else Compile_Time_Known_Value (Type_High_Bound (Ancest))
|
|
then
|
|
HiR := Expr_Value_R (Type_High_Bound (Ancest));
|
|
HiSet := True;
|
|
exit when LoSet;
|
|
end if;
|
|
end if;
|
|
|
|
Ancest := Ancestor_Subtype (Ancest);
|
|
|
|
if No (Ancest) then
|
|
Ancest := Base_Type (T);
|
|
|
|
if Is_Generic_Type (Ancest) then
|
|
return 0;
|
|
end if;
|
|
end if;
|
|
end loop;
|
|
|
|
Lo := UR_To_Uint (LoR / Small_Value (T));
|
|
Hi := UR_To_Uint (HiR / Small_Value (T));
|
|
|
|
-- No other types allowed
|
|
|
|
else
|
|
raise Program_Error;
|
|
end if;
|
|
|
|
-- Fall through with Hi and Lo set. Deal with biased case
|
|
|
|
if (Biased
|
|
and then not Is_Fixed_Point_Type (T)
|
|
and then not (Is_Enumeration_Type (T)
|
|
and then Has_Non_Standard_Rep (T)))
|
|
or else Has_Biased_Representation (T)
|
|
then
|
|
Hi := Hi - Lo;
|
|
Lo := Uint_0;
|
|
end if;
|
|
|
|
-- Signed case. Note that we consider types like range 1 .. -1 to be
|
|
-- signed for the purpose of computing the size, since the bounds have
|
|
-- to be accommodated in the base type.
|
|
|
|
if Lo < 0 or else Hi < 0 then
|
|
S := 1;
|
|
B := Uint_1;
|
|
|
|
-- S = size, B = 2 ** (size - 1) (can accommodate -B .. +(B - 1))
|
|
-- Note that we accommodate the case where the bounds cross. This
|
|
-- can happen either because of the way the bounds are declared
|
|
-- or because of the algorithm in Freeze_Fixed_Point_Type.
|
|
|
|
while Lo < -B
|
|
or else Hi < -B
|
|
or else Lo >= B
|
|
or else Hi >= B
|
|
loop
|
|
B := Uint_2 ** S;
|
|
S := S + 1;
|
|
end loop;
|
|
|
|
-- Unsigned case
|
|
|
|
else
|
|
-- If both bounds are positive, make sure that both are represen-
|
|
-- table in the case where the bounds are crossed. This can happen
|
|
-- either because of the way the bounds are declared, or because of
|
|
-- the algorithm in Freeze_Fixed_Point_Type.
|
|
|
|
if Lo > Hi then
|
|
Hi := Lo;
|
|
end if;
|
|
|
|
-- S = size, (can accommodate 0 .. (2**size - 1))
|
|
|
|
S := 0;
|
|
while Hi >= Uint_2 ** S loop
|
|
S := S + 1;
|
|
end loop;
|
|
end if;
|
|
|
|
return S;
|
|
end Minimum_Size;
|
|
|
|
---------------------------
|
|
-- New_Stream_Subprogram --
|
|
---------------------------
|
|
|
|
procedure New_Stream_Subprogram
|
|
(N : Node_Id;
|
|
Ent : Entity_Id;
|
|
Subp : Entity_Id;
|
|
Nam : TSS_Name_Type)
|
|
is
|
|
Loc : constant Source_Ptr := Sloc (N);
|
|
Sname : constant Name_Id := Make_TSS_Name (Base_Type (Ent), Nam);
|
|
Subp_Id : Entity_Id;
|
|
Subp_Decl : Node_Id;
|
|
F : Entity_Id;
|
|
Etyp : Entity_Id;
|
|
|
|
Defer_Declaration : constant Boolean :=
|
|
Is_Tagged_Type (Ent) or else Is_Private_Type (Ent);
|
|
-- For a tagged type, there is a declaration for each stream attribute
|
|
-- at the freeze point, and we must generate only a completion of this
|
|
-- declaration. We do the same for private types, because the full view
|
|
-- might be tagged. Otherwise we generate a declaration at the point of
|
|
-- the attribute definition clause.
|
|
|
|
function Build_Spec return Node_Id;
|
|
-- Used for declaration and renaming declaration, so that this is
|
|
-- treated as a renaming_as_body.
|
|
|
|
----------------
|
|
-- Build_Spec --
|
|
----------------
|
|
|
|
function Build_Spec return Node_Id is
|
|
Out_P : constant Boolean := (Nam = TSS_Stream_Read);
|
|
Formals : List_Id;
|
|
Spec : Node_Id;
|
|
T_Ref : constant Node_Id := New_Occurrence_Of (Etyp, Loc);
|
|
|
|
begin
|
|
Subp_Id := Make_Defining_Identifier (Loc, Sname);
|
|
|
|
-- S : access Root_Stream_Type'Class
|
|
|
|
Formals := New_List (
|
|
Make_Parameter_Specification (Loc,
|
|
Defining_Identifier =>
|
|
Make_Defining_Identifier (Loc, Name_S),
|
|
Parameter_Type =>
|
|
Make_Access_Definition (Loc,
|
|
Subtype_Mark =>
|
|
New_Occurrence_Of (
|
|
Designated_Type (Etype (F)), Loc))));
|
|
|
|
if Nam = TSS_Stream_Input then
|
|
Spec :=
|
|
Make_Function_Specification (Loc,
|
|
Defining_Unit_Name => Subp_Id,
|
|
Parameter_Specifications => Formals,
|
|
Result_Definition => T_Ref);
|
|
else
|
|
-- V : [out] T
|
|
|
|
Append_To (Formals,
|
|
Make_Parameter_Specification (Loc,
|
|
Defining_Identifier => Make_Defining_Identifier (Loc, Name_V),
|
|
Out_Present => Out_P,
|
|
Parameter_Type => T_Ref));
|
|
|
|
Spec :=
|
|
Make_Procedure_Specification (Loc,
|
|
Defining_Unit_Name => Subp_Id,
|
|
Parameter_Specifications => Formals);
|
|
end if;
|
|
|
|
return Spec;
|
|
end Build_Spec;
|
|
|
|
-- Start of processing for New_Stream_Subprogram
|
|
|
|
begin
|
|
F := First_Formal (Subp);
|
|
|
|
if Ekind (Subp) = E_Procedure then
|
|
Etyp := Etype (Next_Formal (F));
|
|
else
|
|
Etyp := Etype (Subp);
|
|
end if;
|
|
|
|
-- Prepare subprogram declaration and insert it as an action on the
|
|
-- clause node. The visibility for this entity is used to test for
|
|
-- visibility of the attribute definition clause (in the sense of
|
|
-- 8.3(23) as amended by AI-195).
|
|
|
|
if not Defer_Declaration then
|
|
Subp_Decl :=
|
|
Make_Subprogram_Declaration (Loc,
|
|
Specification => Build_Spec);
|
|
|
|
-- For a tagged type, there is always a visible declaration for each
|
|
-- stream TSS (it is a predefined primitive operation), and the
|
|
-- completion of this declaration occurs at the freeze point, which is
|
|
-- not always visible at places where the attribute definition clause is
|
|
-- visible. So, we create a dummy entity here for the purpose of
|
|
-- tracking the visibility of the attribute definition clause itself.
|
|
|
|
else
|
|
Subp_Id :=
|
|
Make_Defining_Identifier (Loc, New_External_Name (Sname, 'V'));
|
|
Subp_Decl :=
|
|
Make_Object_Declaration (Loc,
|
|
Defining_Identifier => Subp_Id,
|
|
Object_Definition => New_Occurrence_Of (Standard_Boolean, Loc));
|
|
end if;
|
|
|
|
Insert_Action (N, Subp_Decl);
|
|
Set_Entity (N, Subp_Id);
|
|
|
|
Subp_Decl :=
|
|
Make_Subprogram_Renaming_Declaration (Loc,
|
|
Specification => Build_Spec,
|
|
Name => New_Occurrence_Of (Subp, Loc));
|
|
|
|
if Defer_Declaration then
|
|
Set_TSS (Base_Type (Ent), Subp_Id);
|
|
else
|
|
Insert_Action (N, Subp_Decl);
|
|
Copy_TSS (Subp_Id, Base_Type (Ent));
|
|
end if;
|
|
end New_Stream_Subprogram;
|
|
|
|
------------------------------------------
|
|
-- Push_Scope_And_Install_Discriminants --
|
|
------------------------------------------
|
|
|
|
procedure Push_Scope_And_Install_Discriminants (E : Entity_Id) is
|
|
begin
|
|
if Has_Discriminants (E) then
|
|
Push_Scope (E);
|
|
|
|
-- Make discriminants visible for type declarations and protected
|
|
-- type declarations, not for subtype declarations (RM 13.1.1 (12/3))
|
|
|
|
if Nkind (Parent (E)) /= N_Subtype_Declaration then
|
|
Install_Discriminants (E);
|
|
end if;
|
|
end if;
|
|
end Push_Scope_And_Install_Discriminants;
|
|
|
|
------------------------
|
|
-- Rep_Item_Too_Early --
|
|
------------------------
|
|
|
|
function Rep_Item_Too_Early (T : Entity_Id; N : Node_Id) return Boolean is
|
|
begin
|
|
-- Cannot apply non-operational rep items to generic types
|
|
|
|
if Is_Operational_Item (N) then
|
|
return False;
|
|
|
|
elsif Is_Type (T)
|
|
and then Is_Generic_Type (Root_Type (T))
|
|
then
|
|
Error_Msg_N ("representation item not allowed for generic type", N);
|
|
return True;
|
|
end if;
|
|
|
|
-- Otherwise check for incomplete type
|
|
|
|
if Is_Incomplete_Or_Private_Type (T)
|
|
and then No (Underlying_Type (T))
|
|
and then
|
|
(Nkind (N) /= N_Pragma
|
|
or else Get_Pragma_Id (N) /= Pragma_Import)
|
|
then
|
|
Error_Msg_N
|
|
("representation item must be after full type declaration", N);
|
|
return True;
|
|
|
|
-- If the type has incomplete components, a representation clause is
|
|
-- illegal but stream attributes and Convention pragmas are correct.
|
|
|
|
elsif Has_Private_Component (T) then
|
|
if Nkind (N) = N_Pragma then
|
|
return False;
|
|
|
|
else
|
|
Error_Msg_N
|
|
("representation item must appear after type is fully defined",
|
|
N);
|
|
return True;
|
|
end if;
|
|
else
|
|
return False;
|
|
end if;
|
|
end Rep_Item_Too_Early;
|
|
|
|
-----------------------
|
|
-- Rep_Item_Too_Late --
|
|
-----------------------
|
|
|
|
function Rep_Item_Too_Late
|
|
(T : Entity_Id;
|
|
N : Node_Id;
|
|
FOnly : Boolean := False) return Boolean
|
|
is
|
|
S : Entity_Id;
|
|
Parent_Type : Entity_Id;
|
|
|
|
procedure No_Type_Rep_Item;
|
|
-- Output message indicating that no type-related aspects can be
|
|
-- specified due to some property of the parent type.
|
|
|
|
procedure Too_Late;
|
|
-- Output message for an aspect being specified too late
|
|
|
|
-- Note that neither of the above errors is considered a serious one,
|
|
-- since the effect is simply that we ignore the representation clause
|
|
-- in these cases.
|
|
-- Is this really true? In any case if we make this change we must
|
|
-- document the requirement in the spec of Rep_Item_Too_Late that
|
|
-- if True is returned, then the rep item must be completely ignored???
|
|
|
|
----------------------
|
|
-- No_Type_Rep_Item --
|
|
----------------------
|
|
|
|
procedure No_Type_Rep_Item is
|
|
begin
|
|
Error_Msg_N ("|type-related representation item not permitted!", N);
|
|
end No_Type_Rep_Item;
|
|
|
|
--------------
|
|
-- Too_Late --
|
|
--------------
|
|
|
|
procedure Too_Late is
|
|
begin
|
|
-- Other compilers seem more relaxed about rep items appearing too
|
|
-- late. Since analysis tools typically don't care about rep items
|
|
-- anyway, no reason to be too strict about this.
|
|
|
|
if not Relaxed_RM_Semantics then
|
|
Error_Msg_N ("|representation item appears too late!", N);
|
|
end if;
|
|
end Too_Late;
|
|
|
|
-- Start of processing for Rep_Item_Too_Late
|
|
|
|
begin
|
|
-- First make sure entity is not frozen (RM 13.1(9))
|
|
|
|
if Is_Frozen (T)
|
|
|
|
-- Exclude imported types, which may be frozen if they appear in a
|
|
-- representation clause for a local type.
|
|
|
|
and then not From_Limited_With (T)
|
|
|
|
-- Exclude generated entities (not coming from source). The common
|
|
-- case is when we generate a renaming which prematurely freezes the
|
|
-- renamed internal entity, but we still want to be able to set copies
|
|
-- of attribute values such as Size/Alignment.
|
|
|
|
and then Comes_From_Source (T)
|
|
then
|
|
Too_Late;
|
|
S := First_Subtype (T);
|
|
|
|
if Present (Freeze_Node (S)) then
|
|
if not Relaxed_RM_Semantics then
|
|
Error_Msg_NE
|
|
("??no more representation items for }", Freeze_Node (S), S);
|
|
end if;
|
|
end if;
|
|
|
|
return True;
|
|
|
|
-- Check for case of non-tagged derived type whose parent either has
|
|
-- primitive operations, or is a by reference type (RM 13.1(10)). In
|
|
-- this case we do not output a Too_Late message, since there is no
|
|
-- earlier point where the rep item could be placed to make it legal.
|
|
|
|
elsif Is_Type (T)
|
|
and then not FOnly
|
|
and then Is_Derived_Type (T)
|
|
and then not Is_Tagged_Type (T)
|
|
then
|
|
Parent_Type := Etype (Base_Type (T));
|
|
|
|
if Has_Primitive_Operations (Parent_Type) then
|
|
No_Type_Rep_Item;
|
|
|
|
if not Relaxed_RM_Semantics then
|
|
Error_Msg_NE
|
|
("\parent type & has primitive operations!", N, Parent_Type);
|
|
end if;
|
|
|
|
return True;
|
|
|
|
elsif Is_By_Reference_Type (Parent_Type) then
|
|
No_Type_Rep_Item;
|
|
|
|
if not Relaxed_RM_Semantics then
|
|
Error_Msg_NE
|
|
("\parent type & is a by reference type!", N, Parent_Type);
|
|
end if;
|
|
|
|
return True;
|
|
end if;
|
|
end if;
|
|
|
|
-- No error, but one more warning to consider. The RM (surprisingly)
|
|
-- allows this pattern:
|
|
|
|
-- type S is ...
|
|
-- primitive operations for S
|
|
-- type R is new S;
|
|
-- rep clause for S
|
|
|
|
-- Meaning that calls on the primitive operations of S for values of
|
|
-- type R may require possibly expensive implicit conversion operations.
|
|
-- This is not an error, but is worth a warning.
|
|
|
|
if not Relaxed_RM_Semantics and then Is_Type (T) then
|
|
declare
|
|
DTL : constant Entity_Id := Derived_Type_Link (Base_Type (T));
|
|
|
|
begin
|
|
if Present (DTL)
|
|
and then Has_Primitive_Operations (Base_Type (T))
|
|
|
|
-- For now, do not generate this warning for the case of aspect
|
|
-- specification using Ada 2012 syntax, since we get wrong
|
|
-- messages we do not understand. The whole business of derived
|
|
-- types and rep items seems a bit confused when aspects are
|
|
-- used, since the aspects are not evaluated till freeze time.
|
|
|
|
and then not From_Aspect_Specification (N)
|
|
then
|
|
Error_Msg_Sloc := Sloc (DTL);
|
|
Error_Msg_N
|
|
("representation item for& appears after derived type "
|
|
& "declaration#??", N);
|
|
Error_Msg_NE
|
|
("\may result in implicit conversions for primitive "
|
|
& "operations of&??", N, T);
|
|
Error_Msg_NE
|
|
("\to change representations when called with arguments "
|
|
& "of type&??", N, DTL);
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
-- No error, link item into head of chain of rep items for the entity,
|
|
-- but avoid chaining if we have an overloadable entity, and the pragma
|
|
-- is one that can apply to multiple overloaded entities.
|
|
|
|
if Is_Overloadable (T) and then Nkind (N) = N_Pragma then
|
|
declare
|
|
Pname : constant Name_Id := Pragma_Name (N);
|
|
begin
|
|
if Nam_In (Pname, Name_Convention, Name_Import, Name_Export,
|
|
Name_External, Name_Interface)
|
|
then
|
|
return False;
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
Record_Rep_Item (T, N);
|
|
return False;
|
|
end Rep_Item_Too_Late;
|
|
|
|
-------------------------------------
|
|
-- Replace_Type_References_Generic --
|
|
-------------------------------------
|
|
|
|
procedure Replace_Type_References_Generic (N : Node_Id; T : Entity_Id) is
|
|
TName : constant Name_Id := Chars (T);
|
|
|
|
function Replace_Node (N : Node_Id) return Traverse_Result;
|
|
-- Processes a single node in the traversal procedure below, checking
|
|
-- if node N should be replaced, and if so, doing the replacement.
|
|
|
|
procedure Replace_Type_Refs is new Traverse_Proc (Replace_Node);
|
|
-- This instantiation provides the body of Replace_Type_References
|
|
|
|
------------------
|
|
-- Replace_Node --
|
|
------------------
|
|
|
|
function Replace_Node (N : Node_Id) return Traverse_Result is
|
|
S : Entity_Id;
|
|
P : Node_Id;
|
|
|
|
begin
|
|
-- Case of identifier
|
|
|
|
if Nkind (N) = N_Identifier then
|
|
|
|
-- If not the type name, check whether it is a reference to
|
|
-- some other type, which must be frozen before the predicate
|
|
-- function is analyzed, i.e. before the freeze node of the
|
|
-- type to which the predicate applies.
|
|
|
|
if Chars (N) /= TName then
|
|
if Present (Current_Entity (N))
|
|
and then Is_Type (Current_Entity (N))
|
|
then
|
|
Freeze_Before (Freeze_Node (T), Current_Entity (N));
|
|
end if;
|
|
|
|
return Skip;
|
|
|
|
-- Otherwise do the replacement and we are done with this node
|
|
|
|
else
|
|
Replace_Type_Reference (N);
|
|
return Skip;
|
|
end if;
|
|
|
|
-- Case of selected component (which is what a qualification
|
|
-- looks like in the unanalyzed tree, which is what we have.
|
|
|
|
elsif Nkind (N) = N_Selected_Component then
|
|
|
|
-- If selector name is not our type, keeping going (we might
|
|
-- still have an occurrence of the type in the prefix).
|
|
|
|
if Nkind (Selector_Name (N)) /= N_Identifier
|
|
or else Chars (Selector_Name (N)) /= TName
|
|
then
|
|
return OK;
|
|
|
|
-- Selector name is our type, check qualification
|
|
|
|
else
|
|
-- Loop through scopes and prefixes, doing comparison
|
|
|
|
S := Current_Scope;
|
|
P := Prefix (N);
|
|
loop
|
|
-- Continue if no more scopes or scope with no name
|
|
|
|
if No (S) or else Nkind (S) not in N_Has_Chars then
|
|
return OK;
|
|
end if;
|
|
|
|
-- Do replace if prefix is an identifier matching the
|
|
-- scope that we are currently looking at.
|
|
|
|
if Nkind (P) = N_Identifier
|
|
and then Chars (P) = Chars (S)
|
|
then
|
|
Replace_Type_Reference (N);
|
|
return Skip;
|
|
end if;
|
|
|
|
-- Go check scope above us if prefix is itself of the
|
|
-- form of a selected component, whose selector matches
|
|
-- the scope we are currently looking at.
|
|
|
|
if Nkind (P) = N_Selected_Component
|
|
and then Nkind (Selector_Name (P)) = N_Identifier
|
|
and then Chars (Selector_Name (P)) = Chars (S)
|
|
then
|
|
S := Scope (S);
|
|
P := Prefix (P);
|
|
|
|
-- For anything else, we don't have a match, so keep on
|
|
-- going, there are still some weird cases where we may
|
|
-- still have a replacement within the prefix.
|
|
|
|
else
|
|
return OK;
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
|
|
-- Continue for any other node kind
|
|
|
|
else
|
|
return OK;
|
|
end if;
|
|
end Replace_Node;
|
|
|
|
begin
|
|
Replace_Type_Refs (N);
|
|
end Replace_Type_References_Generic;
|
|
|
|
-------------------------
|
|
-- Same_Representation --
|
|
-------------------------
|
|
|
|
function Same_Representation (Typ1, Typ2 : Entity_Id) return Boolean is
|
|
T1 : constant Entity_Id := Underlying_Type (Typ1);
|
|
T2 : constant Entity_Id := Underlying_Type (Typ2);
|
|
|
|
begin
|
|
-- A quick check, if base types are the same, then we definitely have
|
|
-- the same representation, because the subtype specific representation
|
|
-- attributes (Size and Alignment) do not affect representation from
|
|
-- the point of view of this test.
|
|
|
|
if Base_Type (T1) = Base_Type (T2) then
|
|
return True;
|
|
|
|
elsif Is_Private_Type (Base_Type (T2))
|
|
and then Base_Type (T1) = Full_View (Base_Type (T2))
|
|
then
|
|
return True;
|
|
end if;
|
|
|
|
-- Tagged types never have differing representations
|
|
|
|
if Is_Tagged_Type (T1) then
|
|
return True;
|
|
end if;
|
|
|
|
-- Representations are definitely different if conventions differ
|
|
|
|
if Convention (T1) /= Convention (T2) then
|
|
return False;
|
|
end if;
|
|
|
|
-- Representations are different if component alignments or scalar
|
|
-- storage orders differ.
|
|
|
|
if (Is_Record_Type (T1) or else Is_Array_Type (T1))
|
|
and then
|
|
(Is_Record_Type (T2) or else Is_Array_Type (T2))
|
|
and then
|
|
(Component_Alignment (T1) /= Component_Alignment (T2)
|
|
or else
|
|
Reverse_Storage_Order (T1) /= Reverse_Storage_Order (T2))
|
|
then
|
|
return False;
|
|
end if;
|
|
|
|
-- For arrays, the only real issue is component size. If we know the
|
|
-- component size for both arrays, and it is the same, then that's
|
|
-- good enough to know we don't have a change of representation.
|
|
|
|
if Is_Array_Type (T1) then
|
|
if Known_Component_Size (T1)
|
|
and then Known_Component_Size (T2)
|
|
and then Component_Size (T1) = Component_Size (T2)
|
|
then
|
|
if VM_Target = No_VM then
|
|
return True;
|
|
|
|
-- In VM targets the representation of arrays with aliased
|
|
-- components differs from arrays with non-aliased components
|
|
|
|
else
|
|
return Has_Aliased_Components (Base_Type (T1))
|
|
=
|
|
Has_Aliased_Components (Base_Type (T2));
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
-- Types definitely have same representation if neither has non-standard
|
|
-- representation since default representations are always consistent.
|
|
-- If only one has non-standard representation, and the other does not,
|
|
-- then we consider that they do not have the same representation. They
|
|
-- might, but there is no way of telling early enough.
|
|
|
|
if Has_Non_Standard_Rep (T1) then
|
|
if not Has_Non_Standard_Rep (T2) then
|
|
return False;
|
|
end if;
|
|
else
|
|
return not Has_Non_Standard_Rep (T2);
|
|
end if;
|
|
|
|
-- Here the two types both have non-standard representation, and we need
|
|
-- to determine if they have the same non-standard representation.
|
|
|
|
-- For arrays, we simply need to test if the component sizes are the
|
|
-- same. Pragma Pack is reflected in modified component sizes, so this
|
|
-- check also deals with pragma Pack.
|
|
|
|
if Is_Array_Type (T1) then
|
|
return Component_Size (T1) = Component_Size (T2);
|
|
|
|
-- Tagged types always have the same representation, because it is not
|
|
-- possible to specify different representations for common fields.
|
|
|
|
elsif Is_Tagged_Type (T1) then
|
|
return True;
|
|
|
|
-- Case of record types
|
|
|
|
elsif Is_Record_Type (T1) then
|
|
|
|
-- Packed status must conform
|
|
|
|
if Is_Packed (T1) /= Is_Packed (T2) then
|
|
return False;
|
|
|
|
-- Otherwise we must check components. Typ2 maybe a constrained
|
|
-- subtype with fewer components, so we compare the components
|
|
-- of the base types.
|
|
|
|
else
|
|
Record_Case : declare
|
|
CD1, CD2 : Entity_Id;
|
|
|
|
function Same_Rep return Boolean;
|
|
-- CD1 and CD2 are either components or discriminants. This
|
|
-- function tests whether they have the same representation.
|
|
|
|
--------------
|
|
-- Same_Rep --
|
|
--------------
|
|
|
|
function Same_Rep return Boolean is
|
|
begin
|
|
if No (Component_Clause (CD1)) then
|
|
return No (Component_Clause (CD2));
|
|
else
|
|
-- Note: at this point, component clauses have been
|
|
-- normalized to the default bit order, so that the
|
|
-- comparison of Component_Bit_Offsets is meaningful.
|
|
|
|
return
|
|
Present (Component_Clause (CD2))
|
|
and then
|
|
Component_Bit_Offset (CD1) = Component_Bit_Offset (CD2)
|
|
and then
|
|
Esize (CD1) = Esize (CD2);
|
|
end if;
|
|
end Same_Rep;
|
|
|
|
-- Start of processing for Record_Case
|
|
|
|
begin
|
|
if Has_Discriminants (T1) then
|
|
|
|
-- The number of discriminants may be different if the
|
|
-- derived type has fewer (constrained by values). The
|
|
-- invisible discriminants retain the representation of
|
|
-- the original, so the discrepancy does not per se
|
|
-- indicate a different representation.
|
|
|
|
CD1 := First_Discriminant (T1);
|
|
CD2 := First_Discriminant (T2);
|
|
while Present (CD1) and then Present (CD2) loop
|
|
if not Same_Rep then
|
|
return False;
|
|
else
|
|
Next_Discriminant (CD1);
|
|
Next_Discriminant (CD2);
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
|
|
CD1 := First_Component (Underlying_Type (Base_Type (T1)));
|
|
CD2 := First_Component (Underlying_Type (Base_Type (T2)));
|
|
while Present (CD1) loop
|
|
if not Same_Rep then
|
|
return False;
|
|
else
|
|
Next_Component (CD1);
|
|
Next_Component (CD2);
|
|
end if;
|
|
end loop;
|
|
|
|
return True;
|
|
end Record_Case;
|
|
end if;
|
|
|
|
-- For enumeration types, we must check each literal to see if the
|
|
-- representation is the same. Note that we do not permit enumeration
|
|
-- representation clauses for Character and Wide_Character, so these
|
|
-- cases were already dealt with.
|
|
|
|
elsif Is_Enumeration_Type (T1) then
|
|
Enumeration_Case : declare
|
|
L1, L2 : Entity_Id;
|
|
|
|
begin
|
|
L1 := First_Literal (T1);
|
|
L2 := First_Literal (T2);
|
|
while Present (L1) loop
|
|
if Enumeration_Rep (L1) /= Enumeration_Rep (L2) then
|
|
return False;
|
|
else
|
|
Next_Literal (L1);
|
|
Next_Literal (L2);
|
|
end if;
|
|
end loop;
|
|
|
|
return True;
|
|
end Enumeration_Case;
|
|
|
|
-- Any other types have the same representation for these purposes
|
|
|
|
else
|
|
return True;
|
|
end if;
|
|
end Same_Representation;
|
|
|
|
--------------------------------
|
|
-- Resolve_Iterable_Operation --
|
|
--------------------------------
|
|
|
|
procedure Resolve_Iterable_Operation
|
|
(N : Node_Id;
|
|
Cursor : Entity_Id;
|
|
Typ : Entity_Id;
|
|
Nam : Name_Id)
|
|
is
|
|
Ent : Entity_Id;
|
|
F1 : Entity_Id;
|
|
F2 : Entity_Id;
|
|
|
|
begin
|
|
if not Is_Overloaded (N) then
|
|
if not Is_Entity_Name (N)
|
|
or else Ekind (Entity (N)) /= E_Function
|
|
or else Scope (Entity (N)) /= Scope (Typ)
|
|
or else No (First_Formal (Entity (N)))
|
|
or else Etype (First_Formal (Entity (N))) /= Typ
|
|
then
|
|
Error_Msg_N ("iterable primitive must be local function name "
|
|
& "whose first formal is an iterable type", N);
|
|
return;
|
|
end if;
|
|
|
|
Ent := Entity (N);
|
|
F1 := First_Formal (Ent);
|
|
if Nam = Name_First then
|
|
|
|
-- First (Container) => Cursor
|
|
|
|
if Etype (Ent) /= Cursor then
|
|
Error_Msg_N ("primitive for First must yield a curosr", N);
|
|
end if;
|
|
|
|
elsif Nam = Name_Next then
|
|
|
|
-- Next (Container, Cursor) => Cursor
|
|
|
|
F2 := Next_Formal (F1);
|
|
|
|
if Etype (F2) /= Cursor
|
|
or else Etype (Ent) /= Cursor
|
|
or else Present (Next_Formal (F2))
|
|
then
|
|
Error_Msg_N ("no match for Next iterable primitive", N);
|
|
end if;
|
|
|
|
elsif Nam = Name_Has_Element then
|
|
|
|
-- Has_Element (Container, Cursor) => Boolean
|
|
|
|
F2 := Next_Formal (F1);
|
|
if Etype (F2) /= Cursor
|
|
or else Etype (Ent) /= Standard_Boolean
|
|
or else Present (Next_Formal (F2))
|
|
then
|
|
Error_Msg_N ("no match for Has_Element iterable primitive", N);
|
|
end if;
|
|
|
|
elsif Nam = Name_Element then
|
|
F2 := Next_Formal (F1);
|
|
|
|
if No (F2)
|
|
or else Etype (F2) /= Cursor
|
|
or else Present (Next_Formal (F2))
|
|
then
|
|
Error_Msg_N ("no match for Element iterable primitive", N);
|
|
end if;
|
|
null;
|
|
|
|
else
|
|
raise Program_Error;
|
|
end if;
|
|
|
|
else
|
|
-- Overloaded case: find subprogram with proper signature.
|
|
-- Caller will report error if no match is found.
|
|
|
|
declare
|
|
I : Interp_Index;
|
|
It : Interp;
|
|
|
|
begin
|
|
Get_First_Interp (N, I, It);
|
|
while Present (It.Typ) loop
|
|
if Ekind (It.Nam) = E_Function
|
|
and then Scope (It.Nam) = Scope (Typ)
|
|
and then Etype (First_Formal (It.Nam)) = Typ
|
|
then
|
|
F1 := First_Formal (It.Nam);
|
|
|
|
if Nam = Name_First then
|
|
if Etype (It.Nam) = Cursor
|
|
and then No (Next_Formal (F1))
|
|
then
|
|
Set_Entity (N, It.Nam);
|
|
exit;
|
|
end if;
|
|
|
|
elsif Nam = Name_Next then
|
|
F2 := Next_Formal (F1);
|
|
|
|
if Present (F2)
|
|
and then No (Next_Formal (F2))
|
|
and then Etype (F2) = Cursor
|
|
and then Etype (It.Nam) = Cursor
|
|
then
|
|
Set_Entity (N, It.Nam);
|
|
exit;
|
|
end if;
|
|
|
|
elsif Nam = Name_Has_Element then
|
|
F2 := Next_Formal (F1);
|
|
|
|
if Present (F2)
|
|
and then No (Next_Formal (F2))
|
|
and then Etype (F2) = Cursor
|
|
and then Etype (It.Nam) = Standard_Boolean
|
|
then
|
|
Set_Entity (N, It.Nam);
|
|
F2 := Next_Formal (F1);
|
|
exit;
|
|
end if;
|
|
|
|
elsif Nam = Name_Element then
|
|
F2 := Next_Formal (F1);
|
|
|
|
if Present (F2)
|
|
and then No (Next_Formal (F2))
|
|
and then Etype (F2) = Cursor
|
|
then
|
|
Set_Entity (N, It.Nam);
|
|
exit;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
Get_Next_Interp (I, It);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
end Resolve_Iterable_Operation;
|
|
|
|
----------------
|
|
-- Set_Biased --
|
|
----------------
|
|
|
|
procedure Set_Biased
|
|
(E : Entity_Id;
|
|
N : Node_Id;
|
|
Msg : String;
|
|
Biased : Boolean := True)
|
|
is
|
|
begin
|
|
if Biased then
|
|
Set_Has_Biased_Representation (E);
|
|
|
|
if Warn_On_Biased_Representation then
|
|
Error_Msg_NE
|
|
("?B?" & Msg & " forces biased representation for&", N, E);
|
|
end if;
|
|
end if;
|
|
end Set_Biased;
|
|
|
|
--------------------
|
|
-- Set_Enum_Esize --
|
|
--------------------
|
|
|
|
procedure Set_Enum_Esize (T : Entity_Id) is
|
|
Lo : Uint;
|
|
Hi : Uint;
|
|
Sz : Nat;
|
|
|
|
begin
|
|
Init_Alignment (T);
|
|
|
|
-- Find the minimum standard size (8,16,32,64) that fits
|
|
|
|
Lo := Enumeration_Rep (Entity (Type_Low_Bound (T)));
|
|
Hi := Enumeration_Rep (Entity (Type_High_Bound (T)));
|
|
|
|
if Lo < 0 then
|
|
if Lo >= -Uint_2**07 and then Hi < Uint_2**07 then
|
|
Sz := Standard_Character_Size; -- May be > 8 on some targets
|
|
|
|
elsif Lo >= -Uint_2**15 and then Hi < Uint_2**15 then
|
|
Sz := 16;
|
|
|
|
elsif Lo >= -Uint_2**31 and then Hi < Uint_2**31 then
|
|
Sz := 32;
|
|
|
|
else pragma Assert (Lo >= -Uint_2**63 and then Hi < Uint_2**63);
|
|
Sz := 64;
|
|
end if;
|
|
|
|
else
|
|
if Hi < Uint_2**08 then
|
|
Sz := Standard_Character_Size; -- May be > 8 on some targets
|
|
|
|
elsif Hi < Uint_2**16 then
|
|
Sz := 16;
|
|
|
|
elsif Hi < Uint_2**32 then
|
|
Sz := 32;
|
|
|
|
else pragma Assert (Hi < Uint_2**63);
|
|
Sz := 64;
|
|
end if;
|
|
end if;
|
|
|
|
-- That minimum is the proper size unless we have a foreign convention
|
|
-- and the size required is 32 or less, in which case we bump the size
|
|
-- up to 32. This is required for C and C++ and seems reasonable for
|
|
-- all other foreign conventions.
|
|
|
|
if Has_Foreign_Convention (T)
|
|
and then Esize (T) < Standard_Integer_Size
|
|
|
|
-- Don't do this if Short_Enums on target
|
|
|
|
and then not Target_Short_Enums
|
|
then
|
|
Init_Esize (T, Standard_Integer_Size);
|
|
else
|
|
Init_Esize (T, Sz);
|
|
end if;
|
|
end Set_Enum_Esize;
|
|
|
|
-----------------------------
|
|
-- Uninstall_Discriminants --
|
|
-----------------------------
|
|
|
|
procedure Uninstall_Discriminants (E : Entity_Id) is
|
|
Disc : Entity_Id;
|
|
Prev : Entity_Id;
|
|
Outer : Entity_Id;
|
|
|
|
begin
|
|
-- Discriminants have been made visible for type declarations and
|
|
-- protected type declarations, not for subtype declarations.
|
|
|
|
if Nkind (Parent (E)) /= N_Subtype_Declaration then
|
|
Disc := First_Discriminant (E);
|
|
while Present (Disc) loop
|
|
if Disc /= Current_Entity (Disc) then
|
|
Prev := Current_Entity (Disc);
|
|
while Present (Prev)
|
|
and then Present (Homonym (Prev))
|
|
and then Homonym (Prev) /= Disc
|
|
loop
|
|
Prev := Homonym (Prev);
|
|
end loop;
|
|
else
|
|
Prev := Empty;
|
|
end if;
|
|
|
|
Set_Is_Immediately_Visible (Disc, False);
|
|
|
|
Outer := Homonym (Disc);
|
|
while Present (Outer) and then Scope (Outer) = E loop
|
|
Outer := Homonym (Outer);
|
|
end loop;
|
|
|
|
-- Reset homonym link of other entities, but do not modify link
|
|
-- between entities in current scope, so that the back-end can
|
|
-- have a proper count of local overloadings.
|
|
|
|
if No (Prev) then
|
|
Set_Name_Entity_Id (Chars (Disc), Outer);
|
|
|
|
elsif Scope (Prev) /= Scope (Disc) then
|
|
Set_Homonym (Prev, Outer);
|
|
end if;
|
|
|
|
Next_Discriminant (Disc);
|
|
end loop;
|
|
end if;
|
|
end Uninstall_Discriminants;
|
|
|
|
-------------------------------------------
|
|
-- Uninstall_Discriminants_And_Pop_Scope --
|
|
-------------------------------------------
|
|
|
|
procedure Uninstall_Discriminants_And_Pop_Scope (E : Entity_Id) is
|
|
begin
|
|
if Has_Discriminants (E) then
|
|
Uninstall_Discriminants (E);
|
|
Pop_Scope;
|
|
end if;
|
|
end Uninstall_Discriminants_And_Pop_Scope;
|
|
|
|
------------------------------
|
|
-- Validate_Address_Clauses --
|
|
------------------------------
|
|
|
|
procedure Validate_Address_Clauses is
|
|
begin
|
|
for J in Address_Clause_Checks.First .. Address_Clause_Checks.Last loop
|
|
declare
|
|
ACCR : Address_Clause_Check_Record
|
|
renames Address_Clause_Checks.Table (J);
|
|
|
|
Expr : Node_Id;
|
|
|
|
X_Alignment : Uint;
|
|
Y_Alignment : Uint;
|
|
|
|
X_Size : Uint;
|
|
Y_Size : Uint;
|
|
|
|
begin
|
|
-- Skip processing of this entry if warning already posted
|
|
|
|
if not Address_Warning_Posted (ACCR.N) then
|
|
Expr := Original_Node (Expression (ACCR.N));
|
|
|
|
-- Get alignments
|
|
|
|
X_Alignment := Alignment (ACCR.X);
|
|
Y_Alignment := Alignment (ACCR.Y);
|
|
|
|
-- Similarly obtain sizes
|
|
|
|
X_Size := Esize (ACCR.X);
|
|
Y_Size := Esize (ACCR.Y);
|
|
|
|
-- Check for large object overlaying smaller one
|
|
|
|
if Y_Size > Uint_0
|
|
and then X_Size > Uint_0
|
|
and then X_Size > Y_Size
|
|
then
|
|
Error_Msg_NE
|
|
("??& overlays smaller object", ACCR.N, ACCR.X);
|
|
Error_Msg_N
|
|
("\??program execution may be erroneous", ACCR.N);
|
|
Error_Msg_Uint_1 := X_Size;
|
|
Error_Msg_NE
|
|
("\??size of & is ^", ACCR.N, ACCR.X);
|
|
Error_Msg_Uint_1 := Y_Size;
|
|
Error_Msg_NE
|
|
("\??size of & is ^", ACCR.N, ACCR.Y);
|
|
|
|
-- Check for inadequate alignment, both of the base object
|
|
-- and of the offset, if any.
|
|
|
|
-- Note: we do not check the alignment if we gave a size
|
|
-- warning, since it would likely be redundant.
|
|
|
|
elsif Y_Alignment /= Uint_0
|
|
and then (Y_Alignment < X_Alignment
|
|
or else (ACCR.Off
|
|
and then
|
|
Nkind (Expr) = N_Attribute_Reference
|
|
and then
|
|
Attribute_Name (Expr) = Name_Address
|
|
and then
|
|
Has_Compatible_Alignment
|
|
(ACCR.X, Prefix (Expr))
|
|
/= Known_Compatible))
|
|
then
|
|
Error_Msg_NE
|
|
("??specified address for& may be inconsistent "
|
|
& "with alignment", ACCR.N, ACCR.X);
|
|
Error_Msg_N
|
|
("\??program execution may be erroneous (RM 13.3(27))",
|
|
ACCR.N);
|
|
Error_Msg_Uint_1 := X_Alignment;
|
|
Error_Msg_NE
|
|
("\??alignment of & is ^", ACCR.N, ACCR.X);
|
|
Error_Msg_Uint_1 := Y_Alignment;
|
|
Error_Msg_NE
|
|
("\??alignment of & is ^", ACCR.N, ACCR.Y);
|
|
if Y_Alignment >= X_Alignment then
|
|
Error_Msg_N
|
|
("\??but offset is not multiple of alignment", ACCR.N);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end;
|
|
end loop;
|
|
end Validate_Address_Clauses;
|
|
|
|
---------------------------
|
|
-- Validate_Independence --
|
|
---------------------------
|
|
|
|
procedure Validate_Independence is
|
|
SU : constant Uint := UI_From_Int (System_Storage_Unit);
|
|
N : Node_Id;
|
|
E : Entity_Id;
|
|
IC : Boolean;
|
|
Comp : Entity_Id;
|
|
Addr : Node_Id;
|
|
P : Node_Id;
|
|
|
|
procedure Check_Array_Type (Atyp : Entity_Id);
|
|
-- Checks if the array type Atyp has independent components, and
|
|
-- if not, outputs an appropriate set of error messages.
|
|
|
|
procedure No_Independence;
|
|
-- Output message that independence cannot be guaranteed
|
|
|
|
function OK_Component (C : Entity_Id) return Boolean;
|
|
-- Checks one component to see if it is independently accessible, and
|
|
-- if so yields True, otherwise yields False if independent access
|
|
-- cannot be guaranteed. This is a conservative routine, it only
|
|
-- returns True if it knows for sure, it returns False if it knows
|
|
-- there is a problem, or it cannot be sure there is no problem.
|
|
|
|
procedure Reason_Bad_Component (C : Entity_Id);
|
|
-- Outputs continuation message if a reason can be determined for
|
|
-- the component C being bad.
|
|
|
|
----------------------
|
|
-- Check_Array_Type --
|
|
----------------------
|
|
|
|
procedure Check_Array_Type (Atyp : Entity_Id) is
|
|
Ctyp : constant Entity_Id := Component_Type (Atyp);
|
|
|
|
begin
|
|
-- OK if no alignment clause, no pack, and no component size
|
|
|
|
if not Has_Component_Size_Clause (Atyp)
|
|
and then not Has_Alignment_Clause (Atyp)
|
|
and then not Is_Packed (Atyp)
|
|
then
|
|
return;
|
|
end if;
|
|
|
|
-- Check actual component size
|
|
|
|
if not Known_Component_Size (Atyp)
|
|
or else not (Addressable (Component_Size (Atyp))
|
|
and then Component_Size (Atyp) < 64)
|
|
or else Component_Size (Atyp) mod Esize (Ctyp) /= 0
|
|
then
|
|
No_Independence;
|
|
|
|
-- Bad component size, check reason
|
|
|
|
if Has_Component_Size_Clause (Atyp) then
|
|
P := Get_Attribute_Definition_Clause
|
|
(Atyp, Attribute_Component_Size);
|
|
|
|
if Present (P) then
|
|
Error_Msg_Sloc := Sloc (P);
|
|
Error_Msg_N ("\because of Component_Size clause#", N);
|
|
return;
|
|
end if;
|
|
end if;
|
|
|
|
if Is_Packed (Atyp) then
|
|
P := Get_Rep_Pragma (Atyp, Name_Pack);
|
|
|
|
if Present (P) then
|
|
Error_Msg_Sloc := Sloc (P);
|
|
Error_Msg_N ("\because of pragma Pack#", N);
|
|
return;
|
|
end if;
|
|
end if;
|
|
|
|
-- No reason found, just return
|
|
|
|
return;
|
|
end if;
|
|
|
|
-- Array type is OK independence-wise
|
|
|
|
return;
|
|
end Check_Array_Type;
|
|
|
|
---------------------
|
|
-- No_Independence --
|
|
---------------------
|
|
|
|
procedure No_Independence is
|
|
begin
|
|
if Pragma_Name (N) = Name_Independent then
|
|
Error_Msg_NE ("independence cannot be guaranteed for&", N, E);
|
|
else
|
|
Error_Msg_NE
|
|
("independent components cannot be guaranteed for&", N, E);
|
|
end if;
|
|
end No_Independence;
|
|
|
|
------------------
|
|
-- OK_Component --
|
|
------------------
|
|
|
|
function OK_Component (C : Entity_Id) return Boolean is
|
|
Rec : constant Entity_Id := Scope (C);
|
|
Ctyp : constant Entity_Id := Etype (C);
|
|
|
|
begin
|
|
-- OK if no component clause, no Pack, and no alignment clause
|
|
|
|
if No (Component_Clause (C))
|
|
and then not Is_Packed (Rec)
|
|
and then not Has_Alignment_Clause (Rec)
|
|
then
|
|
return True;
|
|
end if;
|
|
|
|
-- Here we look at the actual component layout. A component is
|
|
-- addressable if its size is a multiple of the Esize of the
|
|
-- component type, and its starting position in the record has
|
|
-- appropriate alignment, and the record itself has appropriate
|
|
-- alignment to guarantee the component alignment.
|
|
|
|
-- Make sure sizes are static, always assume the worst for any
|
|
-- cases where we cannot check static values.
|
|
|
|
if not (Known_Static_Esize (C)
|
|
and then
|
|
Known_Static_Esize (Ctyp))
|
|
then
|
|
return False;
|
|
end if;
|
|
|
|
-- Size of component must be addressable or greater than 64 bits
|
|
-- and a multiple of bytes.
|
|
|
|
if not Addressable (Esize (C)) and then Esize (C) < Uint_64 then
|
|
return False;
|
|
end if;
|
|
|
|
-- Check size is proper multiple
|
|
|
|
if Esize (C) mod Esize (Ctyp) /= 0 then
|
|
return False;
|
|
end if;
|
|
|
|
-- Check alignment of component is OK
|
|
|
|
if not Known_Component_Bit_Offset (C)
|
|
or else Component_Bit_Offset (C) < Uint_0
|
|
or else Component_Bit_Offset (C) mod Esize (Ctyp) /= 0
|
|
then
|
|
return False;
|
|
end if;
|
|
|
|
-- Check alignment of record type is OK
|
|
|
|
if not Known_Alignment (Rec)
|
|
or else (Alignment (Rec) * SU) mod Esize (Ctyp) /= 0
|
|
then
|
|
return False;
|
|
end if;
|
|
|
|
-- All tests passed, component is addressable
|
|
|
|
return True;
|
|
end OK_Component;
|
|
|
|
--------------------------
|
|
-- Reason_Bad_Component --
|
|
--------------------------
|
|
|
|
procedure Reason_Bad_Component (C : Entity_Id) is
|
|
Rec : constant Entity_Id := Scope (C);
|
|
Ctyp : constant Entity_Id := Etype (C);
|
|
|
|
begin
|
|
-- If component clause present assume that's the problem
|
|
|
|
if Present (Component_Clause (C)) then
|
|
Error_Msg_Sloc := Sloc (Component_Clause (C));
|
|
Error_Msg_N ("\because of Component_Clause#", N);
|
|
return;
|
|
end if;
|
|
|
|
-- If pragma Pack clause present, assume that's the problem
|
|
|
|
if Is_Packed (Rec) then
|
|
P := Get_Rep_Pragma (Rec, Name_Pack);
|
|
|
|
if Present (P) then
|
|
Error_Msg_Sloc := Sloc (P);
|
|
Error_Msg_N ("\because of pragma Pack#", N);
|
|
return;
|
|
end if;
|
|
end if;
|
|
|
|
-- See if record has bad alignment clause
|
|
|
|
if Has_Alignment_Clause (Rec)
|
|
and then Known_Alignment (Rec)
|
|
and then (Alignment (Rec) * SU) mod Esize (Ctyp) /= 0
|
|
then
|
|
P := Get_Attribute_Definition_Clause (Rec, Attribute_Alignment);
|
|
|
|
if Present (P) then
|
|
Error_Msg_Sloc := Sloc (P);
|
|
Error_Msg_N ("\because of Alignment clause#", N);
|
|
end if;
|
|
end if;
|
|
|
|
-- Couldn't find a reason, so return without a message
|
|
|
|
return;
|
|
end Reason_Bad_Component;
|
|
|
|
-- Start of processing for Validate_Independence
|
|
|
|
begin
|
|
for J in Independence_Checks.First .. Independence_Checks.Last loop
|
|
N := Independence_Checks.Table (J).N;
|
|
E := Independence_Checks.Table (J).E;
|
|
IC := Pragma_Name (N) = Name_Independent_Components;
|
|
|
|
-- Deal with component case
|
|
|
|
if Ekind (E) = E_Discriminant or else Ekind (E) = E_Component then
|
|
if not OK_Component (E) then
|
|
No_Independence;
|
|
Reason_Bad_Component (E);
|
|
goto Continue;
|
|
end if;
|
|
end if;
|
|
|
|
-- Deal with record with Independent_Components
|
|
|
|
if IC and then Is_Record_Type (E) then
|
|
Comp := First_Component_Or_Discriminant (E);
|
|
while Present (Comp) loop
|
|
if not OK_Component (Comp) then
|
|
No_Independence;
|
|
Reason_Bad_Component (Comp);
|
|
goto Continue;
|
|
end if;
|
|
|
|
Next_Component_Or_Discriminant (Comp);
|
|
end loop;
|
|
end if;
|
|
|
|
-- Deal with address clause case
|
|
|
|
if Is_Object (E) then
|
|
Addr := Address_Clause (E);
|
|
|
|
if Present (Addr) then
|
|
No_Independence;
|
|
Error_Msg_Sloc := Sloc (Addr);
|
|
Error_Msg_N ("\because of Address clause#", N);
|
|
goto Continue;
|
|
end if;
|
|
end if;
|
|
|
|
-- Deal with independent components for array type
|
|
|
|
if IC and then Is_Array_Type (E) then
|
|
Check_Array_Type (E);
|
|
end if;
|
|
|
|
-- Deal with independent components for array object
|
|
|
|
if IC and then Is_Object (E) and then Is_Array_Type (Etype (E)) then
|
|
Check_Array_Type (Etype (E));
|
|
end if;
|
|
|
|
<<Continue>> null;
|
|
end loop;
|
|
end Validate_Independence;
|
|
|
|
------------------------------
|
|
-- Validate_Iterable_Aspect --
|
|
------------------------------
|
|
|
|
procedure Validate_Iterable_Aspect (Typ : Entity_Id; ASN : Node_Id) is
|
|
Assoc : Node_Id;
|
|
Expr : Node_Id;
|
|
|
|
Prim : Node_Id;
|
|
Cursor : constant Entity_Id := Get_Cursor_Type (ASN, Typ);
|
|
|
|
First_Id : Entity_Id;
|
|
Next_Id : Entity_Id;
|
|
Has_Element_Id : Entity_Id;
|
|
Element_Id : Entity_Id;
|
|
|
|
begin
|
|
-- If previous error aspect is unusable
|
|
|
|
if Cursor = Any_Type then
|
|
return;
|
|
end if;
|
|
|
|
First_Id := Empty;
|
|
Next_Id := Empty;
|
|
Has_Element_Id := Empty;
|
|
Element_Id := Empty;
|
|
|
|
-- Each expression must resolve to a function with the proper signature
|
|
|
|
Assoc := First (Component_Associations (Expression (ASN)));
|
|
while Present (Assoc) loop
|
|
Expr := Expression (Assoc);
|
|
Analyze (Expr);
|
|
|
|
Prim := First (Choices (Assoc));
|
|
|
|
if Nkind (Prim) /= N_Identifier
|
|
or else Present (Next (Prim))
|
|
then
|
|
Error_Msg_N ("illegal name in association", Prim);
|
|
|
|
elsif Chars (Prim) = Name_First then
|
|
Resolve_Iterable_Operation (Expr, Cursor, Typ, Name_First);
|
|
First_Id := Entity (Expr);
|
|
|
|
elsif Chars (Prim) = Name_Next then
|
|
Resolve_Iterable_Operation (Expr, Cursor, Typ, Name_Next);
|
|
Next_Id := Entity (Expr);
|
|
|
|
elsif Chars (Prim) = Name_Has_Element then
|
|
Resolve_Iterable_Operation (Expr, Cursor, Typ, Name_Has_Element);
|
|
Has_Element_Id := Entity (Expr);
|
|
|
|
elsif Chars (Prim) = Name_Element then
|
|
Resolve_Iterable_Operation (Expr, Cursor, Typ, Name_Element);
|
|
Element_Id := Entity (Expr);
|
|
|
|
else
|
|
Error_Msg_N ("invalid name for iterable function", Prim);
|
|
end if;
|
|
|
|
Next (Assoc);
|
|
end loop;
|
|
|
|
if No (First_Id) then
|
|
Error_Msg_N ("match for First primitive not found", ASN);
|
|
|
|
elsif No (Next_Id) then
|
|
Error_Msg_N ("match for Next primitive not found", ASN);
|
|
|
|
elsif No (Has_Element_Id) then
|
|
Error_Msg_N ("match for Has_Element primitive not found", ASN);
|
|
|
|
elsif No (Element_Id) then
|
|
null; -- Optional.
|
|
end if;
|
|
end Validate_Iterable_Aspect;
|
|
|
|
-----------------------------------
|
|
-- Validate_Unchecked_Conversion --
|
|
-----------------------------------
|
|
|
|
procedure Validate_Unchecked_Conversion
|
|
(N : Node_Id;
|
|
Act_Unit : Entity_Id)
|
|
is
|
|
Source : Entity_Id;
|
|
Target : Entity_Id;
|
|
Vnode : Node_Id;
|
|
|
|
begin
|
|
-- Obtain source and target types. Note that we call Ancestor_Subtype
|
|
-- here because the processing for generic instantiation always makes
|
|
-- subtypes, and we want the original frozen actual types.
|
|
|
|
-- If we are dealing with private types, then do the check on their
|
|
-- fully declared counterparts if the full declarations have been
|
|
-- encountered (they don't have to be visible, but they must exist).
|
|
|
|
Source := Ancestor_Subtype (Etype (First_Formal (Act_Unit)));
|
|
|
|
if Is_Private_Type (Source)
|
|
and then Present (Underlying_Type (Source))
|
|
then
|
|
Source := Underlying_Type (Source);
|
|
end if;
|
|
|
|
Target := Ancestor_Subtype (Etype (Act_Unit));
|
|
|
|
-- If either type is generic, the instantiation happens within a generic
|
|
-- unit, and there is nothing to check. The proper check will happen
|
|
-- when the enclosing generic is instantiated.
|
|
|
|
if Is_Generic_Type (Source) or else Is_Generic_Type (Target) then
|
|
return;
|
|
end if;
|
|
|
|
if Is_Private_Type (Target)
|
|
and then Present (Underlying_Type (Target))
|
|
then
|
|
Target := Underlying_Type (Target);
|
|
end if;
|
|
|
|
-- Source may be unconstrained array, but not target
|
|
|
|
if Is_Array_Type (Target) and then not Is_Constrained (Target) then
|
|
Error_Msg_N
|
|
("unchecked conversion to unconstrained array not allowed", N);
|
|
return;
|
|
end if;
|
|
|
|
-- Warn if conversion between two different convention pointers
|
|
|
|
if Is_Access_Type (Target)
|
|
and then Is_Access_Type (Source)
|
|
and then Convention (Target) /= Convention (Source)
|
|
and then Warn_On_Unchecked_Conversion
|
|
then
|
|
-- Give warnings for subprogram pointers only on most targets. The
|
|
-- exception is VMS, where data pointers can have different lengths
|
|
-- depending on the pointer convention.
|
|
|
|
if Is_Access_Subprogram_Type (Target)
|
|
or else Is_Access_Subprogram_Type (Source)
|
|
or else OpenVMS_On_Target
|
|
then
|
|
Error_Msg_N
|
|
("?z?conversion between pointers with different conventions!",
|
|
N);
|
|
end if;
|
|
end if;
|
|
|
|
-- Warn if one of the operands is Ada.Calendar.Time. Do not emit a
|
|
-- warning when compiling GNAT-related sources.
|
|
|
|
if Warn_On_Unchecked_Conversion
|
|
and then not In_Predefined_Unit (N)
|
|
and then RTU_Loaded (Ada_Calendar)
|
|
and then
|
|
(Chars (Source) = Name_Time
|
|
or else
|
|
Chars (Target) = Name_Time)
|
|
then
|
|
-- If Ada.Calendar is loaded and the name of one of the operands is
|
|
-- Time, there is a good chance that this is Ada.Calendar.Time.
|
|
|
|
declare
|
|
Calendar_Time : constant Entity_Id :=
|
|
Full_View (RTE (RO_CA_Time));
|
|
begin
|
|
pragma Assert (Present (Calendar_Time));
|
|
|
|
if Source = Calendar_Time or else Target = Calendar_Time then
|
|
Error_Msg_N
|
|
("?z?representation of 'Time values may change between " &
|
|
"'G'N'A'T versions", N);
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
-- Make entry in unchecked conversion table for later processing by
|
|
-- Validate_Unchecked_Conversions, which will check sizes and alignments
|
|
-- (using values set by the back-end where possible). This is only done
|
|
-- if the appropriate warning is active.
|
|
|
|
if Warn_On_Unchecked_Conversion then
|
|
Unchecked_Conversions.Append
|
|
(New_Val => UC_Entry'(Eloc => Sloc (N),
|
|
Source => Source,
|
|
Target => Target,
|
|
Act_Unit => Act_Unit));
|
|
|
|
-- If both sizes are known statically now, then back end annotation
|
|
-- is not required to do a proper check but if either size is not
|
|
-- known statically, then we need the annotation.
|
|
|
|
if Known_Static_RM_Size (Source)
|
|
and then
|
|
Known_Static_RM_Size (Target)
|
|
then
|
|
null;
|
|
else
|
|
Back_Annotate_Rep_Info := True;
|
|
end if;
|
|
end if;
|
|
|
|
-- If unchecked conversion to access type, and access type is declared
|
|
-- in the same unit as the unchecked conversion, then set the flag
|
|
-- No_Strict_Aliasing (no strict aliasing is implicit here)
|
|
|
|
if Is_Access_Type (Target) and then
|
|
In_Same_Source_Unit (Target, N)
|
|
then
|
|
Set_No_Strict_Aliasing (Implementation_Base_Type (Target));
|
|
end if;
|
|
|
|
-- Generate N_Validate_Unchecked_Conversion node for back end in case
|
|
-- the back end needs to perform special validation checks.
|
|
|
|
-- Shouldn't this be in Exp_Ch13, since the check only gets done if we
|
|
-- have full expansion and the back end is called ???
|
|
|
|
Vnode :=
|
|
Make_Validate_Unchecked_Conversion (Sloc (N));
|
|
Set_Source_Type (Vnode, Source);
|
|
Set_Target_Type (Vnode, Target);
|
|
|
|
-- If the unchecked conversion node is in a list, just insert before it.
|
|
-- If not we have some strange case, not worth bothering about.
|
|
|
|
if Is_List_Member (N) then
|
|
Insert_After (N, Vnode);
|
|
end if;
|
|
end Validate_Unchecked_Conversion;
|
|
|
|
------------------------------------
|
|
-- Validate_Unchecked_Conversions --
|
|
------------------------------------
|
|
|
|
procedure Validate_Unchecked_Conversions is
|
|
begin
|
|
for N in Unchecked_Conversions.First .. Unchecked_Conversions.Last loop
|
|
declare
|
|
T : UC_Entry renames Unchecked_Conversions.Table (N);
|
|
|
|
Eloc : constant Source_Ptr := T.Eloc;
|
|
Source : constant Entity_Id := T.Source;
|
|
Target : constant Entity_Id := T.Target;
|
|
Act_Unit : constant Entity_Id := T.Act_Unit;
|
|
|
|
Source_Siz : Uint;
|
|
Target_Siz : Uint;
|
|
|
|
begin
|
|
-- Skip if function marked as warnings off
|
|
|
|
if Warnings_Off (Act_Unit) then
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- This validation check, which warns if we have unequal sizes for
|
|
-- unchecked conversion, and thus potentially implementation
|
|
-- dependent semantics, is one of the few occasions on which we
|
|
-- use the official RM size instead of Esize. See description in
|
|
-- Einfo "Handling of Type'Size Values" for details.
|
|
|
|
if Serious_Errors_Detected = 0
|
|
and then Known_Static_RM_Size (Source)
|
|
and then Known_Static_RM_Size (Target)
|
|
|
|
-- Don't do the check if warnings off for either type, note the
|
|
-- deliberate use of OR here instead of OR ELSE to get the flag
|
|
-- Warnings_Off_Used set for both types if appropriate.
|
|
|
|
and then not (Has_Warnings_Off (Source)
|
|
or
|
|
Has_Warnings_Off (Target))
|
|
then
|
|
Source_Siz := RM_Size (Source);
|
|
Target_Siz := RM_Size (Target);
|
|
|
|
if Source_Siz /= Target_Siz then
|
|
Error_Msg
|
|
("?z?types for unchecked conversion have different sizes!",
|
|
Eloc);
|
|
|
|
if All_Errors_Mode then
|
|
Error_Msg_Name_1 := Chars (Source);
|
|
Error_Msg_Uint_1 := Source_Siz;
|
|
Error_Msg_Name_2 := Chars (Target);
|
|
Error_Msg_Uint_2 := Target_Siz;
|
|
Error_Msg ("\size of % is ^, size of % is ^?z?", Eloc);
|
|
|
|
Error_Msg_Uint_1 := UI_Abs (Source_Siz - Target_Siz);
|
|
|
|
if Is_Discrete_Type (Source)
|
|
and then
|
|
Is_Discrete_Type (Target)
|
|
then
|
|
if Source_Siz > Target_Siz then
|
|
Error_Msg
|
|
("\?z?^ high order bits of source will "
|
|
& "be ignored!", Eloc);
|
|
|
|
elsif Is_Unsigned_Type (Source) then
|
|
Error_Msg
|
|
("\?z?source will be extended with ^ high order "
|
|
& "zero bits!", Eloc);
|
|
|
|
else
|
|
Error_Msg
|
|
("\?z?source will be extended with ^ high order "
|
|
& "sign bits!", Eloc);
|
|
end if;
|
|
|
|
elsif Source_Siz < Target_Siz then
|
|
if Is_Discrete_Type (Target) then
|
|
if Bytes_Big_Endian then
|
|
Error_Msg
|
|
("\?z?target value will include ^ undefined "
|
|
& "low order bits!", Eloc);
|
|
else
|
|
Error_Msg
|
|
("\?z?target value will include ^ undefined "
|
|
& "high order bits!", Eloc);
|
|
end if;
|
|
|
|
else
|
|
Error_Msg
|
|
("\?z?^ trailing bits of target value will be "
|
|
& "undefined!", Eloc);
|
|
end if;
|
|
|
|
else pragma Assert (Source_Siz > Target_Siz);
|
|
Error_Msg
|
|
("\?z?^ trailing bits of source will be ignored!",
|
|
Eloc);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
-- If both types are access types, we need to check the alignment.
|
|
-- If the alignment of both is specified, we can do it here.
|
|
|
|
if Serious_Errors_Detected = 0
|
|
and then Is_Access_Type (Source)
|
|
and then Is_Access_Type (Target)
|
|
and then Target_Strict_Alignment
|
|
and then Present (Designated_Type (Source))
|
|
and then Present (Designated_Type (Target))
|
|
then
|
|
declare
|
|
D_Source : constant Entity_Id := Designated_Type (Source);
|
|
D_Target : constant Entity_Id := Designated_Type (Target);
|
|
|
|
begin
|
|
if Known_Alignment (D_Source)
|
|
and then
|
|
Known_Alignment (D_Target)
|
|
then
|
|
declare
|
|
Source_Align : constant Uint := Alignment (D_Source);
|
|
Target_Align : constant Uint := Alignment (D_Target);
|
|
|
|
begin
|
|
if Source_Align < Target_Align
|
|
and then not Is_Tagged_Type (D_Source)
|
|
|
|
-- Suppress warning if warnings suppressed on either
|
|
-- type or either designated type. Note the use of
|
|
-- OR here instead of OR ELSE. That is intentional,
|
|
-- we would like to set flag Warnings_Off_Used in
|
|
-- all types for which warnings are suppressed.
|
|
|
|
and then not (Has_Warnings_Off (D_Source)
|
|
or
|
|
Has_Warnings_Off (D_Target)
|
|
or
|
|
Has_Warnings_Off (Source)
|
|
or
|
|
Has_Warnings_Off (Target))
|
|
then
|
|
Error_Msg_Uint_1 := Target_Align;
|
|
Error_Msg_Uint_2 := Source_Align;
|
|
Error_Msg_Node_1 := D_Target;
|
|
Error_Msg_Node_2 := D_Source;
|
|
Error_Msg
|
|
("?z?alignment of & (^) is stricter than "
|
|
& "alignment of & (^)!", Eloc);
|
|
Error_Msg
|
|
("\?z?resulting access value may have invalid "
|
|
& "alignment!", Eloc);
|
|
end if;
|
|
end;
|
|
end if;
|
|
end;
|
|
end if;
|
|
end;
|
|
|
|
<<Continue>>
|
|
null;
|
|
end loop;
|
|
end Validate_Unchecked_Conversions;
|
|
|
|
end Sem_Ch13;
|