mirror of
https://github.com/autc04/Retro68.git
synced 2024-06-03 00:29:47 +00:00
17801 lines
640 KiB
Ada
17801 lines
640 KiB
Ada
------------------------------------------------------------------------------
|
|
-- --
|
|
-- GNAT COMPILER COMPONENTS --
|
|
-- --
|
|
-- S E M _ C H 1 3 --
|
|
-- --
|
|
-- B o d y --
|
|
-- --
|
|
-- Copyright (C) 1992-2022, 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 Contracts; use Contracts;
|
|
with Debug; use Debug;
|
|
with Einfo; use Einfo;
|
|
with Einfo.Entities; use Einfo.Entities;
|
|
with Einfo.Utils; use Einfo.Utils;
|
|
with Elists; use Elists;
|
|
with Errout; use Errout;
|
|
with Exp_Disp; use Exp_Disp;
|
|
with Exp_Tss; use Exp_Tss;
|
|
with Exp_Util; use Exp_Util;
|
|
with Freeze; use Freeze;
|
|
with Ghost; use Ghost;
|
|
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 Par_SCO; use Par_SCO;
|
|
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_Cat; use Sem_Cat;
|
|
with Sem_Ch3; use Sem_Ch3;
|
|
with Sem_Ch6; use Sem_Ch6;
|
|
with Sem_Ch7; use Sem_Ch7;
|
|
with Sem_Ch8; use Sem_Ch8;
|
|
with Sem_Dim; use Sem_Dim;
|
|
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 Sinfo; use Sinfo;
|
|
with Sinfo.Nodes; use Sinfo.Nodes;
|
|
with Sinfo.Utils; use Sinfo.Utils;
|
|
with Sinput; use Sinput;
|
|
with Snames; use Snames;
|
|
with Stand; use Stand;
|
|
with Table;
|
|
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 Adjust_Record_For_Reverse_Bit_Order_Ada_95 (R : Entity_Id);
|
|
-- Helper routine providing the original (pre-AI95-0133) behavior for
|
|
-- Adjust_Record_For_Reverse_Bit_Order.
|
|
|
|
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.
|
|
|
|
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. An
|
|
-- others_choice is static if the corresponding expression is static.
|
|
-- The staticness of the bounds is checked separately.
|
|
|
|
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.
|
|
|
|
function Build_Export_Import_Pragma
|
|
(Asp : Node_Id;
|
|
Id : Entity_Id) return Node_Id;
|
|
-- Create the corresponding pragma for aspect Export or Import denoted by
|
|
-- Asp. Id is the related entity subject to the aspect. Return Empty when
|
|
-- the expression of aspect Asp evaluates to False or is erroneous.
|
|
|
|
function Build_Predicate_Function_Declaration
|
|
(Typ : Entity_Id) return Node_Id;
|
|
-- Build the declaration for a predicate function. The declaration is built
|
|
-- at the end of the declarative part containing the type definition, which
|
|
-- may be before the freeze point of the type. The predicate expression is
|
|
-- preanalyzed at this point, to catch visibility errors.
|
|
|
|
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 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 a 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_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_Related_Rep_Item (N : Node_Id) return Boolean;
|
|
-- Returns True for a representation clause/pragma that specifies a
|
|
-- type-related representation (as opposed to operational) aspect.
|
|
|
|
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_Put_Image_Subprogram
|
|
(N : Node_Id;
|
|
Ent : Entity_Id;
|
|
Subp : Entity_Id);
|
|
-- Similar to New_Stream_Subprogram, but for the Put_Image attribute
|
|
|
|
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 untagged 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.
|
|
|
|
procedure No_Type_Rep_Item (N : Node_Id);
|
|
-- Output message indicating that no type-related aspects can be
|
|
-- specified due to some property of the parent type.
|
|
|
|
procedure Register_Address_Clause_Check
|
|
(N : Node_Id;
|
|
X : Entity_Id;
|
|
A : Uint;
|
|
Y : Entity_Id;
|
|
Off : Boolean);
|
|
-- Register a check for the address clause N. The rest of the parameters
|
|
-- are in keeping with the components of Address_Clause_Check_Record below.
|
|
|
|
procedure Validate_Aspect_Aggregate (N : Node_Id);
|
|
-- Check legality of operations given in the Ada 2022 Aggregate aspect for
|
|
-- containers.
|
|
|
|
procedure Resolve_Aspect_Aggregate
|
|
(Typ : Entity_Id;
|
|
Expr : Node_Id);
|
|
-- Resolve each one of the operations specified in the specification of
|
|
-- Aspect_Aggregate.
|
|
|
|
procedure Validate_Aspect_Stable_Properties
|
|
(E : Entity_Id; N : Node_Id; Class_Present : Boolean);
|
|
-- Check legality of functions given in the Ada 2022 Stable_Properties
|
|
-- (or Stable_Properties'Class) aspect.
|
|
|
|
procedure Validate_Storage_Model_Type_Aspect
|
|
(Typ : Entity_Id; ASN : Node_Id);
|
|
-- Check legality and completeness of the aggregate associations given in
|
|
-- the Storage_Model_Type aspect associated with Typ.
|
|
|
|
procedure Resolve_Storage_Model_Type_Argument
|
|
(N : Node_Id;
|
|
Typ : Entity_Id;
|
|
Addr_Type : in out Entity_Id;
|
|
Nam : Name_Id);
|
|
-- Resolve argument N to be of the proper kind (when a type or constant)
|
|
-- or to have the proper profile (when a subprogram).
|
|
|
|
procedure Resolve_Aspect_Stable_Properties
|
|
(Typ_Or_Subp : Entity_Id;
|
|
Expr : Node_Id;
|
|
Class_Present : Boolean);
|
|
-- Resolve each one of the functions specified in the specification of
|
|
-- aspect Stable_Properties (or Stable_Properties'Class).
|
|
|
|
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.
|
|
|
|
-----------------------------------------------------------
|
|
-- Visibility of Discriminants in Aspect Specifications --
|
|
-----------------------------------------------------------
|
|
|
|
-- The discriminants of a type are visible when analyzing the aspect
|
|
-- specifications of a type declaration or protected type declaration,
|
|
-- but not when analyzing those of a subtype declaration. The following
|
|
-- routines enforce this distinction.
|
|
|
|
procedure Push_Type (E : Entity_Id);
|
|
-- Push scope E and make visible the discriminants of type entity E if E
|
|
-- has discriminants and is not a subtype.
|
|
|
|
procedure Pop_Type (E : Entity_Id);
|
|
-- Remove visibility to the discriminants of type entity E and pop the
|
|
-- scope stack if E has discriminants and is not a subtype.
|
|
|
|
----------------------------------------------
|
|
-- 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 has a value known at compile time or is of the form Y'Address
|
|
-- or recursively is a reference to a constant initialized with either of
|
|
-- these forms, and the value of Expr is not a multiple of X's alignment,
|
|
-- or if Y has a smaller alignment than X, then 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 subject to the address clause
|
|
|
|
A : Uint;
|
|
-- The value of the address in the first case
|
|
|
|
Y : Entity_Id;
|
|
-- The entity of the object being overlaid in the second case
|
|
|
|
Off : Boolean;
|
|
-- Whether the address is offset within Y in the second case
|
|
|
|
Alignment_Checks_Suppressed : Boolean;
|
|
-- Whether alignment checks are suppressed by an active scope suppress
|
|
-- setting. We need to save the value in order to be able to reuse it
|
|
-- after the back end has been run.
|
|
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");
|
|
|
|
function Alignment_Checks_Suppressed
|
|
(ACCR : Address_Clause_Check_Record) return Boolean;
|
|
-- Return whether the alignment check generated for the address clause
|
|
-- is suppressed.
|
|
|
|
---------------------------------
|
|
-- Alignment_Checks_Suppressed --
|
|
---------------------------------
|
|
|
|
function Alignment_Checks_Suppressed
|
|
(ACCR : Address_Clause_Check_Record) return Boolean
|
|
is
|
|
begin
|
|
if Checks_May_Be_Suppressed (ACCR.X) then
|
|
return Is_Check_Suppressed (ACCR.X, Alignment_Check);
|
|
else
|
|
return ACCR.Alignment_Checks_Suppressed;
|
|
end if;
|
|
end Alignment_Checks_Suppressed;
|
|
|
|
-----------------------------------------
|
|
-- Adjust_Record_For_Reverse_Bit_Order --
|
|
-----------------------------------------
|
|
|
|
procedure Adjust_Record_For_Reverse_Bit_Order (R : Entity_Id) is
|
|
Max_Machine_Scalar_Size : constant Uint :=
|
|
UI_From_Int (System_Max_Integer_Size);
|
|
-- We use this as the maximum machine scalar size
|
|
|
|
SSU : constant Uint := UI_From_Int (System_Storage_Unit);
|
|
|
|
CC : Node_Id;
|
|
Comp : Node_Id;
|
|
Num_CC : Natural;
|
|
|
|
begin
|
|
-- The processing done here used to depend on the Ada version, but the
|
|
-- behavior has been changed by AI95-0133. However this AI is a Binding
|
|
-- Interpretation, so we now implement it even in Ada 95 mode. But the
|
|
-- original behavior from unamended Ada 95 is available for the sake of
|
|
-- compatibility under the debugging switch -gnatd.p in Ada 95 mode.
|
|
|
|
if Ada_Version < Ada_2005 and then Debug_Flag_Dot_P then
|
|
Adjust_Record_For_Reverse_Bit_Order_Ada_95 (R);
|
|
return;
|
|
end if;
|
|
|
|
-- 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.
|
|
|
|
-- Note that component clauses found on record types may be inherited,
|
|
-- in which case the layout of the component with such a clause still
|
|
-- has to be done at this point. Therefore, the processing done here
|
|
-- must exclusively rely on the Component_Clause of the component.
|
|
|
|
-- 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 + 1;
|
|
Error_Msg_Uint_2 := Max_Machine_Scalar_Size;
|
|
Error_Msg_F
|
|
("\last bit + 1 (^) 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.5.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);
|
|
|
|
MaxL : Uint;
|
|
-- Maximum last bit value of any component in this set
|
|
|
|
MSS : Uint;
|
|
-- Corresponding machine scalar size
|
|
|
|
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).
|
|
|
|
-----------
|
|
-- 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);
|
|
|
|
FB : constant Uint := Static_Integer (First_Bit (CC));
|
|
LB : constant Uint := Static_Integer (Last_Bit (CC));
|
|
NFB : constant Uint := MSS - 1 - LB;
|
|
NLB : constant Uint := NFB + LB - FB;
|
|
Pos : constant Uint := Static_Integer (Position (CC));
|
|
|
|
begin
|
|
-- Do not warn for the artificial clause built for the tag
|
|
-- in Check_Record_Representation_Clause if it is inherited.
|
|
|
|
if Warn_On_Reverse_Bit_Order
|
|
and then Chars (Comp) /= Name_uTag
|
|
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_Esize (Comp, 1 + (NLB - NFB));
|
|
Set_Normalized_First_Bit (Comp, NFB mod SSU);
|
|
Set_Normalized_Position (Comp, Pos + NFB / SSU);
|
|
end;
|
|
end loop;
|
|
end loop;
|
|
end Sort_CC;
|
|
end Adjust_Record_For_Reverse_Bit_Order;
|
|
|
|
------------------------------------------------
|
|
-- Adjust_Record_For_Reverse_Bit_Order_Ada_95 --
|
|
------------------------------------------------
|
|
|
|
procedure Adjust_Record_For_Reverse_Bit_Order_Ada_95 (R : Entity_Id) is
|
|
CC : Node_Id;
|
|
Comp : Node_Id;
|
|
|
|
begin
|
|
-- 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.
|
|
|
|
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 in "
|
|
& "legacy Ada 95 mode", 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 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_Position (Comp,
|
|
Component_Bit_Offset (Comp) / System_Storage_Unit);
|
|
|
|
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;
|
|
end Adjust_Record_For_Reverse_Bit_Order_Ada_95;
|
|
|
|
-------------------------------------
|
|
-- 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 Present (Size)
|
|
and then Size mod (Alignment (Typ) * SSU) /= 0
|
|
then
|
|
Reinit_Alignment (Typ);
|
|
end if;
|
|
end Alignment_Check_For_Size_Change;
|
|
|
|
-----------------------------------
|
|
-- All_Membership_Choices_Static --
|
|
-----------------------------------
|
|
|
|
function All_Membership_Choices_Static (Expr : Node_Id) return Boolean is
|
|
pragma Assert (Nkind (Expr) in N_Membership_Test);
|
|
begin
|
|
pragma Assert
|
|
(Present (Right_Opnd (Expr))
|
|
xor
|
|
Present (Alternatives (Expr)));
|
|
|
|
if Present (Right_Opnd (Expr)) then
|
|
return Is_Static_Choice (Right_Opnd (Expr));
|
|
else
|
|
return All_Static_Choices (Alternatives (Expr));
|
|
end if;
|
|
end All_Membership_Choices_Static;
|
|
|
|
------------------------
|
|
-- 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;
|
|
|
|
-------------------------------------
|
|
-- Analyze_Aspects_At_Freeze_Point --
|
|
-------------------------------------
|
|
|
|
procedure Analyze_Aspects_At_Freeze_Point (E : Entity_Id) is
|
|
procedure Analyze_Aspect_Default_Value (ASN : Node_Id);
|
|
-- This routine analyzes an Aspect_Default_[Component_]Value denoted by
|
|
-- the aspect specification node ASN.
|
|
|
|
procedure Check_Aspect_Too_Late (N : Node_Id);
|
|
-- This procedure is similar to Rep_Item_Too_Late for representation
|
|
-- aspects that apply to type and that do not have a corresponding
|
|
-- pragma.
|
|
-- Used to check in particular that the expression associated with
|
|
-- aspect node N for the given type (entity) of the aspect does not
|
|
-- appear too late according to the rules in RM 13.1(9) and 13.1(10).
|
|
|
|
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);
|
|
|
|
begin
|
|
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;
|
|
|
|
Check_Aspect_Too_Late (ASN);
|
|
end Analyze_Aspect_Default_Value;
|
|
|
|
---------------------------
|
|
-- Check_Aspect_Too_Late --
|
|
---------------------------
|
|
|
|
procedure Check_Aspect_Too_Late (N : Node_Id) is
|
|
Typ : constant Entity_Id := Entity (N);
|
|
Expr : constant Node_Id := Expression (N);
|
|
|
|
function Find_Type_Reference
|
|
(Typ : Entity_Id; Expr : Node_Id) return Boolean;
|
|
-- Return True if a reference to type Typ is found in the expression
|
|
-- Expr.
|
|
|
|
-------------------------
|
|
-- Find_Type_Reference --
|
|
-------------------------
|
|
|
|
function Find_Type_Reference
|
|
(Typ : Entity_Id; Expr : Node_Id) return Boolean
|
|
is
|
|
function Find_Type (N : Node_Id) return Traverse_Result;
|
|
-- Set Found to True if N refers to Typ
|
|
|
|
---------------
|
|
-- Find_Type --
|
|
---------------
|
|
|
|
function Find_Type (N : Node_Id) return Traverse_Result is
|
|
begin
|
|
if N = Typ
|
|
or else (Nkind (N) in N_Identifier | N_Expanded_Name
|
|
and then Present (Entity (N))
|
|
and then Entity (N) = Typ)
|
|
then
|
|
return Abandon;
|
|
else
|
|
return OK;
|
|
end if;
|
|
end Find_Type;
|
|
|
|
function Search_Type_Reference is new Traverse_Func (Find_Type);
|
|
|
|
begin
|
|
return Search_Type_Reference (Expr) = Abandon;
|
|
end Find_Type_Reference;
|
|
|
|
Parent_Type : Entity_Id;
|
|
|
|
begin
|
|
-- Ensure Expr is analyzed so that e.g. all types are properly
|
|
-- resolved for Find_Type_Reference.
|
|
|
|
Analyze (Expr);
|
|
|
|
-- A self-referential aspect is illegal if it forces freezing the
|
|
-- entity before the corresponding aspect has been analyzed.
|
|
|
|
if Find_Type_Reference (Typ, Expr) then
|
|
Error_Msg_NE
|
|
("aspect specification causes premature freezing of&", N, Typ);
|
|
end if;
|
|
|
|
-- For representation aspects, check for case of untagged derived
|
|
-- type whose parent either has primitive operations (pre Ada 2022),
|
|
-- or is a by-reference type (RM 13.1(10)).
|
|
-- Strictly speaking the check also applies to Ada 2012 but it is
|
|
-- really too constraining for existing code already, so relax it.
|
|
-- ??? Confirming aspects should be allowed here.
|
|
|
|
if Is_Representation_Aspect (Get_Aspect_Id (N))
|
|
and then Is_Derived_Type (Typ)
|
|
and then not Is_Tagged_Type (Typ)
|
|
then
|
|
Parent_Type := Etype (Base_Type (Typ));
|
|
|
|
if Ada_Version <= Ada_2012
|
|
and then Has_Primitive_Operations (Parent_Type)
|
|
then
|
|
Error_Msg_N
|
|
("|representation aspect not permitted before Ada 2022: " &
|
|
"use -gnat2022!", N);
|
|
Error_Msg_NE
|
|
("\parent type & has primitive operations!", N, Parent_Type);
|
|
|
|
elsif Is_By_Reference_Type (Parent_Type) then
|
|
No_Type_Rep_Item (N);
|
|
Error_Msg_NE
|
|
("\parent type & is a by-reference type!", N, Parent_Type);
|
|
end if;
|
|
end if;
|
|
end Check_Aspect_Too_Late;
|
|
|
|
---------------------------------
|
|
-- Inherit_Delayed_Rep_Aspects --
|
|
---------------------------------
|
|
|
|
procedure Inherit_Delayed_Rep_Aspects (ASN : Node_Id) is
|
|
A_Id : constant Aspect_Id := Get_Aspect_Id (ASN);
|
|
P : constant Entity_Id := Entity (ASN);
|
|
-- Entity 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_Full_Access (also Full_Access_Only)
|
|
|
|
when Aspect_Volatile_Full_Access
|
|
| Aspect_Full_Access_Only
|
|
=>
|
|
if Is_Volatile_Full_Access (P) then
|
|
Set_Is_Volatile_Full_Access (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;
|
|
|
|
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);
|
|
|
|
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 Aspect_Volatile_Full_Access
|
|
| Aspect_Full_Access_Only
|
|
=>
|
|
if not Is_Volatile_Full_Access (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;
|
|
|
|
-- Local variables
|
|
|
|
Prag : Node_Id;
|
|
P_Name : Name_Id;
|
|
|
|
-- Start of processing for Make_Pragma_From_Boolean_Aspect
|
|
|
|
begin
|
|
if Present (Expr) and then Is_False (Static_Boolean (Expr)) then
|
|
Check_False_Aspect_For_Derived_Type;
|
|
|
|
else
|
|
-- There is no Full_Access_Only pragma so use VFA instead
|
|
|
|
if A_Name = Name_Full_Access_Only then
|
|
P_Name := Name_Volatile_Full_Access;
|
|
else
|
|
P_Name := A_Name;
|
|
end if;
|
|
|
|
Prag :=
|
|
Make_Pragma (Loc,
|
|
Pragma_Identifier =>
|
|
Make_Identifier (Sloc (Ident), P_Name),
|
|
Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Ident),
|
|
Expression => New_Occurrence_Of (Ent, Sloc (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;
|
|
|
|
-- Local variables
|
|
|
|
A_Id : Aspect_Id;
|
|
ASN : Node_Id;
|
|
Ritem : Node_Id;
|
|
|
|
-- Start of processing for Analyze_Aspects_At_Freeze_Point
|
|
|
|
begin
|
|
-- Must be visible in current scope, but if this is a type from a nested
|
|
-- package it may be frozen from an object declaration in the enclosing
|
|
-- scope, so install the package declarations to complete the analysis
|
|
-- of the aspects, if any. If the package itself is frozen the type will
|
|
-- have been frozen as well.
|
|
|
|
if not Scope_Within_Or_Same (Current_Scope, Scope (E)) then
|
|
if Is_Type (E) and then From_Nested_Package (E) then
|
|
declare
|
|
Pack : constant Entity_Id := Scope (E);
|
|
|
|
begin
|
|
Push_Scope (Pack);
|
|
Install_Visible_Declarations (Pack);
|
|
Install_Private_Declarations (Pack);
|
|
Analyze_Aspects_At_Freeze_Point (E);
|
|
|
|
if Is_Private_Type (E)
|
|
and then Present (Full_View (E))
|
|
then
|
|
Analyze_Aspects_At_Freeze_Point (Full_View (E));
|
|
end if;
|
|
|
|
End_Package_Scope (Pack);
|
|
return;
|
|
end;
|
|
|
|
-- Aspects from other entities in different contexts are analyzed
|
|
-- elsewhere.
|
|
|
|
else
|
|
return;
|
|
end if;
|
|
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 freeze point.
|
|
|
|
when Boolean_Aspects
|
|
| Library_Unit_Aspects
|
|
=>
|
|
-- Aspects Export and Import require special handling.
|
|
-- Both are by definition Boolean and may benefit from
|
|
-- forward references, however their expressions are
|
|
-- treated as static. In addition, the syntax of their
|
|
-- corresponding pragmas requires extra "pieces" which
|
|
-- may also contain forward references. To account for
|
|
-- all of this, the corresponding pragma is created by
|
|
-- Analyze_Aspect_Export_Import, but is not analyzed as
|
|
-- the complete analysis must happen now.
|
|
|
|
-- Aspect Full_Access_Only must be analyzed last so that
|
|
-- aspects Volatile and Atomic, if any, are analyzed.
|
|
|
|
-- Skip creation of pragma Preelaborable_Initialization
|
|
-- in the case where the aspect has an expression,
|
|
-- because the pragma is only needed for setting flag
|
|
-- Known_To_Have_Preelab_Init, which is set by other
|
|
-- means following resolution of the aspect expression.
|
|
|
|
if A_Id not in Aspect_Export
|
|
| Aspect_Full_Access_Only
|
|
| Aspect_Import
|
|
and then (A_Id /= Aspect_Preelaborable_Initialization
|
|
or else not Present (Expression (ASN)))
|
|
then
|
|
Make_Pragma_From_Boolean_Aspect (ASN);
|
|
end if;
|
|
|
|
-- Special handling for aspects that don't correspond to
|
|
-- pragmas/attributes.
|
|
|
|
when Aspect_Default_Value
|
|
| Aspect_Default_Component_Value
|
|
=>
|
|
-- Do not inherit aspect for anonymous base type of a
|
|
-- scalar or array type, because they apply to the first
|
|
-- subtype of the type, and will be processed when that
|
|
-- first subtype is frozen.
|
|
|
|
if Is_Derived_Type (E)
|
|
and then not Comes_From_Source (E)
|
|
and then E /= First_Subtype (E)
|
|
then
|
|
null;
|
|
else
|
|
Analyze_Aspect_Default_Value (ASN);
|
|
end if;
|
|
|
|
-- Ditto for iterator aspects, because the corresponding
|
|
-- attributes may not have been analyzed yet.
|
|
|
|
when Aspect_Constant_Indexing
|
|
| Aspect_Default_Iterator
|
|
| Aspect_Iterator_Element
|
|
| Aspect_Variable_Indexing
|
|
=>
|
|
Analyze (Expression (ASN));
|
|
|
|
if Etype (Expression (ASN)) = Any_Type then
|
|
Error_Msg_NE
|
|
("\aspect must be fully defined before & is frozen",
|
|
ASN, E);
|
|
end if;
|
|
|
|
when Aspect_Integer_Literal
|
|
| Aspect_Real_Literal
|
|
| Aspect_String_Literal
|
|
=>
|
|
Validate_Literal_Aspect (E, ASN);
|
|
|
|
when Aspect_Iterable =>
|
|
Validate_Iterable_Aspect (E, ASN);
|
|
|
|
when Aspect_Designated_Storage_Model =>
|
|
Analyze_And_Resolve (Expression (ASN));
|
|
|
|
if not Is_Entity_Name (Expression (ASN))
|
|
or else not Is_Object (Entity (Expression (ASN)))
|
|
or else
|
|
not Present (Find_Aspect (Etype (Expression (ASN)),
|
|
Aspect_Storage_Model_Type))
|
|
then
|
|
Error_Msg_N
|
|
("must specify name of stand-alone object of type "
|
|
& "with aspect Storage_Model_Type",
|
|
Expression (ASN));
|
|
|
|
-- Set access type's Associated_Storage_Pool to denote
|
|
-- the Storage_Model_Type object given for the aspect
|
|
-- (even though that isn't actually an Ada storage pool).
|
|
|
|
else
|
|
Set_Associated_Storage_Pool
|
|
(E, Entity (Expression (ASN)));
|
|
end if;
|
|
|
|
when Aspect_Storage_Model_Type =>
|
|
Validate_Storage_Model_Type_Aspect (E, ASN);
|
|
|
|
when Aspect_Aggregate =>
|
|
null;
|
|
|
|
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;
|
|
|
|
-- Make a second pass for a Full_Access_Only entry
|
|
|
|
ASN := First_Rep_Item (E);
|
|
while Present (ASN) loop
|
|
if Nkind (ASN) = N_Aspect_Specification then
|
|
exit when Entity (ASN) /= E;
|
|
|
|
if Get_Aspect_Id (ASN) = Aspect_Full_Access_Only then
|
|
Make_Pragma_From_Boolean_Aspect (ASN);
|
|
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;
|
|
|
|
if In_Instance
|
|
and then E /= Base_Type (E)
|
|
and then Is_First_Subtype (E)
|
|
then
|
|
Inherit_Rep_Item_Chain (Base_Type (E), E);
|
|
end if;
|
|
end Analyze_Aspects_At_Freeze_Point;
|
|
|
|
-----------------------------------
|
|
-- Analyze_Aspect_Specifications --
|
|
-----------------------------------
|
|
|
|
procedure Analyze_Aspect_Specifications (N : Node_Id; E : Entity_Id) is
|
|
pragma Assert (Present (E));
|
|
|
|
procedure Decorate (Asp : Node_Id; Prag : Node_Id);
|
|
-- Establish linkages between an aspect and its corresponding pragma
|
|
|
|
procedure Insert_Pragma
|
|
(Prag : Node_Id;
|
|
Is_Instance : Boolean := False);
|
|
-- Subsidiary to the analysis of aspects
|
|
-- Abstract_State
|
|
-- Attach_Handler
|
|
-- Contract_Cases
|
|
-- Depends
|
|
-- Ghost
|
|
-- Global
|
|
-- Initial_Condition
|
|
-- Initializes
|
|
-- Post
|
|
-- Pre
|
|
-- Refined_Depends
|
|
-- Refined_Global
|
|
-- Refined_State
|
|
-- SPARK_Mode
|
|
-- Subprogram_Variant
|
|
-- Warnings
|
|
-- Insert pragma Prag such that it mimics the placement of a source
|
|
-- pragma of the same kind. Flag Is_Generic should be set when the
|
|
-- context denotes a generic instance.
|
|
|
|
--------------
|
|
-- 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_Pragma --
|
|
-------------------
|
|
|
|
procedure Insert_Pragma
|
|
(Prag : Node_Id;
|
|
Is_Instance : Boolean := False)
|
|
is
|
|
Aux : Node_Id;
|
|
Decl : Node_Id;
|
|
Decls : List_Id;
|
|
Def : Node_Id;
|
|
Inserted : Boolean := False;
|
|
|
|
begin
|
|
-- When the aspect appears on an entry, package, protected unit,
|
|
-- subprogram, or task unit body, insert the generated pragma at the
|
|
-- top of the body declarations to emulate the behavior of a source
|
|
-- pragma.
|
|
|
|
-- package body Pack with Aspect is
|
|
|
|
-- package body Pack is
|
|
-- pragma Prag;
|
|
|
|
if Nkind (N) in N_Entry_Body
|
|
| N_Package_Body
|
|
| N_Protected_Body
|
|
| N_Subprogram_Body
|
|
| N_Task_Body
|
|
then
|
|
Decls := Declarations (N);
|
|
|
|
if No (Decls) then
|
|
Decls := New_List;
|
|
Set_Declarations (N, Decls);
|
|
end if;
|
|
|
|
Prepend_To (Decls, Prag);
|
|
|
|
-- When the aspect is associated with a [generic] package declaration
|
|
-- insert the generated pragma at the top of the visible declarations
|
|
-- to emulate the behavior of a source pragma.
|
|
|
|
-- package Pack with Aspect is
|
|
|
|
-- package Pack is
|
|
-- pragma Prag;
|
|
|
|
elsif Nkind (N) in N_Generic_Package_Declaration
|
|
| N_Package_Declaration
|
|
then
|
|
Decls := Visible_Declarations (Specification (N));
|
|
|
|
if No (Decls) then
|
|
Decls := New_List;
|
|
Set_Visible_Declarations (Specification (N), Decls);
|
|
end if;
|
|
|
|
-- 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>
|
|
|
|
-- Insert the pragma before the first source declaration by
|
|
-- skipping the instance "header" to ensure proper visibility of
|
|
-- all formals.
|
|
|
|
if Is_Instance then
|
|
Decl := First (Decls);
|
|
while Present (Decl) loop
|
|
if Comes_From_Source (Decl) then
|
|
Insert_Before (Decl, Prag);
|
|
Inserted := True;
|
|
exit;
|
|
else
|
|
Next (Decl);
|
|
end if;
|
|
end loop;
|
|
|
|
-- The pragma is placed after the instance "header"
|
|
|
|
if not Inserted then
|
|
Append_To (Decls, Prag);
|
|
end if;
|
|
|
|
-- Otherwise this is not a generic instance
|
|
|
|
else
|
|
Prepend_To (Decls, Prag);
|
|
end if;
|
|
|
|
-- When the aspect is associated with a protected unit declaration,
|
|
-- insert the generated pragma at the top of the visible declarations
|
|
-- the emulate the behavior of a source pragma.
|
|
|
|
-- protected [type] Prot with Aspect is
|
|
|
|
-- protected [type] Prot is
|
|
-- pragma Prag;
|
|
|
|
elsif Nkind (N) = N_Protected_Type_Declaration then
|
|
Def := Protected_Definition (N);
|
|
|
|
if No (Def) then
|
|
Def :=
|
|
Make_Protected_Definition (Sloc (N),
|
|
Visible_Declarations => New_List,
|
|
End_Label => Empty);
|
|
|
|
Set_Protected_Definition (N, Def);
|
|
end if;
|
|
|
|
Decls := Visible_Declarations (Def);
|
|
|
|
if No (Decls) then
|
|
Decls := New_List;
|
|
Set_Visible_Declarations (Def, Decls);
|
|
end if;
|
|
|
|
Prepend_To (Decls, Prag);
|
|
|
|
-- When the aspect is associated with a task unit declaration, insert
|
|
-- insert the generated pragma at the top of the visible declarations
|
|
-- the emulate the behavior of a source pragma.
|
|
|
|
-- task [type] Prot with Aspect is
|
|
|
|
-- task [type] Prot is
|
|
-- pragma Prag;
|
|
|
|
elsif Nkind (N) = N_Task_Type_Declaration then
|
|
Def := Task_Definition (N);
|
|
|
|
if No (Def) then
|
|
Def :=
|
|
Make_Task_Definition (Sloc (N),
|
|
Visible_Declarations => New_List,
|
|
End_Label => Empty);
|
|
|
|
Set_Task_Definition (N, Def);
|
|
end if;
|
|
|
|
Decls := Visible_Declarations (Def);
|
|
|
|
if No (Decls) then
|
|
Decls := New_List;
|
|
Set_Visible_Declarations (Def, Decls);
|
|
end if;
|
|
|
|
Prepend_To (Decls, Prag);
|
|
|
|
-- When the context is a library unit, the pragma is added to the
|
|
-- Pragmas_After list.
|
|
|
|
elsif 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));
|
|
|
|
-- Default, the pragma is inserted after the context
|
|
|
|
else
|
|
Insert_After (N, Prag);
|
|
end if;
|
|
end Insert_Pragma;
|
|
|
|
-- Local variables
|
|
|
|
Aspect : Node_Id;
|
|
Aitem : Node_Id := Empty;
|
|
Ent : Node_Id;
|
|
|
|
L : constant List_Id := Aspect_Specifications (N);
|
|
pragma Assert (Present (L));
|
|
|
|
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
|
|
|
|
begin
|
|
-- 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 and Subprogram_Variant 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.
|
|
|
|
-- Loop through aspects
|
|
|
|
Aspect := First (L);
|
|
Aspect_Loop : while Present (Aspect) loop
|
|
Analyze_One_Aspect : declare
|
|
|
|
Aspect_Exit : exception;
|
|
-- This exception is used to exit aspect processing completely. It
|
|
-- is used when an error is detected, and no further processing is
|
|
-- required. It is also used if an earlier error has left the tree
|
|
-- in a state where the aspect should not be processed.
|
|
|
|
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_Convention;
|
|
-- Perform analysis of aspect Convention
|
|
|
|
procedure Analyze_Aspect_Disable_Controlled;
|
|
-- Perform analysis of aspect Disable_Controlled
|
|
|
|
procedure Analyze_Aspect_Export_Import;
|
|
-- Perform analysis of aspects Export or Import
|
|
|
|
procedure Analyze_Aspect_External_Link_Name;
|
|
-- Perform analysis of aspects External_Name or Link_Name
|
|
|
|
procedure Analyze_Aspect_Implicit_Dereference;
|
|
-- Perform analysis of the Implicit_Dereference aspects
|
|
|
|
procedure Analyze_Aspect_Relaxed_Initialization;
|
|
-- Perform analysis of aspect Relaxed_Initialization
|
|
|
|
procedure Analyze_Aspect_Yield;
|
|
-- Perform analysis of aspect Yield
|
|
|
|
procedure Analyze_Aspect_Static;
|
|
-- Ada 2022 (AI12-0075): Perform analysis of aspect Static
|
|
|
|
procedure Check_Expr_Is_OK_Static_Expression
|
|
(Expr : Node_Id;
|
|
Typ : Entity_Id := Empty);
|
|
-- Check the specified expression Expr to make sure that it is a
|
|
-- static expression of the given type (i.e. it will be analyzed
|
|
-- and resolved using this type, which can be any valid argument
|
|
-- to Resolve, e.g. Any_Integer is OK). If not, give an error
|
|
-- and raise Aspect_Exit. If Typ is left Empty, then any static
|
|
-- expression is allowed. Includes checking that the expression
|
|
-- does not raise Constraint_Error.
|
|
|
|
function Directly_Specified
|
|
(Id : Entity_Id; A : Aspect_Id) return Boolean;
|
|
-- Returns True if the given aspect is directly (as opposed to
|
|
-- via any form of inheritance) specified for the given entity.
|
|
|
|
function Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations : List_Id;
|
|
Pragma_Name : Name_Id) return Node_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_Convention --
|
|
-------------------------------
|
|
|
|
procedure Analyze_Aspect_Convention is
|
|
Conv : Node_Id;
|
|
Dummy_1 : Node_Id;
|
|
Dummy_2 : Node_Id;
|
|
Dummy_3 : Node_Id;
|
|
Expo : Node_Id;
|
|
Imp : Node_Id;
|
|
|
|
begin
|
|
-- Obtain all interfacing aspects that apply to the related
|
|
-- entity.
|
|
|
|
Get_Interfacing_Aspects
|
|
(Iface_Asp => Aspect,
|
|
Conv_Asp => Dummy_1,
|
|
EN_Asp => Dummy_2,
|
|
Expo_Asp => Expo,
|
|
Imp_Asp => Imp,
|
|
LN_Asp => Dummy_3,
|
|
Do_Checks => True);
|
|
|
|
-- The related entity is subject to aspect Export or Import.
|
|
-- Do not process Convention now because it must be analysed
|
|
-- as part of Export or Import.
|
|
|
|
if Present (Expo) or else Present (Imp) then
|
|
return;
|
|
|
|
-- Otherwise Convention appears by itself
|
|
|
|
else
|
|
-- The aspect specifies a particular convention
|
|
|
|
if Present (Expr) then
|
|
Conv := New_Copy_Tree (Expr);
|
|
|
|
-- Otherwise assume convention Ada
|
|
|
|
else
|
|
Conv := Make_Identifier (Loc, Name_Ada);
|
|
end if;
|
|
|
|
-- Generate:
|
|
-- pragma Convention (<Conv>, <E>);
|
|
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Name => Name_Convention,
|
|
Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Conv),
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => New_Occurrence_Of (E, Loc))));
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
end if;
|
|
end Analyze_Aspect_Convention;
|
|
|
|
---------------------------------------
|
|
-- Analyze_Aspect_Disable_Controlled --
|
|
---------------------------------------
|
|
|
|
procedure Analyze_Aspect_Disable_Controlled is
|
|
begin
|
|
-- The aspect applies only to controlled records
|
|
|
|
if not (Ekind (E) = E_Record_Type
|
|
and then Is_Controlled_Active (E))
|
|
then
|
|
Error_Msg_N
|
|
("aspect % requires controlled record type", Aspect);
|
|
return;
|
|
end if;
|
|
|
|
-- Preanalyze the expression (if any) when the aspect resides
|
|
-- in a generic unit.
|
|
|
|
if Inside_A_Generic then
|
|
if Present (Expr) then
|
|
Preanalyze_And_Resolve (Expr, Any_Boolean);
|
|
end if;
|
|
|
|
-- Otherwise the aspect resides in a nongeneric context
|
|
|
|
else
|
|
-- A controlled record type loses its controlled semantics
|
|
-- when the expression statically evaluates to True.
|
|
|
|
if Present (Expr) then
|
|
Analyze_And_Resolve (Expr, Any_Boolean);
|
|
|
|
if Is_OK_Static_Expression (Expr) then
|
|
if Is_True (Static_Boolean (Expr)) then
|
|
Set_Disable_Controlled (E);
|
|
end if;
|
|
|
|
-- Otherwise the expression is not static
|
|
|
|
else
|
|
Error_Msg_N
|
|
("expression of aspect % must be static", Aspect);
|
|
end if;
|
|
|
|
-- Otherwise the aspect appears without an expression and
|
|
-- defaults to True.
|
|
|
|
else
|
|
Set_Disable_Controlled (E);
|
|
end if;
|
|
end if;
|
|
end Analyze_Aspect_Disable_Controlled;
|
|
|
|
----------------------------------
|
|
-- Analyze_Aspect_Export_Import --
|
|
----------------------------------
|
|
|
|
procedure Analyze_Aspect_Export_Import is
|
|
Dummy_1 : Node_Id;
|
|
Dummy_2 : Node_Id;
|
|
Dummy_3 : Node_Id;
|
|
Expo : Node_Id;
|
|
Imp : Node_Id;
|
|
|
|
begin
|
|
-- Obtain all interfacing aspects that apply to the related
|
|
-- entity.
|
|
|
|
Get_Interfacing_Aspects
|
|
(Iface_Asp => Aspect,
|
|
Conv_Asp => Dummy_1,
|
|
EN_Asp => Dummy_2,
|
|
Expo_Asp => Expo,
|
|
Imp_Asp => Imp,
|
|
LN_Asp => Dummy_3,
|
|
Do_Checks => True);
|
|
|
|
-- The related entity cannot be subject to both aspects Export
|
|
-- and Import.
|
|
|
|
if Present (Expo) and then Present (Imp) then
|
|
Error_Msg_N
|
|
("incompatible interfacing aspects given for &", E);
|
|
Error_Msg_Sloc := Sloc (Expo);
|
|
Error_Msg_N ("\aspect Export #", E);
|
|
Error_Msg_Sloc := Sloc (Imp);
|
|
Error_Msg_N ("\aspect Import #", E);
|
|
end if;
|
|
|
|
-- A variable is most likely modified from the outside. Take
|
|
-- the optimistic approach to avoid spurious errors.
|
|
|
|
if Ekind (E) = E_Variable then
|
|
Set_Never_Set_In_Source (E, False);
|
|
end if;
|
|
|
|
-- Resolve the expression of an Import or Export here, and
|
|
-- require it to be of type Boolean and static. This is not
|
|
-- quite right, because in general this should be delayed,
|
|
-- but that seems tricky for these, because normally Boolean
|
|
-- aspects are replaced with pragmas at the freeze point in
|
|
-- Make_Pragma_From_Boolean_Aspect.
|
|
|
|
if not Present (Expr)
|
|
or else Is_True (Static_Boolean (Expr))
|
|
then
|
|
if A_Id = Aspect_Import then
|
|
Set_Has_Completion (E);
|
|
Set_Is_Imported (E);
|
|
|
|
-- An imported object cannot be explicitly initialized
|
|
|
|
if Nkind (N) = N_Object_Declaration
|
|
and then Present (Expression (N))
|
|
then
|
|
Error_Msg_N
|
|
("imported entities cannot be initialized "
|
|
& "(RM B.1(24))", Expression (N));
|
|
end if;
|
|
|
|
else
|
|
pragma Assert (A_Id = Aspect_Export);
|
|
Set_Is_Exported (E);
|
|
end if;
|
|
|
|
-- Create the proper form of pragma Export or Import taking
|
|
-- into account Conversion, External_Name, and Link_Name.
|
|
|
|
Aitem := Build_Export_Import_Pragma (Aspect, E);
|
|
|
|
-- Otherwise the expression is either False or erroneous. There
|
|
-- is no corresponding pragma.
|
|
|
|
else
|
|
Aitem := Empty;
|
|
end if;
|
|
end Analyze_Aspect_Export_Import;
|
|
|
|
---------------------------------------
|
|
-- Analyze_Aspect_External_Link_Name --
|
|
---------------------------------------
|
|
|
|
procedure Analyze_Aspect_External_Link_Name is
|
|
Dummy_1 : Node_Id;
|
|
Dummy_2 : Node_Id;
|
|
Dummy_3 : Node_Id;
|
|
Expo : Node_Id;
|
|
Imp : Node_Id;
|
|
|
|
begin
|
|
-- Obtain all interfacing aspects that apply to the related
|
|
-- entity.
|
|
|
|
Get_Interfacing_Aspects
|
|
(Iface_Asp => Aspect,
|
|
Conv_Asp => Dummy_1,
|
|
EN_Asp => Dummy_2,
|
|
Expo_Asp => Expo,
|
|
Imp_Asp => Imp,
|
|
LN_Asp => Dummy_3,
|
|
Do_Checks => True);
|
|
|
|
-- Ensure that aspect External_Name applies to aspect Export or
|
|
-- Import.
|
|
|
|
if A_Id = Aspect_External_Name then
|
|
if No (Expo) and then No (Imp) then
|
|
Error_Msg_N
|
|
("aspect External_Name requires aspect Import or "
|
|
& "Export", Aspect);
|
|
end if;
|
|
|
|
-- Otherwise ensure that aspect Link_Name applies to aspect
|
|
-- Export or Import.
|
|
|
|
else
|
|
pragma Assert (A_Id = Aspect_Link_Name);
|
|
if No (Expo) and then No (Imp) then
|
|
Error_Msg_N
|
|
("aspect Link_Name requires aspect Import or Export",
|
|
Aspect);
|
|
end if;
|
|
end if;
|
|
end Analyze_Aspect_External_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", Expr);
|
|
|
|
elsif not Is_Entity_Name (Expr) then
|
|
Error_Msg_N
|
|
("aspect must name a discriminant of current type", Expr);
|
|
|
|
else
|
|
-- Discriminant type be an anonymous access type or an
|
|
-- anonymous access to subprogram.
|
|
|
|
-- Missing synchronized types???
|
|
|
|
declare
|
|
Disc : Entity_Id := First_Discriminant (E);
|
|
begin
|
|
while Present (Disc) loop
|
|
if Chars (Expr) = Chars (Disc)
|
|
and then Ekind (Etype (Disc)) in
|
|
E_Anonymous_Access_Subprogram_Type |
|
|
E_Anonymous_Access_Type
|
|
then
|
|
Set_Has_Implicit_Dereference (E);
|
|
Set_Has_Implicit_Dereference (Disc);
|
|
exit;
|
|
end if;
|
|
|
|
Next_Discriminant (Disc);
|
|
end loop;
|
|
|
|
-- Error if no proper access discriminant
|
|
|
|
if Present (Disc) then
|
|
-- For a type extension, check whether parent has
|
|
-- a reference discriminant, to verify that use is
|
|
-- proper.
|
|
|
|
if Is_Derived_Type (E)
|
|
and then Has_Discriminants (Etype (E))
|
|
then
|
|
declare
|
|
Parent_Disc : constant Entity_Id :=
|
|
Get_Reference_Discriminant (Etype (E));
|
|
begin
|
|
if Present (Parent_Disc)
|
|
and then Corresponding_Discriminant (Disc) /=
|
|
Parent_Disc
|
|
then
|
|
Error_Msg_N
|
|
("reference discriminant does not match "
|
|
& "discriminant of parent type", Expr);
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("not an access discriminant of&", Expr, E);
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
end Analyze_Aspect_Implicit_Dereference;
|
|
|
|
-------------------------------------------
|
|
-- Analyze_Aspect_Relaxed_Initialization --
|
|
-------------------------------------------
|
|
|
|
procedure Analyze_Aspect_Relaxed_Initialization is
|
|
procedure Analyze_Relaxed_Parameter
|
|
(Subp_Id : Entity_Id;
|
|
Param : Node_Id;
|
|
Seen : in out Elist_Id);
|
|
-- Analyze parameter that appears in the expression of the
|
|
-- aspect Relaxed_Initialization.
|
|
|
|
-------------------------------
|
|
-- Analyze_Relaxed_Parameter --
|
|
-------------------------------
|
|
|
|
procedure Analyze_Relaxed_Parameter
|
|
(Subp_Id : Entity_Id;
|
|
Param : Node_Id;
|
|
Seen : in out Elist_Id)
|
|
is
|
|
begin
|
|
-- Set name of the aspect for error messages
|
|
Error_Msg_Name_1 := Nam;
|
|
|
|
-- The relaxed parameter is a formal parameter
|
|
|
|
if Nkind (Param) in N_Identifier | N_Expanded_Name then
|
|
Analyze (Param);
|
|
|
|
declare
|
|
Item : constant Entity_Id := Entity (Param);
|
|
begin
|
|
-- It must be a formal of the analyzed subprogram
|
|
|
|
if Scope (Item) = Subp_Id then
|
|
|
|
pragma Assert (Is_Formal (Item));
|
|
|
|
-- It must not have scalar or access type
|
|
|
|
if Is_Elementary_Type (Etype (Item)) then
|
|
Error_Msg_N ("illegal aspect % item", Param);
|
|
Error_Msg_N
|
|
("\item must not have elementary type", Param);
|
|
end if;
|
|
|
|
-- Detect duplicated items
|
|
|
|
if Contains (Seen, Item) then
|
|
Error_Msg_N ("duplicate aspect % item", Param);
|
|
else
|
|
Append_New_Elmt (Item, Seen);
|
|
end if;
|
|
else
|
|
Error_Msg_N ("illegal aspect % item", Param);
|
|
end if;
|
|
end;
|
|
|
|
-- The relaxed parameter is the function's Result attribute
|
|
|
|
elsif Is_Attribute_Result (Param) then
|
|
Analyze (Param);
|
|
|
|
declare
|
|
Pref : constant Node_Id := Prefix (Param);
|
|
begin
|
|
if Present (Pref)
|
|
and then
|
|
Nkind (Pref) in N_Identifier | N_Expanded_Name
|
|
and then
|
|
Entity (Pref) = Subp_Id
|
|
then
|
|
-- Function result must not have scalar or access
|
|
-- type.
|
|
|
|
if Is_Elementary_Type (Etype (Pref)) then
|
|
Error_Msg_N ("illegal aspect % item", Param);
|
|
Error_Msg_N
|
|
("\function result must not have elementary"
|
|
& " type", Param);
|
|
end if;
|
|
|
|
-- Detect duplicated items
|
|
|
|
if Contains (Seen, Subp_Id) then
|
|
Error_Msg_N ("duplicate aspect % item", Param);
|
|
else
|
|
Append_New_Elmt (Entity (Pref), Seen);
|
|
end if;
|
|
|
|
else
|
|
Error_Msg_N ("illegal aspect % item", Param);
|
|
end if;
|
|
end;
|
|
else
|
|
Error_Msg_N ("illegal aspect % item", Param);
|
|
end if;
|
|
end Analyze_Relaxed_Parameter;
|
|
|
|
-- Local variables
|
|
|
|
Seen : Elist_Id := No_Elist;
|
|
-- Items that appear in the relaxed initialization aspect
|
|
-- expression of a subprogram; for detecting duplicates.
|
|
|
|
Restore_Scope : Boolean;
|
|
-- Will be set to True if we need to restore the scope table
|
|
-- after analyzing the aspect expression.
|
|
|
|
Prev_Id : Entity_Id;
|
|
|
|
-- Start of processing for Analyze_Aspect_Relaxed_Initialization
|
|
|
|
begin
|
|
-- Set name of the aspect for error messages
|
|
Error_Msg_Name_1 := Nam;
|
|
|
|
-- Annotation of a type; no aspect expression is allowed.
|
|
-- For a private type, the aspect must be attached to the
|
|
-- partial view.
|
|
--
|
|
-- ??? Once the exact rule for this aspect is ready, we will
|
|
-- likely reject concurrent types, etc., so let's keep the code
|
|
-- for types and variable separate.
|
|
|
|
if Is_First_Subtype (E) then
|
|
Prev_Id := Incomplete_Or_Partial_View (E);
|
|
if Present (Prev_Id) then
|
|
|
|
-- Aspect may appear on the full view of an incomplete
|
|
-- type because the incomplete declaration cannot have
|
|
-- any aspects.
|
|
|
|
if Ekind (Prev_Id) = E_Incomplete_Type then
|
|
null;
|
|
else
|
|
Error_Msg_N ("aspect % must apply to partial view", N);
|
|
end if;
|
|
|
|
elsif Present (Expr) then
|
|
Error_Msg_N ("illegal aspect % expression", Expr);
|
|
end if;
|
|
|
|
-- Annotation of a variable; no aspect expression is allowed
|
|
|
|
elsif Ekind (E) = E_Variable then
|
|
if Present (Expr) then
|
|
Error_Msg_N ("illegal aspect % expression", Expr);
|
|
end if;
|
|
|
|
-- Annotation of a constant; no aspect expression is allowed.
|
|
-- For a deferred constant, the aspect must be attached to the
|
|
-- partial view.
|
|
|
|
elsif Ekind (E) = E_Constant then
|
|
if Present (Incomplete_Or_Partial_View (E)) then
|
|
Error_Msg_N
|
|
("aspect % must apply to deferred constant", N);
|
|
|
|
elsif Present (Expr) then
|
|
Error_Msg_N ("illegal aspect % expression", Expr);
|
|
end if;
|
|
|
|
-- Annotation of a subprogram; aspect expression is required
|
|
|
|
elsif Is_Subprogram_Or_Entry (E)
|
|
or else Is_Generic_Subprogram (E)
|
|
then
|
|
if Present (Expr) then
|
|
|
|
-- If we analyze subprogram body that acts as its own
|
|
-- spec, then the subprogram itself and its formals are
|
|
-- already installed; otherwise, we need to install them,
|
|
-- as they must be visible when analyzing the aspect
|
|
-- expression.
|
|
|
|
if In_Open_Scopes (E) then
|
|
Restore_Scope := False;
|
|
else
|
|
Restore_Scope := True;
|
|
Push_Scope (E);
|
|
|
|
-- Only formals of the subprogram itself can appear
|
|
-- in Relaxed_Initialization aspect expression, not
|
|
-- formals of the enclosing generic unit. (This is
|
|
-- different than in Precondition or Depends aspects,
|
|
-- where both kinds of formals are allowed.)
|
|
|
|
Install_Formals (E);
|
|
end if;
|
|
|
|
-- Aspect expression is either an aggregate with list of
|
|
-- parameters (and possibly the Result attribute for a
|
|
-- function).
|
|
|
|
if Nkind (Expr) = N_Aggregate then
|
|
|
|
-- Component associations in the aggregate must be a
|
|
-- parameter name followed by a static boolean
|
|
-- expression.
|
|
|
|
if Present (Component_Associations (Expr)) then
|
|
declare
|
|
Assoc : Node_Id :=
|
|
First (Component_Associations (Expr));
|
|
begin
|
|
while Present (Assoc) loop
|
|
if List_Length (Choices (Assoc)) = 1 then
|
|
Analyze_Relaxed_Parameter
|
|
(E, First (Choices (Assoc)), Seen);
|
|
|
|
if Inside_A_Generic then
|
|
Preanalyze_And_Resolve
|
|
(Expression (Assoc), Any_Boolean);
|
|
else
|
|
Analyze_And_Resolve
|
|
(Expression (Assoc), Any_Boolean);
|
|
end if;
|
|
|
|
if not Is_OK_Static_Expression
|
|
(Expression (Assoc))
|
|
then
|
|
Error_Msg_Name_1 := Nam;
|
|
Error_Msg_N
|
|
("expression of aspect %" &
|
|
"must be static", Aspect);
|
|
end if;
|
|
|
|
else
|
|
Error_Msg_Name_1 := Nam;
|
|
Error_Msg_N
|
|
("illegal aspect % expression", Expr);
|
|
end if;
|
|
Next (Assoc);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
|
|
-- Expressions of the aggregate are parameter names
|
|
|
|
if Present (Expressions (Expr)) then
|
|
declare
|
|
Param : Node_Id := First (Expressions (Expr));
|
|
|
|
begin
|
|
while Present (Param) loop
|
|
Analyze_Relaxed_Parameter (E, Param, Seen);
|
|
Next (Param);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
|
|
-- Mark the aggregate expression itself as analyzed;
|
|
-- its subexpressions were marked when they themselves
|
|
-- were analyzed.
|
|
|
|
Set_Analyzed (Expr);
|
|
|
|
-- Otherwise, it is a single name of a subprogram
|
|
-- parameter (or possibly the Result attribute for
|
|
-- a function).
|
|
|
|
else
|
|
Analyze_Relaxed_Parameter (E, Expr, Seen);
|
|
end if;
|
|
|
|
if Restore_Scope then
|
|
End_Scope;
|
|
end if;
|
|
else
|
|
Error_Msg_N ("missing expression for aspect %", N);
|
|
end if;
|
|
|
|
else
|
|
Error_Msg_N ("inappropriate entity for aspect %", E);
|
|
end if;
|
|
end Analyze_Aspect_Relaxed_Initialization;
|
|
|
|
---------------------------
|
|
-- Analyze_Aspect_Static --
|
|
---------------------------
|
|
|
|
procedure Analyze_Aspect_Static is
|
|
function Has_Convention_Intrinsic (L : List_Id) return Boolean;
|
|
-- Return True if L contains a pragma argument association
|
|
-- node representing a convention Intrinsic.
|
|
|
|
------------------------------
|
|
-- Has_Convention_Intrinsic --
|
|
------------------------------
|
|
|
|
function Has_Convention_Intrinsic
|
|
(L : List_Id) return Boolean
|
|
is
|
|
Arg : Node_Id := First (L);
|
|
begin
|
|
while Present (Arg) loop
|
|
if Nkind (Arg) = N_Pragma_Argument_Association
|
|
and then Chars (Arg) = Name_Convention
|
|
and then Chars (Expression (Arg)) = Name_Intrinsic
|
|
then
|
|
return True;
|
|
end if;
|
|
|
|
Next (Arg);
|
|
end loop;
|
|
|
|
return False;
|
|
end Has_Convention_Intrinsic;
|
|
|
|
Is_Imported_Intrinsic : Boolean;
|
|
|
|
begin
|
|
if Ada_Version < Ada_2022 then
|
|
Error_Msg_Ada_2022_Feature ("aspect %", Sloc (Aspect));
|
|
return;
|
|
end if;
|
|
|
|
Is_Imported_Intrinsic := Is_Imported (E)
|
|
and then
|
|
Has_Convention_Intrinsic
|
|
(Pragma_Argument_Associations (Import_Pragma (E)));
|
|
|
|
-- The aspect applies only to expression functions that
|
|
-- statisfy the requirements for a static expression function
|
|
-- (such as having an expression that is predicate-static) as
|
|
-- well as Intrinsic imported functions as a -gnatX extension.
|
|
|
|
if not Is_Expression_Function (E)
|
|
and then
|
|
not (Extensions_Allowed and then Is_Imported_Intrinsic)
|
|
then
|
|
if Extensions_Allowed then
|
|
Error_Msg_N
|
|
("aspect % requires intrinsic or expression function",
|
|
Aspect);
|
|
|
|
elsif Is_Imported_Intrinsic then
|
|
Error_Msg_N
|
|
("aspect % on intrinsic function is an extension: " &
|
|
"use -gnatX",
|
|
Aspect);
|
|
|
|
else
|
|
Error_Msg_N
|
|
("aspect % requires expression function", Aspect);
|
|
end if;
|
|
|
|
return;
|
|
|
|
-- Ada 2022 (AI12-0075): Check that the function satisfies
|
|
-- several requirements of static functions as specified in
|
|
-- RM 6.8(5.1-5.8). Note that some of the requirements given
|
|
-- there are checked elsewhere.
|
|
|
|
else
|
|
-- The expression of the expression function must be a
|
|
-- potentially static expression (RM 2022 6.8(3.2-3.4)).
|
|
-- That's checked in Sem_Ch6.Analyze_Expression_Function.
|
|
|
|
-- The function must not contain any calls to itself, which
|
|
-- is checked in Sem_Res.Resolve_Call.
|
|
|
|
-- Each formal must be of mode in and have a static subtype
|
|
|
|
declare
|
|
Formal : Entity_Id := First_Formal (E);
|
|
begin
|
|
while Present (Formal) loop
|
|
if Ekind (Formal) /= E_In_Parameter then
|
|
Error_Msg_N
|
|
("aspect % requires formals of mode IN",
|
|
Aspect);
|
|
|
|
return;
|
|
end if;
|
|
|
|
if not Is_Static_Subtype (Etype (Formal)) then
|
|
Error_Msg_N
|
|
("aspect % requires formals with static subtypes",
|
|
Aspect);
|
|
|
|
return;
|
|
end if;
|
|
|
|
Next_Formal (Formal);
|
|
end loop;
|
|
end;
|
|
|
|
-- The function's result subtype must be a static subtype
|
|
|
|
if not Is_Static_Subtype (Etype (E)) then
|
|
Error_Msg_N
|
|
("aspect % requires function with result of "
|
|
& "a static subtype",
|
|
Aspect);
|
|
|
|
return;
|
|
end if;
|
|
|
|
-- Check that the function does not have any applicable
|
|
-- precondition or postcondition expression.
|
|
|
|
for Asp in Pre_Post_Aspects loop
|
|
if Has_Aspect (E, Asp) then
|
|
Error_Msg_Name_1 := Aspect_Names (Asp);
|
|
Error_Msg_N
|
|
("aspect % is not allowed for a static "
|
|
& "expression function",
|
|
Find_Aspect (E, Asp));
|
|
|
|
return;
|
|
end if;
|
|
end loop;
|
|
|
|
-- ??? Must check that "for result type R, if the
|
|
-- function is a boundary entity for type R (see 7.3.2),
|
|
-- no type invariant applies to type R; if R has a
|
|
-- component type C, a similar rule applies to C."
|
|
end if;
|
|
|
|
-- When the expression is present, it must be static. If it
|
|
-- evaluates to True, the expression function is treated as
|
|
-- a static function. Otherwise the aspect appears without
|
|
-- an expression and defaults to True.
|
|
|
|
if Present (Expr) then
|
|
-- Preanalyze the expression when the aspect resides in a
|
|
-- generic unit. (Is this generic-related code necessary
|
|
-- for this aspect? It's modeled on what's done for aspect
|
|
-- Disable_Controlled. ???)
|
|
|
|
if Inside_A_Generic then
|
|
Preanalyze_And_Resolve (Expr, Any_Boolean);
|
|
|
|
-- Otherwise the aspect resides in a nongeneric context
|
|
|
|
else
|
|
Analyze_And_Resolve (Expr, Any_Boolean);
|
|
|
|
-- Error if the boolean expression is not static
|
|
|
|
if not Is_OK_Static_Expression (Expr) then
|
|
Error_Msg_N
|
|
("expression of aspect % must be static", Aspect);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end Analyze_Aspect_Static;
|
|
|
|
--------------------------
|
|
-- Analyze_Aspect_Yield --
|
|
--------------------------
|
|
|
|
procedure Analyze_Aspect_Yield is
|
|
Expr_Value : Boolean := False;
|
|
|
|
begin
|
|
-- Check valid declarations for 'Yield
|
|
|
|
if Nkind (N) in N_Abstract_Subprogram_Declaration
|
|
| N_Entry_Declaration
|
|
| N_Generic_Subprogram_Declaration
|
|
| N_Subprogram_Declaration
|
|
| N_Formal_Subprogram_Declaration
|
|
and then not Within_Protected_Type (E)
|
|
then
|
|
null;
|
|
|
|
elsif Within_Protected_Type (E) then
|
|
Error_Msg_N
|
|
("aspect% not applicable to protected operation", Id);
|
|
return;
|
|
|
|
else
|
|
Error_Msg_N
|
|
("aspect% only applicable to subprogram and entry "
|
|
& "declarations", Id);
|
|
return;
|
|
end if;
|
|
|
|
-- Evaluate its static expression (if available); otherwise it
|
|
-- defaults to True.
|
|
|
|
if No (Expr) then
|
|
Expr_Value := True;
|
|
|
|
-- Otherwise it must have a static boolean expression
|
|
|
|
else
|
|
if Inside_A_Generic then
|
|
Preanalyze_And_Resolve (Expr, Any_Boolean);
|
|
else
|
|
Analyze_And_Resolve (Expr, Any_Boolean);
|
|
end if;
|
|
|
|
if Is_OK_Static_Expression (Expr) then
|
|
if Is_True (Static_Boolean (Expr)) then
|
|
Expr_Value := True;
|
|
end if;
|
|
else
|
|
Error_Msg_N
|
|
("expression of aspect % must be static", Aspect);
|
|
end if;
|
|
end if;
|
|
|
|
if Expr_Value then
|
|
Set_Has_Yield_Aspect (E);
|
|
end if;
|
|
|
|
-- If the Yield aspect is specified for a dispatching
|
|
-- subprogram that inherits the aspect, the specified
|
|
-- value shall be confirming.
|
|
|
|
if Present (Expr)
|
|
and then Is_Dispatching_Operation (E)
|
|
and then Present (Overridden_Operation (E))
|
|
and then Has_Yield_Aspect (Overridden_Operation (E))
|
|
/= Is_True (Static_Boolean (Expr))
|
|
then
|
|
Error_Msg_N ("specification of inherited aspect% can only " &
|
|
"confirm parent value", Id);
|
|
end if;
|
|
end Analyze_Aspect_Yield;
|
|
|
|
----------------------------------------
|
|
-- Check_Expr_Is_OK_Static_Expression --
|
|
----------------------------------------
|
|
|
|
procedure Check_Expr_Is_OK_Static_Expression
|
|
(Expr : Node_Id;
|
|
Typ : Entity_Id := Empty)
|
|
is
|
|
begin
|
|
if Present (Typ) then
|
|
Analyze_And_Resolve (Expr, Typ);
|
|
else
|
|
Analyze_And_Resolve (Expr);
|
|
end if;
|
|
|
|
-- An expression cannot be considered static if its resolution
|
|
-- failed or if it's erroneous. Stop the analysis of the
|
|
-- related aspect.
|
|
|
|
if Etype (Expr) = Any_Type or else Error_Posted (Expr) then
|
|
raise Aspect_Exit;
|
|
|
|
elsif Is_OK_Static_Expression (Expr) then
|
|
return;
|
|
|
|
-- Finally, we have a real error
|
|
|
|
else
|
|
Error_Msg_Name_1 := Nam;
|
|
Flag_Non_Static_Expr
|
|
("entity for aspect% must be a static expression",
|
|
Expr);
|
|
raise Aspect_Exit;
|
|
end if;
|
|
end Check_Expr_Is_OK_Static_Expression;
|
|
|
|
------------------------
|
|
-- Directly_Specified --
|
|
------------------------
|
|
|
|
function Directly_Specified
|
|
(Id : Entity_Id; A : Aspect_Id) return Boolean
|
|
is
|
|
Aspect_Spec : constant Node_Id := Find_Aspect (Id, A);
|
|
begin
|
|
return Present (Aspect_Spec) and then Entity (Aspect_Spec) = Id;
|
|
end Directly_Specified;
|
|
|
|
-----------------------
|
|
-- Make_Aitem_Pragma --
|
|
-----------------------
|
|
|
|
function Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations : List_Id;
|
|
Pragma_Name : Name_Id) return Node_Id
|
|
is
|
|
Args : List_Id := Pragma_Argument_Associations;
|
|
Aitem : Node_Id;
|
|
|
|
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);
|
|
|
|
return Aitem;
|
|
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);
|
|
|
|
-- Build the reference to E that will be used in the built pragmas
|
|
|
|
Ent := New_Occurrence_Of (E, Sloc (Id));
|
|
|
|
if A_Id in Aspect_Attach_Handler | Aspect_Interrupt_Handler then
|
|
|
|
-- Treat the specification as a reference to the protected
|
|
-- operation, which might otherwise appear unreferenced and
|
|
-- generate spurious warnings.
|
|
|
|
Generate_Reference (E, Id);
|
|
end if;
|
|
|
|
-- 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)
|
|
or else A_Id in Aspect_Async_Readers
|
|
| Aspect_Async_Writers
|
|
| Aspect_Effective_Reads
|
|
| Aspect_Effective_Writes
|
|
| Aspect_Preelaborable_Initialization
|
|
then
|
|
Error_Msg_Name_1 := Nam;
|
|
|
|
-- Not allowed for renaming declarations. Examine the original
|
|
-- node because a subprogram renaming may have been rewritten
|
|
-- as a body.
|
|
|
|
if Nkind (Original_Node (N)) in N_Renaming_Declaration then
|
|
Error_Msg_N
|
|
("aspect % not allowed for renaming declaration",
|
|
Aspect);
|
|
end if;
|
|
|
|
-- Not allowed for formal type declarations in previous
|
|
-- versions of the language. Allowed for them only for
|
|
-- shared variable control aspects.
|
|
|
|
-- Original node is used in case expansion rewrote the node -
|
|
-- as is the case with generic derived types.
|
|
|
|
if Nkind (Original_Node (N)) = N_Formal_Type_Declaration then
|
|
if Ada_Version < Ada_2022 then
|
|
Error_Msg_N
|
|
("aspect % not allowed for formal type declaration",
|
|
Aspect);
|
|
|
|
elsif A_Id not in Aspect_Atomic
|
|
| Aspect_Volatile
|
|
| Aspect_Independent
|
|
| Aspect_Atomic_Components
|
|
| Aspect_Independent_Components
|
|
| Aspect_Volatile_Components
|
|
| Aspect_Async_Readers
|
|
| Aspect_Async_Writers
|
|
| Aspect_Effective_Reads
|
|
| Aspect_Effective_Writes
|
|
| Aspect_Preelaborable_Initialization
|
|
then
|
|
Error_Msg_N
|
|
("aspect % not allowed for formal type declaration",
|
|
Aspect);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
-- Copy expression for later processing by the procedures
|
|
-- Check_Aspect_At_[Freeze_Point | End_Of_Declarations]
|
|
|
|
-- The expression may be a subprogram name, and can
|
|
-- be an operator name that appears as a string, but
|
|
-- requires its own analysis procedure (see sem_ch6).
|
|
|
|
if Nkind (Expr) = N_Operator_Symbol then
|
|
Set_Entity (Id, Expr);
|
|
else
|
|
Set_Entity (Id, New_Copy_Tree (Expr));
|
|
end if;
|
|
|
|
-- Set Delay_Required as appropriate to aspect
|
|
|
|
case Aspect_Delay (A_Id) is
|
|
when Always_Delay =>
|
|
-- For Boolean aspects, do not delay if no expression
|
|
|
|
if A_Id in Boolean_Aspects | Library_Unit_Aspects then
|
|
Delay_Required := Present (Expr);
|
|
else
|
|
Delay_Required := True;
|
|
end if;
|
|
|
|
when Never_Delay =>
|
|
Delay_Required := False;
|
|
|
|
when Rep_Aspect =>
|
|
|
|
-- For Boolean aspects, do not delay if no expression except
|
|
-- for Full_Access_Only because we need to process it after
|
|
-- Volatile and Atomic, which can be independently delayed.
|
|
|
|
if A_Id in Boolean_Aspects
|
|
and then A_Id /= Aspect_Full_Access_Only
|
|
and then No (Expr)
|
|
then
|
|
Delay_Required := False;
|
|
|
|
-- For non-Boolean aspects, if the 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.
|
|
|
|
elsif A_Id not in Boolean_Aspects
|
|
and then Present (Expr)
|
|
and then Nkind (Expr) = N_Integer_Literal
|
|
then
|
|
Delay_Required := False;
|
|
|
|
-- For Alignment and various Size aspects, do not delay for
|
|
-- an attribute reference whose prefix is Standard, for
|
|
-- example Standard'Maximum_Alignment or Standard'Word_Size.
|
|
|
|
elsif A_Id in Aspect_Alignment
|
|
| Aspect_Component_Size
|
|
| Aspect_Object_Size
|
|
| Aspect_Size
|
|
| Aspect_Value_Size
|
|
and then Present (Expr)
|
|
and then Nkind (Expr) = N_Attribute_Reference
|
|
and then Nkind (Prefix (Expr)) = N_Identifier
|
|
and then Chars (Prefix (Expr)) = Name_Standard
|
|
then
|
|
Delay_Required := False;
|
|
|
|
-- All other cases are delayed
|
|
|
|
else
|
|
Delay_Required := True;
|
|
Set_Has_Delayed_Rep_Aspects (E);
|
|
end if;
|
|
end case;
|
|
|
|
if Delay_Required
|
|
|
|
and then (A_Id = Aspect_Stable_Properties
|
|
or else A_Id = Aspect_Designated_Storage_Model
|
|
or else A_Id = Aspect_Storage_Model_Type)
|
|
-- ??? It seems like we should do this for all aspects, not
|
|
-- just these, but that causes as-yet-undiagnosed regressions.
|
|
|
|
then
|
|
Set_Has_Delayed_Aspects (E);
|
|
Set_Is_Delayed_Aspect (Aspect);
|
|
end if;
|
|
|
|
-- Check 13.1(9.2/5): A representation aspect of a subtype or type
|
|
-- shall not be specified (whether by a representation item or an
|
|
-- aspect_specification) before the type is completely defined
|
|
-- (see 3.11.1).
|
|
|
|
if Is_Representation_Aspect (A_Id)
|
|
and then Rep_Item_Too_Early (E, N)
|
|
then
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Processing based on specific aspect
|
|
|
|
case A_Id is
|
|
when Aspect_Unimplemented =>
|
|
null; -- ??? temp for now
|
|
|
|
-- 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_Put_Image
|
|
| Aspect_Read
|
|
| Aspect_Scalar_Storage_Order
|
|
| Aspect_Simple_Storage_Pool
|
|
| Aspect_Size
|
|
| Aspect_Small
|
|
| Aspect_Storage_Pool
|
|
| Aspect_Stream_Size
|
|
| Aspect_Value_Size
|
|
| Aspect_Variable_Indexing
|
|
| Aspect_Write
|
|
=>
|
|
-- Indexing aspects apply only to tagged type
|
|
|
|
if A_Id in Aspect_Constant_Indexing
|
|
| Aspect_Variable_Indexing
|
|
and then not (Is_Type (E)
|
|
and then Is_Tagged_Type (E))
|
|
then
|
|
Error_Msg_N
|
|
("indexing aspect can only apply to a tagged type",
|
|
Aspect);
|
|
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 in Aspect_Input
|
|
| Aspect_Output
|
|
| Aspect_Read
|
|
| Aspect_Write
|
|
then
|
|
if not Is_First_Subtype (E) then
|
|
Error_Msg_N
|
|
("local name must be a first subtype", Aspect);
|
|
goto Continue;
|
|
|
|
-- If stream aspect applies to the class-wide type,
|
|
-- the generated attribute definition applies to the
|
|
-- class-wide type as well.
|
|
|
|
elsif Class_Present (Aspect) then
|
|
Ent :=
|
|
Make_Attribute_Reference (Loc,
|
|
Prefix => Ent,
|
|
Attribute_Name => Name_Class);
|
|
end if;
|
|
end if;
|
|
|
|
-- Construct the attribute_definition_clause. The expression
|
|
-- in the aspect specification is simply shared with the
|
|
-- constructed attribute, because it will be fully analyzed
|
|
-- when the attribute is processed.
|
|
|
|
Aitem :=
|
|
Make_Attribute_Definition_Clause (Loc,
|
|
Name => Ent,
|
|
Chars => Nam,
|
|
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
|
|
|
|
when Aspect_Linker_Section =>
|
|
Aitem := 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_Linker_Section);
|
|
|
|
-- Linker_Section does not need delaying, as its argument
|
|
-- must be a static string. Furthermore, if applied to
|
|
-- an object with an explicit initialization, the object
|
|
-- must be frozen in order to elaborate the initialization
|
|
-- code. (This is already done for types with implicit
|
|
-- initialization, such as protected types.)
|
|
|
|
if Nkind (N) = N_Object_Declaration
|
|
and then Has_Init_Expression (N)
|
|
then
|
|
Delay_Required := False;
|
|
end if;
|
|
|
|
-- Synchronization
|
|
|
|
-- Corresponds to pragma Implemented, construct the pragma
|
|
|
|
when Aspect_Synchronization =>
|
|
Aitem := 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 =>
|
|
Aitem := 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;
|
|
|
|
elsif Is_Incomplete_Type (E) then
|
|
Error_Msg_N
|
|
("predicate cannot apply to incomplete view", Aspect);
|
|
|
|
elsif Is_Generic_Type (E) then
|
|
Error_Msg_N
|
|
("predicate cannot apply to formal type", 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.
|
|
|
|
Aitem := 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);
|
|
|
|
-- If the entity has a dynamic predicate, any inherited
|
|
-- static predicate becomes dynamic as well, and the
|
|
-- predicate function includes the conjunction of both.
|
|
|
|
Set_Has_Static_Predicate_Aspect (E, False);
|
|
|
|
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));
|
|
|
|
-- If there is an Underlying_Full_View, also create a
|
|
-- freeze node for that one.
|
|
|
|
if Is_Private_Type (Full_View (E)) then
|
|
declare
|
|
U_Full : constant Entity_Id :=
|
|
Underlying_Full_View (Full_View (E));
|
|
begin
|
|
if Present (U_Full) then
|
|
Set_Has_Delayed_Aspects (U_Full);
|
|
Ensure_Freeze_Node (U_Full);
|
|
end if;
|
|
end;
|
|
end if;
|
|
end if;
|
|
|
|
-- Predicate_Failure
|
|
|
|
when Aspect_Predicate_Failure =>
|
|
|
|
-- This aspect applies only to subtypes
|
|
|
|
if not Is_Type (E) then
|
|
Error_Msg_N
|
|
("predicate can only be specified for a subtype",
|
|
Aspect);
|
|
goto Continue;
|
|
|
|
elsif Is_Incomplete_Type (E) then
|
|
Error_Msg_N
|
|
("predicate cannot apply to incomplete view", Aspect);
|
|
goto Continue;
|
|
|
|
elsif not Has_Predicates (E) then
|
|
Error_Msg_N
|
|
("Predicate_Failure requires previous predicate" &
|
|
" specification", Aspect);
|
|
goto Continue;
|
|
|
|
elsif not (Directly_Specified (E, Aspect_Dynamic_Predicate)
|
|
or else Directly_Specified (E, Aspect_Static_Predicate)
|
|
or else Directly_Specified (E, Aspect_Predicate))
|
|
then
|
|
Error_Msg_N
|
|
("Predicate_Failure requires accompanying" &
|
|
" noninherited predicate specification", Aspect);
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Construct the pragma
|
|
|
|
Aitem := 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_Failure);
|
|
|
|
-- 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 =>
|
|
Analyze_Aspect_Convention;
|
|
goto Continue;
|
|
|
|
-- External_Name, Link_Name
|
|
|
|
when Aspect_External_Name
|
|
| Aspect_Link_Name
|
|
=>
|
|
Analyze_Aspect_External_Link_Name;
|
|
goto Continue;
|
|
|
|
-- 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
|
|
=>
|
|
-- Verify the expression is static when Static_Priorities is
|
|
-- enabled.
|
|
|
|
if not Is_OK_Static_Expression (Expr) then
|
|
Check_Restriction (Static_Priorities, Expr);
|
|
end if;
|
|
|
|
if Nkind (N) in 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. RM D.1 does not forbid this explicitly,
|
|
-- but RM 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 RM 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 'C'P'U 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.
|
|
|
|
if Restricted_Profile then
|
|
Discard_Node (RTE (RE_Activate_Restricted_Tasks));
|
|
else
|
|
Discard_Node (RTE (RE_Activate_Tasks));
|
|
end if;
|
|
|
|
-- Handling for these aspects in subprograms is complete
|
|
|
|
goto Continue;
|
|
|
|
-- For task and protected types pass the aspect as an
|
|
-- attribute.
|
|
|
|
else
|
|
Aitem :=
|
|
Make_Attribute_Definition_Clause (Loc,
|
|
Name => Ent,
|
|
Chars => Nam,
|
|
Expression => Relocate_Node (Expr));
|
|
end if;
|
|
|
|
-- Suppress/Unsuppress
|
|
|
|
when Aspect_Suppress
|
|
| Aspect_Unsuppress
|
|
=>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr)),
|
|
Make_Pragma_Argument_Association (Sloc (Expr),
|
|
Expression => New_Occurrence_Of (E, Loc))),
|
|
Pragma_Name => Nam);
|
|
|
|
Delay_Required := False;
|
|
|
|
-- Warnings
|
|
|
|
when Aspect_Warnings =>
|
|
Aitem := 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 => Name_Warnings);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- 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.
|
|
|
|
Aitem := 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;
|
|
|
|
begin
|
|
-- When aspect Abstract_State appears on a generic package,
|
|
-- it is propagated 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 (Context) in N_Generic_Package_Declaration
|
|
| N_Package_Declaration
|
|
then
|
|
Aitem := 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);
|
|
Insert_Pragma
|
|
(Prag => Aitem,
|
|
Is_Instance =>
|
|
Is_Generic_Instance (Defining_Entity (Context)));
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("aspect & must apply to a package declaration",
|
|
Aspect, Id);
|
|
end if;
|
|
|
|
goto Continue;
|
|
end Abstract_State;
|
|
|
|
-- Aspect Async_Readers is never delayed because it is
|
|
-- equivalent to a source pragma which appears after the
|
|
-- related object declaration.
|
|
|
|
when Aspect_Async_Readers =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Async_Readers);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Aspect Async_Writers is never delayed because it is
|
|
-- equivalent to a source pragma which appears after the
|
|
-- related object declaration.
|
|
|
|
when Aspect_Async_Writers =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Async_Writers);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Aspect Constant_After_Elaboration is never delayed because
|
|
-- it is equivalent to a source pragma which appears after the
|
|
-- related object declaration.
|
|
|
|
when Aspect_Constant_After_Elaboration =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name =>
|
|
Name_Constant_After_Elaboration);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Aspect Default_Internal_Condition is never delayed because
|
|
-- it is equivalent to a source pragma which appears after the
|
|
-- related private type. To deal with forward references, the
|
|
-- generated pragma is stored in the rep chain of the related
|
|
-- private type as types do not carry contracts. The pragma is
|
|
-- wrapped inside of a procedure at the freeze point of the
|
|
-- private type's full view.
|
|
|
|
-- A type entity argument is appended to facilitate inheriting
|
|
-- the aspect from parent types (see Build_DIC_Procedure_Body),
|
|
-- though that extra argument isn't documented for the pragma.
|
|
|
|
when Aspect_Default_Initial_Condition =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr)),
|
|
Make_Pragma_Argument_Association (Sloc (Ent),
|
|
Expression => Ent)),
|
|
Pragma_Name =>
|
|
Name_Default_Initial_Condition);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Default_Storage_Pool
|
|
|
|
when Aspect_Default_Storage_Pool =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name =>
|
|
Name_Default_Storage_Pool);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- 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 =>
|
|
Aitem := 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;
|
|
|
|
-- Aspect Effective_Reads is never delayed because it is
|
|
-- equivalent to a source pragma which appears after the
|
|
-- related object declaration.
|
|
|
|
when Aspect_Effective_Reads =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Effective_Reads);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Aspect Effective_Writes is never delayed because it is
|
|
-- equivalent to a source pragma which appears after the
|
|
-- related object declaration.
|
|
|
|
when Aspect_Effective_Writes =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Effective_Writes);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Aspect Extensions_Visible is never delayed because it is
|
|
-- equivalent to a source pragma which appears after the
|
|
-- related subprogram.
|
|
|
|
when Aspect_Extensions_Visible =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Extensions_Visible);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Aspect Ghost is never delayed because it is equivalent to a
|
|
-- source pragma which appears at the top of [generic] package
|
|
-- declarations or after an object, a [generic] subprogram, or
|
|
-- a type declaration.
|
|
|
|
when Aspect_Ghost =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Ghost);
|
|
|
|
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 =>
|
|
Aitem := 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;
|
|
|
|
begin
|
|
-- When aspect Initial_Condition appears on a generic
|
|
-- package, it is propagated 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 (Context) in N_Generic_Package_Declaration
|
|
| N_Package_Declaration
|
|
then
|
|
Aitem := 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);
|
|
Insert_Pragma
|
|
(Prag => Aitem,
|
|
Is_Instance =>
|
|
Is_Generic_Instance (Defining_Entity (Context)));
|
|
|
|
-- Otherwise the context is illegal
|
|
|
|
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;
|
|
|
|
begin
|
|
-- When aspect Initializes appears on a generic package,
|
|
-- it is propagated 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 (Context) in N_Generic_Package_Declaration
|
|
| N_Package_Declaration
|
|
then
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Initializes);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma
|
|
(Prag => Aitem,
|
|
Is_Instance =>
|
|
Is_Generic_Instance (Defining_Entity (Context)));
|
|
|
|
-- Otherwise the context is illegal
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("aspect & must apply to a package declaration",
|
|
Aspect, Id);
|
|
end if;
|
|
|
|
goto Continue;
|
|
end Initializes;
|
|
|
|
-- Max_Entry_Queue_Depth
|
|
|
|
when Aspect_Max_Entry_Queue_Depth =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Max_Entry_Queue_Depth);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Max_Entry_Queue_Length
|
|
|
|
when Aspect_Max_Entry_Queue_Length =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Max_Entry_Queue_Length);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Max_Queue_Length
|
|
|
|
when Aspect_Max_Queue_Length =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Max_Queue_Length);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Aspect No_Caching is never delayed because it is equivalent
|
|
-- to a source pragma which appears after the related object
|
|
-- declaration.
|
|
|
|
when Aspect_No_Caching =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_No_Caching);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- No_Controlled_Parts, No_Task_Parts
|
|
|
|
when Aspect_No_Controlled_Parts | Aspect_No_Task_Parts =>
|
|
|
|
-- Check appropriate type argument
|
|
|
|
if not Is_Type (E) then
|
|
Error_Msg_N
|
|
("aspect % can only be applied to types", E);
|
|
end if;
|
|
|
|
-- Disallow subtypes
|
|
|
|
if Nkind (Declaration_Node (E)) = N_Subtype_Declaration then
|
|
Error_Msg_N
|
|
("aspect % cannot be applied to subtypes", E);
|
|
end if;
|
|
|
|
-- Resolve the expression to a boolean
|
|
|
|
if Present (Expr) then
|
|
Check_Expr_Is_OK_Static_Expression (Expr, Any_Boolean);
|
|
end if;
|
|
|
|
goto Continue;
|
|
|
|
-- Obsolescent
|
|
|
|
when Aspect_Obsolescent => declare
|
|
Args : List_Id;
|
|
|
|
begin
|
|
if No (Expr) then
|
|
Args := No_List;
|
|
else
|
|
Args := New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Expr),
|
|
Expression => Relocate_Node (Expr)));
|
|
end if;
|
|
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => Args,
|
|
Pragma_Name => Name_Obsolescent);
|
|
end;
|
|
|
|
-- Part_Of
|
|
|
|
when Aspect_Part_Of =>
|
|
if Nkind (N) in N_Object_Declaration
|
|
| N_Package_Instantiation
|
|
or else Is_Single_Concurrent_Type_Declaration (N)
|
|
then
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Part_Of);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("aspect & must apply to package instantiation, "
|
|
& "object, single protected type or single task type",
|
|
Aspect, Id);
|
|
end if;
|
|
|
|
goto Continue;
|
|
|
|
-- SPARK_Mode
|
|
|
|
when Aspect_SPARK_Mode =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_SPARK_Mode);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- 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 =>
|
|
Aitem := 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 =>
|
|
Aitem := 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 =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Refined_Post);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Refined_State
|
|
|
|
when Aspect_Refined_State =>
|
|
|
|
-- 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
|
|
Aitem := 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);
|
|
Insert_Pragma (Aitem);
|
|
|
|
-- Otherwise the context is illegal
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("aspect & must apply to a package body", Aspect, Id);
|
|
end if;
|
|
|
|
goto Continue;
|
|
|
|
-- Relative_Deadline
|
|
|
|
when Aspect_Relative_Deadline =>
|
|
Aitem := 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;
|
|
|
|
-- Relaxed_Initialization
|
|
|
|
when Aspect_Relaxed_Initialization =>
|
|
Analyze_Aspect_Relaxed_Initialization;
|
|
goto Continue;
|
|
|
|
-- Secondary_Stack_Size
|
|
|
|
-- Aspect Secondary_Stack_Size needs to be converted into a
|
|
-- pragma for two reasons: the attribute is not analyzed until
|
|
-- after the expansion of the task type declaration and the
|
|
-- attribute does not have visibility on the discriminant.
|
|
|
|
when Aspect_Secondary_Stack_Size =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name =>
|
|
Name_Secondary_Stack_Size);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Volatile_Function
|
|
|
|
-- Aspect Volatile_Function is never delayed because it is
|
|
-- equivalent to a source pragma which appears after the
|
|
-- related subprogram.
|
|
|
|
when Aspect_Volatile_Function =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Volatile_Function);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Case 2e: Annotate aspect
|
|
|
|
when Aspect_Annotate | Aspect_GNAT_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 -- CODEFIX
|
|
("redundant parentheses", 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));
|
|
|
|
Aitem := 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.
|
|
|
|
when Aspect_Default_Value | Aspect_Default_Component_Value =>
|
|
Error_Msg_Name_1 := Nam;
|
|
|
|
if not Is_Type (E) then
|
|
Error_Msg_N ("aspect% can only apply to a type", Id);
|
|
goto Continue;
|
|
|
|
elsif not Is_First_Subtype (E) then
|
|
Error_Msg_N ("aspect% cannot apply to subtype", Id);
|
|
goto Continue;
|
|
|
|
elsif A_Id = Aspect_Default_Value
|
|
and then not Is_Scalar_Type (E)
|
|
then
|
|
Error_Msg_N
|
|
("aspect% can only be applied to scalar type", Id);
|
|
goto Continue;
|
|
|
|
elsif A_Id = Aspect_Default_Component_Value then
|
|
if not Is_Array_Type (E) then
|
|
Error_Msg_N
|
|
("aspect% can only be applied to array type", Id);
|
|
goto Continue;
|
|
|
|
elsif not Is_Scalar_Type (Component_Type (E)) then
|
|
Error_Msg_N ("aspect% requires scalar components", Id);
|
|
goto Continue;
|
|
end if;
|
|
end if;
|
|
|
|
Aitem := Empty;
|
|
|
|
when Aspect_Aggregate =>
|
|
Validate_Aspect_Aggregate (Expr);
|
|
Record_Rep_Item (E, Aspect);
|
|
goto Continue;
|
|
|
|
when Aspect_Stable_Properties =>
|
|
Validate_Aspect_Stable_Properties
|
|
(E, Expr, Class_Present => Class_Present (Aspect));
|
|
Record_Rep_Item (E, Aspect);
|
|
goto Continue;
|
|
|
|
when Aspect_Designated_Storage_Model =>
|
|
if not Extensions_Allowed then
|
|
Error_Msg_N
|
|
("aspect only allowed if extensions enabled",
|
|
Aspect);
|
|
Error_Msg_N
|
|
("\unit must be compiled with -gnatX switch", Aspect);
|
|
|
|
elsif not Is_Type (E)
|
|
or else Ekind (E) /= E_Access_Type
|
|
then
|
|
Error_Msg_N
|
|
("can only be specified for pool-specific access type",
|
|
Aspect);
|
|
end if;
|
|
|
|
Record_Rep_Item (E, Aspect);
|
|
goto Continue;
|
|
|
|
when Aspect_Storage_Model_Type =>
|
|
if not Extensions_Allowed then
|
|
Error_Msg_N
|
|
("aspect only allowed if extensions enabled",
|
|
Aspect);
|
|
Error_Msg_N
|
|
("\unit must be compiled with -gnatX switch", Aspect);
|
|
|
|
elsif not Is_Type (E)
|
|
or else not Is_Immutably_Limited_Type (E)
|
|
then
|
|
Error_Msg_N
|
|
("can only be specified for immutably limited type",
|
|
Aspect);
|
|
end if;
|
|
|
|
Record_Rep_Item (E, Aspect);
|
|
goto Continue;
|
|
|
|
when Aspect_Integer_Literal
|
|
| Aspect_Real_Literal
|
|
| Aspect_String_Literal
|
|
=>
|
|
|
|
if not Is_First_Subtype (E) then
|
|
Error_Msg_N
|
|
("may only be specified for a first subtype", Aspect);
|
|
goto Continue;
|
|
end if;
|
|
|
|
if Ada_Version < Ada_2022 then
|
|
Check_Restriction
|
|
(No_Implementation_Aspect_Specifications, N);
|
|
end if;
|
|
|
|
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;
|
|
|
|
-- 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/Subprogram_Variant 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 in Aspect_Pre | Aspect_Precondition then
|
|
Pname := Name_Precondition;
|
|
else
|
|
Pname := Name_Postcondition;
|
|
end if;
|
|
|
|
-- Check that the class-wide predicate cannot be applied to
|
|
-- an operation of a synchronized type. AI12-0182 forbids
|
|
-- these altogether, while earlier language semantics made
|
|
-- them legal on tagged synchronized types.
|
|
|
|
-- Other legality checks are performed when analyzing the
|
|
-- contract of the operation.
|
|
|
|
if Class_Present (Aspect)
|
|
and then Is_Concurrent_Type (Current_Scope)
|
|
and then Ekind (E) in E_Entry | E_Function | E_Procedure
|
|
then
|
|
Error_Msg_Name_1 := Original_Aspect_Pragma_Name (Aspect);
|
|
Error_Msg_N
|
|
("aspect % can only be specified for a primitive "
|
|
& "operation of a tagged type", Aspect);
|
|
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Remember class-wide conditions; they will be merged
|
|
-- with inherited conditions.
|
|
|
|
if Class_Present (Aspect)
|
|
and then A_Id in Aspect_Pre | Aspect_Post
|
|
and then Is_Subprogram (E)
|
|
and then not Is_Ignored_Ghost_Entity (E)
|
|
then
|
|
if A_Id = Aspect_Pre then
|
|
if Is_Ignored (Aspect) then
|
|
Set_Ignored_Class_Preconditions (E,
|
|
New_Copy_Tree (Expr));
|
|
else
|
|
Set_Class_Preconditions (E, New_Copy_Tree (Expr));
|
|
end if;
|
|
|
|
-- Postconditions may split into separate aspects, and we
|
|
-- remember the expression before such split (i.e. when
|
|
-- the first postcondition is processed).
|
|
|
|
elsif No (Class_Postconditions (E))
|
|
and then No (Ignored_Class_Postconditions (E))
|
|
then
|
|
if Is_Ignored (Aspect) then
|
|
Set_Ignored_Class_Postconditions (E,
|
|
New_Copy_Tree (Expr));
|
|
else
|
|
Set_Class_Postconditions (E, New_Copy_Tree (Expr));
|
|
end if;
|
|
end if;
|
|
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 don't do this in GNATprove mode, because it brings no
|
|
-- benefit for proof and causes annoyance for flow analysis,
|
|
-- which prefers to be as close to the original source code
|
|
-- as possible. Also we don't do this when analyzing generic
|
|
-- units since it causes spurious visibility errors in the
|
|
-- preanalysis of instantiations.
|
|
|
|
if not GNATprove_Mode
|
|
and then (Pname = Name_Postcondition
|
|
or else not Class_Present (Aspect))
|
|
and then not Inside_A_Generic
|
|
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
|
|
|
|
-- We use Relocate_Node here rather than New_Copy_Tree
|
|
-- because subsequent visibility analysis of the aspect
|
|
-- depends on this sharing. This should be cleaned up???
|
|
|
|
-- If the context is generic, we want to preserve the
|
|
-- original tree, and simply share it between aspect and
|
|
-- generated attribute. This parallels what is done in
|
|
-- sem_prag.adb (see Get_Argument).
|
|
|
|
declare
|
|
New_Expr : Node_Id;
|
|
|
|
begin
|
|
if Inside_A_Generic then
|
|
New_Expr := Expr;
|
|
else
|
|
New_Expr := Relocate_Node (Expr);
|
|
end if;
|
|
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Eloc,
|
|
Chars => Name_Check,
|
|
Expression => New_Expr)),
|
|
Pragma_Name => Pname);
|
|
end;
|
|
|
|
-- 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;
|
|
|
|
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
|
|
or else Null_Record_Present (Expr)
|
|
then
|
|
Error_Msg_Name_1 := Nam;
|
|
Error_Msg_NE
|
|
("wrong syntax for aspect % for &", Id, E);
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Check that the expression is a proper aggregate (no
|
|
-- parentheses).
|
|
|
|
if Paren_Count (Expr) /= 0 then
|
|
Error_Msg_F -- CODEFIX
|
|
("redundant parentheses", Expr);
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Create the list of arguments for building the Test_Case
|
|
-- pragma.
|
|
|
|
Comp_Expr := First (Expressions (Expr));
|
|
while Present (Comp_Expr) loop
|
|
Append_To (Args,
|
|
Make_Pragma_Argument_Association (Sloc (Comp_Expr),
|
|
Expression => Relocate_Node (Comp_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;
|
|
|
|
Append_To (Args,
|
|
Make_Pragma_Argument_Association (Sloc (Comp_Assn),
|
|
Chars => Chars (First (Choices (Comp_Assn))),
|
|
Expression =>
|
|
Relocate_Node (Expression (Comp_Assn))));
|
|
Next (Comp_Assn);
|
|
end loop;
|
|
|
|
-- Build the test-case pragma
|
|
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => Args,
|
|
Pragma_Name => Name_Test_Case);
|
|
end Test_Case;
|
|
|
|
-- Contract_Cases
|
|
|
|
when Aspect_Contract_Cases =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Contract_Cases);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Subprogram_Variant
|
|
|
|
when Aspect_Subprogram_Variant =>
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Expression => Relocate_Node (Expr))),
|
|
Pragma_Name => Name_Subprogram_Variant);
|
|
|
|
Decorate (Aspect, Aitem);
|
|
Insert_Pragma (Aitem);
|
|
goto Continue;
|
|
|
|
-- Case 5: Special handling for aspects with an optional
|
|
-- boolean argument.
|
|
|
|
-- In the delayed 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 type " &
|
|
"or 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 if we wanted to handle the corner case
|
|
-- of "true" or "false" being redefined.
|
|
|
|
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 in Aspect_Export | Aspect_Import then
|
|
Analyze_Aspect_Export_Import;
|
|
|
|
-- Disable_Controlled
|
|
|
|
elsif A_Id = Aspect_Disable_Controlled then
|
|
Analyze_Aspect_Disable_Controlled;
|
|
goto Continue;
|
|
|
|
-- Ada 2022 (AI12-0129): Exclusive_Functions
|
|
|
|
elsif A_Id = Aspect_Exclusive_Functions then
|
|
if Ekind (E) /= E_Protected_Type then
|
|
Error_Msg_Name_1 := Nam;
|
|
Error_Msg_N
|
|
("aspect % only applies to a protected type " &
|
|
"or object",
|
|
Aspect);
|
|
end if;
|
|
|
|
goto Continue;
|
|
|
|
-- Ada 2022 (AI12-0363): Full_Access_Only
|
|
|
|
elsif A_Id = Aspect_Full_Access_Only then
|
|
Error_Msg_Ada_2022_Feature ("aspect %", Sloc (Aspect));
|
|
|
|
-- Ada 2022 (AI12-0075): static expression functions
|
|
|
|
elsif A_Id = Aspect_Static then
|
|
Analyze_Aspect_Static;
|
|
goto Continue;
|
|
|
|
-- Ada 2022 (AI12-0279)
|
|
|
|
elsif A_Id = Aspect_Yield then
|
|
Analyze_Aspect_Yield;
|
|
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 (N) in N_Package_Declaration
|
|
| N_Generic_Package_Declaration
|
|
and then Nkind (Parent (N)) /= N_Compilation_Unit
|
|
|
|
-- Aspect is legal on a local instantiation of a library-
|
|
-- level generic unit.
|
|
|
|
and then not Is_Generic_Instance (Defining_Entity (N))
|
|
then
|
|
Error_Msg_N
|
|
("incorrect context for library unit aspect&", Id);
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- Cases where we do not delay
|
|
|
|
if not Delay_Required then
|
|
|
|
-- Exclude aspects Export and Import because their pragma
|
|
-- syntax does not map directly to a Boolean aspect.
|
|
|
|
if A_Id not in Aspect_Export | Aspect_Import then
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Ent),
|
|
Expression => Ent)),
|
|
Pragma_Name => Nam);
|
|
end if;
|
|
|
|
-- 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.
|
|
|
|
Aitem := 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 => Name_Storage_Size,
|
|
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;
|
|
|
|
-- For an aspect that applies to a type, indicate whether it
|
|
-- appears on a partial view of the type.
|
|
|
|
if Is_Type (E)
|
|
and then Is_Private_Type (E)
|
|
then
|
|
Set_Aspect_On_Partial_View (Aspect);
|
|
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
|
|
Aitem := Make_Aitem_Pragma
|
|
(Pragma_Argument_Associations => New_List (
|
|
Make_Pragma_Argument_Association (Sloc (Ent),
|
|
Expression => Ent)),
|
|
Pragma_Name => Nam);
|
|
|
|
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.
|
|
|
|
if A_Id = Aspect_Default_Value 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 (N) in 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.
|
|
|
|
elsif Present (Aitem) then
|
|
Insert_After (Ins_Node, Aitem);
|
|
Ins_Node := Aitem;
|
|
end if;
|
|
|
|
<<Continue>>
|
|
|
|
-- If a nonoverridable aspect is explicitly specified for a
|
|
-- derived type, then check consistency with the parent type.
|
|
|
|
if A_Id in Nonoverridable_Aspect_Id
|
|
and then Nkind (N) = N_Full_Type_Declaration
|
|
and then Nkind (Type_Definition (N)) = N_Derived_Type_Definition
|
|
and then not In_Instance_Body
|
|
then
|
|
declare
|
|
Parent_Type : constant Entity_Id := Etype (E);
|
|
Inherited_Aspect : constant Node_Id :=
|
|
Find_Aspect (Parent_Type, A_Id);
|
|
begin
|
|
if Present (Inherited_Aspect)
|
|
and then not Is_Confirming
|
|
(A_Id, Inherited_Aspect, Aspect)
|
|
then
|
|
Error_Msg_Name_1 := Aspect_Names (A_Id);
|
|
Error_Msg_Sloc := Sloc (Inherited_Aspect);
|
|
|
|
Error_Msg_N
|
|
("overriding aspect specification for "
|
|
& "nonoverridable aspect % does not confirm "
|
|
& "aspect specification inherited from #",
|
|
Aspect);
|
|
end if;
|
|
end;
|
|
end if;
|
|
exception
|
|
when Aspect_Exit => null;
|
|
end Analyze_One_Aspect;
|
|
|
|
Next (Aspect);
|
|
end loop Aspect_Loop;
|
|
|
|
if Has_Delayed_Aspects (E) then
|
|
Ensure_Freeze_Node (E);
|
|
end if;
|
|
end Analyze_Aspect_Specifications;
|
|
|
|
------------------------------------------------
|
|
-- Analyze_Aspects_On_Subprogram_Body_Or_Stub --
|
|
------------------------------------------------
|
|
|
|
procedure Analyze_Aspects_On_Subprogram_Body_Or_Stub (N : Node_Id) is
|
|
Body_Id : constant Entity_Id := Defining_Entity (N);
|
|
|
|
procedure Diagnose_Misplaced_Aspects (Spec_Id : Entity_Id);
|
|
-- Body [stub] N has aspects, but they are not properly placed. Emit an
|
|
-- error message depending on the aspects involved. Spec_Id denotes the
|
|
-- entity of the corresponding spec.
|
|
|
|
--------------------------------
|
|
-- Diagnose_Misplaced_Aspects --
|
|
--------------------------------
|
|
|
|
procedure Diagnose_Misplaced_Aspects (Spec_Id : Entity_Id) is
|
|
procedure Misplaced_Aspect_Error
|
|
(Asp : Node_Id;
|
|
Ref_Nam : Name_Id);
|
|
-- Emit an error message concerning misplaced aspect Asp. Ref_Nam is
|
|
-- the name of the refined version of the aspect.
|
|
|
|
----------------------------
|
|
-- Misplaced_Aspect_Error --
|
|
----------------------------
|
|
|
|
procedure Misplaced_Aspect_Error
|
|
(Asp : Node_Id;
|
|
Ref_Nam : Name_Id)
|
|
is
|
|
Asp_Nam : constant Name_Id := Chars (Identifier (Asp));
|
|
Asp_Id : constant Aspect_Id := Get_Aspect_Id (Asp_Nam);
|
|
|
|
begin
|
|
-- The corresponding spec already contains the aspect in question
|
|
-- and the one appearing on the body must be the refined form:
|
|
|
|
-- procedure P with Global ...;
|
|
-- procedure P with Global ... is ... end P;
|
|
-- ^
|
|
-- Refined_Global
|
|
|
|
if Has_Aspect (Spec_Id, Asp_Id) then
|
|
Error_Msg_Name_1 := Asp_Nam;
|
|
|
|
-- Subunits cannot carry aspects that apply to a subprogram
|
|
-- declaration.
|
|
|
|
if Nkind (Parent (N)) = N_Subunit then
|
|
Error_Msg_N ("aspect % cannot apply to a subunit", Asp);
|
|
|
|
-- Otherwise suggest the refined form
|
|
|
|
else
|
|
Error_Msg_Name_2 := Ref_Nam;
|
|
Error_Msg_N ("aspect % should be %", Asp);
|
|
end if;
|
|
|
|
-- Otherwise the aspect must appear on the spec, not on the body
|
|
|
|
-- procedure P;
|
|
-- procedure P with Global ... is ... end P;
|
|
|
|
else
|
|
Error_Msg_N
|
|
("aspect specification must appear on initial declaration",
|
|
Asp);
|
|
end if;
|
|
end Misplaced_Aspect_Error;
|
|
|
|
-- Local variables
|
|
|
|
Asp : Node_Id;
|
|
Asp_Nam : Name_Id;
|
|
|
|
-- Start of processing for Diagnose_Misplaced_Aspects
|
|
|
|
begin
|
|
-- Iterate over the aspect specifications and emit specific errors
|
|
-- where applicable.
|
|
|
|
Asp := First (Aspect_Specifications (N));
|
|
while Present (Asp) loop
|
|
Asp_Nam := Chars (Identifier (Asp));
|
|
|
|
-- Do not emit errors on aspects that can appear on a subprogram
|
|
-- body. This scenario occurs when the aspect specification list
|
|
-- contains both misplaced and properly placed aspects.
|
|
|
|
if Aspect_On_Body_Or_Stub_OK (Get_Aspect_Id (Asp_Nam)) then
|
|
null;
|
|
|
|
-- Special diagnostics for SPARK aspects
|
|
|
|
elsif Asp_Nam = Name_Depends then
|
|
Misplaced_Aspect_Error (Asp, Name_Refined_Depends);
|
|
|
|
elsif Asp_Nam = Name_Global then
|
|
Misplaced_Aspect_Error (Asp, Name_Refined_Global);
|
|
|
|
elsif Asp_Nam = Name_Post then
|
|
Misplaced_Aspect_Error (Asp, Name_Refined_Post);
|
|
|
|
-- Otherwise a language-defined aspect is misplaced
|
|
|
|
else
|
|
Error_Msg_N
|
|
("aspect specification must appear on initial declaration",
|
|
Asp);
|
|
end if;
|
|
|
|
Next (Asp);
|
|
end loop;
|
|
end Diagnose_Misplaced_Aspects;
|
|
|
|
-- Local variables
|
|
|
|
Spec_Id : constant Entity_Id := Unique_Defining_Entity (N);
|
|
|
|
-- Start of processing for Analyze_Aspects_On_Subprogram_Body_Or_Stub
|
|
|
|
begin
|
|
-- Language-defined aspects cannot be associated with a subprogram body
|
|
-- [stub] if the subprogram has a spec. Certain implementation defined
|
|
-- aspects are allowed to break this rule (for all applicable cases, see
|
|
-- table Aspects.Aspect_On_Body_Or_Stub_OK).
|
|
|
|
if Spec_Id /= Body_Id and then not Aspects_On_Body_Or_Stub_OK (N) then
|
|
Diagnose_Misplaced_Aspects (Spec_Id);
|
|
else
|
|
Analyze_Aspect_Specifications (N, Body_Id);
|
|
end if;
|
|
end Analyze_Aspects_On_Subprogram_Body_Or_Stub;
|
|
|
|
-----------------------
|
|
-- 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 the full view of an incomplete 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_Put_Image_TSS_Definition;
|
|
|
|
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 there is no error, and False is returned. Size
|
|
-- and Value_Size are considered to conflict, but for compatibility,
|
|
-- this is merely a warning.
|
|
|
|
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
|
|
-- that has the proper type structure.
|
|
|
|
function Check_Primitive_Function (Subp : Entity_Id) return Boolean;
|
|
-- Common legality check for the previous two
|
|
|
|
-----------------------------------
|
|
-- Analyze_Put_Image_TSS_Definition --
|
|
-----------------------------------
|
|
|
|
procedure Analyze_Put_Image_TSS_Definition is
|
|
Subp : Entity_Id := Empty;
|
|
I : Interp_Index;
|
|
It : Interp;
|
|
Pnam : Entity_Id;
|
|
|
|
function Has_Good_Profile
|
|
(Subp : Entity_Id;
|
|
Report : Boolean := False) return Boolean;
|
|
-- Return true if the entity is a subprogram with an appropriate
|
|
-- profile for the attribute being defined. If result is False and
|
|
-- Report is True, function emits appropriate error.
|
|
|
|
----------------------
|
|
-- Has_Good_Profile --
|
|
----------------------
|
|
|
|
function Has_Good_Profile
|
|
(Subp : Entity_Id;
|
|
Report : Boolean := False) return Boolean
|
|
is
|
|
F : Entity_Id;
|
|
Typ : Entity_Id;
|
|
|
|
begin
|
|
if Ekind (Subp) /= E_Procedure then
|
|
return False;
|
|
end if;
|
|
|
|
F := First_Formal (Subp);
|
|
|
|
if No (F) then
|
|
return False;
|
|
end if;
|
|
|
|
if Base_Type (Etype (F))
|
|
/= Class_Wide_Type (RTE (RE_Root_Buffer_Type))
|
|
then
|
|
if Report then
|
|
Error_Msg_N
|
|
("wrong type for Put_Image procedure''s first parameter",
|
|
Parameter_Type (Parent (F)));
|
|
end if;
|
|
|
|
return False;
|
|
end if;
|
|
|
|
if Parameter_Mode (F) /= E_In_Out_Parameter then
|
|
if Report then
|
|
Error_Msg_N
|
|
("wrong mode for Put_Image procedure''s first parameter",
|
|
Parent (F));
|
|
end if;
|
|
|
|
return False;
|
|
end if;
|
|
|
|
Next_Formal (F);
|
|
|
|
Typ := Etype (F);
|
|
|
|
-- 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) then
|
|
if Report then
|
|
Error_Msg_N
|
|
("wrong type for Put_Image procedure''s second parameter",
|
|
Parameter_Type (Parent (F)));
|
|
end if;
|
|
|
|
return False;
|
|
end if;
|
|
|
|
if Parameter_Mode (F) /= E_In_Parameter then
|
|
if Report then
|
|
Error_Msg_N
|
|
("wrong mode for Put_Image procedure''s second parameter",
|
|
Parent (F));
|
|
end if;
|
|
|
|
return False;
|
|
end if;
|
|
|
|
if Present (Next_Formal (F)) then
|
|
return False;
|
|
end if;
|
|
|
|
return True;
|
|
end Has_Good_Profile;
|
|
|
|
-- Start of processing for Analyze_Put_Image_TSS_Definition
|
|
|
|
begin
|
|
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_Put_Image);
|
|
|
|
-- 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 the
|
|
-- Put_Image 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 Put_Image 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), Report => True) 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 ("Put_Image subprogram must not be abstract", Expr);
|
|
return;
|
|
end if;
|
|
|
|
Set_Entity (Expr, Subp);
|
|
Set_Etype (Expr, Etype (Subp));
|
|
|
|
New_Put_Image_Subprogram (N, U_Ent, Subp);
|
|
|
|
else
|
|
Error_Msg_Name_1 := Attr;
|
|
Error_Msg_N ("incorrect expression for% attribute", Expr);
|
|
end if;
|
|
end Analyze_Put_Image_TSS_Definition;
|
|
|
|
-----------------------------------
|
|
-- 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;
|
|
Report : Boolean := False) return Boolean;
|
|
-- Return true if the entity is a subprogram with an appropriate
|
|
-- profile for the attribute being defined. If result is False and
|
|
-- Report is True, function emits appropriate error.
|
|
|
|
----------------------
|
|
-- Has_Good_Profile --
|
|
----------------------
|
|
|
|
function Has_Good_Profile
|
|
(Subp : Entity_Id;
|
|
Report : Boolean := False) return Boolean
|
|
is
|
|
Expected_Ekind : constant array (Boolean) of Entity_Kind :=
|
|
(False => E_Procedure, True => E_Function);
|
|
Is_Function : constant Boolean := (TSS_Nam = TSS_Stream_Input);
|
|
F : Entity_Id;
|
|
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 Base_Type (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);
|
|
|
|
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) then
|
|
return False;
|
|
end if;
|
|
|
|
if 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
|
|
if Report and not Is_First_Subtype (Typ) then
|
|
Error_Msg_N
|
|
("subtype of formal in stream operation must be a first "
|
|
& "subtype", Parameter_Type (Parent (F)));
|
|
end if;
|
|
|
|
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), Report => True) 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;
|
|
|
|
-- A stream subprogram for an interface type must be a null
|
|
-- procedure (RM 13.13.2 (38/3)). Note that the class-wide type
|
|
-- of an interface is not an interface type (3.9.4 (6.b/2)).
|
|
|
|
elsif Is_Interface (U_Ent)
|
|
and then not Is_Class_Wide_Type (U_Ent)
|
|
and then not Inside_A_Generic
|
|
and then
|
|
(Ekind (Subp) = E_Function
|
|
or else
|
|
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;
|
|
|
|
if Is_Class_Wide_Type (Base_Type (Ent)) then
|
|
Error_Msg_N
|
|
("incorrect expression for class-wide% attribute", Expr);
|
|
else
|
|
Error_Msg_N ("incorrect expression for% attribute", Expr);
|
|
end if;
|
|
end if;
|
|
end Analyze_Stream_TSS_Definition;
|
|
|
|
------------------------------
|
|
-- Check_Indexing_Functions --
|
|
------------------------------
|
|
|
|
procedure Check_Indexing_Functions is
|
|
Indexing_Found : Boolean := False;
|
|
|
|
procedure Check_Inherited_Indexing;
|
|
-- For a derived type, check that for a derived type, a specification
|
|
-- of an indexing aspect can only be confirming, i.e. uses the same
|
|
-- name as in the parent type.
|
|
-- AI12-0160: Verify that an indexing cannot be specified for
|
|
-- a derived type unless it is specified for the parent.
|
|
|
|
procedure Check_One_Function (Subp : Entity_Id);
|
|
-- Check one possible interpretation. Sets Indexing_Found True if a
|
|
-- legal indexing function is found.
|
|
|
|
procedure Illegal_Indexing (Msg : String);
|
|
-- Diagnose illegal indexing function if not overloaded. In the
|
|
-- overloaded case indicate that no legal interpretation exists.
|
|
|
|
------------------------------
|
|
-- Check_Inherited_Indexing --
|
|
------------------------------
|
|
|
|
procedure Check_Inherited_Indexing is
|
|
Inherited : Node_Id;
|
|
Other_Indexing : Node_Id;
|
|
|
|
begin
|
|
if Attr = Name_Constant_Indexing then
|
|
Inherited :=
|
|
Find_Aspect (Etype (Ent), Aspect_Constant_Indexing);
|
|
Other_Indexing :=
|
|
Find_Aspect (Etype (Ent), Aspect_Variable_Indexing);
|
|
|
|
else pragma Assert (Attr = Name_Variable_Indexing);
|
|
Inherited :=
|
|
Find_Aspect (Etype (Ent), Aspect_Variable_Indexing);
|
|
Other_Indexing :=
|
|
Find_Aspect (Etype (Ent), Aspect_Constant_Indexing);
|
|
end if;
|
|
|
|
if Present (Inherited) then
|
|
if Debug_Flag_Dot_XX then
|
|
null;
|
|
|
|
-- OK if current attribute_definition_clause is expansion of
|
|
-- inherited aspect.
|
|
|
|
elsif Aspect_Rep_Item (Inherited) = N then
|
|
null;
|
|
|
|
-- Check if this is a confirming specification. The name
|
|
-- may be overloaded between the parent operation and the
|
|
-- inherited one, so we check that the Chars fields match.
|
|
|
|
elsif Is_Entity_Name (Expression (Inherited))
|
|
and then Chars (Entity (Expression (Inherited))) =
|
|
Chars (Entity (Expression (N)))
|
|
then
|
|
Indexing_Found := True;
|
|
|
|
-- Indicate the operation that must be overridden, rather than
|
|
-- redefining the indexing aspect.
|
|
|
|
else
|
|
Illegal_Indexing
|
|
("indexing function already inherited from parent type");
|
|
Error_Msg_NE
|
|
("!override & instead",
|
|
N, Entity (Expression (Inherited)));
|
|
end if;
|
|
|
|
-- If not inherited and the parent has another indexing function
|
|
-- this is illegal, because it leads to inconsistent results in
|
|
-- class-wide calls.
|
|
|
|
elsif Present (Other_Indexing) then
|
|
Error_Msg_N
|
|
("cannot specify indexing operation on derived type"
|
|
& " if not specified for parent", N);
|
|
end if;
|
|
end Check_Inherited_Indexing;
|
|
|
|
------------------------
|
|
-- Check_One_Function --
|
|
------------------------
|
|
|
|
procedure Check_One_Function (Subp : Entity_Id) is
|
|
Default_Element : Node_Id;
|
|
Ret_Type : constant Entity_Id := Etype (Subp);
|
|
|
|
begin
|
|
if not Is_Overloadable (Subp) then
|
|
Illegal_Indexing ("illegal indexing function for type&");
|
|
return;
|
|
|
|
elsif Scope (Subp) /= Scope (Ent) then
|
|
if Nkind (Expr) = N_Expanded_Name then
|
|
|
|
-- Indexing function can't be declared elsewhere
|
|
|
|
Illegal_Indexing
|
|
("indexing function must be declared"
|
|
& " in scope of type&");
|
|
end if;
|
|
|
|
if Is_Derived_Type (Ent) then
|
|
Check_Inherited_Indexing;
|
|
end if;
|
|
|
|
return;
|
|
|
|
elsif No (First_Formal (Subp)) then
|
|
Illegal_Indexing
|
|
("Indexing requires a function that applies to type&");
|
|
return;
|
|
|
|
elsif No (Next_Formal (First_Formal (Subp))) then
|
|
Illegal_Indexing
|
|
("indexing function must have at least two parameters");
|
|
return;
|
|
|
|
elsif Is_Derived_Type (Ent) then
|
|
Check_Inherited_Indexing;
|
|
end if;
|
|
|
|
if not Check_Primitive_Function (Subp) then
|
|
Illegal_Indexing
|
|
("Indexing aspect requires a function that applies to type&");
|
|
return;
|
|
end if;
|
|
|
|
-- If partial declaration exists, verify that it is not tagged.
|
|
|
|
if Ekind (Current_Scope) = E_Package
|
|
and then Has_Private_Declaration (Ent)
|
|
and then From_Aspect_Specification (N)
|
|
and then
|
|
List_Containing (Parent (Ent)) =
|
|
Private_Declarations
|
|
(Specification (Unit_Declaration_Node (Current_Scope)))
|
|
and then Nkind (N) = N_Attribute_Definition_Clause
|
|
then
|
|
declare
|
|
Decl : Node_Id;
|
|
|
|
begin
|
|
Decl :=
|
|
First (Visible_Declarations
|
|
(Specification
|
|
(Unit_Declaration_Node (Current_Scope))));
|
|
|
|
while Present (Decl) loop
|
|
if Nkind (Decl) = N_Private_Type_Declaration
|
|
and then Ent = Full_View (Defining_Identifier (Decl))
|
|
and then Tagged_Present (Decl)
|
|
and then No (Aspect_Specifications (Decl))
|
|
then
|
|
Illegal_Indexing
|
|
("Indexing aspect cannot be specified on full view "
|
|
& "if partial view is tagged");
|
|
return;
|
|
end if;
|
|
|
|
Next (Decl);
|
|
end loop;
|
|
end;
|
|
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.
|
|
|
|
Default_Element :=
|
|
Find_Value_Of_Aspect
|
|
(Etype (First_Formal (Subp)), Aspect_Iterator_Element);
|
|
|
|
if Present (Default_Element) then
|
|
Analyze (Default_Element);
|
|
end if;
|
|
|
|
-- For variable_indexing the return type must be a reference type
|
|
|
|
if Attr = Name_Variable_Indexing then
|
|
if not Has_Implicit_Dereference (Ret_Type) then
|
|
Illegal_Indexing
|
|
("variable indexing must return a reference type");
|
|
return;
|
|
|
|
elsif Is_Access_Constant
|
|
(Etype (First_Discriminant (Ret_Type)))
|
|
then
|
|
Illegal_Indexing
|
|
("variable indexing must return an access to variable");
|
|
return;
|
|
end if;
|
|
|
|
else
|
|
if Has_Implicit_Dereference (Ret_Type)
|
|
and then not
|
|
Is_Access_Constant
|
|
(Etype (Get_Reference_Discriminant (Ret_Type)))
|
|
then
|
|
Illegal_Indexing
|
|
("constant indexing must return an access to constant");
|
|
return;
|
|
|
|
elsif Is_Access_Type (Etype (First_Formal (Subp)))
|
|
and then not Is_Access_Constant (Etype (First_Formal (Subp)))
|
|
then
|
|
Illegal_Indexing
|
|
("constant indexing must apply to an access to constant");
|
|
return;
|
|
end if;
|
|
end if;
|
|
|
|
-- All checks succeeded
|
|
|
|
Indexing_Found := True;
|
|
end Check_One_Function;
|
|
|
|
-----------------------
|
|
-- Illegal_Indexing --
|
|
-----------------------
|
|
|
|
procedure Illegal_Indexing (Msg : String) is
|
|
begin
|
|
Error_Msg_NE (Msg, N, Ent);
|
|
end Illegal_Indexing;
|
|
|
|
-- Start of processing for Check_Indexing_Functions
|
|
|
|
begin
|
|
if In_Instance then
|
|
Check_Inherited_Indexing;
|
|
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. Ignore homonyms that may come from
|
|
-- derived types in the context.
|
|
|
|
if Is_Overloadable (It.Nam)
|
|
and then Comes_From_Source (It.Nam)
|
|
then
|
|
Check_One_Function (It.Nam);
|
|
end if;
|
|
|
|
Get_Next_Interp (I, It);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
|
|
if not Indexing_Found and then not Error_Posted (N) then
|
|
Error_Msg_NE
|
|
("aspect Indexing requires a local function that applies to "
|
|
& "type&", Expr, Ent);
|
|
end if;
|
|
end Check_Indexing_Functions;
|
|
|
|
------------------------------
|
|
-- Check_Iterator_Functions --
|
|
------------------------------
|
|
|
|
procedure Check_Iterator_Functions is
|
|
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
|
|
Root_T : constant Entity_Id := Root_Type (Etype (Etype (Subp)));
|
|
Formal : Entity_Id;
|
|
|
|
begin
|
|
if not Check_Primitive_Function (Subp) then
|
|
return False;
|
|
|
|
-- The return type must be derived from a type in an instance
|
|
-- of Iterator.Interfaces, and thus its root type must have a
|
|
-- predefined name.
|
|
|
|
elsif Chars (Root_T) /= Name_Forward_Iterator
|
|
and then Chars (Root_T) /= Name_Reversible_Iterator
|
|
then
|
|
return False;
|
|
|
|
else
|
|
Formal := First_Formal (Subp);
|
|
end if;
|
|
|
|
-- False if any subsequent formal has no default expression
|
|
|
|
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 Entity (Expr) /= Any_Id
|
|
and then not Check_Primitive_Function (Entity (Expr))
|
|
then
|
|
Error_Msg_NE
|
|
("aspect Indexing requires a function that applies to type&",
|
|
Entity (Expr), Ent);
|
|
end if;
|
|
|
|
-- Flag the default_iterator as well as the denoted function.
|
|
|
|
if not Valid_Default_Iterator (Entity (Expr)) then
|
|
Error_Msg_N ("improper function for default iterator!", Expr);
|
|
end if;
|
|
|
|
else
|
|
declare
|
|
Default : Entity_Id := Empty;
|
|
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
|
|
|
|
-- An explicit one should override an implicit one
|
|
|
|
if Comes_From_Source (Default) =
|
|
Comes_From_Source (It.Nam)
|
|
then
|
|
Error_Msg_N ("default iterator must be unique", Expr);
|
|
Error_Msg_Sloc := Sloc (Default);
|
|
Error_Msg_N ("\\possible interpretation#", Expr);
|
|
Error_Msg_Sloc := Sloc (It.Nam);
|
|
Error_Msg_N ("\\possible interpretation#", Expr);
|
|
|
|
elsif Comes_From_Source (It.Nam) then
|
|
Default := It.Nam;
|
|
end if;
|
|
else
|
|
Default := It.Nam;
|
|
end if;
|
|
|
|
Get_Next_Interp (I, It);
|
|
end loop;
|
|
|
|
if Present (Default) then
|
|
Set_Entity (Expr, Default);
|
|
Set_Is_Overloaded (Expr, False);
|
|
else
|
|
Error_Msg_N
|
|
("no interpretation is a valid default iterator!", Expr);
|
|
end if;
|
|
end;
|
|
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;
|
|
|
|
-- To be a primitive operation subprogram has to be in same scope.
|
|
|
|
if Scope (Ctrl) /= Scope (Subp) then
|
|
return False;
|
|
end if;
|
|
|
|
-- Type of formal may be the class-wide type, an access to such,
|
|
-- or an incomplete view.
|
|
|
|
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)))
|
|
or else
|
|
(Ekind (Ctrl) = E_Incomplete_Type
|
|
and then Full_View (Ctrl) = Ent)
|
|
then
|
|
null;
|
|
else
|
|
return False;
|
|
end if;
|
|
|
|
return True;
|
|
end Check_Primitive_Function;
|
|
|
|
----------------------
|
|
-- Duplicate_Clause --
|
|
----------------------
|
|
|
|
function Duplicate_Clause return Boolean is
|
|
|
|
function Check_One_Attr (Attr_1, Attr_2 : Name_Id) return Boolean;
|
|
-- Check for one attribute; Attr_1 is the attribute_designator we are
|
|
-- looking for. Attr_2 is the attribute_designator of the current
|
|
-- node. Normally, this is called just once by Duplicate_Clause, with
|
|
-- Attr_1 = Attr_2. However, it needs to be called twice for Size and
|
|
-- Value_Size, because these mean the same thing. For compatibility,
|
|
-- we allow specifying both Size and Value_Size, but only if the two
|
|
-- sizes are equal.
|
|
|
|
--------------------
|
|
-- Check_One_Attr --
|
|
--------------------
|
|
|
|
function Check_One_Attr (Attr_1, Attr_2 : Name_Id) return Boolean is
|
|
A : constant Node_Id :=
|
|
Get_Rep_Item (U_Ent, Attr_1, Check_Parents => False);
|
|
begin
|
|
if Present (A) then
|
|
if Attr_1 = Attr_2 then
|
|
Error_Msg_Name_1 := Attr_1;
|
|
Error_Msg_Sloc := Sloc (A);
|
|
Error_Msg_NE ("aspect% for & previously given#", N, U_Ent);
|
|
|
|
else
|
|
pragma Assert (Attr_1 in Name_Size | Name_Value_Size);
|
|
pragma Assert (Attr_2 in Name_Size | Name_Value_Size);
|
|
|
|
Error_Msg_Name_1 := Attr_2;
|
|
Error_Msg_Name_2 := Attr_1;
|
|
Error_Msg_Sloc := Sloc (A);
|
|
Error_Msg_NE ("?% for & conflicts with % #", N, U_Ent);
|
|
end if;
|
|
|
|
return True;
|
|
end if;
|
|
|
|
return False;
|
|
end Check_One_Attr;
|
|
|
|
-- Start of processing for Duplicate_Clause
|
|
|
|
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;
|
|
|
|
-- Special cases for Size and Value_Size
|
|
|
|
if (Chars (N) = Name_Size
|
|
and then Check_One_Attr (Name_Value_Size, Name_Size))
|
|
or else
|
|
(Chars (N) = Name_Value_Size
|
|
and then Check_One_Attr (Name_Size, Name_Value_Size))
|
|
then
|
|
return True;
|
|
end if;
|
|
|
|
-- Normal case (including Size and Value_Size)
|
|
|
|
return Check_One_Attr (Chars (N), Chars (N));
|
|
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;
|
|
|
|
Check_Restriction_No_Use_Of_Attribute (N);
|
|
|
|
if Is_Aspect_Id (Chars (N)) then
|
|
-- 6.1/3 No_Specification_of_Aspect: Identifies an aspect for which
|
|
-- no aspect_specification, attribute_definition_clause, or pragma
|
|
-- is given.
|
|
Check_Restriction_No_Specification_Of_Aspect (N);
|
|
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_Default_Scalar_Storage_Order
|
|
| Attribute_Machine_Radix
|
|
| Attribute_Object_Size
|
|
| Attribute_Scalar_Storage_Order
|
|
| 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_Put_Image
|
|
| 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 them 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 (underlying) full view of private or incomplete
|
|
-- type if we have one (if not, this is a premature use of the type).
|
|
-- However, some 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))
|
|
and then No (Underlying_Full_View (Ent))
|
|
then
|
|
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
|
|
-- In the case of a renamed object from source, this is an error
|
|
-- unless the object is an aggregate and the renaming is created
|
|
-- for an object declaration.
|
|
|
|
if Comes_From_Source (Renamed_Object (Ent))
|
|
and then Nkind (Renamed_Object (Ent)) /= N_Aggregate
|
|
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);
|
|
|
|
-- 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.
|
|
-- Also call Set_Address_Taken to indicate that an address clause
|
|
-- was present, even if we are about to remove it.
|
|
|
|
if Ignore_Rep_Clauses then
|
|
Set_Address_Taken (U_Ent);
|
|
|
|
if Ekind (U_Ent) in 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 class-wide object, which is
|
|
-- considered erroneous.
|
|
|
|
elsif Is_Class_Wide_Type (Etype (U_Ent)) then
|
|
Error_Msg_NE
|
|
("??class-wide 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 an object
|
|
|
|
elsif Ekind (U_Ent) in E_Constant | E_Variable 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);
|
|
|
|
if Present (O_Ent) then
|
|
|
|
-- If the object overlays a constant object, mark it so
|
|
|
|
if Is_Constant_Object (O_Ent) then
|
|
Set_Overlays_Constant (U_Ent);
|
|
end if;
|
|
|
|
-- If the address clause is of the form:
|
|
|
|
-- for X'Address use Y'Address;
|
|
|
|
-- or
|
|
|
|
-- C : constant Address := Y'Address;
|
|
-- ...
|
|
-- for X'Address use C;
|
|
|
|
-- then we make an entry in the table to check the size
|
|
-- and alignment of the overlaying variable. But 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 Is_Object (O_Ent)
|
|
and then not Is_Generic_Formal (O_Ent)
|
|
and then not Is_Generic_Type (Etype (U_Ent))
|
|
and then Address_Clause_Overlay_Warnings
|
|
then
|
|
Register_Address_Clause_Check
|
|
(N, U_Ent, No_Uint, O_Ent, Off);
|
|
end if;
|
|
|
|
-- If the overlay changes the storage order, warn since
|
|
-- the construct is not really supported by the back end.
|
|
-- Also mark the entity as being volatile to block the
|
|
-- optimizer, even if there is no warranty on the result.
|
|
|
|
if (Is_Record_Type (Etype (U_Ent))
|
|
or else Is_Array_Type (Etype (U_Ent)))
|
|
and then (Is_Record_Type (Etype (O_Ent))
|
|
or else Is_Array_Type (Etype (O_Ent)))
|
|
and then Reverse_Storage_Order (Etype (U_Ent)) /=
|
|
Reverse_Storage_Order (Etype (O_Ent))
|
|
then
|
|
Error_Msg_N
|
|
("??overlay changes scalar storage order", Expr);
|
|
Set_Treat_As_Volatile (U_Ent);
|
|
end if;
|
|
|
|
else
|
|
-- If this is not an overlay, mark a variable as being
|
|
-- volatile to prevent unwanted optimizations. It's a
|
|
-- conservative interpretation of RM 13.3(19) for the
|
|
-- cases where the compiler cannot detect potential
|
|
-- aliasing issues easily and it also covers the case
|
|
-- of an absolute address where the volatile aspect is
|
|
-- kind of implicit.
|
|
|
|
if Ekind (U_Ent) = E_Variable then
|
|
Set_Treat_As_Volatile (U_Ent);
|
|
end if;
|
|
|
|
-- Make an entry in the table for an absolute address as
|
|
-- above to check that the value is compatible with the
|
|
-- alignment of the object.
|
|
|
|
declare
|
|
Addr : constant Node_Id := Address_Value (Expr);
|
|
begin
|
|
if Compile_Time_Known_Value (Addr)
|
|
and then Address_Clause_Overlay_Warnings
|
|
then
|
|
Register_Address_Clause_Check
|
|
(N, U_Ent, Expr_Value (Addr), Empty, False);
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
-- Issue an unconditional warning for a constant overlaying
|
|
-- a variable. For the reverse case, we will issue it only
|
|
-- if the variable is modified.
|
|
-- Within a generic unit an In_Parameter is a constant.
|
|
-- It can be instantiated with a variable, in which case
|
|
-- there will be a warning on the instance.
|
|
|
|
if Ekind (U_Ent) = E_Constant
|
|
and then Present (O_Ent)
|
|
and then Ekind (O_Ent) /= E_Generic_In_Parameter
|
|
and then not Overlays_Constant (U_Ent)
|
|
and then Address_Clause_Overlay_Warnings
|
|
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);
|
|
|
|
-- 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;
|
|
|
|
-- 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);
|
|
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 Present (Align) 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 =>
|
|
if not Is_Record_Type (U_Ent) then
|
|
Error_Msg_N
|
|
("Bit_Order can only be defined for record type", Nam);
|
|
|
|
elsif Is_Tagged_Type (U_Ent) and then Is_Derived_Type (U_Ent) then
|
|
Error_Msg_N
|
|
("Bit_Order cannot be defined for record extensions", 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);
|
|
|
|
elsif (Expr_Value (Expr) = 0) /= Bytes_Big_Endian then
|
|
Set_Reverse_Bit_Order (Base_Type (U_Ent), True);
|
|
end if;
|
|
end if;
|
|
|
|
--------------------
|
|
-- 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 Present (Csize) 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.
|
|
|
|
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);
|
|
Reinit_Esize (New_Ctyp);
|
|
Set_RM_Size (New_Ctyp, Csize);
|
|
Reinit_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);
|
|
|
|
-- 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 =>
|
|
pragma Assert (From_Aspect_Specification (N));
|
|
-- The parser forbids this clause in source code, so it must have
|
|
-- come from an aspect specification.
|
|
|
|
if not Is_Task_Type (U_Ent) then
|
|
Error_Msg_N ("'C'P'U 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 components must be established
|
|
-- and restored before and after analysis.
|
|
|
|
Push_Type (U_Ent);
|
|
Preanalyze_Spec_Expression (Expr, RTE (RE_CPU_Range));
|
|
Pop_Type (U_Ent);
|
|
|
|
-- AI12-0117-1, "Restriction No_Tasks_Unassigned_To_CPU":
|
|
-- If the expression is static, and its value is
|
|
-- System.Multiprocessors.Not_A_Specific_CPU (i.e. zero) then
|
|
-- that's a violation of No_Tasks_Unassigned_To_CPU. It might
|
|
-- seem better to refer to Not_A_Specific_CPU here, but that
|
|
-- involves a lot of horsing around with Rtsfind, and this
|
|
-- value is not going to change, so it's better to hardwire
|
|
-- Uint_0.
|
|
--
|
|
-- AI12-0055-1, "All properties of a usage profile are defined
|
|
-- by pragmas": If the expression is nonstatic, that's a
|
|
-- violation of No_Dynamic_CPU_Assignment.
|
|
|
|
if Is_OK_Static_Expression (Expr) then
|
|
if Expr_Value (Expr) = Uint_0 then
|
|
Check_Restriction (No_Tasks_Unassigned_To_CPU, Expr);
|
|
end if;
|
|
else
|
|
Check_Restriction (No_Dynamic_CPU_Assignment, Expr);
|
|
end if;
|
|
end if;
|
|
|
|
----------------------
|
|
-- Default_Iterator --
|
|
----------------------
|
|
|
|
when Attribute_Default_Iterator => Default_Iterator : declare
|
|
Func : Entity_Id;
|
|
Typ : Entity_Id;
|
|
|
|
begin
|
|
-- If target type is untagged, further checks are irrelevant
|
|
|
|
if not Is_Tagged_Type (U_Ent) then
|
|
Error_Msg_N
|
|
("aspect Default_Iterator applies to tagged type", Nam);
|
|
return;
|
|
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);
|
|
return;
|
|
else
|
|
Func := Entity (Expr);
|
|
end if;
|
|
|
|
-- The type of the first parameter must be T, T'class, or a
|
|
-- corresponding access type (5.5.1 (8/3). If function is
|
|
-- parameterless label type accordingly.
|
|
|
|
if No (First_Formal (Func)) then
|
|
Typ := Any_Type;
|
|
else
|
|
Typ := Etype (First_Formal (Func));
|
|
end if;
|
|
|
|
if Typ = U_Ent
|
|
or else Typ = Class_Wide_Type (U_Ent)
|
|
or else (Is_Access_Type (Typ)
|
|
and then Designated_Type (Typ) = U_Ent)
|
|
or else (Is_Access_Type (Typ)
|
|
and then Designated_Type (Typ) =
|
|
Class_Wide_Type (U_Ent))
|
|
then
|
|
null;
|
|
|
|
else
|
|
Error_Msg_NE
|
|
("Default_Iterator must be a primitive of&", Func, U_Ent);
|
|
end if;
|
|
end Default_Iterator;
|
|
|
|
------------------------
|
|
-- Dispatching_Domain --
|
|
------------------------
|
|
|
|
when Attribute_Dispatching_Domain =>
|
|
pragma Assert (From_Aspect_Specification (N));
|
|
-- The parser forbids this clause in source code, so it must have
|
|
-- come from an aspect specification.
|
|
|
|
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 components must be restored
|
|
|
|
Push_Type (U_Ent);
|
|
|
|
Preanalyze_Spec_Expression
|
|
(Expr, RTE (RE_Dispatching_Domain));
|
|
|
|
Pop_Type (U_Ent);
|
|
end if;
|
|
|
|
------------------
|
|
-- External_Tag --
|
|
------------------
|
|
|
|
when Attribute_External_Tag =>
|
|
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 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;
|
|
|
|
--------------------------
|
|
-- 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 =>
|
|
pragma Assert (From_Aspect_Specification (N));
|
|
-- The parser forbids this clause in source code, so it must have
|
|
-- come from an aspect specification.
|
|
|
|
if not Is_Concurrent_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 components must be restored
|
|
|
|
Push_Type (U_Ent);
|
|
|
|
Preanalyze_Spec_Expression
|
|
(Expr, RTE (RE_Interrupt_Priority));
|
|
|
|
Pop_Type (U_Ent);
|
|
|
|
-- Check the No_Task_At_Interrupt_Priority restriction
|
|
|
|
if Is_Task_Type (U_Ent) then
|
|
Check_Restriction (No_Task_At_Interrupt_Priority, N);
|
|
end if;
|
|
end if;
|
|
|
|
--------------
|
|
-- 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
|
|
Analyze (Expression (Assoc));
|
|
|
|
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);
|
|
return;
|
|
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 Present (Radix) 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 No (Size) or else Size <= 0 then
|
|
Error_Msg_N ("Object_Size must be positive", Expr);
|
|
|
|
elsif 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 attribute definition clause not allowed except from
|
|
-- aspect specification.
|
|
|
|
if From_Aspect_Specification (N) then
|
|
if not (Is_Concurrent_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 components must be restored
|
|
|
|
Push_Type (U_Ent);
|
|
Preanalyze_Spec_Expression (Expr, Standard_Integer);
|
|
Pop_Type (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;
|
|
|
|
---------------
|
|
-- Put_Image --
|
|
---------------
|
|
|
|
when Attribute_Put_Image =>
|
|
Analyze_Put_Image_TSS_Definition;
|
|
|
|
----------
|
|
-- 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 =>
|
|
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;
|
|
|
|
------------------------
|
|
-- Size or Value_Size --
|
|
------------------------
|
|
|
|
-- Size or Value_Size attribute definition clause. These are treated
|
|
-- the same, except that Size is allowed on objects, and Value_Size
|
|
-- is allowed on nonfirst subtypes. First subtypes allow both Size
|
|
-- and Value_Size; the treatment is the same for both.
|
|
|
|
when Attribute_Size | Attribute_Value_Size => Size : declare
|
|
Size : constant Uint := Static_Integer (Expr);
|
|
|
|
Attr_Name : constant String :=
|
|
(if Id = Attribute_Size then "size"
|
|
elsif Id = Attribute_Value_Size then "value size"
|
|
else ""); -- can't happen
|
|
-- Name of the attribute for printing in messages
|
|
|
|
OK_Prefix : constant Boolean :=
|
|
(if Id = Attribute_Size then
|
|
Ekind (U_Ent) in Type_Kind | Constant_Or_Variable_Kind
|
|
elsif Id = Attribute_Value_Size then
|
|
Ekind (U_Ent) in Type_Kind
|
|
else False); -- can't happen
|
|
-- For X'Size, X can be a type or object; for X'Value_Size,
|
|
-- X can be a type. Note that we already checked that 'Size
|
|
-- can be specified only for a first subtype.
|
|
|
|
begin
|
|
FOnly := True;
|
|
|
|
if not OK_Prefix then
|
|
Error_Msg_N (Attr_Name & " 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
|
|
(Attr_Name & " cannot be given for unconstrained array", Nam);
|
|
|
|
elsif Present (Size) then
|
|
declare
|
|
Etyp : constant Entity_Id :=
|
|
(if Is_Type (U_Ent) then U_Ent else Etype (U_Ent));
|
|
|
|
begin
|
|
-- 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
|
|
declare
|
|
Biased : Boolean;
|
|
begin
|
|
Check_Size (Expr, Etyp, Size, Biased);
|
|
Set_Biased (U_Ent, N, Attr_Name & " clause", Biased);
|
|
end;
|
|
end if;
|
|
|
|
-- For types, set RM_Size and Esize if appropriate
|
|
|
|
if Is_Type (U_Ent) then
|
|
Set_RM_Size (U_Ent, Size);
|
|
|
|
-- If we are specifying the Size or Value_Size of a
|
|
-- first subtype, then 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.
|
|
|
|
-- For nonfirst subtypes ('Value_Size only), we do
|
|
-- nothing here.
|
|
|
|
if Is_First_Subtype (U_Ent) then
|
|
if Is_Elementary_Type (U_Ent) then
|
|
if Size <= System_Storage_Unit then
|
|
Set_Esize
|
|
(U_Ent, UI_From_Int (System_Storage_Unit));
|
|
elsif Size <= 16 then
|
|
Set_Esize (U_Ent, Uint_16);
|
|
elsif Size <= 32 then
|
|
Set_Esize (U_Ent, Uint_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;
|
|
end if;
|
|
|
|
-- For Object'Size, set Esize only
|
|
|
|
else
|
|
if Is_Elementary_Type (Etyp)
|
|
and then Size /= System_Storage_Unit
|
|
and then Size /= 16
|
|
and then Size /= 32
|
|
and then Size /= 64
|
|
and then Size /= System_Max_Integer_Size
|
|
then
|
|
Error_Msg_Uint_1 := UI_From_Int (System_Storage_Unit);
|
|
Error_Msg_Uint_2 :=
|
|
UI_From_Int (System_Max_Integer_Size);
|
|
Error_Msg_N
|
|
("size for primitive object must be a power of 2 in "
|
|
& "the range ^-^", N);
|
|
end if;
|
|
|
|
Set_Esize (U_Ent, Size);
|
|
end if;
|
|
|
|
Set_Has_Size_Clause (U_Ent);
|
|
end;
|
|
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_Simple_Storage_Pool
|
|
| Attribute_Storage_Pool
|
|
=>
|
|
Storage_Pool : declare
|
|
Pool : Entity_Id;
|
|
T : Entity_Id;
|
|
|
|
procedure Associate_Storage_Pool
|
|
(Ent : Entity_Id; Pool : Entity_Id);
|
|
-- Associate Pool to Ent and perform legality checks on subpools
|
|
|
|
----------------------------
|
|
-- Associate_Storage_Pool --
|
|
----------------------------
|
|
|
|
procedure Associate_Storage_Pool
|
|
(Ent : Entity_Id; Pool : Entity_Id)
|
|
is
|
|
function Object_From (Pool : Entity_Id) return Entity_Id;
|
|
-- Return the entity of which Pool is a part of
|
|
|
|
-----------------
|
|
-- Object_From --
|
|
-----------------
|
|
|
|
function Object_From
|
|
(Pool : Entity_Id) return Entity_Id
|
|
is
|
|
N : Node_Id := Pool;
|
|
begin
|
|
if Present (Renamed_Object (Pool)) then
|
|
N := Renamed_Object (Pool);
|
|
end if;
|
|
|
|
while Present (N) loop
|
|
case Nkind (N) is
|
|
when N_Defining_Identifier =>
|
|
return N;
|
|
|
|
when N_Identifier | N_Expanded_Name =>
|
|
return Entity (N);
|
|
|
|
when N_Indexed_Component | N_Selected_Component |
|
|
N_Explicit_Dereference
|
|
=>
|
|
N := Prefix (N);
|
|
|
|
when N_Type_Conversion =>
|
|
N := Expression (N);
|
|
|
|
when others =>
|
|
-- ??? we probably should handle more cases but
|
|
-- this is good enough in practice for this check
|
|
-- on a corner case.
|
|
|
|
return Empty;
|
|
end case;
|
|
end loop;
|
|
|
|
return Empty;
|
|
end Object_From;
|
|
|
|
Obj : Entity_Id;
|
|
|
|
begin
|
|
Set_Associated_Storage_Pool (Ent, Pool);
|
|
|
|
-- Check RM 13.11.4(22-23/3): a specification of a storage pool
|
|
-- is illegal if the storage pool supports subpools and:
|
|
-- (A) The access type is a general access type.
|
|
-- (B) The access type is statically deeper than the storage
|
|
-- pool object;
|
|
-- (C) The storage pool object is a part of a formal parameter;
|
|
-- (D) The storage pool object is a part of the dereference of
|
|
-- a non-library level general access type;
|
|
|
|
if Ada_Version >= Ada_2012
|
|
and then RTU_Loaded (System_Storage_Pools_Subpools)
|
|
and then
|
|
Is_Ancestor (RTE (RE_Root_Storage_Pool_With_Subpools),
|
|
Etype (Pool))
|
|
then
|
|
-- check (A)
|
|
|
|
if Ekind (Etype (Ent)) = E_General_Access_Type then
|
|
Error_Msg_N
|
|
("subpool cannot be used on general access type", Ent);
|
|
end if;
|
|
|
|
-- check (B)
|
|
|
|
if Type_Access_Level (Ent)
|
|
> Static_Accessibility_Level
|
|
(Pool, Object_Decl_Level)
|
|
then
|
|
Error_Msg_N
|
|
("subpool access type has deeper accessibility "
|
|
& "level than pool", Ent);
|
|
return;
|
|
end if;
|
|
|
|
Obj := Object_From (Pool);
|
|
|
|
-- check (C)
|
|
|
|
if Present (Obj) and then Is_Formal (Obj) then
|
|
Error_Msg_N
|
|
("subpool cannot be part of a parameter", Ent);
|
|
return;
|
|
end if;
|
|
|
|
-- check (D)
|
|
|
|
if Present (Obj)
|
|
and then Ekind (Etype (Obj)) = E_General_Access_Type
|
|
and then not Is_Library_Level_Entity (Etype (Obj))
|
|
then
|
|
Error_Msg_N
|
|
("subpool cannot be part of the dereference of a " &
|
|
"nested general access type", Ent);
|
|
return;
|
|
end if;
|
|
end if;
|
|
end Associate_Storage_Pool;
|
|
|
|
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 Ekind (U_Ent) not in 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 Is_RTE (Base_Type (T), RE_Stack_Bounded_Pool) then
|
|
Error_Msg_N ("non-shareable internal Pool", Expr);
|
|
return;
|
|
end if;
|
|
|
|
-- Validate_Remote_Access_To_Class_Wide_Type for attribute
|
|
-- Storage_Pool since this attribute cannot be defined for such
|
|
-- types (RM E.2.2(17)).
|
|
|
|
Validate_Remote_Access_To_Class_Wide_Type (N);
|
|
|
|
-- 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);
|
|
Associate_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;
|
|
|
|
Associate_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));
|
|
Associate_Storage_Pool (U_Ent, Pool);
|
|
|
|
else
|
|
Error_Msg_N ("incorrect reference to a Storage Pool", Expr);
|
|
return;
|
|
end if;
|
|
end Storage_Pool;
|
|
|
|
------------------
|
|
-- 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
|
|
-- Validate_Remote_Access_To_Class_Wide_Type for attribute
|
|
-- Storage_Size since this attribute cannot be defined for such
|
|
-- types (RM E.2.2(17)).
|
|
|
|
Validate_Remote_Access_To_Class_Wide_Type (N);
|
|
|
|
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
|
|
-- Size will be empty if we already detected an error
|
|
-- (e.g. Expr is of the wrong type); we might as well
|
|
-- give the useful hint below even in that case.
|
|
|
|
if No (Size) or else
|
|
(Size /= System_Storage_Unit
|
|
and then Size /= System_Storage_Unit * 2
|
|
and then Size /= System_Storage_Unit * 3
|
|
and then Size /= System_Storage_Unit * 4
|
|
and then Size /= System_Storage_Unit * 8)
|
|
then
|
|
Error_Msg_N
|
|
("stream size for elementary type must be 8, 16, 24, " &
|
|
"32 or 64", N);
|
|
|
|
elsif Known_RM_Size (U_Ent) and then RM_Size (U_Ent) > Size then
|
|
Error_Msg_Uint_1 := RM_Size (U_Ent);
|
|
Error_Msg_N
|
|
("stream size for elementary type must be 8, 16, 24, " &
|
|
"32 or 64 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;
|
|
|
|
-----------------------
|
|
-- 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
|
|
-- Accept foreign code statements for CodePeer. The analysis is skipped
|
|
-- to avoid rejecting unrecognized constructs.
|
|
|
|
if CodePeer_Mode then
|
|
Set_Analyzed (N);
|
|
return;
|
|
end if;
|
|
|
|
-- 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 not Is_RTE (Etype (Expression (N)), 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 Nkind (DeclO) not in N_Pragma
|
|
| N_Use_Package_Clause
|
|
| N_Use_Type_Clause
|
|
| N_Implicit_Label_Declaration
|
|
then
|
|
Error_Msg_N
|
|
("this declaration is 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 Nkind (StmtO) not in
|
|
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 := Empty; -- init to avoid warning
|
|
-- 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_F
|
|
("extra parentheses surrounding aggregate not allowed", Aggr);
|
|
return;
|
|
|
|
-- Reject the mixing of named and positional entries in the aggregate
|
|
|
|
elsif Present (Expressions (Aggr))
|
|
and then Present (Component_Associations (Aggr))
|
|
then
|
|
Error_Msg_N ("cannot mix positional and named entries in "
|
|
& "enumeration rep clause", N);
|
|
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);
|
|
|
|
-- Process positional entries
|
|
|
|
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 No (Val) then
|
|
Err := True;
|
|
|
|
elsif Val < Lo or else Hi < Val then
|
|
Error_Msg_N ("value outside permitted range", Expr);
|
|
Err := True;
|
|
|
|
else
|
|
Set_Enumeration_Rep (Elit, Val);
|
|
Set_Enumeration_Rep_Expr (Elit, Expr);
|
|
end if;
|
|
|
|
Next (Expr);
|
|
Next (Elit);
|
|
end loop;
|
|
|
|
-- Process named entries
|
|
|
|
elsif 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 No (Val) then
|
|
Err := True;
|
|
|
|
elsif Val < Lo or else Hi < Val then
|
|
Error_Msg_N ("value outside permitted range", Expr);
|
|
Err := True;
|
|
|
|
else
|
|
Set_Enumeration_Rep (Elit, Val);
|
|
end if;
|
|
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 No (Min) then
|
|
Min := Val;
|
|
end if;
|
|
|
|
if Present (Val) then
|
|
if Present (Max) 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));
|
|
|
|
Copy_Alignment (To => Base_Type (Enumtype), From => 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
|
|
E : constant Entity_Id := Entity (N);
|
|
|
|
begin
|
|
if not Is_Frozen (E) and then Has_Delayed_Aspects (E) then
|
|
Analyze_Aspects_At_Freeze_Point (E);
|
|
end if;
|
|
|
|
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;
|
|
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 to 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
|
|
M : constant Node_Id := Mod_Clause (N);
|
|
P : constant List_Id := Pragmas_Before (M);
|
|
Ignore : Uint;
|
|
|
|
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;
|
|
|
|
-- Get the alignment value to perform error checking
|
|
|
|
Ignore := Get_Alignment_Value (Expression (M));
|
|
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 Present (Posit)
|
|
and then Present (Fbit)
|
|
and then Present (Lbit)
|
|
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_With_Checks (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_Uint_1 := RM_Size (Rectype);
|
|
Error_Msg_Uint_2 := Lbit + 1;
|
|
Error_Msg_N ("bit number out of range of specified "
|
|
& "size (expected ^, got ^)",
|
|
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;
|
|
|
|
Check_Size
|
|
(Component_Name (CC),
|
|
Etype (Comp),
|
|
Esize (Comp),
|
|
Biased);
|
|
|
|
Set_Biased
|
|
(Comp, First_Node (CC), "component clause", Biased);
|
|
|
|
-- 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 Present (Ocomp) and then Ocomp /= Comp then
|
|
Set_Component_Clause (Ocomp, CC);
|
|
Set_Component_Bit_Offset (Ocomp, Fbit);
|
|
Set_Esize (Ocomp, 1 + (Lbit - Fbit));
|
|
Set_Normalized_First_Bit (Ocomp, Fbit mod SSU);
|
|
Set_Normalized_Position (Ocomp, Fbit / SSU);
|
|
|
|
-- 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)
|
|
|
|
-- Ignore discriminant in unchecked union, since it is
|
|
-- not there, and cannot have a component clause.
|
|
|
|
and then (not Is_Unchecked_Union (Rectype)
|
|
or else Ekind (Comp) /= E_Discriminant)
|
|
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);
|
|
|
|
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 : Uint;
|
|
THi : Uint;
|
|
-- Bounds for constructing the static predicate. We use the bound of the
|
|
-- subtype if it is static, otherwise the corresponding base type bound.
|
|
-- Note: a non-static subtype can have a static predicate.
|
|
|
|
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_Discrete_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;
|
|
Static : access Boolean) 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.
|
|
-- Static.all will be set to False if the expression is found to be non
|
|
-- static. Note that Static.all should be set to True by the caller.
|
|
|
|
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; Static : access Boolean) return RList;
|
|
-- Given a single membership entry (range, value, or subtype), returns
|
|
-- the corresponding range list. Set Static.all to False if not static.
|
|
|
|
function Membership_Entries
|
|
(N : Node_Id; Static : access Boolean) 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).
|
|
-- Set Static.all to False if not static.
|
|
|
|
function Stat_Pred
|
|
(Typ : Entity_Id;
|
|
Static : access Boolean) return RList;
|
|
-- Given a type, if it has a static predicate, then set Result to the
|
|
-- predicate as a range list, otherwise set Static.all to False.
|
|
|
|
-----------
|
|
-- "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;
|
|
Static : access Boolean) 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_And_Then
|
|
| N_Op_And
|
|
=>
|
|
return Get_RList (Left_Opnd (Exp), Static)
|
|
and
|
|
Get_RList (Right_Opnd (Exp), Static);
|
|
|
|
-- Or
|
|
|
|
when N_Op_Or
|
|
| N_Or_Else
|
|
=>
|
|
return Get_RList (Left_Opnd (Exp), Static)
|
|
or
|
|
Get_RList (Right_Opnd (Exp), Static);
|
|
|
|
-- Not
|
|
|
|
when N_Op_Not =>
|
|
return not Get_RList (Right_Opnd (Exp), Static);
|
|
|
|
-- 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
|
|
Static.all := False;
|
|
return False_Range;
|
|
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
|
|
Static.all := False;
|
|
return False_Range;
|
|
end if;
|
|
|
|
if Present (Right_Opnd (Exp)) then
|
|
return Membership_Entry (Right_Opnd (Exp), Static);
|
|
else
|
|
return Membership_Entries
|
|
(First (Alternatives (Exp)), Static);
|
|
end if;
|
|
|
|
-- Negative membership (NOT IN)
|
|
|
|
when N_Not_In =>
|
|
if not Is_Type_Ref (Left_Opnd (Exp)) then
|
|
Static.all := False;
|
|
return False_Range;
|
|
end if;
|
|
|
|
if Present (Right_Opnd (Exp)) then
|
|
return not Membership_Entry (Right_Opnd (Exp), Static);
|
|
else
|
|
return not Membership_Entries
|
|
(First (Alternatives (Exp)), Static);
|
|
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)), Static);
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
-- Other function call cases are non-static
|
|
|
|
Static.all := False;
|
|
return False_Range;
|
|
|
|
-- Qualified expression, dig out the expression
|
|
|
|
when N_Qualified_Expression =>
|
|
return Get_RList (Expression (Exp), Static);
|
|
|
|
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 denote 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
|
|
Static.all := False;
|
|
return False_Range;
|
|
|
|
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), Static);
|
|
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), Static);
|
|
else
|
|
Static.all := False;
|
|
return False_Range;
|
|
end if;
|
|
|
|
-- Xor operator
|
|
|
|
when N_Op_Xor =>
|
|
return (Get_RList (Left_Opnd (Exp), Static)
|
|
and not Get_RList (Right_Opnd (Exp), Static))
|
|
or (Get_RList (Right_Opnd (Exp), Static)
|
|
and not Get_RList (Left_Opnd (Exp), Static));
|
|
|
|
-- Any other node type is non-static
|
|
|
|
when others =>
|
|
Static.all := False;
|
|
return False_Range;
|
|
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; Static : access Boolean) return RList is
|
|
begin
|
|
if No (Next (N)) then
|
|
return Membership_Entry (N, Static);
|
|
else
|
|
return Membership_Entry (N, Static)
|
|
or Membership_Entries (Next (N), Static);
|
|
end if;
|
|
end Membership_Entries;
|
|
|
|
----------------------
|
|
-- Membership_Entry --
|
|
----------------------
|
|
|
|
function Membership_Entry
|
|
(N : Node_Id; Static : access Boolean) 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
|
|
Static.all := False;
|
|
return False_Range;
|
|
else
|
|
SLo := Expr_Value (Low_Bound (N));
|
|
SHi := Expr_Value (High_Bound (N));
|
|
return RList'(1 => REnt'(SLo, SHi));
|
|
end if;
|
|
|
|
-- Others case
|
|
|
|
elsif Nkind (N) = N_Others_Choice then
|
|
declare
|
|
Choices : constant List_Id := Others_Discrete_Choices (N);
|
|
Choice : Node_Id;
|
|
Range_List : RList (1 .. List_Length (Choices));
|
|
|
|
begin
|
|
Choice := First (Choices);
|
|
|
|
for J in Range_List'Range loop
|
|
Range_List (J) := REnt'(Lo_Val (Choice), Hi_Val (Choice));
|
|
Next (Choice);
|
|
end loop;
|
|
|
|
return Range_List;
|
|
end;
|
|
|
|
-- 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) in N_Expanded_Name | 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), Static);
|
|
|
|
-- 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
|
|
Static.all := False;
|
|
return False_Range;
|
|
end if;
|
|
|
|
-- Any other kind of identifier in predicate (e.g. a non-static
|
|
-- expression value) means this is not a static predicate.
|
|
|
|
else
|
|
Static.all := False;
|
|
return False_Range;
|
|
end if;
|
|
end if;
|
|
end Membership_Entry;
|
|
|
|
---------------
|
|
-- Stat_Pred --
|
|
---------------
|
|
|
|
function Stat_Pred
|
|
(Typ : Entity_Id;
|
|
Static : access Boolean) return RList is
|
|
begin
|
|
-- Not static if type does not have static predicates
|
|
|
|
if not Has_Static_Predicate (Typ) then
|
|
Static.all := False;
|
|
return False_Range;
|
|
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
|
|
-- Establish bounds for the predicate
|
|
|
|
if Compile_Time_Known_Value (Type_Low_Bound (Typ)) then
|
|
TLo := Expr_Value (Type_Low_Bound (Typ));
|
|
else
|
|
TLo := BLo;
|
|
end if;
|
|
|
|
if Compile_Time_Known_Value (Type_High_Bound (Typ)) then
|
|
THi := Expr_Value (Type_High_Bound (Typ));
|
|
else
|
|
THi := BHi;
|
|
end if;
|
|
|
|
-- Analyze the expression to see if it is a static predicate
|
|
|
|
declare
|
|
Static : aliased Boolean := True;
|
|
Ranges : constant RList := Get_RList (Expr, Static'Access);
|
|
-- Range list from expression if it is static
|
|
|
|
Plist : List_Id;
|
|
|
|
begin
|
|
-- If non-static, return doing nothing
|
|
|
|
if not Static then
|
|
return;
|
|
end if;
|
|
|
|
-- 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);
|
|
|
|
-- Within a generic the predicate functions themselves need not
|
|
-- be constructed.
|
|
|
|
if Inside_A_Generic then
|
|
return;
|
|
end if;
|
|
|
|
-- 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;
|
|
end Build_Discrete_Static_Predicate;
|
|
|
|
--------------------------------
|
|
-- Build_Export_Import_Pragma --
|
|
--------------------------------
|
|
|
|
function Build_Export_Import_Pragma
|
|
(Asp : Node_Id;
|
|
Id : Entity_Id) return Node_Id
|
|
is
|
|
Asp_Id : constant Aspect_Id := Get_Aspect_Id (Asp);
|
|
Expr : constant Node_Id := Expression (Asp);
|
|
Loc : constant Source_Ptr := Sloc (Asp);
|
|
|
|
Args : List_Id;
|
|
Conv : Node_Id;
|
|
Conv_Arg : Node_Id;
|
|
Dummy_1 : Node_Id;
|
|
Dummy_2 : Node_Id;
|
|
EN : Node_Id;
|
|
LN : Node_Id;
|
|
Prag : Node_Id;
|
|
|
|
Create_Pragma : Boolean := False;
|
|
-- This flag is set when the aspect form is such that it warrants the
|
|
-- creation of a corresponding pragma.
|
|
|
|
begin
|
|
if Present (Expr) then
|
|
if Error_Posted (Expr) then
|
|
null;
|
|
|
|
elsif Is_True (Expr_Value (Expr)) then
|
|
Create_Pragma := True;
|
|
end if;
|
|
|
|
-- Otherwise the aspect defaults to True
|
|
|
|
else
|
|
Create_Pragma := True;
|
|
end if;
|
|
|
|
-- Nothing to do when the expression is False or is erroneous
|
|
|
|
if not Create_Pragma then
|
|
return Empty;
|
|
end if;
|
|
|
|
-- Obtain all interfacing aspects that apply to the related entity
|
|
|
|
Get_Interfacing_Aspects
|
|
(Iface_Asp => Asp,
|
|
Conv_Asp => Conv,
|
|
EN_Asp => EN,
|
|
Expo_Asp => Dummy_1,
|
|
Imp_Asp => Dummy_2,
|
|
LN_Asp => LN);
|
|
|
|
Args := New_List;
|
|
|
|
-- Handle the convention argument
|
|
|
|
if Present (Conv) then
|
|
Conv_Arg := New_Copy_Tree (Expression (Conv));
|
|
|
|
-- Assume convention "Ada' when aspect Convention is missing
|
|
|
|
else
|
|
Conv_Arg := Make_Identifier (Loc, Name_Ada);
|
|
end if;
|
|
|
|
Append_To (Args,
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Chars => Name_Convention,
|
|
Expression => Conv_Arg));
|
|
|
|
-- Handle the entity argument
|
|
|
|
Append_To (Args,
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Chars => Name_Entity,
|
|
Expression => New_Occurrence_Of (Id, Loc)));
|
|
|
|
-- Handle the External_Name argument
|
|
|
|
if Present (EN) then
|
|
Append_To (Args,
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Chars => Name_External_Name,
|
|
Expression => New_Copy_Tree (Expression (EN))));
|
|
end if;
|
|
|
|
-- Handle the Link_Name argument
|
|
|
|
if Present (LN) then
|
|
Append_To (Args,
|
|
Make_Pragma_Argument_Association (Loc,
|
|
Chars => Name_Link_Name,
|
|
Expression => New_Copy_Tree (Expression (LN))));
|
|
end if;
|
|
|
|
-- Generate:
|
|
-- pragma Export/Import
|
|
-- (Convention => <Conv>/Ada,
|
|
-- Entity => <Id>,
|
|
-- [External_Name => <EN>,]
|
|
-- [Link_Name => <LN>]);
|
|
|
|
Prag :=
|
|
Make_Pragma (Loc,
|
|
Pragma_Identifier =>
|
|
Make_Identifier (Loc, Chars (Identifier (Asp))),
|
|
Pragma_Argument_Associations => Args);
|
|
|
|
-- Decorate the relevant aspect and the pragma
|
|
|
|
Set_Aspect_Rep_Item (Asp, Prag);
|
|
|
|
Set_Corresponding_Aspect (Prag, Asp);
|
|
Set_From_Aspect_Specification (Prag);
|
|
Set_Parent (Prag, Asp);
|
|
|
|
if Asp_Id = Aspect_Import and then Is_Subprogram (Id) then
|
|
Set_Import_Pragma (Id, Prag);
|
|
end if;
|
|
|
|
return Prag;
|
|
end Build_Export_Import_Pragma;
|
|
|
|
-------------------------------
|
|
-- Build_Predicate_Functions --
|
|
-------------------------------
|
|
|
|
-- The functions that are constructed here have the form:
|
|
|
|
-- function typPredicate (Ixxx : typ) return Boolean is
|
|
-- begin
|
|
-- return
|
|
-- typ1Predicate (typ1 (Ixxx))
|
|
-- and then typ2Predicate (typ2 (Ixxx))
|
|
-- and then ...
|
|
-- and then exp1 and then exp2 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.
|
|
|
|
-- Note that the inherited predicates are evaluated first, as required by
|
|
-- AI12-0071-1.
|
|
|
|
-- Note that Sem_Eval.Real_Or_String_Static_Predicate_Matches depends on
|
|
-- the form of this return expression.
|
|
|
|
-- 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".
|
|
|
|
-- WARNING: This routine manages Ghost regions. Return statements must be
|
|
-- replaced by gotos which jump to the end of the routine and restore the
|
|
-- Ghost mode.
|
|
|
|
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 := Empty; -- init to avoid warning
|
|
-- 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 : Name_Id;
|
|
-- 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 : Entity_Id;
|
|
-- Entity for argument of Predicate procedure
|
|
|
|
Object_Entity_M : Entity_Id;
|
|
-- Entity for argument of separate Predicate procedure when exceptions
|
|
-- are present in expression.
|
|
|
|
FDecl : Node_Id;
|
|
-- The function declaration
|
|
|
|
SId : Entity_Id;
|
|
-- Its entity
|
|
|
|
Raise_Expression_Present : Boolean := False;
|
|
-- Set True if Expr has at least one Raise_Expression
|
|
|
|
procedure Add_Condition (Cond : Node_Id);
|
|
-- Append Cond to Expr using "and then" (or just copy Cond to Expr if
|
|
-- Expr is 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.
|
|
|
|
procedure Add_Call (T : Entity_Id);
|
|
-- Includes a call to the predicate function for type T in Expr if
|
|
-- Predicate_Function (T) is non-empty.
|
|
|
|
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
|
|
|
|
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
|
|
|
|
--------------
|
|
-- Add_Call --
|
|
--------------
|
|
|
|
procedure Add_Call (T : Entity_Id) is
|
|
Exp : Node_Id;
|
|
|
|
begin
|
|
if Present (Predicate_Function (T)) then
|
|
pragma Assert (Has_Predicates (Typ));
|
|
|
|
-- Build the call to the predicate function of T. The type may be
|
|
-- derived, so use an unchecked conversion for the actual.
|
|
|
|
Exp :=
|
|
Make_Predicate_Call
|
|
(Typ => T,
|
|
Expr =>
|
|
Unchecked_Convert_To (T,
|
|
Make_Identifier (Loc, Object_Name)));
|
|
|
|
-- "and"-in the call to evolving expression
|
|
|
|
Add_Condition (Exp);
|
|
|
|
-- 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_Condition --
|
|
-------------------
|
|
|
|
procedure Add_Condition (Cond : Node_Id) is
|
|
begin
|
|
-- This is the first predicate expression
|
|
|
|
if No (Expr) then
|
|
Expr := Cond;
|
|
|
|
-- Otherwise concatenate to the existing predicate expressions by
|
|
-- using "and then".
|
|
|
|
else
|
|
Expr :=
|
|
Make_And_Then (Loc,
|
|
Left_Opnd => Relocate_Node (Expr),
|
|
Right_Opnd => Cond);
|
|
end if;
|
|
end Add_Condition;
|
|
|
|
--------------------
|
|
-- Add_Predicates --
|
|
--------------------
|
|
|
|
procedure Add_Predicates is
|
|
procedure Add_Predicate (Prag : Node_Id);
|
|
-- Concatenate the expression of predicate pragma Prag to Expr by
|
|
-- using a short circuit "and then" operator.
|
|
|
|
-------------------
|
|
-- Add_Predicate --
|
|
-------------------
|
|
|
|
procedure Add_Predicate (Prag : Node_Id) is
|
|
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);
|
|
end Replace_Type_Reference;
|
|
|
|
-- Local variables
|
|
|
|
Asp : constant Node_Id := Corresponding_Aspect (Prag);
|
|
Arg1 : Node_Id;
|
|
Arg2 : Node_Id;
|
|
|
|
-- Start of processing for Add_Predicate
|
|
|
|
begin
|
|
-- Mark corresponding SCO as enabled
|
|
|
|
Set_SCO_Pragma_Enabled (Sloc (Prag));
|
|
|
|
-- Extract the arguments of the pragma. The expression itself
|
|
-- is copied for use in the predicate function, to preserve the
|
|
-- original version for ASIS use.
|
|
-- Is this still needed???
|
|
|
|
Arg1 := First (Pragma_Argument_Associations (Prag));
|
|
Arg2 := Next (Arg1);
|
|
|
|
Arg1 := Get_Pragma_Arg (Arg1);
|
|
Arg2 := New_Copy_Tree (Get_Pragma_Arg (Arg2));
|
|
|
|
-- When the predicate pragma applies to the current type or its
|
|
-- full view, replace all occurrences of the subtype name with
|
|
-- references to the formal parameter of the predicate function.
|
|
|
|
if Entity (Arg1) = Typ
|
|
or else Full_View (Entity (Arg1)) = Typ
|
|
then
|
|
Replace_Type_References (Arg2, Typ);
|
|
|
|
-- If the predicate pragma comes from an aspect, replace the
|
|
-- saved expression because we need the subtype references
|
|
-- replaced for the calls to Preanalyze_Spec_Expression in
|
|
-- Check_Aspect_At_xxx routines.
|
|
|
|
if Present (Asp) then
|
|
Set_Entity (Identifier (Asp), New_Copy_Tree (Arg2));
|
|
end if;
|
|
|
|
-- "and"-in the Arg2 condition to evolving expression
|
|
|
|
Add_Condition (Relocate_Node (Arg2));
|
|
end if;
|
|
end Add_Predicate;
|
|
|
|
-- Local variables
|
|
|
|
Ritem : Node_Id;
|
|
|
|
-- Start of processing for Add_Predicates
|
|
|
|
begin
|
|
Ritem := First_Rep_Item (Typ);
|
|
|
|
-- If the type is private, check whether full view has inherited
|
|
-- predicates.
|
|
|
|
if Is_Private_Type (Typ)
|
|
and then No (Ritem)
|
|
and then Present (Full_View (Typ))
|
|
then
|
|
Ritem := First_Rep_Item (Full_View (Typ));
|
|
end if;
|
|
|
|
while Present (Ritem) loop
|
|
if Nkind (Ritem) = N_Pragma
|
|
and then Pragma_Name (Ritem) = Name_Predicate
|
|
then
|
|
Add_Predicate (Ritem);
|
|
|
|
-- If the type is declared in an inner package it may be frozen
|
|
-- outside of the package, and the generated pragma has not been
|
|
-- analyzed yet, so capture the expression for the predicate
|
|
-- function at this point.
|
|
|
|
elsif Nkind (Ritem) = N_Aspect_Specification
|
|
and then Present (Aspect_Rep_Item (Ritem))
|
|
and then Scope_Depth (Scope (Typ)) > Scope_Depth (Current_Scope)
|
|
then
|
|
declare
|
|
Prag : constant Node_Id := Aspect_Rep_Item (Ritem);
|
|
|
|
begin
|
|
if Nkind (Prag) = N_Pragma
|
|
and then Pragma_Name (Prag) = Name_Predicate
|
|
then
|
|
Add_Predicate (Prag);
|
|
end if;
|
|
end;
|
|
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;
|
|
|
|
-- Local variables
|
|
|
|
Saved_GM : constant Ghost_Mode_Type := Ghost_Mode;
|
|
Saved_IGR : constant Node_Id := Ignored_Ghost_Region;
|
|
-- Save the Ghost-related attributes to restore on exit
|
|
|
|
-- Start of processing for Build_Predicate_Functions
|
|
|
|
begin
|
|
-- Return if already built, if type does not have predicates,
|
|
-- or if type is a constructed subtype that will inherit a
|
|
-- predicate function from its ancestor. In a generic context
|
|
-- the predicated parent may not have a predicate function yet
|
|
-- but we don't want to build a new one for the subtype. This can
|
|
-- happen in an instance body which is nested within a generic
|
|
-- unit, in which case Within_A_Generic may be false, SId is
|
|
-- Empty, but uses of Typ will receive a predicate check in a
|
|
-- context where expansion and tests are enabled.
|
|
|
|
SId := Predicate_Function (Typ);
|
|
if not Has_Predicates (Typ)
|
|
or else (Present (SId) and then Has_Completion (SId))
|
|
or else
|
|
(Is_Itype (Typ)
|
|
and then not Comes_From_Source (Typ)
|
|
and then Ekind (Typ) in E_Array_Subtype
|
|
| E_Record_Subtype
|
|
| E_Record_Subtype_With_Private
|
|
and then Present (Predicated_Parent (Typ)))
|
|
then
|
|
return;
|
|
|
|
-- Do not generate predicate bodies within a generic unit. The
|
|
-- expressions have been analyzed already, and the bodies play no role
|
|
-- if not within an executable unit. However, if a static predicate is
|
|
-- present it must be processed for legality checks such as case
|
|
-- coverage in an expression.
|
|
|
|
elsif Inside_A_Generic
|
|
and then not Has_Static_Predicate_Aspect (Typ)
|
|
then
|
|
return;
|
|
end if;
|
|
|
|
-- The related type may be subject to pragma Ghost. Set the mode now to
|
|
-- ensure that the predicate functions are properly marked as Ghost.
|
|
|
|
Set_Ghost_Mode (Typ);
|
|
|
|
-- Prepare to construct predicate expression
|
|
|
|
Expr := Empty;
|
|
|
|
if Present (SId) then
|
|
FDecl := Unit_Declaration_Node (SId);
|
|
|
|
else
|
|
FDecl := Build_Predicate_Function_Declaration (Typ);
|
|
SId := Defining_Entity (FDecl);
|
|
end if;
|
|
|
|
-- Recover name of formal parameter of function that replaces references
|
|
-- to the type in predicate expressions.
|
|
|
|
Object_Entity :=
|
|
Defining_Identifier
|
|
(First (Parameter_Specifications (Specification (FDecl))));
|
|
|
|
Object_Name := Chars (Object_Entity);
|
|
Object_Entity_M := Make_Defining_Identifier (Loc, Chars => Object_Name);
|
|
|
|
-- Add predicates for ancestor if present. These must come before the
|
|
-- ones for the current type, as required by AI12-0071-1.
|
|
|
|
-- Looks like predicates aren't added for case of inheriting from
|
|
-- multiple progenitors???
|
|
|
|
declare
|
|
Atyp : Entity_Id;
|
|
begin
|
|
Atyp := Nearest_Ancestor (Typ);
|
|
|
|
-- The type may be private but the full view may inherit predicates
|
|
|
|
if No (Atyp) and then Is_Private_Type (Typ) then
|
|
Atyp := Nearest_Ancestor (Full_View (Typ));
|
|
end if;
|
|
|
|
if Present (Atyp) then
|
|
Add_Call (Atyp);
|
|
end if;
|
|
end;
|
|
|
|
-- Add Predicates for the current type
|
|
|
|
Add_Predicates;
|
|
|
|
-- 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
|
|
function Reset_Loop_Variable
|
|
(N : Node_Id) return Traverse_Result;
|
|
|
|
procedure Reset_Loop_Variables is
|
|
new Traverse_Proc (Reset_Loop_Variable);
|
|
|
|
------------------------
|
|
-- Reset_Loop_Variable --
|
|
------------------------
|
|
|
|
function Reset_Loop_Variable
|
|
(N : Node_Id) return Traverse_Result
|
|
is
|
|
begin
|
|
if Nkind (N) = N_Iterator_Specification then
|
|
Set_Defining_Identifier (N,
|
|
Make_Defining_Identifier
|
|
(Sloc (N), Chars (Defining_Identifier (N))));
|
|
end if;
|
|
|
|
return OK;
|
|
end Reset_Loop_Variable;
|
|
|
|
-- Local variables
|
|
|
|
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);
|
|
|
|
-- The unanalyzed expression will be copied and appear in
|
|
-- both functions. Normally expressions do not declare new
|
|
-- entities, but quantified expressions do, so we need to
|
|
-- create new entities for their bound variables, to prevent
|
|
-- multiple definitions in gigi.
|
|
|
|
Reset_Loop_Variables (Expr_M);
|
|
end;
|
|
end if;
|
|
|
|
-- Build the main predicate function
|
|
|
|
declare
|
|
SIdB : constant Entity_Id :=
|
|
Make_Defining_Identifier (Loc,
|
|
Chars => New_External_Name (Chars (Typ), "Predicate"));
|
|
-- The entity for the function body
|
|
|
|
Spec : Node_Id;
|
|
FBody : Node_Id;
|
|
|
|
begin
|
|
Mutate_Ekind (SIdB, E_Function);
|
|
Set_Is_Predicate_Function (SIdB);
|
|
|
|
-- 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))));
|
|
|
|
-- The declaration has been analyzed when created, and placed
|
|
-- after type declaration. Insert body itself after freeze node,
|
|
-- unless subprogram declaration is already there, in which case
|
|
-- body better be placed afterwards.
|
|
|
|
if FDecl = Next (N) then
|
|
Insert_After_And_Analyze (FDecl, FBody);
|
|
else
|
|
Insert_After_And_Analyze (N, FBody);
|
|
end if;
|
|
|
|
-- The defining identifier of a quantified expression carries the
|
|
-- scope in which the type appears, but when unnesting we need
|
|
-- to indicate that its proper scope is the constructed predicate
|
|
-- function. The quantified expressions have been converted into
|
|
-- loops during analysis and expansion.
|
|
|
|
declare
|
|
function Reset_Quantified_Variable_Scope
|
|
(N : Node_Id) return Traverse_Result;
|
|
|
|
procedure Reset_Quantified_Variables_Scope is
|
|
new Traverse_Proc (Reset_Quantified_Variable_Scope);
|
|
|
|
-------------------------------------
|
|
-- Reset_Quantified_Variable_Scope --
|
|
-------------------------------------
|
|
|
|
function Reset_Quantified_Variable_Scope
|
|
(N : Node_Id) return Traverse_Result is
|
|
begin
|
|
if Nkind (N) in N_Iterator_Specification
|
|
| N_Loop_Parameter_Specification
|
|
then
|
|
Set_Scope (Defining_Identifier (N),
|
|
Predicate_Function (Typ));
|
|
end if;
|
|
|
|
return OK;
|
|
end Reset_Quantified_Variable_Scope;
|
|
|
|
begin
|
|
if Unnest_Subprogram_Mode then
|
|
Reset_Quantified_Variables_Scope (Expr);
|
|
end if;
|
|
end;
|
|
|
|
-- Within a generic unit, prevent a double analysis of the body
|
|
-- which will not be marked analyzed yet. This will happen when
|
|
-- the freeze node is created during the preanalysis of an
|
|
-- expression function.
|
|
|
|
if Inside_A_Generic then
|
|
Set_Analyzed (FBody);
|
|
end if;
|
|
|
|
-- Static predicate functions are always side-effect free, and
|
|
-- in most cases dynamic predicate functions are as well. Mark
|
|
-- them as such whenever possible, so redundant predicate checks
|
|
-- can be optimized. If there is a variable reference within the
|
|
-- expression, the function is not pure.
|
|
|
|
if Expander_Active then
|
|
Set_Is_Pure (SId,
|
|
Side_Effect_Free (Expr, Variable_Ref => True));
|
|
Set_Is_Inlined (SId);
|
|
end if;
|
|
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 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;
|
|
FBody : Node_Id;
|
|
FDecl : Node_Id;
|
|
BTemp : Entity_Id;
|
|
|
|
CRec_Typ : Entity_Id;
|
|
-- The corresponding record type of Full_Typ
|
|
|
|
Full_Typ : Entity_Id;
|
|
-- The full view of Typ
|
|
|
|
Priv_Typ : Entity_Id;
|
|
-- The partial view of Typ
|
|
|
|
UFull_Typ : Entity_Id;
|
|
-- The underlying full view of Full_Typ
|
|
|
|
begin
|
|
-- Mark any raise expressions for special expansion
|
|
|
|
Process_REs (Expr_M);
|
|
|
|
-- Build function declaration
|
|
|
|
Mutate_Ekind (SId, E_Function);
|
|
Set_Is_Predicate_Function_M (SId);
|
|
Set_Predicate_Function_M (Typ, SId);
|
|
|
|
-- Obtain all views of the input type
|
|
|
|
Get_Views (Typ, Priv_Typ, Full_Typ, UFull_Typ, CRec_Typ);
|
|
|
|
-- Associate the predicate function with all views
|
|
|
|
Propagate_Predicate_Attributes (Priv_Typ, From_Typ => Typ);
|
|
Propagate_Predicate_Attributes (Full_Typ, From_Typ => Typ);
|
|
Propagate_Predicate_Attributes (UFull_Typ, From_Typ => Typ);
|
|
Propagate_Predicate_Attributes (CRec_Typ, From_Typ => Typ);
|
|
|
|
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_Temporary (Loc, '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);
|
|
|
|
-- Should quantified expressions be handled here as well ???
|
|
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),
|
|
-- where A is an inherited predicate, then use the right
|
|
-- operand for the Sloc. This avoids getting confused by a call
|
|
-- to an inherited predicate with a less convenient source
|
|
-- location.
|
|
|
|
EN := Expr;
|
|
while Nkind (EN) = N_And_Then
|
|
and then Nkind (Left_Opnd (EN)) = N_Function_Call
|
|
and then Is_Predicate_Function
|
|
(Entity (Name (Left_Opnd (EN))))
|
|
loop
|
|
EN := Right_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;
|
|
|
|
Restore_Ghost_Region (Saved_GM, Saved_IGR);
|
|
end Build_Predicate_Functions;
|
|
|
|
------------------------------------------
|
|
-- Build_Predicate_Function_Declaration --
|
|
------------------------------------------
|
|
|
|
-- WARNING: This routine manages Ghost regions. Return statements must be
|
|
-- replaced by gotos which jump to the end of the routine and restore the
|
|
-- Ghost mode.
|
|
|
|
function Build_Predicate_Function_Declaration
|
|
(Typ : Entity_Id) return Node_Id
|
|
is
|
|
Loc : constant Source_Ptr := Sloc (Typ);
|
|
|
|
Saved_GM : constant Ghost_Mode_Type := Ghost_Mode;
|
|
Saved_IGR : constant Node_Id := Ignored_Ghost_Region;
|
|
-- Save the Ghost-related attributes to restore on exit
|
|
|
|
Func_Decl : Node_Id;
|
|
Func_Id : Entity_Id;
|
|
Spec : Node_Id;
|
|
|
|
CRec_Typ : Entity_Id;
|
|
-- The corresponding record type of Full_Typ
|
|
|
|
Full_Typ : Entity_Id;
|
|
-- The full view of Typ
|
|
|
|
Priv_Typ : Entity_Id;
|
|
-- The partial view of Typ
|
|
|
|
UFull_Typ : Entity_Id;
|
|
-- The underlying full view of Full_Typ
|
|
|
|
begin
|
|
-- The related type may be subject to pragma Ghost. Set the mode now to
|
|
-- ensure that the predicate functions are properly marked as Ghost.
|
|
|
|
Set_Ghost_Mode (Typ);
|
|
|
|
Func_Id :=
|
|
Make_Defining_Identifier (Loc,
|
|
Chars => New_External_Name (Chars (Typ), "Predicate"));
|
|
|
|
Mutate_Ekind (Func_Id, E_Function);
|
|
Set_Etype (Func_Id, Standard_Boolean);
|
|
Set_Is_Internal (Func_Id);
|
|
Set_Is_Predicate_Function (Func_Id);
|
|
Set_Predicate_Function (Typ, Func_Id);
|
|
|
|
-- The predicate function requires debug info when the predicates are
|
|
-- subject to Source Coverage Obligations.
|
|
|
|
if Opt.Generate_SCO then
|
|
Set_Debug_Info_Needed (Func_Id);
|
|
end if;
|
|
|
|
-- Obtain all views of the input type
|
|
|
|
Get_Views (Typ, Priv_Typ, Full_Typ, UFull_Typ, CRec_Typ);
|
|
|
|
-- Associate the predicate function and various flags with all views
|
|
|
|
Propagate_Predicate_Attributes (Priv_Typ, From_Typ => Typ);
|
|
Propagate_Predicate_Attributes (Full_Typ, From_Typ => Typ);
|
|
Propagate_Predicate_Attributes (UFull_Typ, From_Typ => Typ);
|
|
Propagate_Predicate_Attributes (CRec_Typ, From_Typ => Typ);
|
|
|
|
Spec :=
|
|
Make_Function_Specification (Loc,
|
|
Defining_Unit_Name => Func_Id,
|
|
Parameter_Specifications => New_List (
|
|
Make_Parameter_Specification (Loc,
|
|
Defining_Identifier => Make_Temporary (Loc, 'I'),
|
|
Parameter_Type => New_Occurrence_Of (Typ, Loc))),
|
|
Result_Definition =>
|
|
New_Occurrence_Of (Standard_Boolean, Loc));
|
|
|
|
Func_Decl := Make_Subprogram_Declaration (Loc, Specification => Spec);
|
|
|
|
Insert_After (Parent (Typ), Func_Decl);
|
|
Analyze (Func_Decl);
|
|
|
|
Restore_Ghost_Region (Saved_GM, Saved_IGR);
|
|
|
|
return Func_Decl;
|
|
end Build_Predicate_Function_Declaration;
|
|
|
|
-----------------------------------------
|
|
-- 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 :=
|
|
(if Present (Freeze_Expr) and (A_Id /= Aspect_Stable_Properties)
|
|
then Etype (Original_Node (Freeze_Expr))
|
|
else Empty);
|
|
-- Type required for preanalyze call. We use the original expression to
|
|
-- get the proper type, to prevent cascaded errors when the expression
|
|
-- is constant-folded. For Stable_Properties, the aspect value is
|
|
-- not semantically an expression (although it is syntactically);
|
|
-- in particular, it has no type.
|
|
|
|
Err : Boolean;
|
|
-- Set True 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
|
|
-- In an instance we do not perform the consistency check between freeze
|
|
-- point and end of declarations, because it was done already in the
|
|
-- analysis of the generic. Furthermore, the delayed analysis of an
|
|
-- aspect of the instance may produce spurious errors when the generic
|
|
-- is a child unit that references entities in the parent (which might
|
|
-- not be in scope at the freeze point of the instance).
|
|
|
|
if In_Instance then
|
|
return;
|
|
|
|
-- The enclosing scope may have been rewritten during expansion (.e.g. a
|
|
-- task body is rewritten as a procedure) after this conformance check
|
|
-- has been performed, so do not perform it again (it may not easily be
|
|
-- done if full visibility of local entities is not available).
|
|
|
|
elsif not Comes_From_Source (Current_Scope) then
|
|
return;
|
|
|
|
-- Case of aspects Dimension, Dimension_System and Synchronization
|
|
|
|
elsif A_Id = Aspect_Synchronization then
|
|
return;
|
|
|
|
-- Case of stream attributes and Put_Image, just have to compare
|
|
-- entities. However, the expression is just a possibly-overloaded
|
|
-- name, so we need to verify that one of these interpretations is
|
|
-- the one available at at the freeze point.
|
|
|
|
elsif A_Id in Aspect_Input
|
|
| Aspect_Output
|
|
| Aspect_Read
|
|
| Aspect_Write
|
|
| Aspect_Put_Image
|
|
then
|
|
Analyze (End_Decl_Expr);
|
|
Check_Overloaded_Name;
|
|
|
|
elsif A_Id in Aspect_Variable_Indexing
|
|
| Aspect_Constant_Indexing
|
|
| Aspect_Default_Iterator
|
|
| Aspect_Iterator_Element
|
|
| Aspect_Integer_Literal
|
|
| Aspect_Real_Literal
|
|
| Aspect_String_Literal
|
|
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
|
|
-- In a generic context freeze nodes are not always generated, so
|
|
-- analyze the expression now. If the aspect is for a type, we must
|
|
-- also make its potential components accessible.
|
|
|
|
if not Analyzed (Freeze_Expr) and then Inside_A_Generic then
|
|
if A_Id in Aspect_Dynamic_Predicate | Aspect_Predicate |
|
|
Aspect_Static_Predicate
|
|
then
|
|
Push_Type (Ent);
|
|
Preanalyze_Spec_Expression (Freeze_Expr, Standard_Boolean);
|
|
Pop_Type (Ent);
|
|
|
|
elsif A_Id = Aspect_Priority then
|
|
Push_Type (Ent);
|
|
Preanalyze_Spec_Expression (Freeze_Expr, Any_Integer);
|
|
Pop_Type (Ent);
|
|
|
|
else
|
|
Preanalyze (Freeze_Expr);
|
|
end if;
|
|
end if;
|
|
|
|
-- Indicate that the expression comes from an aspect specification,
|
|
-- which is used in subsequent analysis even if expansion is off.
|
|
|
|
if Present (End_Decl_Expr) then
|
|
Set_Parent (End_Decl_Expr, ASN);
|
|
end if;
|
|
|
|
-- In a generic context the original aspect expressions have not
|
|
-- been preanalyzed, so do it now. There are no conformance checks
|
|
-- to perform in this case. As before, we have to make components
|
|
-- visible for aspects that may reference them.
|
|
|
|
if Present (Freeze_Expr) and then No (T) then
|
|
if A_Id in Aspect_Dynamic_Predicate
|
|
| Aspect_Predicate
|
|
| Aspect_Priority
|
|
| Aspect_Static_Predicate
|
|
then
|
|
Push_Type (Ent);
|
|
Check_Aspect_At_Freeze_Point (ASN);
|
|
Pop_Type (Ent);
|
|
|
|
else
|
|
Check_Aspect_At_Freeze_Point (ASN);
|
|
end if;
|
|
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 in Aspect_Default_Component_Value | Aspect_Default_Value
|
|
and then Is_Private_Type (T)
|
|
then
|
|
Preanalyze_Spec_Expression (End_Decl_Expr, Full_View (T));
|
|
|
|
-- The following aspect expressions may contain references to
|
|
-- components and discriminants of the type.
|
|
|
|
elsif A_Id in Aspect_CPU
|
|
| Aspect_Dynamic_Predicate
|
|
| Aspect_Predicate
|
|
| Aspect_Priority
|
|
| Aspect_Static_Predicate
|
|
then
|
|
Push_Type (Ent);
|
|
Preanalyze_Spec_Expression (End_Decl_Expr, T);
|
|
Pop_Type (Ent);
|
|
|
|
elsif A_Id = Aspect_Predicate_Failure then
|
|
Preanalyze_Spec_Expression (End_Decl_Expr, Standard_String);
|
|
elsif Present (End_Decl_Expr) then
|
|
Preanalyze_Spec_Expression (End_Decl_Expr, T);
|
|
end if;
|
|
|
|
Err :=
|
|
not Fully_Conformant_Expressions
|
|
(End_Decl_Expr, Freeze_Expr, Report => True);
|
|
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, (RM 13.1.1 (13/3))??",
|
|
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)
|
|
|
|
Expr : constant Node_Id := Expression (ASN);
|
|
-- For cases where using Entity (Identifier) doesn't work
|
|
|
|
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));
|
|
|
|
when Aspect_Default_Storage_Pool =>
|
|
T := Class_Wide_Type (RTE (RE_Root_Storage_Pool));
|
|
|
|
-- Default_Value is resolved with the type entity in question
|
|
|
|
when Aspect_Default_Value =>
|
|
T := Entity (ASN);
|
|
|
|
when Aspect_Dispatching_Domain =>
|
|
T := RTE (RE_Dispatching_Domain);
|
|
|
|
when Aspect_External_Tag =>
|
|
T := Standard_String;
|
|
|
|
when Aspect_External_Name =>
|
|
T := Standard_String;
|
|
|
|
when Aspect_Link_Name =>
|
|
T := Standard_String;
|
|
|
|
when Aspect_Interrupt_Priority
|
|
| Aspect_Priority
|
|
=>
|
|
T := Standard_Integer;
|
|
|
|
when Aspect_Relative_Deadline =>
|
|
T := RTE (RE_Time_Span);
|
|
|
|
when Aspect_Secondary_Stack_Size =>
|
|
T := Standard_Integer;
|
|
|
|
when Aspect_Small =>
|
|
|
|
-- Note that the expression can be of any real type (not just a
|
|
-- real universal literal) as long as it is a static constant.
|
|
|
|
T := Any_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_Put_Image
|
|
| Aspect_Read
|
|
| 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;
|
|
|
|
-- Same for Literal aspects, where the expression is a function
|
|
-- name. Legality rules are checked separately. Use Expr to avoid
|
|
-- losing track of the previous resolution of Expression.
|
|
|
|
when Aspect_Integer_Literal
|
|
| Aspect_Real_Literal
|
|
| Aspect_String_Literal
|
|
=>
|
|
Set_Entity (Expression (ASN), Entity (Expr));
|
|
Set_Etype (Expression (ASN), Etype (Expr));
|
|
Set_Is_Overloaded (Expression (ASN), False);
|
|
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;
|
|
|
|
when Aspect_Aggregate =>
|
|
Resolve_Aspect_Aggregate (Entity (ASN), Expression (ASN));
|
|
return;
|
|
|
|
when Aspect_Stable_Properties =>
|
|
Resolve_Aspect_Stable_Properties
|
|
(Entity (ASN), Expression (ASN),
|
|
Class_Present => Class_Present (ASN));
|
|
return;
|
|
|
|
-- Invariant/Predicate take boolean expressions
|
|
|
|
when Aspect_Dynamic_Predicate
|
|
| Aspect_Invariant
|
|
| Aspect_Predicate
|
|
| Aspect_Static_Predicate
|
|
| Aspect_Type_Invariant
|
|
=>
|
|
T := Standard_Boolean;
|
|
|
|
when Aspect_Predicate_Failure =>
|
|
T := Standard_String;
|
|
|
|
-- Here is the list of aspects that don't require delay analysis
|
|
|
|
when Aspect_Designated_Storage_Model =>
|
|
return;
|
|
|
|
when Aspect_Storage_Model_Type =>
|
|
T := Entity (ASN);
|
|
|
|
declare
|
|
Assoc : Node_Id;
|
|
Expr : Node_Id;
|
|
Addr_Type : Entity_Id := Empty;
|
|
|
|
begin
|
|
Assoc := First (Component_Associations (Expression (ASN)));
|
|
while Present (Assoc) loop
|
|
Expr := Expression (Assoc);
|
|
Analyze (Expr);
|
|
|
|
if not Error_Posted (Expr) then
|
|
Resolve_Storage_Model_Type_Argument
|
|
(Expr, T, Addr_Type, Chars (First (Choices (Assoc))));
|
|
end if;
|
|
|
|
Next (Assoc);
|
|
end loop;
|
|
end;
|
|
|
|
return;
|
|
|
|
when Aspect_Abstract_State
|
|
| Aspect_Annotate
|
|
| Aspect_Async_Readers
|
|
| Aspect_Async_Writers
|
|
| Aspect_Constant_After_Elaboration
|
|
| Aspect_Contract_Cases
|
|
| Aspect_Default_Initial_Condition
|
|
| Aspect_Depends
|
|
| Aspect_Dimension
|
|
| Aspect_Dimension_System
|
|
| Aspect_Effective_Reads
|
|
| Aspect_Effective_Writes
|
|
| Aspect_Extensions_Visible
|
|
| Aspect_Ghost
|
|
| Aspect_Global
|
|
| Aspect_GNAT_Annotate
|
|
| Aspect_Implicit_Dereference
|
|
| Aspect_Initial_Condition
|
|
| Aspect_Initializes
|
|
| Aspect_Max_Entry_Queue_Depth
|
|
| Aspect_Max_Entry_Queue_Length
|
|
| Aspect_Max_Queue_Length
|
|
| Aspect_No_Caching
|
|
| Aspect_No_Controlled_Parts
|
|
| Aspect_No_Task_Parts
|
|
| Aspect_Obsolescent
|
|
| Aspect_Part_Of
|
|
| Aspect_Post
|
|
| Aspect_Postcondition
|
|
| Aspect_Pre
|
|
| Aspect_Precondition
|
|
| Aspect_Refined_Depends
|
|
| Aspect_Refined_Global
|
|
| Aspect_Refined_Post
|
|
| Aspect_Refined_State
|
|
| Aspect_Relaxed_Initialization
|
|
| Aspect_SPARK_Mode
|
|
| Aspect_Subprogram_Variant
|
|
| Aspect_Suppress
|
|
| Aspect_Test_Case
|
|
| Aspect_Unimplemented
|
|
| Aspect_Unsuppress
|
|
| Aspect_Volatile_Function
|
|
=>
|
|
raise Program_Error;
|
|
|
|
end case;
|
|
|
|
-- Do the preanalyze call
|
|
|
|
if Present (Expression (ASN)) then
|
|
Preanalyze_Spec_Expression (Expression (ASN), T);
|
|
end if;
|
|
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_Expanded_Name
|
|
| N_Identifier
|
|
=>
|
|
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 Is_Rewrite_Substitution (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 Is_Named_Number (Ent) or else Is_Type (Ent) then
|
|
return;
|
|
|
|
elsif Ekind (Ent) in E_Constant | 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_Character_Literal
|
|
| N_Real_Literal
|
|
| N_String_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 Attribute_Name (Nod) in Name_Address
|
|
| Name_Access
|
|
| Name_Unchecked_Access
|
|
| Name_Unrestricted_Access
|
|
then
|
|
Check_At_Constant_Address (Prefix (Nod));
|
|
|
|
-- Normally, System'To_Address will have been transformed into
|
|
-- an Unchecked_Conversion, but in -gnatc mode, it will not,
|
|
-- and we don't want to give an error, because the whole point
|
|
-- of 'To_Address is that it is static.
|
|
|
|
elsif Attribute_Name (Nod) = Name_To_Address then
|
|
pragma Assert (Operating_Mode = Check_Semantics);
|
|
null;
|
|
|
|
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_Membership_Test
|
|
| N_Short_Circuit
|
|
=>
|
|
Check_Expr_Constants (Left_Opnd (Nod));
|
|
Check_Expr_Constants (Right_Opnd (Nod));
|
|
|
|
when N_Unary_Op =>
|
|
Check_Expr_Constants (Right_Opnd (Nod));
|
|
|
|
when N_Allocator
|
|
| N_Qualified_Expression
|
|
| N_Type_Conversion
|
|
| 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. Similarly in CodePeer mode,
|
|
-- we want to relax these checks.
|
|
|
|
if not Ignore_Rep_Clauses and not CodePeer_Mode 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 := No_Uint;
|
|
Lbit : Uint := No_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 an extension for which we have either a
|
|
-- size clause or Is_Fully_Repped_Tagged_Type True (indicating that all
|
|
-- components are positioned by record representation clauses) on the
|
|
-- parent type. In this case we check for overlap between components of
|
|
-- this tagged type and the parent component. Tagged_Parent will point
|
|
-- to this parent type. For all other cases, Tagged_Parent is Empty.
|
|
|
|
Parent_Last_Bit : Uint := No_Uint; -- init to avoid warning
|
|
-- 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.
|
|
|
|
procedure Record_Hole_Check
|
|
(Rectype : Entity_Id; After_Last : out Uint; Warn : Boolean);
|
|
-- Checks for gaps in the given Rectype. Compute After_Last, the bit
|
|
-- number after the last component. Warn is True on the initial call,
|
|
-- and warnings are given for gaps. For a type extension, this is called
|
|
-- recursively to compute After_Last for the parent type; in this case
|
|
-- Warn is False and the warnings are suppressed.
|
|
|
|
procedure Component_Order_Check (Rectype : Entity_Id);
|
|
-- Check that the order of component clauses agrees with the order of
|
|
-- component declarations, and that the component clauses are given in
|
|
-- increasing order of bit offset.
|
|
|
|
-----------------------------
|
|
-- 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 Chars (C1_Ent) = 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;
|
|
|
|
---------------------------
|
|
-- Component_Order_Check --
|
|
---------------------------
|
|
|
|
procedure Component_Order_Check (Rectype : Entity_Id) is
|
|
Comp : Entity_Id := First_Component (Rectype);
|
|
Clause : Node_Id := First (Component_Clauses (N));
|
|
Prev_Bit_Offset : Uint := Uint_0;
|
|
OOO : constant String :=
|
|
"?_r?component clause out of order with respect to declaration";
|
|
|
|
begin
|
|
-- Step Comp through components and Clause through component clauses,
|
|
-- skipping pragmas. We ignore discriminants and variant parts,
|
|
-- because we get most of the benefit from the plain vanilla
|
|
-- component cases, without the extra complexity. If we find a Comp
|
|
-- and Clause that don't match, give a warning on both and quit. If
|
|
-- we find two subsequent clauses out of order by bit layout, give
|
|
-- warning and quit. On each iteration, Prev_Bit_Offset is the one
|
|
-- from the previous iteration (or 0 to start).
|
|
|
|
while Present (Comp) and then Present (Clause) loop
|
|
if Nkind (Clause) = N_Component_Clause
|
|
and then Ekind (Entity (Component_Name (Clause))) = E_Component
|
|
then
|
|
if Entity (Component_Name (Clause)) /= Comp then
|
|
Error_Msg_N (OOO, Comp);
|
|
Error_Msg_N (OOO, Clause);
|
|
exit;
|
|
end if;
|
|
|
|
if not Reverse_Bit_Order (Rectype)
|
|
and then not Reverse_Storage_Order (Rectype)
|
|
and then Component_Bit_Offset (Comp) < Prev_Bit_Offset
|
|
then
|
|
Error_Msg_N ("?_r?memory layout out of order", Clause);
|
|
exit;
|
|
end if;
|
|
|
|
Prev_Bit_Offset := Component_Bit_Offset (Comp);
|
|
Next_Component (Comp);
|
|
end if;
|
|
|
|
Next (Clause);
|
|
end loop;
|
|
end Component_Order_Check;
|
|
|
|
--------------------
|
|
-- 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;
|
|
|
|
-----------------------
|
|
-- Record_Hole_Check --
|
|
-----------------------
|
|
|
|
procedure Record_Hole_Check
|
|
(Rectype : Entity_Id; After_Last : out Uint; Warn : Boolean)
|
|
is
|
|
Decl : constant Node_Id := Declaration_Node (Base_Type (Rectype));
|
|
-- Full declaration of record type
|
|
|
|
procedure Check_Component_List
|
|
(DS : List_Id;
|
|
CL : Node_Id;
|
|
Sbit : Uint;
|
|
Abit : out Uint);
|
|
-- Check component list CL for holes. DS is a list of discriminant
|
|
-- specifications to be included in the consideration of components.
|
|
-- Sbit is the starting bit, which is zero if there are no preceding
|
|
-- components (before a variant part, or a parent type, or a tag
|
|
-- field). If there are preceding components, Sbit is the bit just
|
|
-- after the last such component. Abit is set to the bit just after
|
|
-- the last component of DS and CL.
|
|
|
|
--------------------------
|
|
-- Check_Component_List --
|
|
--------------------------
|
|
|
|
procedure Check_Component_List
|
|
(DS : List_Id;
|
|
CL : Node_Id;
|
|
Sbit : Uint;
|
|
Abit : out Uint)
|
|
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
|
|
K1 : constant Boolean :=
|
|
Known_Component_Bit_Offset (Comps (Op1));
|
|
K2 : constant Boolean :=
|
|
Known_Component_Bit_Offset (Comps (Op2));
|
|
-- Record representation clauses can be incomplete, so the
|
|
-- Component_Bit_Offsets can be unknown.
|
|
begin
|
|
if K1 then
|
|
if K2 then
|
|
return Component_Bit_Offset (Comps (Op1))
|
|
< Component_Bit_Offset (Comps (Op2));
|
|
else
|
|
return True;
|
|
end if;
|
|
else
|
|
return K2;
|
|
end if;
|
|
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);
|
|
pragma Annotate (CodePeer, Modified, CEnt);
|
|
|
|
declare
|
|
CBO : constant Uint := Component_Bit_Offset (CEnt);
|
|
|
|
begin
|
|
-- Skip components with unknown offsets
|
|
|
|
if Present (CBO) and then CBO >= 0 then
|
|
Error_Msg_Uint_1 := CBO - Nbit;
|
|
|
|
if Warn and then Error_Msg_Uint_1 > 0 then
|
|
Error_Msg_NE
|
|
("?.h?^-bit gap before component&",
|
|
Component_Name (Component_Clause (CEnt)),
|
|
CEnt);
|
|
end if;
|
|
|
|
Nbit := CBO + Esize (CEnt);
|
|
end if;
|
|
end;
|
|
end loop;
|
|
|
|
-- Set Abit to just after the last nonvariant component
|
|
|
|
Abit := Nbit;
|
|
|
|
-- Process variant parts recursively if present. Set Abit to
|
|
-- the maximum for all variant parts.
|
|
|
|
if Present (Variant_Part (CL)) then
|
|
declare
|
|
Var_Start : constant Uint := Nbit;
|
|
begin
|
|
Variant := First (Variants (Variant_Part (CL)));
|
|
while Present (Variant) loop
|
|
Check_Component_List
|
|
(No_List, Component_List (Variant), Var_Start, Nbit);
|
|
Next (Variant);
|
|
if Nbit > Abit then
|
|
Abit := Nbit;
|
|
end if;
|
|
end loop;
|
|
end;
|
|
end if;
|
|
end;
|
|
end Check_Component_List;
|
|
|
|
-- Local variables
|
|
|
|
Sbit : Uint;
|
|
-- Starting bit for call to Check_Component_List. Zero for an
|
|
-- untagged type. The size of the Tag for a nonderived tagged
|
|
-- type. Parent size for a type extension.
|
|
|
|
Record_Definition : Node_Id;
|
|
-- Record_Definition containing Component_List to pass to
|
|
-- Check_Component_List.
|
|
|
|
-- Start of processing for Record_Hole_Check
|
|
|
|
begin
|
|
if Is_Tagged_Type (Rectype) then
|
|
Sbit := UI_From_Int (System_Address_Size);
|
|
else
|
|
Sbit := Uint_0;
|
|
end if;
|
|
|
|
After_Last := Uint_0;
|
|
|
|
if Nkind (Decl) = N_Full_Type_Declaration then
|
|
Record_Definition := Type_Definition (Decl);
|
|
|
|
-- If we have a record extension, set Sbit to point after the last
|
|
-- component of the parent type, by calling Record_Hole_Check
|
|
-- recursively.
|
|
|
|
if Nkind (Record_Definition) = N_Derived_Type_Definition then
|
|
Record_Definition := Record_Extension_Part (Record_Definition);
|
|
Record_Hole_Check (Underlying_Type (Parent_Subtype (Rectype)),
|
|
After_Last => Sbit, Warn => False);
|
|
end if;
|
|
|
|
if Nkind (Record_Definition) = N_Record_Definition then
|
|
Check_Component_List
|
|
(Discriminant_Specifications (Decl),
|
|
Component_List (Record_Definition),
|
|
Sbit, After_Last);
|
|
end if;
|
|
end if;
|
|
end Record_Hole_Check;
|
|
|
|
-- Start of processing for Check_Record_Representation_Clause
|
|
|
|
begin
|
|
Find_Type (Ident);
|
|
Rectype := Entity (Ident);
|
|
|
|
if Rectype = Any_Type then
|
|
return;
|
|
end if;
|
|
|
|
Rectype := Underlying_Type (Rectype);
|
|
|
|
-- See if we have a fully repped derived tagged type
|
|
|
|
declare
|
|
PS : constant Entity_Id := Parent_Subtype (Rectype);
|
|
|
|
begin
|
|
if Present (PS) and then Known_Static_RM_Size (PS) then
|
|
Tagged_Parent := PS;
|
|
Parent_Last_Bit := RM_Size (PS) - 1;
|
|
|
|
elsif 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_Component_Or_Discriminant (Tagged_Parent);
|
|
while Present (Pcomp) loop
|
|
if Present (Component_Bit_Offset (Pcomp))
|
|
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_Component_Or_Discriminant (Pcomp);
|
|
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_Esize (Fent, UI_From_Int (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 - 1))));
|
|
|
|
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_Uint_1 := RM_Size (Rectype);
|
|
Error_Msg_Uint_2 := Lbit + 1;
|
|
Error_Msg_N ("bit number out of range of specified "
|
|
& "size (expected ^, got ^)",
|
|
Last_Bit (CC));
|
|
|
|
-- Check for overlap with tag or parent 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;
|
|
|
|
elsif Present (Tagged_Parent)
|
|
and then Fbit <= Parent_Last_Bit
|
|
then
|
|
Error_Msg_NE
|
|
("component overlaps parent field of&",
|
|
Component_Name (CC), Rectype);
|
|
Overlap_Detected := True;
|
|
end if;
|
|
|
|
if Hbit < Lbit then
|
|
Hbit := Lbit;
|
|
end if;
|
|
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 Ekind (C1_Ent) not in 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
|
|
-- but be careful not to flag a non-stored discriminant
|
|
-- and the stored discriminant it renames as overlapping.
|
|
|
|
if Nkind (Clist) in 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
|
|
Original_Record_Component (C1_Ent) =
|
|
Original_Record_Component (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;
|
|
|
|
-- Skip the following warnings if overlap was detected; programmer
|
|
-- should fix the errors first. Also skip the warnings for types in
|
|
-- generics, because their representation information is not fully
|
|
-- computed.
|
|
|
|
if not Overlap_Detected and then not In_Generic_Scope (Rectype) then
|
|
-- Check for record holes (gaps)
|
|
|
|
if Warn_On_Record_Holes then
|
|
declare
|
|
Ignore : Uint;
|
|
begin
|
|
Record_Hole_Check (Rectype, After_Last => Ignore, Warn => True);
|
|
end;
|
|
end if;
|
|
|
|
-- Check for out-of-order component clauses
|
|
|
|
if Warn_On_Component_Order then
|
|
Component_Order_Check (Rectype);
|
|
end if;
|
|
end if;
|
|
|
|
-- For records that have component clauses for all components, and whose
|
|
-- size is less than or equal to 32, and which can be fully packed, 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 not Known_RM_Size (Rectype)
|
|
and then Hbit + 1 <= 32
|
|
and then not Strict_Alignment (Rectype)
|
|
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
|
|
procedure Size_Too_Small_Error (Min_Siz : Uint);
|
|
-- Emit an error concerning illegal size Siz. Min_Siz denotes the
|
|
-- minimum size.
|
|
|
|
--------------------------
|
|
-- Size_Too_Small_Error --
|
|
--------------------------
|
|
|
|
procedure Size_Too_Small_Error (Min_Siz : Uint) is
|
|
begin
|
|
Error_Msg_Uint_1 := Min_Siz;
|
|
Error_Msg_NE (Size_Too_Small_Message, N, T);
|
|
end Size_Too_Small_Error;
|
|
|
|
-- Local variables
|
|
|
|
UT : constant Entity_Id := Underlying_Type (T);
|
|
M : Uint;
|
|
|
|
-- Start of processing for Check_Size
|
|
|
|
begin
|
|
Biased := False;
|
|
|
|
-- Reject patently improper size values
|
|
|
|
if Is_Elementary_Type (T)
|
|
and then Siz > 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
|
|
Size_Too_Small_Error (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
|
|
Size_Too_Small_Error (M);
|
|
else
|
|
Biased := True;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end Check_Size;
|
|
|
|
--------------------------
|
|
-- Freeze_Entity_Checks --
|
|
--------------------------
|
|
|
|
procedure Freeze_Entity_Checks (N : Node_Id) is
|
|
procedure Hide_Non_Overridden_Subprograms (Typ : Entity_Id);
|
|
-- Inspect the primitive operations of type Typ and hide all pairs of
|
|
-- implicitly declared non-overridden non-fully conformant homographs
|
|
-- (Ada RM 8.3 12.3/2).
|
|
|
|
-------------------------------------
|
|
-- Hide_Non_Overridden_Subprograms --
|
|
-------------------------------------
|
|
|
|
procedure Hide_Non_Overridden_Subprograms (Typ : Entity_Id) is
|
|
procedure Hide_Matching_Homographs
|
|
(Subp_Id : Entity_Id;
|
|
Start_Elmt : Elmt_Id);
|
|
-- Inspect a list of primitive operations starting with Start_Elmt
|
|
-- and find matching implicitly declared non-overridden non-fully
|
|
-- conformant homographs of Subp_Id. If found, all matches along
|
|
-- with Subp_Id are hidden from all visibility.
|
|
|
|
function Is_Non_Overridden_Or_Null_Procedure
|
|
(Subp_Id : Entity_Id) return Boolean;
|
|
-- Determine whether subprogram Subp_Id is implicitly declared non-
|
|
-- overridden subprogram or an implicitly declared null procedure.
|
|
|
|
------------------------------
|
|
-- Hide_Matching_Homographs --
|
|
------------------------------
|
|
|
|
procedure Hide_Matching_Homographs
|
|
(Subp_Id : Entity_Id;
|
|
Start_Elmt : Elmt_Id)
|
|
is
|
|
Prim : Entity_Id;
|
|
Prim_Elmt : Elmt_Id;
|
|
|
|
begin
|
|
Prim_Elmt := Start_Elmt;
|
|
while Present (Prim_Elmt) loop
|
|
Prim := Node (Prim_Elmt);
|
|
|
|
-- The current primitive is implicitly declared non-overridden
|
|
-- non-fully conformant homograph of Subp_Id. Both subprograms
|
|
-- must be hidden from visibility.
|
|
|
|
if Chars (Prim) = Chars (Subp_Id)
|
|
and then Is_Non_Overridden_Or_Null_Procedure (Prim)
|
|
and then not Fully_Conformant (Prim, Subp_Id)
|
|
then
|
|
Set_Is_Hidden_Non_Overridden_Subpgm (Prim);
|
|
Set_Is_Immediately_Visible (Prim, False);
|
|
Set_Is_Potentially_Use_Visible (Prim, False);
|
|
|
|
Set_Is_Hidden_Non_Overridden_Subpgm (Subp_Id);
|
|
Set_Is_Immediately_Visible (Subp_Id, False);
|
|
Set_Is_Potentially_Use_Visible (Subp_Id, False);
|
|
end if;
|
|
|
|
Next_Elmt (Prim_Elmt);
|
|
end loop;
|
|
end Hide_Matching_Homographs;
|
|
|
|
-----------------------------------------
|
|
-- Is_Non_Overridden_Or_Null_Procedure --
|
|
-----------------------------------------
|
|
|
|
function Is_Non_Overridden_Or_Null_Procedure
|
|
(Subp_Id : Entity_Id) return Boolean
|
|
is
|
|
Alias_Id : Entity_Id;
|
|
|
|
begin
|
|
-- The subprogram is inherited (implicitly declared), it does not
|
|
-- override and does not cover a primitive of an interface.
|
|
|
|
if Ekind (Subp_Id) in E_Function | E_Procedure
|
|
and then Present (Alias (Subp_Id))
|
|
and then No (Interface_Alias (Subp_Id))
|
|
and then No (Overridden_Operation (Subp_Id))
|
|
then
|
|
Alias_Id := Alias (Subp_Id);
|
|
|
|
if Requires_Overriding (Alias_Id) then
|
|
return True;
|
|
|
|
elsif Nkind (Parent (Alias_Id)) = N_Procedure_Specification
|
|
and then Null_Present (Parent (Alias_Id))
|
|
then
|
|
return True;
|
|
end if;
|
|
end if;
|
|
|
|
return False;
|
|
end Is_Non_Overridden_Or_Null_Procedure;
|
|
|
|
-- Local variables
|
|
|
|
Prim_Ops : constant Elist_Id := Direct_Primitive_Operations (Typ);
|
|
Prim : Entity_Id;
|
|
Prim_Elmt : Elmt_Id;
|
|
|
|
-- Start of processing for Hide_Non_Overridden_Subprograms
|
|
|
|
begin
|
|
-- Inspect the list of primitives looking for non-overridden
|
|
-- subprograms.
|
|
|
|
if Present (Prim_Ops) then
|
|
Prim_Elmt := First_Elmt (Prim_Ops);
|
|
while Present (Prim_Elmt) loop
|
|
Prim := Node (Prim_Elmt);
|
|
Next_Elmt (Prim_Elmt);
|
|
|
|
if Is_Non_Overridden_Or_Null_Procedure (Prim) then
|
|
Hide_Matching_Homographs
|
|
(Subp_Id => Prim,
|
|
Start_Elmt => Prim_Elmt);
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
end Hide_Non_Overridden_Subprograms;
|
|
|
|
-- Local variables
|
|
|
|
E : constant Entity_Id := Entity (N);
|
|
|
|
Nongeneric_Case : constant Boolean := Nkind (N) = N_Freeze_Entity;
|
|
-- True in nongeneric 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.
|
|
|
|
-- Start of processing for Freeze_Entity_Checks
|
|
|
|
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 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 Nongeneric_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;
|
|
|
|
-- After all forms of overriding have been resolved, a tagged type may
|
|
-- be left with a set of implicitly declared and possibly erroneous
|
|
-- abstract subprograms, null procedures and subprograms that require
|
|
-- overriding. If this set contains fully conformant homographs, then
|
|
-- one is chosen arbitrarily (already done during resolution), otherwise
|
|
-- all remaining non-fully conformant homographs are hidden from
|
|
-- visibility (Ada RM 8.3 12.3/2).
|
|
|
|
if Is_Tagged_Type (E) then
|
|
Hide_Non_Overridden_Subprograms (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 Present (Etype (E)) -- defend against errors
|
|
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, nor within e.g. TSS subprograms and
|
|
-- other predefined primitives. For a derived type, ensure that the
|
|
-- parent type is already frozen so that its predicate function has been
|
|
-- constructed already. This is necessary if the parent is declared
|
|
-- in a nested package and its own freeze point has not been reached.
|
|
|
|
if Is_Type (E)
|
|
and then Nongeneric_Case
|
|
and then Has_Predicates (E)
|
|
and then Predicate_Check_In_Scope (N)
|
|
then
|
|
declare
|
|
Atyp : constant Entity_Id := Nearest_Ancestor (E);
|
|
begin
|
|
if Present (Atyp)
|
|
and then Has_Predicates (Atyp)
|
|
and then not Is_Frozen (Atyp)
|
|
then
|
|
Freeze_Before (N, Atyp);
|
|
end if;
|
|
end;
|
|
|
|
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 Nongeneric_Case
|
|
and then Has_Delayed_Aspects (E)
|
|
and then Scope (E) = Current_Scope
|
|
then
|
|
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
|
|
if Get_Aspect_Id (Ritem) in Aspect_CPU
|
|
| Aspect_Dynamic_Predicate
|
|
| Aspect_Predicate
|
|
| Aspect_Static_Predicate
|
|
| Aspect_Priority
|
|
then
|
|
-- Retrieve the visibility to components and discriminants
|
|
-- in order to properly analyze the aspects.
|
|
|
|
Push_Type (E);
|
|
Check_Aspect_At_Freeze_Point (Ritem);
|
|
|
|
-- In the case of predicate aspects, there will be
|
|
-- a corresponding Predicate pragma associated with
|
|
-- the aspect, and the expression of the pragma also
|
|
-- needs to be analyzed at this point, to ensure that
|
|
-- Save_Global_References will capture global refs in
|
|
-- expressions that occur in generic bodies, for proper
|
|
-- later resolution of the pragma in instantiations.
|
|
|
|
if Is_Type (E)
|
|
and then Inside_A_Generic
|
|
and then Has_Predicates (E)
|
|
and then Present (Aspect_Rep_Item (Ritem))
|
|
then
|
|
declare
|
|
Pragma_Args : constant List_Id :=
|
|
Pragma_Argument_Associations
|
|
(Aspect_Rep_Item (Ritem));
|
|
Pragma_Expr : constant Node_Id :=
|
|
Expression (Next (First (Pragma_Args)));
|
|
begin
|
|
if Present (Pragma_Expr) then
|
|
Analyze_And_Resolve
|
|
(Pragma_Expr, Standard_Boolean);
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
Pop_Type (E);
|
|
|
|
else
|
|
Check_Aspect_At_Freeze_Point (Ritem);
|
|
end if;
|
|
|
|
-- A pragma Predicate should be checked like one of the
|
|
-- corresponding aspects, wrt possible misuse of ghost
|
|
-- entities.
|
|
|
|
elsif Nkind (Ritem) = N_Pragma
|
|
and then No (Corresponding_Aspect (Ritem))
|
|
and then
|
|
Get_Pragma_Id (Pragma_Name (Ritem)) = Pragma_Predicate
|
|
then
|
|
-- Retrieve the visibility to components and discriminants
|
|
-- in order to properly analyze the pragma.
|
|
|
|
declare
|
|
Arg : constant Node_Id :=
|
|
Next (First (Pragma_Argument_Associations (Ritem)));
|
|
begin
|
|
Push_Type (E);
|
|
Preanalyze_Spec_Expression
|
|
(Expression (Arg), Standard_Boolean);
|
|
Pop_Type (E);
|
|
end;
|
|
end if;
|
|
|
|
Next_Rep_Item (Ritem);
|
|
end loop;
|
|
end;
|
|
|
|
end if;
|
|
|
|
-- For a record type, deal with variant parts. This has to be delayed to
|
|
-- this point, because of the issue of statically predicated 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 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 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;
|
|
|
|
if not In_Generic_Scope (E)
|
|
and then Ekind (E) = E_Record_Type
|
|
and then Is_Tagged_Type (E)
|
|
then
|
|
Process_Class_Conditions_At_Freeze_Point (E);
|
|
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 No (Align) then
|
|
return No_Uint;
|
|
|
|
elsif Align < 0 then
|
|
Error_Msg_N ("alignment value must be positive", Expr);
|
|
return No_Uint;
|
|
|
|
-- If Alignment is specified to be 0, we treat it the same as 1
|
|
|
|
elsif Align = 0 then
|
|
return Uint_1;
|
|
|
|
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;
|
|
|
|
-----------------------------------
|
|
-- Has_Compatible_Representation --
|
|
-----------------------------------
|
|
|
|
function Has_Compatible_Representation
|
|
(Target_Type, Operand_Type : Entity_Id) return Boolean
|
|
is
|
|
T1 : constant Entity_Id := Underlying_Type (Target_Type);
|
|
T2 : constant Entity_Id := Underlying_Type (Operand_Type);
|
|
|
|
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;
|
|
|
|
-- If T2 is a generic actual it is declared as a subtype, so
|
|
-- check against its base type.
|
|
|
|
elsif Is_Generic_Actual_Type (T1)
|
|
and then Has_Compatible_Representation (Base_Type (T1), T2)
|
|
then
|
|
return True;
|
|
end if;
|
|
|
|
-- Tagged types always have the same representation, because it is not
|
|
-- possible to specify different representations for common fields.
|
|
|
|
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
|
|
|
|
-- In a view conversion, if the target type is an array type having
|
|
-- aliased components and the operand type is an array type having
|
|
-- unaliased components, then a new object is created (4.6(58.3/4)).
|
|
|
|
if Has_Aliased_Components (T1)
|
|
and then not Has_Aliased_Components (T2)
|
|
then
|
|
return False;
|
|
end if;
|
|
|
|
if Known_Component_Size (T1)
|
|
and then Known_Component_Size (T2)
|
|
and then Component_Size (T1) = Component_Size (T2)
|
|
then
|
|
return True;
|
|
end if;
|
|
end if;
|
|
|
|
-- For records, representations are different if reorderings differ
|
|
|
|
if Is_Record_Type (T1)
|
|
and then Is_Record_Type (T2)
|
|
and then No_Reordering (T1) /= No_Reordering (T2)
|
|
then
|
|
return False;
|
|
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);
|
|
|
|
-- 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 Has_Compatible_Representation;
|
|
|
|
-------------------------------------
|
|
-- 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 corresponding 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.
|
|
|
|
-- In addition, Convention must be propagated from base type to subtype,
|
|
-- because the subtype may have been declared on an incomplete view.
|
|
|
|
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;
|
|
|
|
-- Ada_2022
|
|
|
|
if not Has_Rep_Item (Typ, Name_Ada_2022, False)
|
|
and then Has_Rep_Item (Typ, Name_Ada_2022)
|
|
and then Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Get_Rep_Item (Typ, Name_Ada_2022))
|
|
then
|
|
Set_Is_Ada_2022_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_Is_Volatile (Typ);
|
|
Set_Treat_As_Volatile (Typ);
|
|
end if;
|
|
|
|
-- Convention
|
|
|
|
if Is_Record_Type (Typ)
|
|
and then Typ /= Base_Type (Typ) and then Is_Frozen (Base_Type (Typ))
|
|
then
|
|
Set_Convention (Typ, Convention (Base_Type (Typ)));
|
|
end if;
|
|
|
|
-- Default_Component_Value
|
|
|
|
-- Verify that there is no rep_item declared for the type, and there
|
|
-- is one coming from an ancestor.
|
|
|
|
if Is_Array_Type (Typ)
|
|
and then Is_Base_Type (Typ)
|
|
and then not 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 not Has_Rep_Item (Typ, Name_Default_Value, False)
|
|
and then Has_Rep_Item (Typ, Name_Default_Value)
|
|
then
|
|
Set_Has_Default_Aspect (Typ);
|
|
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;
|
|
|
|
-- 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_Is_Volatile (Typ);
|
|
Set_Treat_As_Volatile (Typ);
|
|
end if;
|
|
|
|
-- Volatile_Full_Access and Full_Access_Only
|
|
|
|
if not Has_Rep_Item (Typ, Name_Volatile_Full_Access, False)
|
|
and then not Has_Rep_Item (Typ, Name_Full_Access_Only, False)
|
|
and then (Has_Rep_Item (Typ, Name_Volatile_Full_Access)
|
|
or else Has_Rep_Item (Typ, Name_Full_Access_Only))
|
|
and then Is_Pragma_Or_Corr_Pragma_Present_In_Rep_Item
|
|
(Get_Rep_Item (Typ, Name_Volatile_Full_Access))
|
|
then
|
|
Set_Is_Volatile_Full_Access (Typ);
|
|
Set_Is_Volatile (Typ);
|
|
Set_Treat_As_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;
|
|
|
|
-- Bit_Order
|
|
|
|
if Is_Record_Type (Typ) and then Typ = Bas_Typ then
|
|
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
|
|
(Implementation_Base_Type (Etype (Bas_Typ))));
|
|
end if;
|
|
end if;
|
|
|
|
-- Scalar_Storage_Order
|
|
|
|
if (Is_Record_Type (Typ) or else Is_Array_Type (Typ))
|
|
and then Typ = Bas_Typ
|
|
then
|
|
-- For a type extension, always inherit from parent; otherwise
|
|
-- inherit if no default applies. Note: we do not check for
|
|
-- an explicit rep item on the parent type when inheriting,
|
|
-- because the parent SSO may itself have been set by default.
|
|
|
|
if not Has_Rep_Item (First_Subtype (Typ),
|
|
Name_Scalar_Storage_Order, False)
|
|
and then (Is_Tagged_Type (Bas_Typ)
|
|
or else not (SSO_Set_Low_By_Default (Bas_Typ)
|
|
or else
|
|
SSO_Set_High_By_Default (Bas_Typ)))
|
|
then
|
|
Set_Reverse_Storage_Order (Bas_Typ,
|
|
Reverse_Storage_Order
|
|
(Implementation_Base_Type (Etype (Bas_Typ))));
|
|
|
|
-- 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;
|
|
Unchecked_Conversions.Init;
|
|
|
|
-- The following might be needed in the future for some non-GCC back
|
|
-- ends:
|
|
-- if AAMP_On_Target then
|
|
-- Independence_Checks.Init;
|
|
-- end if;
|
|
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
|
|
-- List of operational items is given in AARM 13.1(8.mm/1). It is
|
|
-- clearly incomplete, as it does not include iterator aspects, among
|
|
-- others.
|
|
|
|
return Nkind (N) = N_Attribute_Definition_Clause
|
|
and then
|
|
Get_Attribute_Id (Chars (N)) in Attribute_Constant_Indexing
|
|
| Attribute_External_Tag
|
|
| Attribute_Default_Iterator
|
|
| Attribute_Implicit_Dereference
|
|
| Attribute_Input
|
|
| Attribute_Iterable
|
|
| Attribute_Iterator_Element
|
|
| Attribute_Output
|
|
| Attribute_Put_Image
|
|
| Attribute_Read
|
|
| Attribute_Variable_Indexing
|
|
| Attribute_Write;
|
|
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 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.
|
|
--
|
|
-- The routine also returns True for function calls generated during the
|
|
-- expansion of comparison operators on strings, which are intended to
|
|
-- be legal in static predicates, and are converted into calls to array
|
|
-- comparison routines in the body of the corresponding predicate
|
|
-- function.
|
|
|
|
----------------------------------
|
|
-- 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;
|
|
|
|
-----------------
|
|
-- 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)
|
|
or else Nkind (N) = N_Function_Call;
|
|
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 All_Membership_Choices_Static (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).
|
|
|
|
-- In fact, it appears that the intent of the ARG is to extend static
|
|
-- predicates to strings, and that the extension should probably apply
|
|
-- to static expressions themselves. The code below accepts comparison
|
|
-- operators that apply to static strings.
|
|
|
|
elsif Nkind (Expr) in N_Op_Compare
|
|
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 (Expr) in 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:
|
|
|
|
-- xxPredicate (typ (Inns)) and then expression
|
|
|
|
-- 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. Note that if the parent predicate is dynamic then
|
|
-- eventually this type will be marked as dynamic, but you are allowed
|
|
-- to specify a static predicate for a subtype which is inheriting a
|
|
-- dynamic predicate, so the static predicate validation here ignores
|
|
-- the inherited predicate even if it is dynamic.
|
|
-- In all cases, a static predicate can only apply to a scalar type.
|
|
|
|
elsif Nkind (Expr) = N_Function_Call
|
|
and then Is_Predicate_Function (Entity (Name (Expr)))
|
|
and then Is_Scalar_Type (Etype (First_Entity (Entity (Name (Expr)))))
|
|
then
|
|
return True;
|
|
|
|
elsif Is_Entity_Name (Expr)
|
|
and then Entity (Expr) = Standard_True
|
|
then
|
|
Error_Msg_N ("predicate is redundant (always True)?", Expr);
|
|
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;
|
|
|
|
----------------------
|
|
-- Is_Static_Choice --
|
|
----------------------
|
|
|
|
function Is_Static_Choice (N : Node_Id) return Boolean is
|
|
begin
|
|
return Nkind (N) = N_Others_Choice
|
|
or else 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_Related_Rep_Item --
|
|
------------------------------
|
|
|
|
function Is_Type_Related_Rep_Item (N : Node_Id) return Boolean is
|
|
begin
|
|
case Nkind (N) is
|
|
when N_Attribute_Definition_Clause =>
|
|
-- See AARM 13.1(8.f-8.x) list items that end in "clause"
|
|
-- ???: include any GNAT-defined attributes here?
|
|
return Get_Attribute_Id (Chars (N)) in Attribute_Bit_Order
|
|
| Attribute_Component_Size
|
|
| Attribute_Machine_Radix
|
|
| Attribute_Storage_Pool
|
|
| Attribute_Stream_Size;
|
|
|
|
when N_Pragma =>
|
|
case Get_Pragma_Id (N) is
|
|
-- See AARM 13.1(8.f-8.x) list items that start with "pragma"
|
|
-- ???: include any GNAT-defined pragmas here?
|
|
when Pragma_Pack
|
|
| Pragma_Import
|
|
| Pragma_Export
|
|
| Pragma_Convention
|
|
| Pragma_Atomic
|
|
| Pragma_Independent
|
|
| Pragma_Volatile
|
|
| Pragma_Atomic_Components
|
|
| Pragma_Independent_Components
|
|
| Pragma_Volatile_Components
|
|
| Pragma_Discard_Names
|
|
=>
|
|
return True;
|
|
when others =>
|
|
null;
|
|
end case;
|
|
|
|
when N_Enumeration_Representation_Clause
|
|
| N_Record_Representation_Clause
|
|
=>
|
|
return True;
|
|
|
|
when others =>
|
|
null;
|
|
end case;
|
|
|
|
return False;
|
|
end Is_Type_Related_Rep_Item;
|
|
|
|
---------------------
|
|
-- 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
|
|
-- tools 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 tools ignore 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 Int
|
|
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
|
|
-- Bad type
|
|
|
|
if T = Any_Type then
|
|
return Unknown_Minimum_Size;
|
|
|
|
-- For generic types, just return unknown. 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 Unknown_Minimum_Size;
|
|
|
|
-- Access types (cannot have size smaller than System.Address)
|
|
|
|
elsif Is_Access_Type (T) then
|
|
return System_Address_Size;
|
|
|
|
-- 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 Unknown_Minimum_Size;
|
|
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 Unknown_Minimum_Size;
|
|
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 Unknown_Minimum_Size;
|
|
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 Unknown_Minimum_Size;
|
|
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;
|
|
|
|
-- Null range case, size is always zero. We only do this in the discrete
|
|
-- type case, since that's the odd case that came up. Probably we should
|
|
-- also do this in the fixed-point case, but doing so causes peculiar
|
|
-- gigi failures, and it is not worth worrying about this incredibly
|
|
-- marginal case (explicit null-range fixed-point type declarations).
|
|
|
|
if Lo > Hi and then Is_Discrete_Type (T) then
|
|
S := 0;
|
|
|
|
-- 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.
|
|
|
|
elsif 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_Put_Image_Subprogram --
|
|
------------------------------
|
|
|
|
procedure New_Put_Image_Subprogram
|
|
(N : Node_Id;
|
|
Ent : Entity_Id;
|
|
Subp : Entity_Id)
|
|
is
|
|
Loc : constant Source_Ptr := Sloc (N);
|
|
Sname : constant Name_Id :=
|
|
Make_TSS_Name (Base_Type (Ent), TSS_Put_Image);
|
|
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 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. If the attribute definition comes from an aspect
|
|
-- specification the declaration is part of the freeze actions of the
|
|
-- type.
|
|
|
|
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
|
|
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 : Root_Buffer_Type'Class
|
|
|
|
Formals := New_List (
|
|
Make_Parameter_Specification (Loc,
|
|
Defining_Identifier =>
|
|
Make_Defining_Identifier (Loc, Name_S),
|
|
In_Present => True,
|
|
Out_Present => True,
|
|
Parameter_Type =>
|
|
New_Occurrence_Of (Etype (F), Loc)));
|
|
|
|
-- V : T
|
|
|
|
Append_To (Formals,
|
|
Make_Parameter_Specification (Loc,
|
|
Defining_Identifier => Make_Defining_Identifier (Loc, Name_V),
|
|
Parameter_Type => T_Ref));
|
|
|
|
Spec :=
|
|
Make_Procedure_Specification (Loc,
|
|
Defining_Unit_Name => Subp_Id,
|
|
Parameter_Specifications => Formals);
|
|
|
|
return Spec;
|
|
end Build_Spec;
|
|
|
|
-- Start of processing for New_Put_Image_Subprogram
|
|
|
|
begin
|
|
F := First_Formal (Subp);
|
|
|
|
Etyp := Etype (Next_Formal (F));
|
|
|
|
-- 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 the
|
|
-- Put_Image 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;
|
|
|
|
if not Defer_Declaration
|
|
and then From_Aspect_Specification (N)
|
|
and then Has_Delayed_Freeze (Ent)
|
|
then
|
|
Append_Freeze_Action (Ent, Subp_Decl);
|
|
|
|
else
|
|
Insert_Action (N, Subp_Decl);
|
|
Set_Entity (N, Subp_Id);
|
|
end if;
|
|
|
|
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
|
|
if From_Aspect_Specification (N) then
|
|
Append_Freeze_Action (Ent, Subp_Decl);
|
|
else
|
|
Insert_Action (N, Subp_Decl);
|
|
end if;
|
|
|
|
Copy_TSS (Subp_Id, Base_Type (Ent));
|
|
end if;
|
|
end New_Put_Image_Subprogram;
|
|
|
|
---------------------------
|
|
-- 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. If the attribute definition comes
|
|
-- from an aspect specification the declaration is part of the freeze
|
|
-- actions of the type.
|
|
|
|
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;
|
|
|
|
if not Defer_Declaration
|
|
and then From_Aspect_Specification (N)
|
|
and then Has_Delayed_Freeze (Ent)
|
|
then
|
|
Append_Freeze_Action (Ent, Subp_Decl);
|
|
|
|
else
|
|
Insert_Action (N, Subp_Decl);
|
|
Set_Entity (N, Subp_Id);
|
|
end if;
|
|
|
|
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
|
|
if From_Aspect_Specification (N) then
|
|
Append_Freeze_Action (Ent, Subp_Decl);
|
|
else
|
|
Insert_Action (N, Subp_Decl);
|
|
end if;
|
|
|
|
Copy_TSS (Subp_Id, Base_Type (Ent));
|
|
end if;
|
|
end New_Stream_Subprogram;
|
|
|
|
----------------------
|
|
-- No_Type_Rep_Item --
|
|
----------------------
|
|
|
|
procedure No_Type_Rep_Item (N : Node_Id) is
|
|
begin
|
|
Error_Msg_N ("|type-related representation item not permitted!", N);
|
|
end No_Type_Rep_Item;
|
|
|
|
--------------
|
|
-- Pop_Type --
|
|
--------------
|
|
|
|
procedure Pop_Type (E : Entity_Id) is
|
|
begin
|
|
if Ekind (E) = E_Record_Type and then E = Current_Scope then
|
|
End_Scope;
|
|
|
|
elsif Is_Type (E)
|
|
and then Has_Discriminants (E)
|
|
and then Nkind (Parent (E)) /= N_Subtype_Declaration
|
|
then
|
|
Uninstall_Discriminants (E);
|
|
Pop_Scope;
|
|
end if;
|
|
end Pop_Type;
|
|
|
|
---------------
|
|
-- Push_Type --
|
|
---------------
|
|
|
|
procedure Push_Type (E : Entity_Id) is
|
|
Comp : Entity_Id;
|
|
|
|
begin
|
|
if Ekind (E) = E_Record_Type then
|
|
Push_Scope (E);
|
|
|
|
Comp := First_Component (E);
|
|
while Present (Comp) loop
|
|
Install_Entity (Comp);
|
|
Next_Component (Comp);
|
|
end loop;
|
|
|
|
if Has_Discriminants (E) then
|
|
Install_Discriminants (E);
|
|
end if;
|
|
|
|
elsif Is_Type (E)
|
|
and then Has_Discriminants (E)
|
|
and then Nkind (Parent (E)) /= N_Subtype_Declaration
|
|
then
|
|
Push_Scope (E);
|
|
Install_Discriminants (E);
|
|
end if;
|
|
end Push_Type;
|
|
|
|
-----------------------------------
|
|
-- Register_Address_Clause_Check --
|
|
-----------------------------------
|
|
|
|
procedure Register_Address_Clause_Check
|
|
(N : Node_Id;
|
|
X : Entity_Id;
|
|
A : Uint;
|
|
Y : Entity_Id;
|
|
Off : Boolean)
|
|
is
|
|
ACS : constant Boolean := Scope_Suppress.Suppress (Alignment_Check);
|
|
begin
|
|
Address_Clause_Checks.Append ((N, X, A, Y, Off, ACS));
|
|
end Register_Address_Clause_Check;
|
|
|
|
------------------------
|
|
-- Rep_Item_Too_Early --
|
|
------------------------
|
|
|
|
function Rep_Item_Too_Early (T : Entity_Id; N : Node_Id) return Boolean is
|
|
function Has_Generic_Parent (E : Entity_Id) return Boolean;
|
|
-- Return True if R or any ancestor is a generic type
|
|
|
|
------------------------
|
|
-- Has_Generic_Parent --
|
|
------------------------
|
|
|
|
function Has_Generic_Parent (E : Entity_Id) return Boolean is
|
|
Ancestor_Type : Entity_Id := Etype (E);
|
|
|
|
begin
|
|
if Is_Generic_Type (E) then
|
|
return True;
|
|
end if;
|
|
|
|
while Present (Ancestor_Type)
|
|
and then not Is_Generic_Type (Ancestor_Type)
|
|
and then Etype (Ancestor_Type) /= Ancestor_Type
|
|
loop
|
|
Ancestor_Type := Etype (Ancestor_Type);
|
|
end loop;
|
|
|
|
return
|
|
Present (Ancestor_Type) and then Is_Generic_Type (Ancestor_Type);
|
|
end Has_Generic_Parent;
|
|
|
|
-- Start of processing for Rep_Item_Too_Early
|
|
|
|
begin
|
|
-- Cannot apply non-operational rep items to generic types
|
|
|
|
if Is_Operational_Item (N) then
|
|
return False;
|
|
|
|
elsif Is_Type (T)
|
|
and then Has_Generic_Parent (T)
|
|
and then (Nkind (N) /= N_Pragma
|
|
or else Get_Pragma_Id (N) /= Pragma_Convention)
|
|
then
|
|
if Ada_Version < Ada_2022 then
|
|
Error_Msg_N
|
|
("representation item not allowed for generic type", N);
|
|
return True;
|
|
else
|
|
return False;
|
|
end if;
|
|
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
|
|
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???
|
|
|
|
--------------
|
|
-- 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;
|
|
|
|
-- Local variables
|
|
|
|
Parent_Type : Entity_Id;
|
|
S : Entity_Id;
|
|
|
|
-- 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
|
|
-- A self-referential aspect is illegal if it forces freezing the
|
|
-- entity before the corresponding pragma has been analyzed.
|
|
|
|
if Nkind (N) in N_Attribute_Definition_Clause | N_Pragma
|
|
and then From_Aspect_Specification (N)
|
|
then
|
|
Error_Msg_NE
|
|
("aspect specification causes premature freezing of&", N, T);
|
|
Set_Has_Delayed_Freeze (T, False);
|
|
return True;
|
|
end if;
|
|
|
|
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 untagged derived type whose parent either has
|
|
-- primitive operations (pre Ada 2022), 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.
|
|
-- ??? Confirming representation clauses should be allowed here.
|
|
|
|
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 Relaxed_RM_Semantics then
|
|
null;
|
|
|
|
elsif Ada_Version <= Ada_2012
|
|
and then Has_Primitive_Operations (Parent_Type)
|
|
then
|
|
Error_Msg_N
|
|
("|representation item not permitted before Ada 2022!", N);
|
|
Error_Msg_NE
|
|
("\parent type & has primitive operations!", N, Parent_Type);
|
|
return True;
|
|
|
|
elsif Is_By_Reference_Type (Parent_Type) then
|
|
No_Type_Rep_Item (N);
|
|
Error_Msg_NE
|
|
("\parent type & is a by-reference type!", N, Parent_Type);
|
|
return True;
|
|
end if;
|
|
end if;
|
|
|
|
-- No error, but one more warning to consider. The RM (surprisingly)
|
|
-- allows this pattern in some cases:
|
|
|
|
-- 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.
|
|
-- So even when this is not an error, it is still 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)
|
|
|
|
-- 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. However, AI12-0109 confirms (in an AARM
|
|
-- ramification) that inheritance in this case is required
|
|
-- to work.
|
|
|
|
and then not From_Aspect_Specification (N)
|
|
then
|
|
if Is_By_Reference_Type (T)
|
|
and then not Is_Tagged_Type (T)
|
|
and then Is_Type_Related_Rep_Item (N)
|
|
and then (Ada_Version >= Ada_2012
|
|
or else Has_Primitive_Operations (Base_Type (T)))
|
|
then
|
|
-- Treat as hard error (AI12-0109, binding interpretation).
|
|
-- Implementing a change of representation is not really
|
|
-- an option in the case of a by-reference type, so we
|
|
-- take this path for all Ada dialects if primitive
|
|
-- operations are present.
|
|
Error_Msg_Sloc := Sloc (DTL);
|
|
Error_Msg_N
|
|
("representation item for& appears after derived type "
|
|
& "declaration#", N);
|
|
|
|
elsif Has_Primitive_Operations (Base_Type (T)) 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 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 Pname in 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_Type_Ref (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.
|
|
|
|
function Visible_Component (Comp : Name_Id) return Entity_Id;
|
|
-- Given an identifier in the expression, check whether there is a
|
|
-- discriminant, component, protected procedure, or entry of the type
|
|
-- that is directy visible, and rewrite it as the corresponding selected
|
|
-- component of the formal of the subprogram.
|
|
|
|
----------------------
|
|
-- Replace_Type_Ref --
|
|
----------------------
|
|
|
|
function Replace_Type_Ref (N : Node_Id) return Traverse_Result is
|
|
Loc : constant Source_Ptr := Sloc (N);
|
|
|
|
procedure Add_Prefix (Ref : Node_Id; Comp : Entity_Id);
|
|
-- Add the proper prefix to a reference to a component of the type
|
|
-- when it is not already a selected component.
|
|
|
|
----------------
|
|
-- Add_Prefix --
|
|
----------------
|
|
|
|
procedure Add_Prefix (Ref : Node_Id; Comp : Entity_Id) is
|
|
begin
|
|
Rewrite (Ref,
|
|
Make_Selected_Component (Loc,
|
|
Prefix => New_Occurrence_Of (T, Loc),
|
|
Selector_Name => New_Occurrence_Of (Comp, Loc)));
|
|
Replace_Type_Reference (Prefix (Ref));
|
|
end Add_Prefix;
|
|
|
|
-- Local variables
|
|
|
|
Comp : Entity_Id;
|
|
Pref : Node_Id;
|
|
Scop : Entity_Id;
|
|
|
|
-- Start of processing for Replace_Type_Ref
|
|
|
|
begin
|
|
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;
|
|
|
|
-- The components of the type are directly visible and can
|
|
-- be referenced without a prefix.
|
|
|
|
if Nkind (Parent (N)) = N_Selected_Component then
|
|
null;
|
|
|
|
-- In expression C (I), C may be a directly visible function
|
|
-- or a visible component that has an array type. Disambiguate
|
|
-- by examining the component type.
|
|
|
|
elsif Nkind (Parent (N)) = N_Indexed_Component
|
|
and then N = Prefix (Parent (N))
|
|
then
|
|
Comp := Visible_Component (Chars (N));
|
|
|
|
if Present (Comp) and then Is_Array_Type (Etype (Comp)) then
|
|
Add_Prefix (N, Comp);
|
|
end if;
|
|
|
|
else
|
|
Comp := Visible_Component (Chars (N));
|
|
|
|
if Present (Comp) then
|
|
Add_Prefix (N, Comp);
|
|
end if;
|
|
end if;
|
|
|
|
return Skip;
|
|
|
|
-- Otherwise do the replacement if this is not a qualified
|
|
-- reference to a homograph of the type itself. Note that the
|
|
-- current instance could not appear in such a context, e.g.
|
|
-- the prefix of a type conversion.
|
|
|
|
else
|
|
if Nkind (Parent (N)) /= N_Selected_Component
|
|
or else N /= Selector_Name (Parent (N))
|
|
then
|
|
Replace_Type_Reference (N);
|
|
end if;
|
|
|
|
return Skip;
|
|
end if;
|
|
|
|
-- Case of selected component, which may be a subcomponent of the
|
|
-- current instance, or an expanded name which is still unanalyzed.
|
|
|
|
elsif Nkind (N) = N_Selected_Component then
|
|
|
|
-- If selector name is not our type, keep going (we might still
|
|
-- have an occurrence of the type in the prefix). If it is a
|
|
-- subcomponent of the current entity, add prefix.
|
|
|
|
if Nkind (Selector_Name (N)) /= N_Identifier
|
|
or else Chars (Selector_Name (N)) /= TName
|
|
then
|
|
if Nkind (Prefix (N)) = N_Identifier then
|
|
Comp := Visible_Component (Chars (Prefix (N)));
|
|
|
|
if Present (Comp) then
|
|
Add_Prefix (Prefix (N), Comp);
|
|
end if;
|
|
end if;
|
|
|
|
return OK;
|
|
|
|
-- Selector name is our type, check qualification
|
|
|
|
else
|
|
-- Loop through scopes and prefixes, doing comparison
|
|
|
|
Scop := Current_Scope;
|
|
Pref := Prefix (N);
|
|
loop
|
|
-- Continue if no more scopes or scope with no name
|
|
|
|
if No (Scop) or else Nkind (Scop) 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 (Pref) = N_Identifier
|
|
and then Chars (Pref) = Chars (Scop)
|
|
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 (Pref) = N_Selected_Component
|
|
and then Nkind (Selector_Name (Pref)) = N_Identifier
|
|
and then Chars (Selector_Name (Pref)) = Chars (Scop)
|
|
then
|
|
Scop := Scope (Scop);
|
|
Pref := Prefix (Pref);
|
|
|
|
-- 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_Type_Ref;
|
|
|
|
procedure Replace_Type_Refs is new Traverse_Proc (Replace_Type_Ref);
|
|
|
|
-----------------------
|
|
-- Visible_Component --
|
|
-----------------------
|
|
|
|
function Visible_Component (Comp : Name_Id) return Entity_Id is
|
|
E : Entity_Id;
|
|
begin
|
|
-- Types with nameable components are record, task, and protected
|
|
-- types, and discriminated private types.
|
|
|
|
if Ekind (T) in E_Record_Type
|
|
| E_Task_Type
|
|
| E_Protected_Type
|
|
or else (Is_Private_Type (T) and then Has_Discriminants (T))
|
|
then
|
|
-- This is a sequential search, which seems acceptable
|
|
-- efficiency-wise, given the typical size of component
|
|
-- lists, protected operation lists, task item lists, and
|
|
-- check expressions.
|
|
|
|
E := First_Entity (T);
|
|
while Present (E) loop
|
|
if Comes_From_Source (E) and then Chars (E) = Comp then
|
|
return E;
|
|
end if;
|
|
|
|
Next_Entity (E);
|
|
end loop;
|
|
end if;
|
|
|
|
-- Nothing by that name
|
|
|
|
return Empty;
|
|
end Visible_Component;
|
|
|
|
-- Start of processing for Replace_Type_References_Generic
|
|
|
|
begin
|
|
Replace_Type_Refs (N);
|
|
end Replace_Type_References_Generic;
|
|
|
|
--------------------------------
|
|
-- Resolve_Aspect_Expressions --
|
|
--------------------------------
|
|
|
|
procedure Resolve_Aspect_Expressions (E : Entity_Id) is
|
|
function Resolve_Name (N : Node_Id) return Traverse_Result;
|
|
-- Verify that all identifiers in the expression, with the exception
|
|
-- of references to the current entity, denote visible entities. This
|
|
-- is done only to detect visibility errors, as the expression will be
|
|
-- properly analyzed/expanded during analysis of the predicate function
|
|
-- body. We omit quantified expressions from this test, given that they
|
|
-- introduce a local identifier that would require proper expansion to
|
|
-- handle properly.
|
|
|
|
------------------
|
|
-- Resolve_Name --
|
|
------------------
|
|
|
|
function Resolve_Name (N : Node_Id) return Traverse_Result is
|
|
Dummy : Traverse_Result;
|
|
|
|
begin
|
|
if Nkind (N) = N_Selected_Component then
|
|
if Nkind (Prefix (N)) = N_Identifier
|
|
and then Chars (Prefix (N)) /= Chars (E)
|
|
then
|
|
Find_Selected_Component (N);
|
|
end if;
|
|
|
|
return Skip;
|
|
|
|
-- Resolve identifiers that are not selectors in parameter
|
|
-- associations (these are never resolved by visibility).
|
|
|
|
elsif Nkind (N) = N_Identifier
|
|
and then Chars (N) /= Chars (E)
|
|
and then (Nkind (Parent (N)) /= N_Parameter_Association
|
|
or else N /= Selector_Name (Parent (N)))
|
|
then
|
|
Find_Direct_Name (N);
|
|
|
|
-- Reset the Entity if N is overloaded since the entity may not
|
|
-- be the correct one.
|
|
|
|
if Is_Overloaded (N) then
|
|
Set_Entity (N, Empty);
|
|
end if;
|
|
|
|
-- The name in a component association needs no resolution
|
|
|
|
elsif Nkind (N) = N_Component_Association then
|
|
Dummy := Resolve_Name (Expression (N));
|
|
return Skip;
|
|
|
|
elsif Nkind (N) = N_Quantified_Expression then
|
|
return Skip;
|
|
end if;
|
|
|
|
return OK;
|
|
end Resolve_Name;
|
|
|
|
procedure Resolve_Aspect_Expression is new Traverse_Proc (Resolve_Name);
|
|
|
|
-- Local variables
|
|
|
|
ASN : Node_Id := First_Rep_Item (E);
|
|
|
|
-- Start of processing for Resolve_Aspect_Expressions
|
|
|
|
begin
|
|
while Present (ASN) loop
|
|
if Nkind (ASN) = N_Aspect_Specification and then Entity (ASN) = E then
|
|
declare
|
|
A_Id : constant Aspect_Id := Get_Aspect_Id (ASN);
|
|
Expr : constant Node_Id := Expression (ASN);
|
|
|
|
begin
|
|
case A_Id is
|
|
|
|
when Aspect_Aggregate =>
|
|
Resolve_Aspect_Aggregate (Entity (ASN), Expr);
|
|
|
|
when Aspect_Stable_Properties =>
|
|
Resolve_Aspect_Stable_Properties
|
|
(Entity (ASN), Expr, Class_Present (ASN));
|
|
|
|
-- For now we only deal with aspects that do not generate
|
|
-- subprograms, or that may mention current instances of
|
|
-- types. These will require special handling???.
|
|
|
|
when Aspect_Invariant
|
|
| Aspect_Predicate_Failure
|
|
=>
|
|
null;
|
|
|
|
when Aspect_Dynamic_Predicate
|
|
| Aspect_Static_Predicate
|
|
| Aspect_Predicate
|
|
=>
|
|
-- Preanalyze expression after type replacement to catch
|
|
-- name resolution errors if the predicate function has
|
|
-- not been built yet.
|
|
-- Note that we cannot use Preanalyze_Spec_Expression
|
|
-- because of the special handling required for
|
|
-- quantifiers, see comments on Resolve_Aspect_Expression
|
|
-- above.
|
|
|
|
if No (Predicate_Function (E)) then
|
|
Push_Type (E);
|
|
Resolve_Aspect_Expression (Expr);
|
|
Pop_Type (E);
|
|
end if;
|
|
|
|
when Pre_Post_Aspects =>
|
|
null;
|
|
|
|
when Aspect_Iterable =>
|
|
if Nkind (Expr) = N_Aggregate then
|
|
declare
|
|
Assoc : Node_Id;
|
|
|
|
begin
|
|
Assoc := First (Component_Associations (Expr));
|
|
while Present (Assoc) loop
|
|
if Nkind (Expression (Assoc)) in N_Has_Entity
|
|
then
|
|
Find_Direct_Name (Expression (Assoc));
|
|
end if;
|
|
|
|
Next (Assoc);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
|
|
-- The expression for Default_Value is a static expression
|
|
-- of the type, but this expression does not freeze the
|
|
-- type, so it can still appear in a representation clause
|
|
-- before the actual freeze point.
|
|
|
|
when Aspect_Default_Value =>
|
|
Set_Must_Not_Freeze (Expr);
|
|
Preanalyze_Spec_Expression (Expr, E);
|
|
|
|
when Aspect_Priority =>
|
|
Push_Type (E);
|
|
Preanalyze_Spec_Expression (Expr, Any_Integer);
|
|
Pop_Type (E);
|
|
|
|
-- Ditto for Storage_Size. Any other aspects that carry
|
|
-- expressions that should not freeze ??? This is only
|
|
-- relevant to the misuse of deferred constants.
|
|
|
|
when Aspect_Storage_Size =>
|
|
Set_Must_Not_Freeze (Expr);
|
|
Preanalyze_Spec_Expression (Expr, Any_Integer);
|
|
|
|
when others =>
|
|
if Present (Expr) then
|
|
case Aspect_Argument (A_Id) is
|
|
when Expression
|
|
| Optional_Expression
|
|
=>
|
|
Analyze_And_Resolve (Expr);
|
|
|
|
when Name
|
|
| Optional_Name
|
|
=>
|
|
if Nkind (Expr) = N_Identifier then
|
|
Find_Direct_Name (Expr);
|
|
|
|
elsif Nkind (Expr) = N_Selected_Component then
|
|
Find_Selected_Component (Expr);
|
|
end if;
|
|
end case;
|
|
end if;
|
|
end case;
|
|
end;
|
|
end if;
|
|
|
|
Next_Rep_Item (ASN);
|
|
end loop;
|
|
end Resolve_Aspect_Expressions;
|
|
|
|
----------------------------
|
|
-- Parse_Aspect_Aggregate --
|
|
----------------------------
|
|
|
|
procedure Parse_Aspect_Aggregate
|
|
(N : Node_Id;
|
|
Empty_Subp : in out Node_Id;
|
|
Add_Named_Subp : in out Node_Id;
|
|
Add_Unnamed_Subp : in out Node_Id;
|
|
New_Indexed_Subp : in out Node_Id;
|
|
Assign_Indexed_Subp : in out Node_Id)
|
|
is
|
|
Assoc : Node_Id := First (Component_Associations (N));
|
|
Op_Name : Name_Id;
|
|
Subp : Node_Id;
|
|
|
|
begin
|
|
while Present (Assoc) loop
|
|
Subp := Expression (Assoc);
|
|
Op_Name := Chars (First (Choices (Assoc)));
|
|
if Op_Name = Name_Empty then
|
|
Empty_Subp := Subp;
|
|
|
|
elsif Op_Name = Name_Add_Named then
|
|
Add_Named_Subp := Subp;
|
|
|
|
elsif Op_Name = Name_Add_Unnamed then
|
|
Add_Unnamed_Subp := Subp;
|
|
|
|
elsif Op_Name = Name_New_Indexed then
|
|
New_Indexed_Subp := Subp;
|
|
|
|
elsif Op_Name = Name_Assign_Indexed then
|
|
Assign_Indexed_Subp := Subp;
|
|
end if;
|
|
|
|
Next (Assoc);
|
|
end loop;
|
|
end Parse_Aspect_Aggregate;
|
|
|
|
------------------------------------
|
|
-- Parse_Aspect_Stable_Properties --
|
|
------------------------------------
|
|
|
|
function Parse_Aspect_Stable_Properties
|
|
(Aspect_Spec : Node_Id; Negated : out Boolean) return Subprogram_List
|
|
is
|
|
function Extract_Entity (Expr : Node_Id) return Entity_Id;
|
|
-- Given an element of a Stable_Properties aspect spec, return the
|
|
-- associated entity.
|
|
-- This function updates the Negated flag as a side-effect.
|
|
|
|
--------------------
|
|
-- Extract_Entity --
|
|
--------------------
|
|
|
|
function Extract_Entity (Expr : Node_Id) return Entity_Id is
|
|
Name : Node_Id;
|
|
begin
|
|
if Nkind (Expr) = N_Op_Not then
|
|
Negated := True;
|
|
Name := Right_Opnd (Expr);
|
|
else
|
|
Name := Expr;
|
|
end if;
|
|
|
|
if Nkind (Name) in N_Has_Entity then
|
|
return Entity (Name);
|
|
else
|
|
return Empty;
|
|
end if;
|
|
end Extract_Entity;
|
|
|
|
-- Local variables
|
|
|
|
L : List_Id;
|
|
Id : Node_Id;
|
|
|
|
-- Start of processing for Parse_Aspect_Stable_Properties
|
|
|
|
begin
|
|
Negated := False;
|
|
|
|
if Nkind (Aspect_Spec) /= N_Aggregate then
|
|
return (1 => Extract_Entity (Aspect_Spec));
|
|
else
|
|
L := Expressions (Aspect_Spec);
|
|
Id := First (L);
|
|
|
|
return Result : Subprogram_List (1 .. List_Length (L)) do
|
|
for I in Result'Range loop
|
|
Result (I) := Extract_Entity (Id);
|
|
|
|
if No (Result (I)) then
|
|
pragma Assert (Serious_Errors_Detected > 0);
|
|
goto Ignore_Aspect;
|
|
end if;
|
|
|
|
Next (Id);
|
|
end loop;
|
|
end return;
|
|
end if;
|
|
|
|
<<Ignore_Aspect>> return (1 .. 0 => <>);
|
|
end Parse_Aspect_Stable_Properties;
|
|
|
|
-------------------------------
|
|
-- Validate_Aspect_Aggregate --
|
|
-------------------------------
|
|
|
|
procedure Validate_Aspect_Aggregate (N : Node_Id) is
|
|
Empty_Subp : Node_Id := Empty;
|
|
Add_Named_Subp : Node_Id := Empty;
|
|
Add_Unnamed_Subp : Node_Id := Empty;
|
|
New_Indexed_Subp : Node_Id := Empty;
|
|
Assign_Indexed_Subp : Node_Id := Empty;
|
|
|
|
begin
|
|
Error_Msg_Ada_2022_Feature ("aspect Aggregate", Sloc (N));
|
|
|
|
if Nkind (N) /= N_Aggregate
|
|
or else Present (Expressions (N))
|
|
or else No (Component_Associations (N))
|
|
then
|
|
Error_Msg_N ("aspect Aggregate requires an aggregate "
|
|
& "with component associations", N);
|
|
return;
|
|
end if;
|
|
|
|
Parse_Aspect_Aggregate (N,
|
|
Empty_Subp, Add_Named_Subp, Add_Unnamed_Subp,
|
|
New_Indexed_Subp, Assign_Indexed_Subp);
|
|
|
|
if No (Empty_Subp) then
|
|
Error_Msg_N ("missing specification for Empty in aggregate", N);
|
|
end if;
|
|
|
|
if Present (Add_Named_Subp) then
|
|
if Present (Add_Unnamed_Subp)
|
|
or else Present (Assign_Indexed_Subp)
|
|
then
|
|
Error_Msg_N
|
|
("conflicting operations for aggregate (RM 4.3.5)", N);
|
|
return;
|
|
end if;
|
|
|
|
elsif Present (New_Indexed_Subp) /= Present (Assign_Indexed_Subp) then
|
|
Error_Msg_N ("incomplete specification for indexed aggregate", N);
|
|
end if;
|
|
end Validate_Aspect_Aggregate;
|
|
|
|
-------------------------------
|
|
-- Validate_Aspect_Stable_Properties --
|
|
-------------------------------
|
|
|
|
procedure Validate_Aspect_Stable_Properties
|
|
(E : Entity_Id; N : Node_Id; Class_Present : Boolean)
|
|
is
|
|
Is_Aspect_Of_Type : constant Boolean := Is_Type (E);
|
|
|
|
type Permission is (Forbidden, Optional, Required);
|
|
Modifier_Permission : Permission :=
|
|
(if Is_Aspect_Of_Type then Forbidden else Optional);
|
|
Modifier_Error_Called : Boolean := False;
|
|
|
|
procedure Check_Property_Function_Arg (PF_Arg : Node_Id);
|
|
-- Check syntax of a property function argument
|
|
|
|
----------------------------------
|
|
-- Check_Property_Function_Arg --
|
|
----------------------------------
|
|
|
|
procedure Check_Property_Function_Arg (PF_Arg : Node_Id) is
|
|
procedure Modifier_Error;
|
|
-- Generate message about bad "not" modifier if no message already
|
|
-- generated. Errors include specifying "not" for an aspect of
|
|
-- of a type and specifying "not" for some but not all of the
|
|
-- names in a list.
|
|
|
|
--------------------
|
|
-- Modifier_Error --
|
|
--------------------
|
|
|
|
procedure Modifier_Error is
|
|
begin
|
|
if Modifier_Error_Called then
|
|
return; -- error message already generated
|
|
end if;
|
|
|
|
Modifier_Error_Called := True;
|
|
|
|
if Is_Aspect_Of_Type then
|
|
Error_Msg_N
|
|
("NOT modifier not allowed for Stable_Properties aspect"
|
|
& " of a type", PF_Arg);
|
|
else
|
|
Error_Msg_N ("mixed use of NOT modifiers", PF_Arg);
|
|
end if;
|
|
end Modifier_Error;
|
|
|
|
PF_Name : Node_Id := PF_Arg;
|
|
|
|
-- Start of processing for Check_Property_Function_Arg
|
|
|
|
begin
|
|
if Nkind (PF_Arg) = N_Op_Not then
|
|
PF_Name := Right_Opnd (PF_Arg);
|
|
|
|
case Modifier_Permission is
|
|
when Forbidden =>
|
|
Modifier_Error;
|
|
when Optional =>
|
|
Modifier_Permission := Required;
|
|
when Required =>
|
|
null;
|
|
end case;
|
|
else
|
|
case Modifier_Permission is
|
|
when Forbidden =>
|
|
null;
|
|
when Optional =>
|
|
Modifier_Permission := Forbidden;
|
|
when Required =>
|
|
Modifier_Error;
|
|
end case;
|
|
end if;
|
|
|
|
if Nkind (PF_Name) not in
|
|
N_Identifier | N_Operator_Symbol | N_Selected_Component
|
|
then
|
|
Error_Msg_N ("bad property function name", PF_Name);
|
|
end if;
|
|
end Check_Property_Function_Arg;
|
|
|
|
-- Start of processing for Validate_Aspect_Stable_Properties
|
|
|
|
begin
|
|
Error_Msg_Ada_2022_Feature ("aspect Stable_Properties", Sloc (N));
|
|
|
|
if (not Is_Aspect_Of_Type) and then (not Is_Subprogram (E)) then
|
|
Error_Msg_N ("Stable_Properties aspect can only be specified for "
|
|
& "a type or a subprogram", N);
|
|
elsif Class_Present then
|
|
if Is_Aspect_Of_Type then
|
|
if not Is_Tagged_Type (E) then
|
|
Error_Msg_N
|
|
("Stable_Properties''Class aspect cannot be specified for "
|
|
& "an untagged type", N);
|
|
end if;
|
|
else
|
|
if not Is_Dispatching_Operation (E) then
|
|
Error_Msg_N
|
|
("Stable_Properties''Class aspect cannot be specified for "
|
|
& "a subprogram that is not a primitive subprogram "
|
|
& "of a tagged type", N);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
if Nkind (N) = N_Aggregate then
|
|
if Present (Component_Associations (N))
|
|
or else Null_Record_Present (N)
|
|
or else not Present (Expressions (N))
|
|
then
|
|
Error_Msg_N ("bad Stable_Properties aspect specification", N);
|
|
return;
|
|
end if;
|
|
|
|
declare
|
|
PF_Arg : Node_Id := First (Expressions (N));
|
|
begin
|
|
while Present (PF_Arg) loop
|
|
Check_Property_Function_Arg (PF_Arg);
|
|
Next (PF_Arg);
|
|
end loop;
|
|
end;
|
|
else
|
|
Check_Property_Function_Arg (N);
|
|
end if;
|
|
end Validate_Aspect_Stable_Properties;
|
|
|
|
--------------------------------
|
|
-- 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 in Name_First | Name_Last then
|
|
|
|
-- First or Last (Container) => Cursor
|
|
|
|
if Etype (Ent) /= Cursor then
|
|
Error_Msg_N ("primitive for First must yield a cursor", 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_Previous then
|
|
|
|
-- Previous (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 Previous 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;
|
|
|
|
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;
|
|
|
|
------------------------------
|
|
-- Resolve_Aspect_Aggregate --
|
|
------------------------------
|
|
|
|
procedure Resolve_Aspect_Aggregate
|
|
(Typ : Entity_Id;
|
|
Expr : Node_Id)
|
|
is
|
|
function Valid_Empty (E : Entity_Id) return Boolean;
|
|
function Valid_Add_Named (E : Entity_Id) return Boolean;
|
|
function Valid_Add_Unnamed (E : Entity_Id) return Boolean;
|
|
function Valid_New_Indexed (E : Entity_Id) return Boolean;
|
|
function Valid_Assign_Indexed (E : Entity_Id) return Boolean;
|
|
-- Predicates that establish the legality of each possible operation in
|
|
-- an Aggregate aspect.
|
|
|
|
generic
|
|
with function Pred (Id : Node_Id) return Boolean;
|
|
procedure Resolve_Operation (Subp_Id : Node_Id);
|
|
-- Common processing to resolve each aggregate operation.
|
|
|
|
------------------------
|
|
-- Valid_Assign_Index --
|
|
------------------------
|
|
|
|
function Valid_Assign_Indexed (E : Entity_Id) return Boolean is
|
|
begin
|
|
-- The profile must be the same as for Add_Named, with the added
|
|
-- requirement that the key_type be a discrete type.
|
|
|
|
if Valid_Add_Named (E) then
|
|
return Is_Discrete_Type (Etype (Next_Formal (First_Formal (E))));
|
|
else
|
|
return False;
|
|
end if;
|
|
end Valid_Assign_Indexed;
|
|
|
|
-----------------
|
|
-- Valid_Empty --
|
|
-----------------
|
|
|
|
function Valid_Empty (E : Entity_Id) return Boolean is
|
|
begin
|
|
if Etype (E) /= Typ or else Scope (E) /= Scope (Typ) then
|
|
return False;
|
|
|
|
elsif Ekind (E) = E_Constant then
|
|
return True;
|
|
|
|
elsif Ekind (E) = E_Function then
|
|
return No (First_Formal (E))
|
|
or else
|
|
(Is_Integer_Type (Etype (First_Formal (E)))
|
|
and then No (Next_Formal (First_Formal (E))));
|
|
else
|
|
return False;
|
|
end if;
|
|
end Valid_Empty;
|
|
|
|
---------------------
|
|
-- Valid_Add_Named --
|
|
---------------------
|
|
|
|
function Valid_Add_Named (E : Entity_Id) return Boolean is
|
|
F2, F3 : Entity_Id;
|
|
begin
|
|
if Ekind (E) = E_Procedure
|
|
and then Scope (E) = Scope (Typ)
|
|
and then Number_Formals (E) = 3
|
|
and then Etype (First_Formal (E)) = Typ
|
|
and then Ekind (First_Formal (E)) = E_In_Out_Parameter
|
|
then
|
|
F2 := Next_Formal (First_Formal (E));
|
|
F3 := Next_Formal (F2);
|
|
return Ekind (F2) = E_In_Parameter
|
|
and then Ekind (F3) = E_In_Parameter
|
|
and then not Is_Limited_Type (Etype (F2))
|
|
and then not Is_Limited_Type (Etype (F3));
|
|
else
|
|
return False;
|
|
end if;
|
|
end Valid_Add_Named;
|
|
|
|
-----------------------
|
|
-- Valid_Add_Unnamed --
|
|
-----------------------
|
|
|
|
function Valid_Add_Unnamed (E : Entity_Id) return Boolean is
|
|
begin
|
|
return Ekind (E) = E_Procedure
|
|
and then Scope (E) = Scope (Typ)
|
|
and then Number_Formals (E) = 2
|
|
and then Etype (First_Formal (E)) = Typ
|
|
and then Ekind (First_Formal (E)) = E_In_Out_Parameter
|
|
and then
|
|
not Is_Limited_Type (Etype (Next_Formal (First_Formal (E))));
|
|
end Valid_Add_Unnamed;
|
|
|
|
-----------------------
|
|
-- Valid_Nmw_Indexed --
|
|
-----------------------
|
|
|
|
function Valid_New_Indexed (E : Entity_Id) return Boolean is
|
|
begin
|
|
return Ekind (E) = E_Function
|
|
and then Scope (E) = Scope (Typ)
|
|
and then Etype (E) = Typ
|
|
and then Number_Formals (E) = 2
|
|
and then Is_Discrete_Type (Etype (First_Formal (E)))
|
|
and then Etype (First_Formal (E)) =
|
|
Etype (Next_Formal (First_Formal (E)));
|
|
end Valid_New_Indexed;
|
|
|
|
-----------------------
|
|
-- Resolve_Operation --
|
|
-----------------------
|
|
|
|
procedure Resolve_Operation (Subp_Id : Node_Id) is
|
|
Subp : Entity_Id;
|
|
|
|
I : Interp_Index;
|
|
It : Interp;
|
|
|
|
begin
|
|
if not Is_Overloaded (Subp_Id) then
|
|
Subp := Entity (Subp_Id);
|
|
if not Pred (Subp) then
|
|
Error_Msg_NE
|
|
("improper aggregate operation for&", Subp_Id, Typ);
|
|
end if;
|
|
|
|
else
|
|
Set_Entity (Subp_Id, Empty);
|
|
Get_First_Interp (Subp_Id, I, It);
|
|
while Present (It.Nam) loop
|
|
if Pred (It.Nam) then
|
|
Set_Is_Overloaded (Subp_Id, False);
|
|
Set_Entity (Subp_Id, It.Nam);
|
|
exit;
|
|
end if;
|
|
|
|
Get_Next_Interp (I, It);
|
|
end loop;
|
|
|
|
if No (Entity (Subp_Id)) then
|
|
Error_Msg_NE
|
|
("improper aggregate operation for&", Subp_Id, Typ);
|
|
end if;
|
|
end if;
|
|
end Resolve_Operation;
|
|
|
|
Assoc : Node_Id;
|
|
Op_Name : Name_Id;
|
|
Subp_Id : Node_Id;
|
|
|
|
procedure Resolve_Empty is new Resolve_Operation (Valid_Empty);
|
|
procedure Resolve_Unnamed is new Resolve_Operation (Valid_Add_Unnamed);
|
|
procedure Resolve_Named is new Resolve_Operation (Valid_Add_Named);
|
|
procedure Resolve_Indexed is new Resolve_Operation (Valid_New_Indexed);
|
|
procedure Resolve_Assign_Indexed
|
|
is new Resolve_Operation
|
|
(Valid_Assign_Indexed);
|
|
|
|
-- Start of processing for Resolve_Aspect_Aggregate
|
|
|
|
begin
|
|
Assoc := First (Component_Associations (Expr));
|
|
|
|
while Present (Assoc) loop
|
|
Op_Name := Chars (First (Choices (Assoc)));
|
|
|
|
-- When verifying the consistency of aspects between the freeze point
|
|
-- and the end of declarqtions, we use a copy which is not analyzed
|
|
-- yet, so do it now.
|
|
|
|
Subp_Id := Expression (Assoc);
|
|
if No (Etype (Subp_Id)) then
|
|
Analyze (Subp_Id);
|
|
end if;
|
|
|
|
if Op_Name = Name_Empty then
|
|
Resolve_Empty (Subp_Id);
|
|
|
|
elsif Op_Name = Name_Add_Named then
|
|
Resolve_Named (Subp_Id);
|
|
|
|
elsif Op_Name = Name_Add_Unnamed then
|
|
Resolve_Unnamed (Subp_Id);
|
|
|
|
elsif Op_Name = Name_New_Indexed then
|
|
Resolve_Indexed (Subp_Id);
|
|
|
|
elsif Op_Name = Name_Assign_Indexed then
|
|
Resolve_Assign_Indexed (Subp_Id);
|
|
end if;
|
|
|
|
Next (Assoc);
|
|
end loop;
|
|
end Resolve_Aspect_Aggregate;
|
|
|
|
--------------------------------------
|
|
-- Resolve_Aspect_Stable_Properties --
|
|
--------------------------------------
|
|
|
|
procedure Resolve_Aspect_Stable_Properties
|
|
(Typ_Or_Subp : Entity_Id; Expr : Node_Id; Class_Present : Boolean)
|
|
is
|
|
Is_Aspect_Of_Type : constant Boolean := Is_Type (Typ_Or_Subp);
|
|
|
|
Singleton : constant Boolean := Nkind (Expr) /= N_Aggregate;
|
|
Subp_Name : Node_Id := (if Singleton
|
|
then Expr
|
|
else First (Expressions (Expr)));
|
|
Has_Not : Boolean;
|
|
begin
|
|
if Is_Aspect_Of_Type
|
|
and then Has_Private_Declaration (Typ_Or_Subp)
|
|
and then not Is_Private_Type (Typ_Or_Subp)
|
|
then
|
|
Error_Msg_N
|
|
("Stable_Properties aspect cannot be specified " &
|
|
"for the completion of a private type", Typ_Or_Subp);
|
|
end if;
|
|
|
|
-- Analogous checks that the aspect is not specified for a completion
|
|
-- in the subprogram case are not performed here because they are not
|
|
-- specific to this particular aspect. Right ???
|
|
|
|
loop
|
|
Has_Not := Nkind (Subp_Name) = N_Op_Not;
|
|
if Has_Not then
|
|
Set_Analyzed (Subp_Name); -- ???
|
|
Subp_Name := Right_Opnd (Subp_Name);
|
|
end if;
|
|
|
|
if No (Etype (Subp_Name)) then
|
|
Analyze (Subp_Name);
|
|
end if;
|
|
|
|
declare
|
|
Subp : Entity_Id := Empty;
|
|
|
|
I : Interp_Index;
|
|
It : Interp;
|
|
|
|
function Is_Property_Function (E : Entity_Id) return Boolean;
|
|
-- Implements RM 7.3.4 definition of "property function".
|
|
|
|
function Is_Property_Function (E : Entity_Id) return Boolean is
|
|
begin
|
|
if Ekind (E) not in E_Function | E_Operator
|
|
or else Number_Formals (E) /= 1
|
|
then
|
|
return False;
|
|
end if;
|
|
|
|
declare
|
|
Param_Type : constant Entity_Id :=
|
|
Base_Type (Etype (First_Formal (E)));
|
|
|
|
function Matches_Param_Type (Typ : Entity_Id)
|
|
return Boolean is
|
|
((Base_Type (Typ) = Param_Type)
|
|
or else
|
|
(Is_Class_Wide_Type (Param_Type)
|
|
and then Is_Ancestor (Root_Type (Param_Type),
|
|
Base_Type (Typ))));
|
|
begin
|
|
if Is_Aspect_Of_Type then
|
|
if Matches_Param_Type (Typ_Or_Subp) then
|
|
return True;
|
|
end if;
|
|
elsif Is_Primitive (Typ_Or_Subp) then
|
|
declare
|
|
Formal : Entity_Id := First_Formal (Typ_Or_Subp);
|
|
begin
|
|
while Present (Formal) loop
|
|
if Matches_Param_Type (Etype (Formal)) then
|
|
|
|
-- Test whether Typ_Or_Subp (which is a subp
|
|
-- in this case) is primitive op of the type
|
|
-- of this parameter.
|
|
if Scope (Typ_Or_Subp) = Scope (Param_Type) then
|
|
return True;
|
|
end if;
|
|
end if;
|
|
Next_Formal (Formal);
|
|
end loop;
|
|
end;
|
|
end if;
|
|
end;
|
|
|
|
return False;
|
|
end Is_Property_Function;
|
|
begin
|
|
if not Is_Overloaded (Subp_Name) then
|
|
Subp := Entity (Subp_Name);
|
|
if not Is_Property_Function (Subp) then
|
|
Error_Msg_NE ("improper property function for&",
|
|
Subp_Name, Typ_Or_Subp);
|
|
return;
|
|
end if;
|
|
else
|
|
Set_Entity (Subp_Name, Empty);
|
|
Get_First_Interp (Subp_Name, I, It);
|
|
while Present (It.Nam) loop
|
|
if Is_Property_Function (It.Nam) then
|
|
if Present (Subp) then
|
|
Error_Msg_NE
|
|
("ambiguous property function name for&",
|
|
Subp_Name, Typ_Or_Subp);
|
|
return;
|
|
end if;
|
|
|
|
Subp := It.Nam;
|
|
Set_Is_Overloaded (Subp_Name, False);
|
|
Set_Entity (Subp_Name, Subp);
|
|
end if;
|
|
|
|
Get_Next_Interp (I, It);
|
|
end loop;
|
|
|
|
if No (Subp) then
|
|
Error_Msg_NE ("improper property function for&",
|
|
Subp_Name, Typ_Or_Subp);
|
|
return;
|
|
end if;
|
|
end if;
|
|
|
|
-- perform legality (as opposed to name resolution) Subp checks
|
|
|
|
if Is_Limited_Type (Etype (Subp)) then
|
|
Error_Msg_NE
|
|
("result type of property function for& is limited",
|
|
Subp_Name, Typ_Or_Subp);
|
|
end if;
|
|
|
|
if Ekind (First_Formal (Subp)) /= E_In_Parameter then
|
|
Error_Msg_NE
|
|
("mode of parameter of property function for& is not IN",
|
|
Subp_Name, Typ_Or_Subp);
|
|
end if;
|
|
|
|
if Is_Class_Wide_Type (Etype (First_Formal (Subp))) then
|
|
if not Covers (Etype (First_Formal (Subp)), Typ_Or_Subp) then
|
|
Error_Msg_NE
|
|
("class-wide parameter type of property function " &
|
|
"for& does not cover the type",
|
|
Subp_Name, Typ_Or_Subp);
|
|
|
|
-- ??? This test is slightly stricter than 7.3.4(12/5);
|
|
-- some legal corner cases may be incorrectly rejected.
|
|
elsif Scope (Subp) /= Scope (Etype (First_Formal (Subp)))
|
|
then
|
|
Error_Msg_NE
|
|
("property function for& not declared in same scope " &
|
|
"as parameter type",
|
|
Subp_Name, Typ_Or_Subp);
|
|
end if;
|
|
elsif Is_Aspect_Of_Type and then
|
|
Scope (Subp) /= Scope (Typ_Or_Subp) and then
|
|
Scope (Subp) /= Standard_Standard -- e.g., derived type's "abs"
|
|
then
|
|
Error_Msg_NE
|
|
("property function for& " &
|
|
"not a primitive function of the type",
|
|
Subp_Name, Typ_Or_Subp);
|
|
end if;
|
|
|
|
if Has_Not then
|
|
-- check that Subp was mentioned in param type's aspect spec
|
|
declare
|
|
Param_Type : constant Entity_Id :=
|
|
Base_Type (Etype (First_Formal (Subp)));
|
|
Aspect_Spec : constant Node_Id :=
|
|
Find_Value_Of_Aspect
|
|
(Param_Type, Aspect_Stable_Properties,
|
|
Class_Present => Class_Present);
|
|
Found : Boolean := False;
|
|
begin
|
|
if Present (Aspect_Spec) then
|
|
declare
|
|
Ignored : Boolean;
|
|
SPF_List : constant Subprogram_List :=
|
|
Parse_Aspect_Stable_Properties
|
|
(Aspect_Spec, Negated => Ignored);
|
|
begin
|
|
Found := (for some E of SPF_List => E = Subp);
|
|
-- look through renamings ???
|
|
end;
|
|
end if;
|
|
if not Found then
|
|
declare
|
|
CW_Modifier : constant String :=
|
|
(if Class_Present then "class-wide " else "");
|
|
begin
|
|
Error_Msg_NE
|
|
(CW_Modifier
|
|
& "property function for& mentioned after NOT "
|
|
& "but not a "
|
|
& CW_Modifier
|
|
& "stable property function of its parameter type",
|
|
Subp_Name, Typ_Or_Subp);
|
|
end;
|
|
end if;
|
|
end;
|
|
end if;
|
|
end;
|
|
|
|
exit when Singleton;
|
|
Subp_Name :=
|
|
Next ((if Has_Not then Parent (Subp_Name) else Subp_Name));
|
|
exit when No (Subp_Name);
|
|
end loop;
|
|
|
|
Set_Analyzed (Expr);
|
|
end Resolve_Aspect_Stable_Properties;
|
|
|
|
-----------------------------------------
|
|
-- Resolve_Storage_Model_Type_Argument --
|
|
-----------------------------------------
|
|
|
|
procedure Resolve_Storage_Model_Type_Argument
|
|
(N : Node_Id;
|
|
Typ : Entity_Id;
|
|
Addr_Type : in out Entity_Id;
|
|
Nam : Name_Id)
|
|
is
|
|
|
|
type Formal_Profile is record
|
|
Subt : Entity_Id;
|
|
Mode : Formal_Kind;
|
|
end record;
|
|
|
|
type Formal_Profiles is array (Positive range <>) of Formal_Profile;
|
|
|
|
function Aspect_Argument_Profile_Matches
|
|
(Subp : Entity_Id;
|
|
Profiles : Formal_Profiles;
|
|
Result_Subt : Entity_Id;
|
|
Err_On_Mismatch : Boolean) return Boolean;
|
|
-- Checks that the formal parameters of subprogram Subp conform to the
|
|
-- subtypes and modes specified by Profiles, as well as to the result
|
|
-- subtype Result_Subt when that is nonempty.
|
|
|
|
function Aspect_Argument_Profile_Matches
|
|
(Subp : Entity_Id;
|
|
Profiles : Formal_Profiles;
|
|
Result_Subt : Entity_Id;
|
|
Err_On_Mismatch : Boolean) return Boolean
|
|
is
|
|
|
|
procedure Report_Argument_Error
|
|
(Msg : String;
|
|
Formal : Entity_Id := Empty;
|
|
Subt : Entity_Id := Empty);
|
|
-- If Err_On_Mismatch is True, reports an argument error given by Msg
|
|
-- associated with Formal and/or Subt.
|
|
|
|
procedure Report_Argument_Error
|
|
(Msg : String;
|
|
Formal : Entity_Id := Empty;
|
|
Subt : Entity_Id := Empty)
|
|
is
|
|
begin
|
|
if Err_On_Mismatch then
|
|
if Present (Formal) then
|
|
if Present (Subt) then
|
|
Error_Msg_Node_2 := Subt;
|
|
end if;
|
|
Error_Msg_NE (Msg, N, Formal);
|
|
|
|
elsif Present (Subt) then
|
|
Error_Msg_NE (Msg, N, Subt);
|
|
|
|
else
|
|
Error_Msg_N (Msg, N);
|
|
end if;
|
|
end if;
|
|
end Report_Argument_Error;
|
|
|
|
-- Local variables
|
|
|
|
Formal : Entity_Id := First_Formal (Subp);
|
|
Is_Error : Boolean := False;
|
|
|
|
-- Start of processing for Aspect_Argument_Profile_Matches
|
|
|
|
begin
|
|
for FP of Profiles loop
|
|
if not Present (Formal) then
|
|
Is_Error := True;
|
|
Report_Argument_Error ("missing formal of }", Subt => FP.Subt);
|
|
exit;
|
|
|
|
elsif not Subtypes_Statically_Match
|
|
(Etype (Formal), FP.Subt)
|
|
then
|
|
Is_Error := True;
|
|
Report_Argument_Error
|
|
("formal& must be of subtype&",
|
|
Formal => Formal, Subt => FP.Subt);
|
|
exit;
|
|
|
|
elsif Ekind (Formal) /= FP.Mode then
|
|
Is_Error := True;
|
|
Report_Argument_Error
|
|
("formal& has wrong mode", Formal => Formal);
|
|
exit;
|
|
end if;
|
|
|
|
Formal := Next_Formal (Formal);
|
|
end loop;
|
|
|
|
if not Is_Error
|
|
and then Present (Formal)
|
|
then
|
|
Is_Error := True;
|
|
Report_Argument_Error
|
|
("too many formals for subprogram in aspect");
|
|
end if;
|
|
|
|
if not Is_Error
|
|
and then Present (Result_Subt)
|
|
and then not Subtypes_Statically_Match (Etype (Subp), Result_Subt)
|
|
then
|
|
Is_Error := True;
|
|
Report_Argument_Error
|
|
("subprogram must have result}", Subt => Result_Subt);
|
|
end if;
|
|
|
|
return not Is_Error;
|
|
end Aspect_Argument_Profile_Matches;
|
|
|
|
-- Local variables
|
|
|
|
Ent : Entity_Id;
|
|
|
|
Storage_Count_Type : constant Entity_Id := RTE (RE_Storage_Count);
|
|
System_Address_Type : constant Entity_Id := RTE (RE_Address);
|
|
|
|
-- Start of processing for Resolve_Storage_Model_Type_Argument
|
|
|
|
begin
|
|
if Nam = Name_Address_Type then
|
|
if not Is_Entity_Name (N)
|
|
or else not Is_Type (Entity (N))
|
|
or else (Root_Type (Entity (N)) /= System_Address_Type
|
|
and then not Is_Integer_Type (Entity (N)))
|
|
then
|
|
Error_Msg_N ("named entity must be a descendant of System.Address "
|
|
& "or an integer type", N);
|
|
end if;
|
|
|
|
Addr_Type := Entity (N);
|
|
|
|
return;
|
|
|
|
elsif not Present (Addr_Type) then
|
|
Error_Msg_N ("argument association for Address_Type missing; "
|
|
& "must be specified as first aspect argument", N);
|
|
return;
|
|
|
|
elsif Nam = Name_Null_Address then
|
|
if not Is_Entity_Name (N)
|
|
or else not Is_Constant_Object (Entity (N))
|
|
or else
|
|
not Subtypes_Statically_Match (Etype (Entity (N)), Addr_Type)
|
|
then
|
|
Error_Msg_NE
|
|
("named entity must be constant of subtype}", N, Addr_Type);
|
|
end if;
|
|
|
|
return;
|
|
|
|
elsif not Is_Overloaded (N) then
|
|
if not Is_Entity_Name (N)
|
|
or else Ekind (Entity (N)) not in E_Function | E_Procedure
|
|
or else Scope (Entity (N)) /= Scope (Typ)
|
|
then
|
|
Error_Msg_N ("argument must be local subprogram name", N);
|
|
return;
|
|
end if;
|
|
|
|
Ent := Entity (N);
|
|
|
|
if Nam = Name_Allocate then
|
|
if not Aspect_Argument_Profile_Matches
|
|
(Ent,
|
|
Profiles =>
|
|
((Typ, E_In_Out_Parameter),
|
|
(Addr_Type, E_Out_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter)),
|
|
Result_Subt => Empty,
|
|
Err_On_Mismatch => True)
|
|
then
|
|
Error_Msg_N ("no match for Allocate operation", N);
|
|
end if;
|
|
|
|
elsif Nam = Name_Deallocate then
|
|
if not Aspect_Argument_Profile_Matches
|
|
(Ent,
|
|
Profiles =>
|
|
((Typ, E_In_Out_Parameter),
|
|
(Addr_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter)),
|
|
Result_Subt => Empty,
|
|
Err_On_Mismatch => True)
|
|
then
|
|
Error_Msg_N ("no match for Deallocate operation", N);
|
|
end if;
|
|
|
|
elsif Nam = Name_Copy_From then
|
|
if not Aspect_Argument_Profile_Matches
|
|
(Ent,
|
|
Profiles =>
|
|
((Typ, E_In_Out_Parameter),
|
|
(System_Address_Type, E_In_Parameter),
|
|
(Addr_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter)),
|
|
Result_Subt => Empty,
|
|
Err_On_Mismatch => True)
|
|
then
|
|
Error_Msg_N ("no match for Copy_From operation", N);
|
|
end if;
|
|
|
|
elsif Nam = Name_Copy_To then
|
|
if not Aspect_Argument_Profile_Matches
|
|
(Ent,
|
|
Profiles =>
|
|
((Typ, E_In_Out_Parameter),
|
|
(Addr_Type, E_In_Parameter),
|
|
(System_Address_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter)),
|
|
Result_Subt => Empty,
|
|
Err_On_Mismatch => True)
|
|
then
|
|
Error_Msg_N ("no match for Copy_To operation", N);
|
|
end if;
|
|
|
|
elsif Nam = Name_Storage_Size then
|
|
if not Aspect_Argument_Profile_Matches
|
|
(Ent,
|
|
Profiles => (1 => (Typ, E_In_Parameter)),
|
|
Result_Subt => Storage_Count_Type,
|
|
Err_On_Mismatch => True)
|
|
then
|
|
Error_Msg_N ("no match for Storage_Size operation", N);
|
|
end if;
|
|
|
|
else
|
|
null; -- Error will be caught in Validate_Storage_Model_Type_Aspect
|
|
end if;
|
|
|
|
else
|
|
-- Overloaded case: find subprogram with proper signature
|
|
|
|
declare
|
|
I : Interp_Index;
|
|
It : Interp;
|
|
Found_Match : Boolean := False;
|
|
|
|
begin
|
|
Get_First_Interp (N, I, It);
|
|
while Present (It.Typ) loop
|
|
if Ekind (It.Nam) in E_Function | E_Procedure
|
|
and then Scope (It.Nam) = Scope (Typ)
|
|
then
|
|
if Nam = Name_Allocate then
|
|
Found_Match :=
|
|
Aspect_Argument_Profile_Matches
|
|
(It.Nam,
|
|
Profiles =>
|
|
((Typ, E_In_Out_Parameter),
|
|
(Addr_Type, E_Out_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter)),
|
|
Result_Subt => Empty,
|
|
Err_On_Mismatch => False);
|
|
|
|
elsif Nam = Name_Deallocate then
|
|
Found_Match :=
|
|
Aspect_Argument_Profile_Matches
|
|
(It.Nam,
|
|
Profiles =>
|
|
((Typ, E_In_Out_Parameter),
|
|
(Addr_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter)),
|
|
Result_Subt => Empty,
|
|
Err_On_Mismatch => False);
|
|
|
|
elsif Nam = Name_Copy_From then
|
|
Found_Match :=
|
|
Aspect_Argument_Profile_Matches
|
|
(It.Nam,
|
|
Profiles =>
|
|
((Typ, E_In_Out_Parameter),
|
|
(System_Address_Type, E_In_Parameter),
|
|
(Addr_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter)),
|
|
Result_Subt => Empty,
|
|
Err_On_Mismatch => False);
|
|
|
|
elsif Nam = Name_Copy_To then
|
|
Found_Match :=
|
|
Aspect_Argument_Profile_Matches
|
|
(It.Nam,
|
|
Profiles =>
|
|
((Typ, E_In_Out_Parameter),
|
|
(Addr_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter),
|
|
(System_Address_Type, E_In_Parameter),
|
|
(Storage_Count_Type, E_In_Parameter)),
|
|
Result_Subt => Empty,
|
|
Err_On_Mismatch => False);
|
|
|
|
elsif Nam = Name_Storage_Size then
|
|
Found_Match :=
|
|
Aspect_Argument_Profile_Matches
|
|
(It.Nam,
|
|
Profiles => (1 => (Typ, E_In_Parameter)),
|
|
Result_Subt => Storage_Count_Type,
|
|
Err_On_Mismatch => False);
|
|
end if;
|
|
|
|
if Found_Match then
|
|
Set_Entity (N, It.Nam);
|
|
exit;
|
|
end if;
|
|
end if;
|
|
|
|
Get_Next_Interp (I, It);
|
|
end loop;
|
|
|
|
if not Found_Match then
|
|
Error_Msg_N
|
|
("no match found for Storage_Model_Type operation", N);
|
|
end if;
|
|
end;
|
|
end if;
|
|
end Resolve_Storage_Model_Type_Argument;
|
|
|
|
----------------
|
|
-- 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 : Unat;
|
|
|
|
begin
|
|
Reinit_Alignment (T);
|
|
|
|
-- Find the minimum standard size (8,16,32,64,128) 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**7 and then Hi < Uint_2**7 then
|
|
Sz := UI_From_Int (Standard_Character_Size);
|
|
-- Might be > 8 on some targets
|
|
|
|
elsif Lo >= -Uint_2**15 and then Hi < Uint_2**15 then
|
|
Sz := Uint_16;
|
|
|
|
elsif Lo >= -Uint_2**31 and then Hi < Uint_2**31 then
|
|
Sz := Uint_32;
|
|
|
|
elsif Lo >= -Uint_2**63 and then Hi < Uint_2**63 then
|
|
Sz := Uint_64;
|
|
|
|
else pragma Assert (Lo >= -Uint_2**127 and then Hi < Uint_2**127);
|
|
Sz := Uint_128;
|
|
end if;
|
|
|
|
else
|
|
if Hi < Uint_2**8 then
|
|
Sz := UI_From_Int (Standard_Character_Size);
|
|
|
|
elsif Hi < Uint_2**16 then
|
|
Sz := Uint_16;
|
|
|
|
elsif Hi < Uint_2**32 then
|
|
Sz := Uint_32;
|
|
|
|
elsif Hi < Uint_2**64 then
|
|
Sz := Uint_64;
|
|
|
|
else pragma Assert (Hi < Uint_2**128);
|
|
Sz := Uint_128;
|
|
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
|
|
Set_Esize (T, UI_From_Int (Standard_Integer_Size));
|
|
else
|
|
Set_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;
|
|
|
|
------------------------------
|
|
-- Validate_Address_Clauses --
|
|
------------------------------
|
|
|
|
procedure Validate_Address_Clauses is
|
|
function Offset_Value (Expr : Node_Id) return Uint;
|
|
-- Given an Address attribute reference, return the value in bits of its
|
|
-- offset from the first bit of the underlying entity, or 0 if it is not
|
|
-- known at compile time.
|
|
|
|
------------------
|
|
-- Offset_Value --
|
|
------------------
|
|
|
|
function Offset_Value (Expr : Node_Id) return Uint is
|
|
N : Node_Id := Prefix (Expr);
|
|
Off : Uint;
|
|
Val : Uint := Uint_0;
|
|
|
|
begin
|
|
-- Climb the prefix chain and compute the cumulative offset
|
|
|
|
loop
|
|
if Is_Entity_Name (N) then
|
|
return Val;
|
|
|
|
elsif Nkind (N) = N_Selected_Component then
|
|
Off := Component_Bit_Offset (Entity (Selector_Name (N)));
|
|
if Present (Off) and then Off >= Uint_0 then
|
|
Val := Val + Off;
|
|
N := Prefix (N);
|
|
else
|
|
return Uint_0;
|
|
end if;
|
|
|
|
elsif Nkind (N) = N_Indexed_Component then
|
|
Off := Indexed_Component_Bit_Offset (N);
|
|
if Present (Off) then
|
|
Val := Val + Off;
|
|
N := Prefix (N);
|
|
else
|
|
return Uint_0;
|
|
end if;
|
|
|
|
else
|
|
return Uint_0;
|
|
end if;
|
|
end loop;
|
|
end Offset_Value;
|
|
|
|
-- Start of processing for Validate_Address_Clauses
|
|
|
|
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 := Uint_0;
|
|
|
|
X_Size : Uint;
|
|
Y_Size : Uint := Uint_0;
|
|
|
|
X_Offs : Uint;
|
|
|
|
begin
|
|
-- Skip processing of this entry if warning already posted, or if
|
|
-- alignments are not set.
|
|
|
|
if not Address_Warning_Posted (ACCR.N)
|
|
and then Known_Alignment (ACCR.X)
|
|
and then Known_Alignment (ACCR.Y)
|
|
then
|
|
Expr := Original_Node (Expression (ACCR.N));
|
|
|
|
-- Get alignments, sizes and offset, if any
|
|
|
|
X_Alignment := Alignment (ACCR.X);
|
|
X_Size := Esize (ACCR.X);
|
|
|
|
if Present (ACCR.Y) then
|
|
Y_Alignment := Alignment (ACCR.Y);
|
|
Y_Size :=
|
|
(if Known_Esize (ACCR.Y) then Esize (ACCR.Y) else Uint_0);
|
|
end if;
|
|
|
|
if ACCR.Off
|
|
and then Nkind (Expr) = N_Attribute_Reference
|
|
and then Attribute_Name (Expr) = Name_Address
|
|
then
|
|
X_Offs := Offset_Value (Expr);
|
|
else
|
|
X_Offs := Uint_0;
|
|
end if;
|
|
|
|
-- Check for known value not multiple of alignment
|
|
|
|
if No (ACCR.Y) then
|
|
if not Alignment_Checks_Suppressed (ACCR)
|
|
and then X_Alignment /= 0
|
|
and then ACCR.A mod X_Alignment /= 0
|
|
then
|
|
Error_Msg_NE
|
|
("??specified address for& is 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);
|
|
end if;
|
|
|
|
-- Check for large object overlaying smaller one
|
|
|
|
elsif Y_Size > Uint_0
|
|
and then X_Size > Uint_0
|
|
and then X_Offs + 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);
|
|
|
|
if Y_Size >= X_Size then
|
|
Error_Msg_Uint_1 := X_Offs;
|
|
Error_Msg_NE ("\??but offset of & is ^", ACCR.N, ACCR.X);
|
|
end if;
|
|
|
|
-- Check for inadequate alignment, both of the base object
|
|
-- and of the offset, if any. We only do this check if the
|
|
-- run-time Alignment_Check is active. No point in warning
|
|
-- if this check has been suppressed (or is suppressed by
|
|
-- default in the non-strict alignment machine case).
|
|
|
|
-- Note: we do not check the alignment if we gave a size
|
|
-- warning, since it would likely be redundant.
|
|
|
|
elsif not Alignment_Checks_Suppressed (ACCR)
|
|
and then 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), True) /=
|
|
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_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;
|
|
Last_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;
|
|
Last_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_Last then
|
|
Resolve_Iterable_Operation (Expr, Cursor, Typ, Name_Last);
|
|
Last_Id := Entity (Expr);
|
|
|
|
elsif Chars (Prim) = Name_Previous then
|
|
Resolve_Iterable_Operation (Expr, Cursor, Typ, Name_Previous);
|
|
Last_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) or else No (Last_Id) then
|
|
null; -- optional
|
|
end if;
|
|
end Validate_Iterable_Aspect;
|
|
|
|
------------------------------
|
|
-- Validate_Literal_Aspect --
|
|
------------------------------
|
|
|
|
procedure Validate_Literal_Aspect (Typ : Entity_Id; ASN : Node_Id) is
|
|
A_Id : constant Aspect_Id := Get_Aspect_Id (ASN);
|
|
pragma Assert (A_Id in Aspect_Integer_Literal |
|
|
Aspect_Real_Literal | Aspect_String_Literal);
|
|
Func_Name : constant Node_Id := Expression (ASN);
|
|
Overloaded : Boolean := Is_Overloaded (Func_Name);
|
|
|
|
I : Interp_Index := 0;
|
|
It : Interp;
|
|
Param_Type : Entity_Id;
|
|
Match_Found : Boolean := False;
|
|
Match2_Found : Boolean := False;
|
|
Is_Match : Boolean;
|
|
Match : Interp;
|
|
Match2 : Entity_Id := Empty;
|
|
|
|
function Matching
|
|
(Param_Id : Entity_Id; Param_Type : Entity_Id) return Boolean;
|
|
-- Return True if Param_Id is a non aliased in parameter whose base type
|
|
-- is Param_Type.
|
|
|
|
--------------
|
|
-- Matching --
|
|
--------------
|
|
|
|
function Matching
|
|
(Param_Id : Entity_Id; Param_Type : Entity_Id) return Boolean is
|
|
begin
|
|
return Base_Type (Etype (Param_Id)) = Param_Type
|
|
and then Ekind (Param_Id) = E_In_Parameter
|
|
and then not Is_Aliased (Param_Id);
|
|
end Matching;
|
|
|
|
begin
|
|
if not Is_Type (Typ) then
|
|
Error_Msg_N ("aspect can only be specified for a type", ASN);
|
|
return;
|
|
|
|
elsif not Is_First_Subtype (Typ) then
|
|
Error_Msg_N ("aspect cannot be specified for a subtype", ASN);
|
|
return;
|
|
end if;
|
|
|
|
if A_Id = Aspect_String_Literal then
|
|
if Is_String_Type (Typ) then
|
|
Error_Msg_N ("aspect cannot be specified for a string type", ASN);
|
|
return;
|
|
end if;
|
|
|
|
Param_Type := Standard_Wide_Wide_String;
|
|
|
|
else
|
|
if Is_Numeric_Type (Typ) then
|
|
Error_Msg_N ("aspect cannot be specified for a numeric type", ASN);
|
|
return;
|
|
end if;
|
|
|
|
Param_Type := Standard_String;
|
|
end if;
|
|
|
|
if not Overloaded and then not Present (Entity (Func_Name)) then
|
|
-- The aspect is specified by a subprogram name, which
|
|
-- may be an operator name given originally by a string.
|
|
|
|
if Is_Operator_Name (Chars (Func_Name)) then
|
|
Analyze_Operator_Symbol (Func_Name);
|
|
else
|
|
Analyze (Func_Name);
|
|
end if;
|
|
|
|
Overloaded := Is_Overloaded (Func_Name);
|
|
end if;
|
|
|
|
if Overloaded then
|
|
Get_First_Interp (Func_Name, I => I, It => It);
|
|
else
|
|
-- only one possible interpretation
|
|
It.Nam := Entity (Func_Name);
|
|
pragma Assert (Present (It.Nam));
|
|
end if;
|
|
|
|
while It.Nam /= Empty loop
|
|
Is_Match := False;
|
|
|
|
if Ekind (It.Nam) = E_Function
|
|
and then Base_Type (Etype (It.Nam)) = Base_Type (Typ)
|
|
then
|
|
declare
|
|
Params : constant List_Id :=
|
|
Parameter_Specifications (Parent (It.Nam));
|
|
Param_Spec : Node_Id;
|
|
|
|
begin
|
|
if List_Length (Params) = 1 then
|
|
Param_Spec := First (Params);
|
|
Is_Match :=
|
|
Matching (Defining_Identifier (Param_Spec), Param_Type);
|
|
|
|
-- Look for the optional overloaded 2-param Real_Literal
|
|
|
|
elsif List_Length (Params) = 2
|
|
and then A_Id = Aspect_Real_Literal
|
|
then
|
|
Param_Spec := First (Params);
|
|
|
|
if Matching (Defining_Identifier (Param_Spec), Param_Type)
|
|
then
|
|
Param_Spec := Next (Param_Spec);
|
|
|
|
if Matching (Defining_Identifier (Param_Spec), Param_Type)
|
|
then
|
|
if No (Match2) then
|
|
Match2 := It.Nam;
|
|
Match2_Found := True;
|
|
else
|
|
-- If we find more than one possible match then
|
|
-- do not take any into account here: since the
|
|
-- 2-parameter version of Real_Literal is optional
|
|
-- we cannot generate an error here, so let
|
|
-- standard resolution fail later if we do need to
|
|
-- call this variant.
|
|
|
|
Match2_Found := False;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end;
|
|
end if;
|
|
|
|
if Is_Match then
|
|
if Match_Found then
|
|
Error_Msg_N ("aspect specification is ambiguous", ASN);
|
|
return;
|
|
end if;
|
|
|
|
Match_Found := True;
|
|
Match := It;
|
|
end if;
|
|
|
|
exit when not Overloaded;
|
|
|
|
if not Is_Match then
|
|
Remove_Interp (I => I);
|
|
end if;
|
|
|
|
Get_Next_Interp (I => I, It => It);
|
|
end loop;
|
|
|
|
if not Match_Found then
|
|
Error_Msg_N
|
|
("function name in aspect specification cannot be resolved", ASN);
|
|
return;
|
|
end if;
|
|
|
|
Set_Entity (Func_Name, Match.Nam);
|
|
Set_Etype (Func_Name, Etype (Match.Nam));
|
|
Set_Is_Overloaded (Func_Name, False);
|
|
|
|
-- Record the match for 2-parameter function if found
|
|
|
|
if Match2_Found then
|
|
Set_Related_Expression (Match.Nam, Match2);
|
|
end if;
|
|
end Validate_Literal_Aspect;
|
|
|
|
----------------------------------------
|
|
-- Validate_Storage_Model_Type_Aspect --
|
|
----------------------------------------
|
|
|
|
procedure Validate_Storage_Model_Type_Aspect
|
|
(Typ : Entity_Id; ASN : Node_Id)
|
|
is
|
|
Assoc : Node_Id;
|
|
Choice : Entity_Id;
|
|
Expr : Node_Id;
|
|
|
|
Address_Type_Id : Entity_Id := Empty;
|
|
Null_Address_Id : Entity_Id := Empty;
|
|
Allocate_Id : Entity_Id := Empty;
|
|
Deallocate_Id : Entity_Id := Empty;
|
|
Copy_From_Id : Entity_Id := Empty;
|
|
Copy_To_Id : Entity_Id := Empty;
|
|
Storage_Size_Id : Entity_Id := Empty;
|
|
|
|
begin
|
|
-- Each expression must resolve to an entity of the right kind or proper
|
|
-- profile.
|
|
|
|
Assoc := First (Component_Associations (Expression (ASN)));
|
|
while Present (Assoc) loop
|
|
Expr := Expression (Assoc);
|
|
Analyze (Expr);
|
|
|
|
Choice := First (Choices (Assoc));
|
|
|
|
if Nkind (Choice) /= N_Identifier or else Present (Next (Choice)) then
|
|
Error_Msg_N ("illegal name in association", Choice);
|
|
|
|
elsif Chars (Choice) = Name_Address_Type then
|
|
if Assoc /= First (Component_Associations (Expression (ASN))) then
|
|
Error_Msg_N ("Address_Type must be first association", Choice);
|
|
end if;
|
|
|
|
Resolve_Storage_Model_Type_Argument
|
|
(Expr, Typ, Address_Type_Id, Name_Address_Type);
|
|
Address_Type_Id := Entity (Expr);
|
|
|
|
-- Shouldn't we check for duplicates of the same subaspect name,
|
|
-- and issue an error in such cases???
|
|
|
|
elsif not Present (Address_Type_Id) then
|
|
Error_Msg_N
|
|
("Address_Type missing, must be first association", Choice);
|
|
|
|
elsif Chars (Choice) = Name_Null_Address then
|
|
Resolve_Storage_Model_Type_Argument
|
|
(Expr, Typ, Address_Type_Id, Name_Null_Address);
|
|
Null_Address_Id := Entity (Expr);
|
|
|
|
elsif Chars (Choice) = Name_Allocate then
|
|
Resolve_Storage_Model_Type_Argument
|
|
(Expr, Typ, Address_Type_Id, Name_Allocate);
|
|
Allocate_Id := Entity (Expr);
|
|
|
|
elsif Chars (Choice) = Name_Deallocate then
|
|
Resolve_Storage_Model_Type_Argument
|
|
(Expr, Typ, Address_Type_Id, Name_Deallocate);
|
|
Deallocate_Id := Entity (Expr);
|
|
|
|
elsif Chars (Choice) = Name_Copy_From then
|
|
Resolve_Storage_Model_Type_Argument
|
|
(Expr, Typ, Address_Type_Id, Name_Copy_From);
|
|
Copy_From_Id := Entity (Expr);
|
|
|
|
elsif Chars (Choice) = Name_Copy_To then
|
|
Resolve_Storage_Model_Type_Argument
|
|
(Expr, Typ, Address_Type_Id, Name_Copy_To);
|
|
Copy_To_Id := Entity (Expr);
|
|
|
|
elsif Chars (Choice) = Name_Storage_Size then
|
|
Resolve_Storage_Model_Type_Argument
|
|
(Expr, Typ, Address_Type_Id, Name_Storage_Size);
|
|
Storage_Size_Id := Entity (Expr);
|
|
|
|
else
|
|
Error_Msg_N
|
|
("invalid name for Storage_Model_Type argument", Choice);
|
|
end if;
|
|
|
|
Next (Assoc);
|
|
end loop;
|
|
|
|
if No (Address_Type_Id) then
|
|
Error_Msg_N ("match for Address_Type not found", ASN);
|
|
|
|
elsif No (Null_Address_Id) then
|
|
Error_Msg_N ("match for Null_Address primitive not found", ASN);
|
|
|
|
elsif No (Allocate_Id) then
|
|
Error_Msg_N ("match for Allocate primitive not found", ASN);
|
|
|
|
elsif No (Deallocate_Id) then
|
|
Error_Msg_N ("match for Deallocate primitive not found", ASN);
|
|
|
|
elsif No (Copy_From_Id) then
|
|
Error_Msg_N ("match for Copy_From primitive not found", ASN);
|
|
|
|
elsif No (Copy_To_Id) then
|
|
Error_Msg_N ("match for Copy_To primitive not found", ASN);
|
|
|
|
elsif No (Storage_Size_Id) then
|
|
Error_Msg_N ("match for Storage_Size primitive not found", ASN);
|
|
end if;
|
|
end Validate_Storage_Model_Type_Aspect;
|
|
|
|
-----------------------------------
|
|
-- Validate_Unchecked_Conversion --
|
|
-----------------------------------
|
|
|
|
procedure Validate_Unchecked_Conversion
|
|
(N : Node_Id;
|
|
Act_Unit : Entity_Id)
|
|
is
|
|
Source : Entity_Id;
|
|
Target : Entity_Id;
|
|
|
|
procedure Warn_Nonportable (RE : RE_Id);
|
|
-- Warn if either source or target of the conversion is a predefined
|
|
-- private type, whose representation might differ between releases and
|
|
-- targets of the compiler.
|
|
|
|
----------------------
|
|
-- Warn_Nonportable --
|
|
----------------------
|
|
|
|
procedure Warn_Nonportable (RE : RE_Id) is
|
|
begin
|
|
if Is_RTE (Source, RE) or else Is_RTE (Target, RE) then
|
|
pragma Assert (Is_Private_Type (RTE (RE)));
|
|
Error_Msg_NE
|
|
("?z?representation of & values may change between "
|
|
& "'G'N'A'T versions", N, RTE (RE));
|
|
end if;
|
|
end Warn_Nonportable;
|
|
|
|
-- Local variables
|
|
|
|
Vnode : Node_Id;
|
|
|
|
-- Start of processing for Validate_Unchecked_Conversion
|
|
|
|
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.
|
|
|
|
Source := Ancestor_Subtype (Etype (First_Formal (Act_Unit)));
|
|
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;
|
|
|
|
-- Warn if one of the operands is a private type declared in
|
|
-- Ada.Calendar or Ada.Real_Time. Do not emit a warning when compiling
|
|
-- GNAT-related sources.
|
|
|
|
if Warn_On_Unchecked_Conversion
|
|
and then not In_Predefined_Unit (N)
|
|
then
|
|
Warn_Nonportable (RO_CA_Time);
|
|
Warn_Nonportable (RO_RT_Time);
|
|
Warn_Nonportable (RE_Time_Span);
|
|
end if;
|
|
|
|
-- 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).
|
|
|
|
if Is_Private_Type (Source)
|
|
and then Present (Underlying_Type (Source))
|
|
then
|
|
Source := Underlying_Type (Source);
|
|
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, except in relaxed
|
|
-- semantics mode.
|
|
|
|
if Is_Array_Type (Target)
|
|
and then not Is_Constrained (Target)
|
|
and then not Relaxed_RM_Semantics
|
|
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
|
|
|
|
if Is_Access_Subprogram_Type (Target)
|
|
or else Is_Access_Subprogram_Type (Source)
|
|
then
|
|
Error_Msg_N
|
|
("?z?conversion between pointers with different conventions!",
|
|
N);
|
|
end if;
|
|
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;
|
|
|
|
-- If the unchecked conversion is between Address and an access
|
|
-- subprogram type, show that we shouldn't use an internal
|
|
-- representation for the access subprogram type.
|
|
|
|
if Is_Access_Subprogram_Type (Target)
|
|
and then Is_Descendant_Of_Address (Source)
|
|
and then In_Same_Source_Unit (Target, N)
|
|
then
|
|
Set_Can_Use_Internal_Rep (Target, False);
|
|
elsif Is_Access_Subprogram_Type (Source)
|
|
and then Is_Descendant_Of_Address (Target)
|
|
and then In_Same_Source_Unit (Source, N)
|
|
then
|
|
Set_Can_Use_Internal_Rep (Source, False);
|
|
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
|
|
function Is_Null_Array (T : Entity_Id) return Boolean;
|
|
-- We want to warn in the case of converting to a wrong-sized array of
|
|
-- bytes, including the zero-size case. This returns True in that case,
|
|
-- which is necessary because a size of 0 is used to indicate both an
|
|
-- unknown size and a size of 0. It's OK for this to return True in
|
|
-- other zero-size cases, but we don't go out of our way; for example,
|
|
-- we don't bother with multidimensional arrays.
|
|
|
|
function Is_Null_Array (T : Entity_Id) return Boolean is
|
|
begin
|
|
if Is_Array_Type (T) and then Is_Constrained (T) then
|
|
declare
|
|
Index : constant Node_Id := First_Index (T);
|
|
R : Node_Id; -- N_Range
|
|
begin
|
|
case Nkind (Index) is
|
|
when N_Range =>
|
|
R := Index;
|
|
when N_Subtype_Indication =>
|
|
R := Range_Expression (Constraint (Index));
|
|
when N_Identifier | N_Expanded_Name =>
|
|
R := Scalar_Range (Entity (Index));
|
|
when others =>
|
|
raise Program_Error;
|
|
end case;
|
|
|
|
return Is_Null_Range (Low_Bound (R), High_Bound (R));
|
|
end;
|
|
end if;
|
|
|
|
return False;
|
|
end Is_Null_Array;
|
|
|
|
begin
|
|
for N in Unchecked_Conversions.First .. Unchecked_Conversions.Last loop
|
|
declare
|
|
T : UC_Entry renames Unchecked_Conversions.Table (N);
|
|
|
|
Act_Unit : constant Entity_Id := T.Act_Unit;
|
|
Eloc : constant Source_Ptr := T.Eloc;
|
|
Source : constant Entity_Id := T.Source;
|
|
Target : constant Entity_Id := T.Target;
|
|
|
|
Source_Siz : Uint;
|
|
Target_Siz : Uint;
|
|
|
|
begin
|
|
-- Skip if function marked as warnings off
|
|
|
|
if Warnings_Off (Act_Unit) or else Serious_Errors_Detected > 0 then
|
|
goto Continue;
|
|
end if;
|
|
|
|
-- 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.
|
|
|
|
if Has_Warnings_Off (Source) or Has_Warnings_Off (Target) then
|
|
goto Continue;
|
|
end if;
|
|
|
|
if (Known_Static_RM_Size (Source)
|
|
and then Known_Static_RM_Size (Target))
|
|
or else Is_Null_Array (Target)
|
|
then
|
|
-- This validation check, which warns if we have unequal sizes
|
|
-- for unchecked conversion, and thus 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.
|
|
|
|
Source_Siz := RM_Size (Source);
|
|
Target_Siz := RM_Size (Target);
|
|
|
|
if Present (Source_Siz) and then Present (Target_Siz)
|
|
and then Source_Siz /= Target_Siz
|
|
then
|
|
Error_Msg
|
|
("?z?types for unchecked conversion have different sizes!",
|
|
Eloc, Act_Unit);
|
|
|
|
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, Act_Unit);
|
|
else
|
|
Error_Msg
|
|
("\?z?target value will include ^ undefined "
|
|
& "high order bits!", Eloc, Act_Unit);
|
|
end if;
|
|
|
|
else
|
|
Error_Msg
|
|
("\?z?^ trailing bits of target value will be "
|
|
& "undefined!", Eloc, Act_Unit);
|
|
end if;
|
|
|
|
else pragma Assert (Source_Siz > Target_Siz);
|
|
if Is_Discrete_Type (Source) then
|
|
if Bytes_Big_Endian then
|
|
Error_Msg
|
|
("\?z?^ low order bits of source will be "
|
|
& "ignored!", Eloc, Act_Unit);
|
|
else
|
|
Error_Msg
|
|
("\?z?^ high order bits of source will be "
|
|
& "ignored!", Eloc, Act_Unit);
|
|
end if;
|
|
|
|
else
|
|
Error_Msg
|
|
("\?z?^ trailing bits of source will be "
|
|
& "ignored!", Eloc, Act_Unit);
|
|
end if;
|
|
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, Act_Unit);
|
|
Error_Msg
|
|
("\?z?resulting access value may have invalid "
|
|
& "alignment!", Eloc, Act_Unit);
|
|
end if;
|
|
end;
|
|
end if;
|
|
end;
|
|
end if;
|
|
end;
|
|
|
|
<<Continue>>
|
|
null;
|
|
end loop;
|
|
end Validate_Unchecked_Conversions;
|
|
|
|
end Sem_Ch13;
|