| ------------------------------------------------------------------------------ |
| -- -- |
| -- 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_Ch3; use Exp_Ch3; |
| 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 System.Case_Util; use System.Case_Util; |
| 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_Function (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. |
| |
| 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; |
| Warn : Boolean := True) 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 little 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. |
| -- |
| -- The Warn parameter is True iff this is not a recursive call. This |
| -- parameter is used to avoid generating warnings for subexpressions and |
| -- for cases where the predicate expression (as originally written by |
| -- the user, before any transformations) is a Boolean literal. |
| |
| 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 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; |
| |
| ------------------------------------- |
| -- 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 No (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 |
| No (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; |
| |
| 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. |
| |
| function Relocate_Expression (Source : Node_Id) return Node_Id; |
| -- Outside of a generic this function is equivalent to Relocate_Node. |
| -- Inside a generic it is an identity function, because Relocate_Node |
| -- would create a new node that is not associated with the generic |
| -- template. This association is needed to save references to entities |
| -- that are global to the generic (and might be not visible from where |
| -- the generic is instantiated). |
| -- |
| -- Inside a generic the original tree is shared between aspect and |
| -- a corresponding pragma (or an attribute definition clause). This |
| -- parallels what is done in sem_prag.adb (see Get_Argument). |
| |
| -------------- |
| -- 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; |
| |
| ------------------------- |
| -- Relocate_Expression -- |
| ------------------------- |
| |
| function Relocate_Expression (Source : Node_Id) return Node_Id is |
| begin |
| if Inside_A_Generic then |
| return Source; |
| else |
| return Atree.Relocate_Node (Source); |
| end if; |
| end Relocate_Expression; |
| |
| -- 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 No (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 (All_Extensions_Allowed and then Is_Imported_Intrinsic) |
| then |
| if All_Extensions_Allowed then |
| Error_Msg_N |
| ("aspect % requires intrinsic or expression function", |
| Aspect); |
| |
| elsif Is_Imported_Intrinsic then |
| Error_Msg_GNAT_Extension |
| ("aspect % on intrinsic function", Sloc (Aspect), |
| Is_Core_Extension => True); |
| |
| 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 entity for 'Yield |
| |
| if (Is_Subprogram (E) |
| or else Is_Generic_Subprogram (E) |
| or else Is_Entry (E)) |
| 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_Expression (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_Expression (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_Expression (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_Expression (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 All_Extensions_Allowed then |
| Error_Msg_GNAT_Extension ("aspect %", Sloc (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 All_Extensions_Allowed then |
| Error_Msg_GNAT_Extension ("aspect %", Sloc (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 |
| |
| Aitem := Make_Aitem_Pragma |
| (Pragma_Argument_Associations => New_List ( |
| Make_Pragma_Argument_Association (Eloc, |
| Chars => Name_Check, |
| Expression => Relocate_Expression (Expr))), |
| Pragma_Name => Pname); |
| |
| -- Add message unless exception messages are suppressed |
| |
| if not Opt.Exception_Locations_Suppressed then |
| Append_To (Pragma_Argument_Associations (Aitem), |
| Make_Pragma_Argument_Association (Eloc, |
| Chars => Name_Message, |
| Expression => |
| Make_String_Literal (Eloc, |
| Strval => "failed " |
| & Get_Name_String (Pname) |
| & " from " |
| & Build_Location_String (Eloc)))); |
| end if; |
| |
| Set_Is_Delayed_Aspect (Aspect); |
| |
| -- For Pre/Post cases, insert immediately after the entity |
| -- declaration, since that is the required pragma placement. |
| -- Note that for these aspects, we do not have to worry |
| -- about delay issues, since the pragmas themselves deal |
| -- with delay of visibility for the expression analysis. |
| |
| Insert_Pragma (Aitem); |
| |
| goto Continue; |
| end Pre_Post; |
| |
| -- Test_Case |
| |
| when Aspect_Test_Case => Test_Case : declare |
| Args : List_Id; |
| Comp_Expr : Node_Id; |
| Comp_Assn : Node_Id; |
| |
| 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 address clause for an object |
| |
| elsif Ekind (U_Ent) in E_Constant | E_Variable then |
| |
| -- Disallow case of an address clause for an object of an |
| -- indefinite subtype which takes its bounds/discriminant/tag |
| -- from its initial value. Without this, we get a Gigi |
| -- assertion failure for things like |
| -- X : String := Some_Function (...) with Address => ...; |
| -- where the result subtype of the function is unconstrained. |
| -- |
| -- We want to reject two cases: the class-wide case, and the |
| -- case where the FE conjures up a renaming declaration and |
| -- would then otherwise generate an address specification for |
| -- that renaming (which is a malformed tree, which is why Gigi |
| -- complains). |
| |
| if Is_Class_Wide_Type (Etype (U_Ent)) then |
| Error_Msg_N |
| ("address specification not supported for class-wide " & |
| "object declaration", Nam); |
| return; |
| elsif Is_Constr_Subt_For_U_Nominal (Etype (U_Ent)) |
| and then |
| Nkind (Parent (U_Ent)) = N_Object_Renaming_Declaration |
| then |
| -- Confirm accuracy of " and dynamic size" message text |
| -- before including it. We want to include that text when |
| -- it is correct because it may be useful to the reader. |
| -- The case where we omit that part of the message text |
| -- might be dead code, but let's not rely on that. |
| |
| Error_Msg_N |
| ("address specification not supported for object " & |
| "declaration with indefinite nominal subtype" & |
| (if Size_Known_At_Compile_Time (Etype (U_Ent)) |
| then "" |
| else " and dynamic size"), Nam); |
| return; |
| end if; |
| |
| 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 ("?o?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); |
| return; |
| 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)) |
| or else Ekind (Entity (Expression (Assoc))) /= E_Function |
| 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; |
| |
| -- As of RM 13.1, only confirming size |
| -- (i.e. (Size = Esize (Etyp))) for aliased object of |
| -- elementary type must be supported. |
| -- GNAT rejects nonconfirming size for such object. |
| |
| if Is_Aliased (U_Ent) |
| and then Is_Elementary_Type (Etyp) |
| and then Known_Esize (U_Ent) |
| and then Size /= Esize (Etyp) |
| then |
| Error_Msg_N |
| ("nonconfirming Size for aliased object is not " |
| & "supported", N); |
| 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 No (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) 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_Function -- |
| ------------------------------ |
| |
| -- The function constructed here has 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; |
| |
| -- If Predicate_Function_Needs_Membership_Parameter is true, then this |
| -- function takes an additional boolean parameter; the parameter |
| -- indicates whether the predicate evaluation is part of a membership |
| -- test. This parameter is used in two cases: 1) It is passed along |
| -- if another predicate function is called and that predicate function |
| -- expects to be passed a boolean parameter. 2) If the Predicate_Failure |
| -- aspect is directly specified for typ, then we replace the return |
| -- expression described above with |
| -- (if <expression described above> then True |
| -- elsif For_Membership_Test then False |
| -- else (raise Assertion_Error |
| -- with <Predicate_Failure expression>)) |
| -- 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. |
| |
| -- 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_Function (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. |
| |
| 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 |
| |
| FDecl : Node_Id; |
| -- The function declaration |
| |
| SId : Entity_Id; |
| -- Its entity |
| |
| Ancestor_Predicate_Function_Called : Boolean := False; |
| -- Does this predicate function include a call to the |
| -- predication function of an ancestor subtype? |
| |
| 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. |
| |
| procedure Replace_Current_Instance_References |
| (N : Node_Id; Typ, New_Entity : Entity_Id); |
| -- Replace all references to Typ in the tree rooted at N with |
| -- references to Param. [New_Entity will be a formal parameter of a |
| -- predicate function.] |
| |
| -------------- |
| -- 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. |
| |
| declare |
| Dynamic_Mem : Node_Id := Empty; |
| Second_Formal : constant Entity_Id := |
| Next_Entity (Object_Entity); |
| begin |
| -- Some predicate functions require a second parameter; |
| -- If one predicate function calls another and the second |
| -- requires two parameters, then the first should also |
| -- take two parameters (so that the first function has |
| -- something to pass to the second function). |
| if Predicate_Function_Needs_Membership_Parameter (T) then |
| pragma Assert (Present (Second_Formal)); |
| Dynamic_Mem := New_Occurrence_Of (Second_Formal, Loc); |
| end if; |
| |
| Exp := |
| Make_Predicate_Call |
| (Typ => T, |
| Expr => |
| Unchecked_Convert_To (T, |
| Make_Identifier (Loc, Object_Name)), |
| Dynamic_Mem => Dynamic_Mem); |
| end; |
| |
| -- "and"-in the call to evolving expression |
| |
| Add_Condition (Exp); |
| Ancestor_Predicate_Function_Called := True; |
| |
| -- 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 List_Inherited_Aspects |
| and then not Is_Generic_Actual_Type (Typ) |
| and then Instantiation_Location (Sloc (Typ)) = No_Location |
| 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 |
| -- 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 |
| |
| Arg1 := First (Pragma_Argument_Associations (Prag)); |
| Arg2 := Next (Arg1); |
| |
| Arg1 := Get_Pragma_Arg (Arg1); |
| Arg2 := 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 |
| declare |
| Arg2_Copy : constant Node_Id := New_Copy_Tree (Arg2); |
| begin |
| Replace_Current_Instance_References |
| (Arg2_Copy, Typ => Typ, New_Entity => Object_Entity); |
| |
| -- 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_Copy)); |
| end if; |
| |
| -- "and"-in the Arg2 condition to evolving expression |
| |
| Add_Condition (Arg2_Copy); |
| end; |
| 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; |
| |
| ----------------------------------------- |
| -- Replace_Current_Instance_References -- |
| ----------------------------------------- |
| |
| procedure Replace_Current_Instance_References |
| (N : Node_Id; Typ, New_Entity : Entity_Id) |
| is |
| Root : Node_Id renames N; |
| |
| procedure Replace_One_Reference (N : Node_Id); |
| -- Actual parameter for Replace_Type_References_Generic instance |
| |
| --------------------------- |
| -- Replace_One_Reference -- |
| --------------------------- |
| |
| procedure Replace_One_Reference (N : Node_Id) is |
| pragma Assert (In_Subtree (N, Root => Root)); |
| begin |
| Rewrite (N, New_Occurrence_Of (New_Entity, Sloc (N))); |
| -- Use the Sloc of the usage name, not the defining name |
| end Replace_One_Reference; |
| |
| procedure Replace_Type_References is |
| new Replace_Type_References_Generic (Replace_One_Reference); |
| begin |
| Replace_Type_References (N, Typ); |
| end Replace_Current_Instance_References; |
| |
| -- 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_Function |
| |
| 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); |
| |
| -- 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 |
| |
| -- 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 |
| |
| declare |
| Param_Specs : constant List_Id := New_List ( |
| Make_Parameter_Specification (Loc, |
| Defining_Identifier => |
| Make_Defining_Identifier (Loc, Object_Name), |
| Parameter_Type => |
| New_Occurrence_Of (Typ, Loc))); |
| begin |
| -- if Spec has 2 parameters, then body should too |
| if Present (Next_Entity (Object_Entity)) then |
| Append (Make_Parameter_Specification (Loc, |
| Defining_Identifier => |
| Make_Defining_Identifier |
| (Loc, Chars (Next_Entity (Object_Entity))), |
| Parameter_Type => |
| New_Occurrence_Of (Standard_Boolean, Loc)), |
| Param_Specs); |
| end if; |
| |
| Spec := |
| Make_Function_Specification (Loc, |
| Defining_Unit_Name => SIdB, |
| Parameter_Specifications => Param_Specs, |
| Result_Definition => |
| New_Occurrence_Of (Standard_Boolean, Loc)); |
| end; |
| |
| -- The Predicate_Expression attribute is used by SPARK. |
| -- |
| -- If Ancestor_Predicate_Function_Called is True, then |
| -- we try to exclude that call to the ancestor's |
| -- predicate function by calling Right_Opnd. |
| -- The call is not excluded in the case where |
| -- it is not "and"ed with anything else (so we don't have |
| -- an N_And_Then node). This exclusion is required if the |
| -- Predicate_Failure aspect is specified for Typ because |
| -- in that case we are going to drop the N_And_Then node |
| -- on the floor. Otherwise, it is a question of what is |
| -- most convenient for SPARK. |
| |
| Set_Predicate_Expression |
| (SId, (if Ancestor_Predicate_Function_Called |
| and then Nkind (Expr) = N_And_Then |
| then Right_Opnd (Expr) |
| else Expr)); |
| |
| declare |
| Result_Expr : Node_Id := Expr; |
| PF_Expr : Node_Id := Predicate_Failure_Expression |
| (Typ, Inherited_OK => False); |
| PF_Expr_Copy : Node_Id; |
| Second_Formal : constant Entity_Id := |
| Next_Entity (Object_Entity); |
| begin |
| -- In GNATprove mode we are only interested in the predicate |
| -- expression itself and don't want a raise expression that |
| -- comes from the Predicate_Failure. Ditto for CodePeer. |
| -- And an illegal Predicate_Failure aspect can lead to cases |
| -- we want to avoid. |
| |
| if Present (PF_Expr) |
| and then not GNATprove_Mode |
| and then not CodePeer_Mode |
| and then Serious_Errors_Detected = 0 |
| then |
| pragma Assert (Present (Second_Formal)); |
| |
| -- This is an ugly hack to cope with an ugly situation. |
| -- PF_Expr may have children whose Parent attribute |
| -- does not point back to PF_Expr. If we pass such a |
| -- tree to New_Copy_Tree, then it does not make a deep |
| -- copy. But we need a deep copy. So we need to find a |
| -- tree for which New_Copy_Tree *will* make a deep copy. |
| |
| declare |
| function Check_Node_Parent (Parent_Node, Node : Node_Id) |
| return Traverse_Result; |
| function Check_Node_Parent (Parent_Node, Node : Node_Id) |
| return Traverse_Result is |
| begin |
| if Parent_Node = PF_Expr |
| and then not Is_List_Member (Node) |
| then |
| pragma Assert |
| (Nkind (PF_Expr) = Nkind (Parent (Node))); |
| |
| -- We need PF_Expr to be a node for which |
| -- New_Copy_Tree will make a deep copy. |
| PF_Expr := Parent (Node); |
| return Abandon; |
| end if; |
| return OK; |
| end Check_Node_Parent; |
| procedure Check_Parentage is |
| new Traverse_Proc_With_Parent (Check_Node_Parent); |
| begin |
| Check_Parentage (PF_Expr); |
| PF_Expr_Copy := New_Copy_Tree (PF_Expr); |
| end; |
| |
| -- Current instance uses need to have their Entity |
| -- fields set so that Replace_Current_Instance_References |
| -- can find them. So we preanalyze. Just for purposes of |
| -- calls to Is_Current_Instance during this preanalysis, |
| -- we set the Parent field. |
| Set_Parent (PF_Expr_Copy, Parent (PF_Expr)); |
| Preanalyze (PF_Expr_Copy); |
| Set_Parent (PF_Expr_Copy, Empty); |
| |
| Replace_Current_Instance_References |
| (PF_Expr_Copy, Typ => Typ, New_Entity => Object_Entity); |
| |
| if Ancestor_Predicate_Function_Called then |
| -- If the call to an ancestor predicate function |
| -- returns False, we do not want to raise an |
| -- exception here. Our Predicate_Failure aspect does |
| -- not apply in that case. So we have to build a |
| -- more complicated result expression: |
| -- (if not Ancestor_Predicate_Function (...) then False |
| -- elsif Noninherited_Predicates (...) then True |
| -- elsif Is_Membership_Test then False |
| -- else (raise Assertion_Error with PF text)) |
| |
| declare |
| Ancestor_Call : constant Node_Id := |
| Left_Opnd (Result_Expr); |
| Local_Preds : constant Node_Id := |
| Right_Opnd (Result_Expr); |
| begin |
| Result_Expr := |
| Make_If_Expression (Loc, |
| Expressions => New_List ( |
| Make_Op_Not (Loc, Ancestor_Call), |
| New_Occurrence_Of (Standard_False, Loc), |
| Make_If_Expression (Loc, |
| Is_Elsif => True, |
| Expressions => New_List ( |
| Local_Preds, |
| New_Occurrence_Of (Standard_True, Loc), |
| Make_If_Expression (Loc, |
| Is_Elsif => True, |
| Expressions => New_List ( |
| New_Occurrence_Of (Second_Formal, Loc), |
| New_Occurrence_Of (Standard_False, Loc), |
| Make_Raise_Expression (Loc, |
| New_Occurrence_Of (RTE |
| (RE_Assert_Failure), Loc), |
| PF_Expr_Copy))))))); |
| end; |
| |
| else |
| -- Build a conditional expression: |
| -- (if <predicate evaluates to True> then True |
| -- elsif Is_Membership_Test then False |
| -- else (raise Assertion_Error with PF text)) |
| |
| Result_Expr := |
| Make_If_Expression (Loc, |
| Expressions => New_List ( |
| Result_Expr, |
| New_Occurrence_Of (Standard_True, Loc), |
| Make_If_Expression (Loc, |
| Is_Elsif => True, |
| Expressions => New_List ( |
| New_Occurrence_Of (Second_Formal, Loc), |
| New_Occurrence_Of (Standard_False, Loc), |
| Make_Raise_Expression (Loc, |
| New_Occurrence_Of (RTE |
| (RE_Assert_Failure), Loc), |
| PF_Expr_Copy))))); |
| end if; |
| end if; |
| |
| 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 => Result_Expr)))); |
| end; |
| |
| -- 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; |
| |
| -- 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_Function; |
| |
| ------------------------------------------ |
| -- 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); |
| |
| declare |
| Param_Specs : constant List_Id := New_List ( |
| Make_Parameter_Specification (Loc, |
| Defining_Identifier => Make_Temporary (Loc, 'I'), |
| Parameter_Type => New_Occurrence_Of (Typ, Loc))); |
| begin |
| if Predicate_Function_Needs_Membership_Parameter (Typ) then |
| -- Add Boolean-valued For_Membership_Test param |
| Append (Make_Parameter_Specification (Loc, |
| Defining_Identifier => Make_Temporary (Loc, 'M'), |
| Parameter_Type => |
| New_Occurrence_Of (Standard_Boolean, Loc)), |
| Param_Specs); |
| end if; |
| |
| Spec := |
| Make_Function_Specification (Loc, |
| Defining_Unit_Name => Func_Id, |
| Parameter_Specifications => Param_Specs, |
| Result_Definition => |
| New_Occurrence_Of (Standard_Boolean, Loc)); |
| end; |
| |
| 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; |
| |
| -- As for some other aspects above, the expression of this aspect is |
| -- just an entity that does not need any resolution, so just analyze. |
| |
| when Aspect_Designated_Storage_Model => |
| Analyze (Expression (ASN)); |
| return; |
| |
| when Aspect_Storage_Model_Type => |
| |
| -- The aggregate argument of Storage_Model_Type is optional, and |
| -- when not present the aspect defaults to the native storage |
| -- model (where the address type is System.Address, and other |
| -- arguments default to corresponding native storage operations). |
| |
| if No (Expression (ASN)) then |
| return; |
| end if; |
| |
| 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; |
| |
| -- Here is the list of aspects that don't require delay analysis |
| |
| 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 |
| Nod1 := First (Lst); |
| while Present (Nod1) loop |
| Check_Expr_Constants (Nod1); |
| Next (Nod1); |
| end loop; |
| 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 |
| |
| 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; |
| |
| -- 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; |
| |
| -- 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 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; |
| |
| -- Before we build a predicate function, ensure that discriminant |
| -- checking functions are available. The predicate function might |
| -- need to call these functions if the predicate references any |
| -- components declared in a variant part. |
| |
| if Ekind (E) = E_Record_Type and then Has_Discriminants (E) then |
| Build_Or_Copy_Discr_Checking_Funcs (Parent (E)); |
| end if; |
| |
| Build_Predicate_Function (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_Function 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; |
| |
| 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_Typ, Operand_Typ : Entity_Id) return Boolean |
| is |
| -- The subtype-specific representation attributes (Size and Alignment) |
| -- do not affect representation from the point of view of this function. |
| |
| T1 : constant Entity_Id := Implementation_Base_Type (Target_Typ); |
| T2 : constant Entity_Id := Implementation_Base_Type (Operand_Typ); |
| |
| begin |
| -- Return true immediately for the same base type |
| |
| if T1 = T2 then |
| return True; |
| |
| -- Tagged types always have the same representation, because it is not |
| -- possible to specify different representations for common fields. |
| |
| elsif Is_Tagged_Type (T1) then |
| return True; |
| |
| -- Representations are definitely different if conventions differ |
| |
| elsif Convention (T1) /= Convention (T2) then |
| return False; |
| |
| -- Representations are different if component alignments or scalar |
| -- storage orders differ. |
| |
| elsif (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; |
| |
| -- For records, representations are different if reordering differs |
| |
| elsif 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; |
| |
| -- If the operand type is derived from the target type and no clause |
| -- has been given after the derivation, then the representations are |
| -- the same since the derived type inherits that of the parent type. |
| |
| elsif Is_Derived_Type (T2) |
| and then Etype (T2) = T1 |
| and then not Has_Record_Rep_Clause (T2) |
| then |
| return True; |
| |
| -- 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. |
| |
| -- 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 |
| declare |
| E : Entity_Id; |
| |
| begin |
| E := Entity (Get_Rep_Item (Typ, Name_Default_Component_Value)); |
| |
| -- Deal with private types |
| |
| if Is_Private_Type (E) then |
| E := Full_View (E); |
| end if; |
| |
| Set_Default_Aspect_Component_Value (Typ, |
| Default_Aspect_Component_Value (E)); |
| end; |
| 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); |
| |
| declare |
| E : Entity_Id; |
| |
| begin |
| E := Entity (Get_Rep_Item (Typ, Name_Default_Value)); |
| |
| -- Deal with private types |
| |
| if Is_Private_Type (E) then |
| E := Full_View (E); |
| end if; |
| |
| Set_Default_Aspect_Value (Typ, Default_Aspect_Value (E)); |
| end; |
| 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; |
| |
| --------------------------------- |
| -- Inherit_Delayed_Rep_Aspects -- |
| --------------------------------- |
| |
| procedure Inherit_Delayed_Rep_Aspects (Typ : Entity_Id) is |
| A : Aspect_Id; |
| N : Node_Id; |
| P : Entity_Id; |
| |
| begin |
| -- Find the first aspect that has been inherited |
| |
| N := First_Rep_Item (Typ); |
| while Present (N) loop |
| if Nkind (N) = N_Aspect_Specification then |
| exit when Entity (N) /= Typ; |
| end if; |
| |
| Next_Rep_Item (N); |
| end loop; |
| |
| -- There must be one if we reach here |
| |
| pragma Assert (Present (N)); |
| P := Entity (N); |
| |
| -- Loop through delayed aspects for the parent type |
| |
| 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 (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 (Typ) then |
| Set_Alignment (Typ, Alignment (P)); |
| end if; |
| |
| -- Atomic |
| |
| when Aspect_Atomic => |
| if Is_Atomic (P) then |
| Set_Is_Atomic (Typ); |
| end if; |
| |
| -- Atomic_Components |
| |
| when Aspect_Atomic_Components => |
| if Has_Atomic_Components (P) then |
| Set_Has_Atomic_Components (Base_Type (Typ)); |
| end if; |
| |
| -- Bit_Order |
| |
| when Aspect_Bit_Order => |
| if Is_Record_Type (Typ) |
| and then No (Get_Attribute_Definition_Clause |
| (Typ, Attribute_Bit_Order)) |
| and then Reverse_Bit_Order (P) |
| then |
| Set_Reverse_Bit_Order (Base_Type (Typ)); |
| end if; |
| |
| -- Component_Size |
| |
| when Aspect_Component_Size => |
| if Is_Array_Type (Typ) |
| and then not Has_Component_Size_Clause (Typ) |
| then |
| Set_Component_Size |
| (Base_Type (Typ), Component_Size (P)); |
| end if; |
| |
| -- Machine_Radix |
| |
| when Aspect_Machine_Radix => |
| if Is_Decimal_Fixed_Point_Type (Typ) |
| and then not Has_Machine_Radix_Clause (Typ) |
| then |
| Set_Machine_Radix_10 (Typ, 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 (Typ) |
| and then |
| No (Get_Attribute_Definition_Clause |
| (Typ, Attribute_Object_Size)) |
| then |
| Set_Esize (Typ, Esize (P)); |
| end if; |
| |
| -- Pack |
| |
| when Aspect_Pack => |
| if not Is_Packed (Typ) then |
| Set_Is_Packed (Base_Type (Typ)); |
| |
| if Is_Bit_Packed_Array (P) then |
| Set_Is_Bit_Packed_Array (Base_Type (Typ)); |
| Set_Packed_Array_Impl_Type |
| (Typ, Packed_Array_Impl_Type (P)); |
| end if; |
| end if; |
| |
| -- Scalar_Storage_Order |
| |
| when Aspect_Scalar_Storage_Order => |
| if (Is_Record_Type (Typ) or else Is_Array_Type (Typ)) |
| and then No (Get_Attribute_Definition_Clause |
| (Typ, Attribute_Scalar_Storage_Order)) |
| and then Reverse_Storage_Order (P) |
| then |
| Set_Reverse_Storage_Order (Base_Type (Typ)); |
| |
| -- Clear default SSO indications, since the aspect |
| -- overrides the default. |
| |
| Set_SSO_Set_Low_By_Default (Base_Type (Typ), False); |
| Set_SSO_Set_High_By_Default (Base_Type (Typ), False); |
| end if; |
| |
| -- Small |
| |
| when Aspect_Small => |
| if Is_Fixed_Point_Type (Typ) |
| and then not Has_Small_Clause (Typ) |
| then |
| Set_Small_Value (Typ, Small_Value (P)); |
| end if; |
| |
| -- Storage_Size |
| |
| when Aspect_Storage_Size => |
| if (Is_Access_Type (Typ) or else Is_Task_Type (Typ)) |
| and then not Has_Storage_Size_Clause (Typ) |
| then |
| Set_Storage_Size_Variable |
| (Base_Type (Typ), 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 (Typ); |
| 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 (Typ); |
| end if; |
| |
| -- Volatile_Components |
| |
| when Aspect_Volatile_Components => |
| if Has_Volatile_Components (P) then |
| Set_Has_Volatile_Components (Base_Type (Typ)); |
| end if; |
| |
| -- That should be all the Rep Aspects |
| |
| when others => |
| pragma Assert (Aspect_Delay (A) /= Rep_Aspect); |
| null; |
| end case; |
| end if; |
| end if; |
| |
| Next_Rep_Item (N); |
| end loop; |
| end Inherit_Delayed_Rep_Aspects; |
| |
| ---------------- |
| -- 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; |
| Warn : Boolean := True) 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); |
| end Is_Type_Ref; |
| |
| -- helper function for recursive calls |
| function Is_Predicate_Static_Aux (Expr : Node_Id) return Boolean is |
| (Is_Predicate_Static (Expr, Nam, Warn => False)); |
| |
| -- Start of processing for Is_Predicate_Static |
| |
| begin |
| -- Handle cases like |
| -- subtype S is Integer with Static_Predicate => |
| -- (Some_Integer_Variable in Integer) and then (S /= 0); |
| -- where the predicate (which should be rejected) might have been |
| -- transformed into just "(S /= 0)", which would appear to be |
| -- a predicate-static expression (and therefore legal). |
| |
| if Original_Node (Expr) /= Expr then |
| |
| -- Emit warnings for predicates that are always True or always False |
| -- and were not originally expressed as Boolean literals. |
| |
| return Result : constant Boolean := |
| Is_Predicate_Static_Aux (Original_Node (Expr)) |
| do |
| if Result and then Warn and then Is_Entity_Name (Expr) then |
| if Entity (Expr) = Standard_True then |
| Error_Msg_N ("predicate is redundant (always True)?", Expr); |
| elsif Entity (Expr) = Standard_False then |
| Error_Msg_N |
| ("predicate is unsatisfiable (always False)?", Expr); |
| end if; |
| end if; |
| end return; |
| end if; |
| |
| -- 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 Is_Type_Ref (Left_Opnd (Expr)) |
| 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_Aux (Left_Opnd (Expr)) |
| and then Is_Predicate_Static_Aux (Right_Opnd (Expr))) |
| or else |
| (Nkind (Expr) = N_Op_Not |
| and then Is_Predicate_Static_Aux (Right_Opnd (Expr))) |
| 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_Aux (Left_Opnd (Expr)) |
| and then Is_Predicate_Static_Aux (Right_Opnd (Expr)) |
| 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; |
| |
| -- 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 in the source code without a prefix. |
| -- If a name denoting a component doesn't already have a |
| -- prefix, then normalize it by adding a reference to the |
| -- current instance of the type as a prefix. |
| -- |
| -- This isn't right in the pathological corner case of an |
| -- object-declaring expression (e.g., a quantified expression |
| -- or a declare expression) that declares an object with the |
| -- same name as a visible component declaration, thereby hiding |
| -- the component within that expression. For example, given a |
| -- record with a Boolean component "C" and a dynamic predicate |
| -- "C = (for some C in Character => Some_Function (C))", only |
| -- the first of the two uses of C should have a prefix added |
| -- here; instead, both will get prefixes. |
| |
| if Nkind (Parent (N)) /= N_Selected_Component |
| or else N /= Selector_Name (Parent (N)) |
| then |
| 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); |
| F2 := Next_Formal (F1); |
| |
| if Nam = Name_First then |
| |
| -- First (Container) => Cursor |
| |
| if Etype (Ent) /= Cursor then |
| Error_Msg_N ("primitive for First must yield a cursor", N); |
| elsif Present (F2) then |
| Error_Msg_N ("no match for First iterable primitive", N); |
| end if; |
| |
| elsif Nam = Name_Last then |
| |
| -- Last (Container) => Cursor |
| |
| if Etype (Ent) /= Cursor then |
| Error_Msg_N ("primitive for Last must yield a cursor", N); |
| elsif Present (F2) then |
| Error_Msg_N ("no match for Last iterable primitive", N); |
| end if; |
| |
| elsif Nam = Name_Next then |
| |
| -- Next (Container, Cursor) => Cursor |
| |
| if No (F2) |
| or else 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 |
| |
| if No (F2) |
| or else 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 |
| |
| if No (F2) |
| or else 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 |
| |
| -- Element (Container, Cursor) => Element_Type; |
| |
| 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 Present (First_Formal (It.Nam)) |
| 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 No (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; |
| |
| -- If Addr_Type is not present as the first association, then we default |
| -- it to System.Address. |
| |
| elsif No (Addr_Type) then |
| Addr_Type := RTE (RE_Address); |
| end if; |
| |
| if 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 |
| Aggr : constant Node_Id := Expression (ASN); |
| Assoc : Node_Id; |
| Expr : Node_Id; |
| |
| Prim : Node_Id; |
| Cursor : Entity_Id; |
| |
| First_Id : Entity_Id := Empty; |
| Last_Id : Entity_Id := Empty; |
| Next_Id : Entity_Id := Empty; |
| Has_Element_Id : Entity_Id := Empty; |
| Element_Id : Entity_Id := Empty; |
| |
| begin |
| if Nkind (Aggr) /= N_Aggregate then |
| Error_Msg_N ("aspect Iterable must be an aggregate", Aggr); |
| return; |
| end if; |
| |
| Cursor := Get_Cursor_Type (ASN, Typ); |
| |
| -- If previous error aspect is unusable |
| |
| if Cursor = Any_Type then |
| return; |
| end if; |
| |
| if not Is_Empty_List (Expressions (Aggr)) then |
| Error_Msg_N |
| ("illegal positional association", First (Expressions (Aggr))); |
| end if; |
| |
| -- Each expression must resolve to a function with the proper signature |
| |
| Assoc := First (Component_Associations (Aggr)); |
| 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 No (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; |
| Choice_Name : Name_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; |
| |
| procedure Check_And_Resolve_Storage_Model_Type_Argument |
| (Expr : Node_Id; |
| Typ : Entity_Id; |
| Argument_Id : in out Entity_Id; |
| Nam : Name_Id); |
| -- Checks that the subaspect for Nam has not already been specified for |
| -- Typ's Storage_Model_Type aspect (i.e., checks Argument_Id = Empty), |
| -- resolves Expr, and sets Argument_Id to the entity resolved for Expr. |
| |
| procedure Check_And_Resolve_Storage_Model_Type_Argument |
| (Expr : Node_Id; |
| Typ : Entity_Id; |
| Argument_Id : in out Entity_Id; |
| Nam : Name_Id) |
| is |
| Name_String : String := Get_Name_String (Nam); |
| |
| begin |
| To_Mixed (Name_String); |
| |
| if Present (Argument_Id) then |
| Error_Msg_String (1 .. Name_String'Length) := Name_String; |
| Error_Msg_Strlen := Name_String'Length; |
| |
| Error_Msg_N ("~ already specified", Expr); |
| end if; |
| |
| Resolve_Storage_Model_Type_Argument (Expr, Typ, Address_Type_Id, Nam); |
| Argument_Id := Entity (Expr); |
| end Check_And_Resolve_Storage_Model_Type_Argument; |
| |
| -- Start of processing for Validate_Storage_Model_Type_Aspect |
| |
| begin |
| -- The aggregate argument of Storage_Model_Type is optional, and when |
| -- not present the aspect defaults to the native storage model (where |
| -- the address type is System.Address, and other arguments default to |
| -- the corresponding native storage operations). |
| |
| if No (Expression (ASN)) then |
| return; |
| end if; |
| |
| -- 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)); |
| |
| Choice_Name := Chars (Choice); |
| |
| if Nkind (Choice) /= N_Identifier or else Present (Next (Choice)) then |
| Error_Msg_N ("illegal name in association", Choice); |
| |
| elsif Choice_Name = Name_Address_Type then |
| if Assoc /= First (Component_Associations (Expression (ASN))) then |
| Error_Msg_N ("Address_Type must be first association", Choice); |
| end if; |
| |
| Check_And_Resolve_Storage_Model_Type_Argument |
| (Expr, Typ, Address_Type_Id, Name_Address_Type); |
| |
| else |
| -- It's allowed to leave out the Address_Type argument, in which |
| -- case the address type is defined to default to System.Address. |
| |
| if No (Address_Type_Id) then |
| Address_Type_Id := RTE (RE_Address); |
| end if; |
| |
| if Choice_Name = Name_Null_Address then |
| Check_And_Resolve_Storage_Model_Type_Argument |
| (Expr, Typ, Null_Address_Id, Name_Null_Address); |
| |
| elsif Choice_Name = Name_Allocate then |
| Check_And_Resolve_Storage_Model_Type_Argument |
| (Expr, Typ, Allocate_Id, Name_Allocate); |
| |
| elsif Choice_Name = Name_Deallocate then |
| Check_And_Resolve_Storage_Model_Type_Argument |
| (Expr, Typ, Deallocate_Id, Name_Deallocate); |
| |
| elsif Choice_Name = Name_Copy_From then |
| Check_And_Resolve_Storage_Model_Type_Argument |
| (Expr, Typ, Copy_From_Id, Name_Copy_From); |
| |
| elsif Choice_Name = Name_Copy_To then |
| Check_And_Resolve_Storage_Model_Type_Argument |
| (Expr, Typ, Copy_To_Id, Name_Copy_To); |
| |
| elsif Choice_Name = Name_Storage_Size then |
| Check_And_Resolve_Storage_Model_Type_Argument |
| (Expr, Typ, Storage_Size_Id, Name_Storage_Size); |
| |
| else |
| Error_Msg_N |
| ("invalid name for Storage_Model_Type argument", Choice); |
| end if; |
| end if; |
| |
| Next (Assoc); |
| end loop; |
| |
| -- If Address_Type has been specified as or defaults to System.Address, |
| -- then other "subaspect" arguments can be specified, but are optional. |
| -- Otherwise, all other arguments are required and an error is flagged |
| -- about any that are missing. |
| |
| if Address_Type_Id = RTE (RE_Address) then |
| return; |
| |
| 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 Has_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; |