| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- E X P _ A G G R -- |
| -- -- |
| -- 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 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 Expander; use Expander; |
| with Exp_Util; use Exp_Util; |
| with Exp_Ch3; use Exp_Ch3; |
| with Exp_Ch6; use Exp_Ch6; |
| with Exp_Ch7; use Exp_Ch7; |
| with Exp_Ch9; use Exp_Ch9; |
| with Exp_Disp; use Exp_Disp; |
| with Exp_Tss; use Exp_Tss; |
| with Freeze; use Freeze; |
| with Itypes; use Itypes; |
| with Lib; use Lib; |
| with Namet; use Namet; |
| with Nmake; use Nmake; |
| with Nlists; use Nlists; |
| with Opt; use Opt; |
| with Restrict; use Restrict; |
| with Rident; use Rident; |
| with Rtsfind; use Rtsfind; |
| with Ttypes; use Ttypes; |
| with Sem; use Sem; |
| with Sem_Aggr; use Sem_Aggr; |
| with Sem_Aux; use Sem_Aux; |
| with Sem_Case; use Sem_Case; |
| with Sem_Ch3; use Sem_Ch3; |
| with Sem_Ch8; use Sem_Ch8; |
| with Sem_Ch13; use Sem_Ch13; |
| with Sem_Eval; use Sem_Eval; |
| with Sem_Mech; use Sem_Mech; |
| with Sem_Res; use Sem_Res; |
| with Sem_Util; use Sem_Util; |
| with Sinfo; use Sinfo; |
| with Sinfo.Nodes; use Sinfo.Nodes; |
| with Sinfo.Utils; use Sinfo.Utils; |
| with Snames; use Snames; |
| with Stand; use Stand; |
| with Stringt; use Stringt; |
| with Tbuild; use Tbuild; |
| with Uintp; use Uintp; |
| with Urealp; use Urealp; |
| |
| package body Exp_Aggr is |
| |
| type Case_Bounds is record |
| Choice_Lo : Node_Id; |
| Choice_Hi : Node_Id; |
| Choice_Node : Node_Id; |
| end record; |
| |
| type Case_Table_Type is array (Nat range <>) of Case_Bounds; |
| -- Table type used by Check_Case_Choices procedure |
| |
| procedure Expand_Delta_Array_Aggregate (N : Node_Id; Deltas : List_Id); |
| procedure Expand_Delta_Record_Aggregate (N : Node_Id; Deltas : List_Id); |
| procedure Expand_Container_Aggregate (N : Node_Id); |
| |
| function Get_Base_Object (N : Node_Id) return Entity_Id; |
| -- Return the base object, i.e. the outermost prefix object, that N refers |
| -- to statically, or Empty if it cannot be determined. The assumption is |
| -- that all dereferences are explicit in the tree rooted at N. |
| |
| function Has_Default_Init_Comps (N : Node_Id) return Boolean; |
| -- N is an aggregate (record or array). Checks the presence of default |
| -- initialization (<>) in any component (Ada 2005: AI-287). |
| |
| function Is_CCG_Supported_Aggregate (N : Node_Id) return Boolean; |
| -- Return True if aggregate N is located in a context supported by the |
| -- CCG backend; False otherwise. |
| |
| function Is_Static_Dispatch_Table_Aggregate (N : Node_Id) return Boolean; |
| -- Returns true if N is an aggregate used to initialize the components |
| -- of a statically allocated dispatch table. |
| |
| function Late_Expansion |
| (N : Node_Id; |
| Typ : Entity_Id; |
| Target : Node_Id) return List_Id; |
| -- This routine implements top-down expansion of nested aggregates. In |
| -- doing so, it avoids the generation of temporaries at each level. N is |
| -- a nested record or array aggregate with the Expansion_Delayed flag. |
| -- Typ is the expected type of the aggregate. Target is a (duplicatable) |
| -- expression that will hold the result of the aggregate expansion. |
| |
| function Make_OK_Assignment_Statement |
| (Sloc : Source_Ptr; |
| Name : Node_Id; |
| Expression : Node_Id) return Node_Id; |
| -- This is like Make_Assignment_Statement, except that Assignment_OK |
| -- is set in the left operand. All assignments built by this unit use |
| -- this routine. This is needed to deal with assignments to initialized |
| -- constants that are done in place. |
| |
| function Must_Slide |
| (Aggr : Node_Id; |
| Obj_Type : Entity_Id; |
| Typ : Entity_Id) return Boolean; |
| -- A static array aggregate in an object declaration can in most cases be |
| -- expanded in place. The one exception is when the aggregate is given |
| -- with component associations that specify different bounds from those of |
| -- the type definition in the object declaration. In this pathological |
| -- case the aggregate must slide, and we must introduce an intermediate |
| -- temporary to hold it. |
| -- |
| -- The same holds in an assignment to one-dimensional array of arrays, |
| -- when a component may be given with bounds that differ from those of the |
| -- component type. |
| |
| function Number_Of_Choices (N : Node_Id) return Nat; |
| -- Returns the number of discrete choices (not including the others choice |
| -- if present) contained in (sub-)aggregate N. |
| |
| procedure Process_Transient_Component |
| (Loc : Source_Ptr; |
| Comp_Typ : Entity_Id; |
| Init_Expr : Node_Id; |
| Fin_Call : out Node_Id; |
| Hook_Clear : out Node_Id; |
| Aggr : Node_Id := Empty; |
| Stmts : List_Id := No_List); |
| -- Subsidiary to the expansion of array and record aggregates. Generate |
| -- part of the necessary code to finalize a transient component. Comp_Typ |
| -- is the component type. Init_Expr is the initialization expression of the |
| -- component which is always a function call. Fin_Call is the finalization |
| -- call used to clean up the transient function result. Hook_Clear is the |
| -- hook reset statement. Aggr and Stmts both control the placement of the |
| -- generated code. Aggr is the related aggregate. If present, all code is |
| -- inserted prior to Aggr using Insert_Action. Stmts is the initialization |
| -- statements of the component. If present, all code is added to Stmts. |
| |
| procedure Process_Transient_Component_Completion |
| (Loc : Source_Ptr; |
| Aggr : Node_Id; |
| Fin_Call : Node_Id; |
| Hook_Clear : Node_Id; |
| Stmts : List_Id); |
| -- Subsidiary to the expansion of array and record aggregates. Generate |
| -- part of the necessary code to finalize a transient component. Aggr is |
| -- the related aggregate. Fin_Clear is the finalization call used to clean |
| -- up the transient component. Hook_Clear is the hook reset statement. |
| -- Stmts is the initialization statement list for the component. All |
| -- generated code is added to Stmts. |
| |
| procedure Sort_Case_Table (Case_Table : in out Case_Table_Type); |
| -- Sort the Case Table using the Lower Bound of each Choice as the key. |
| -- A simple insertion sort is used since the number of choices in a case |
| -- statement of variant part will usually be small and probably in near |
| -- sorted order. |
| |
| ------------------------------------------------------ |
| -- Local subprograms for Record Aggregate Expansion -- |
| ------------------------------------------------------ |
| |
| function Is_Build_In_Place_Aggregate_Return (N : Node_Id) return Boolean; |
| -- True if N is an aggregate (possibly qualified or converted) that is |
| -- being returned from a build-in-place function. |
| |
| function Build_Record_Aggr_Code |
| (N : Node_Id; |
| Typ : Entity_Id; |
| Lhs : Node_Id) return List_Id; |
| -- N is an N_Aggregate or an N_Extension_Aggregate. Typ is the type of the |
| -- aggregate. Target is an expression containing the location on which the |
| -- component by component assignments will take place. Returns the list of |
| -- assignments plus all other adjustments needed for tagged and controlled |
| -- types. |
| |
| procedure Convert_To_Assignments (N : Node_Id; Typ : Entity_Id); |
| -- Transform a record aggregate into a sequence of assignments performed |
| -- component by component. N is an N_Aggregate or N_Extension_Aggregate. |
| -- Typ is the type of the record aggregate. |
| |
| procedure Expand_Record_Aggregate |
| (N : Node_Id; |
| Orig_Tag : Node_Id := Empty; |
| Parent_Expr : Node_Id := Empty); |
| -- This is the top level procedure for record aggregate expansion. |
| -- Expansion for record aggregates needs expand aggregates for tagged |
| -- record types. Specifically Expand_Record_Aggregate adds the Tag |
| -- field in front of the Component_Association list that was created |
| -- during resolution by Resolve_Record_Aggregate. |
| -- |
| -- N is the record aggregate node. |
| -- Orig_Tag is the value of the Tag that has to be provided for this |
| -- specific aggregate. It carries the tag corresponding to the type |
| -- of the outermost aggregate during the recursive expansion |
| -- Parent_Expr is the ancestor part of the original extension |
| -- aggregate |
| |
| function Has_Mutable_Components (Typ : Entity_Id) return Boolean; |
| -- Return true if one of the components is of a discriminated type with |
| -- defaults. An aggregate for a type with mutable components must be |
| -- expanded into individual assignments. |
| |
| function In_Place_Assign_OK |
| (N : Node_Id; |
| Target_Object : Entity_Id := Empty) return Boolean; |
| -- Predicate to determine whether an aggregate assignment can be done in |
| -- place, because none of the new values can depend on the components of |
| -- the target of the assignment. |
| |
| procedure Initialize_Discriminants (N : Node_Id; Typ : Entity_Id); |
| -- If the type of the aggregate is a type extension with renamed discrimi- |
| -- nants, we must initialize the hidden discriminants of the parent. |
| -- Otherwise, the target object must not be initialized. The discriminants |
| -- are initialized by calling the initialization procedure for the type. |
| -- This is incorrect if the initialization of other components has any |
| -- side effects. We restrict this call to the case where the parent type |
| -- has a variant part, because this is the only case where the hidden |
| -- discriminants are accessed, namely when calling discriminant checking |
| -- functions of the parent type, and when applying a stream attribute to |
| -- an object of the derived type. |
| |
| ----------------------------------------------------- |
| -- Local Subprograms for Array Aggregate Expansion -- |
| ----------------------------------------------------- |
| |
| function Aggr_Assignment_OK_For_Backend (N : Node_Id) return Boolean; |
| -- Returns true if an aggregate assignment can be done by the back end |
| |
| function Aggr_Size_OK (N : Node_Id) return Boolean; |
| -- Very large static aggregates present problems to the back-end, and are |
| -- transformed into assignments and loops. This function verifies that the |
| -- total number of components of an aggregate is acceptable for rewriting |
| -- into a purely positional static form. Aggr_Size_OK must be called before |
| -- calling Flatten. |
| -- |
| -- This function also detects and warns about one-component aggregates that |
| -- appear in a nonstatic context. Even if the component value is static, |
| -- such an aggregate must be expanded into an assignment. |
| |
| function Backend_Processing_Possible (N : Node_Id) return Boolean; |
| -- This function checks if array aggregate N can be processed directly |
| -- by the backend. If this is the case, True is returned. |
| |
| function Build_Array_Aggr_Code |
| (N : Node_Id; |
| Ctype : Entity_Id; |
| Index : Node_Id; |
| Into : Node_Id; |
| Scalar_Comp : Boolean; |
| Indexes : List_Id := No_List) return List_Id; |
| -- This recursive routine returns a list of statements containing the |
| -- loops and assignments that are needed for the expansion of the array |
| -- aggregate N. |
| -- |
| -- N is the (sub-)aggregate node to be expanded into code. This node has |
| -- been fully analyzed, and its Etype is properly set. |
| -- |
| -- Index is the index node corresponding to the array subaggregate N |
| -- |
| -- Into is the target expression into which we are copying the aggregate. |
| -- Note that this node may not have been analyzed yet, and so the Etype |
| -- field may not be set. |
| -- |
| -- Scalar_Comp is True if the component type of the aggregate is scalar |
| -- |
| -- Indexes is the current list of expressions used to index the object we |
| -- are writing into. |
| |
| procedure Convert_Array_Aggr_In_Allocator |
| (Decl : Node_Id; |
| Aggr : Node_Id; |
| Target : Node_Id); |
| -- If the aggregate appears within an allocator and can be expanded in |
| -- place, this routine generates the individual assignments to components |
| -- of the designated object. This is an optimization over the general |
| -- case, where a temporary is first created on the stack and then used to |
| -- construct the allocated object on the heap. |
| |
| procedure Convert_To_Positional |
| (N : Node_Id; |
| Handle_Bit_Packed : Boolean := False); |
| -- If possible, convert named notation to positional notation. This |
| -- conversion is possible only in some static cases. If the conversion is |
| -- possible, then N is rewritten with the analyzed converted aggregate. |
| -- The parameter Handle_Bit_Packed is usually set False (since we do |
| -- not expect the back end to handle bit packed arrays, so the normal case |
| -- of conversion is pointless), but in the special case of a call from |
| -- Packed_Array_Aggregate_Handled, we set this parameter to True, since |
| -- these are cases we handle in there. |
| |
| procedure Expand_Array_Aggregate (N : Node_Id); |
| -- This is the top-level routine to perform array aggregate expansion. |
| -- N is the N_Aggregate node to be expanded. |
| |
| function Is_Two_Dim_Packed_Array (Typ : Entity_Id) return Boolean; |
| -- For two-dimensional packed aggregates with constant bounds and constant |
| -- components, it is preferable to pack the inner aggregates because the |
| -- whole matrix can then be presented to the back-end as a one-dimensional |
| -- list of literals. This is much more efficient than expanding into single |
| -- component assignments. This function determines if the type Typ is for |
| -- an array that is suitable for this optimization: it returns True if Typ |
| -- is a two dimensional bit packed array with component size 1, 2, or 4. |
| |
| function Max_Aggregate_Size |
| (N : Node_Id; |
| Default_Size : Nat := 5000) return Nat; |
| -- Return the max size for a static aggregate N. Return Default_Size if no |
| -- other special criteria trigger. |
| |
| function Packed_Array_Aggregate_Handled (N : Node_Id) return Boolean; |
| -- Given an array aggregate, this function handles the case of a packed |
| -- array aggregate with all constant values, where the aggregate can be |
| -- evaluated at compile time. If this is possible, then N is rewritten |
| -- to be its proper compile time value with all the components properly |
| -- assembled. The expression is analyzed and resolved and True is returned. |
| -- If this transformation is not possible, N is unchanged and False is |
| -- returned. |
| |
| function Two_Dim_Packed_Array_Handled (N : Node_Id) return Boolean; |
| -- If the type of the aggregate is a two-dimensional bit_packed array |
| -- it may be transformed into an array of bytes with constant values, |
| -- and presented to the back-end as a static value. The function returns |
| -- false if this transformation cannot be performed. THis is similar to, |
| -- and reuses part of the machinery in Packed_Array_Aggregate_Handled. |
| |
| ------------------------------------ |
| -- Aggr_Assignment_OK_For_Backend -- |
| ------------------------------------ |
| |
| -- Back-end processing by Gigi/gcc is possible only if all the following |
| -- conditions are met: |
| |
| -- 1. N consists of a single OTHERS choice, possibly recursively, or |
| -- of a single choice, possibly recursively, if it is surrounded by |
| -- a qualified expression whose subtype mark is unconstrained. |
| |
| -- 2. The array type has no null ranges (the purpose of this is to |
| -- avoid a bogus warning for an out-of-range value). |
| |
| -- 3. The array type has no atomic components |
| |
| -- 4. The component type is elementary |
| |
| -- 5. The component size is a multiple of Storage_Unit |
| |
| -- 6. The component size is Storage_Unit or the value is of the form |
| -- M * (1 + A**1 + A**2 + .. A**(K-1)) where A = 2**(Storage_Unit) |
| -- and M in 0 .. A-1. This can also be viewed as K occurrences of |
| -- the Storage_Unit value M, concatenated together. |
| |
| -- The ultimate goal is to generate a call to a fast memset routine |
| -- specifically optimized for the target. |
| |
| function Aggr_Assignment_OK_For_Backend (N : Node_Id) return Boolean is |
| |
| function Is_OK_Aggregate (Aggr : Node_Id) return Boolean; |
| -- Return true if Aggr is suitable for back-end assignment |
| |
| --------------------- |
| -- Is_OK_Aggregate -- |
| --------------------- |
| |
| function Is_OK_Aggregate (Aggr : Node_Id) return Boolean is |
| Assoc : constant List_Id := Component_Associations (Aggr); |
| |
| begin |
| -- An "others" aggregate is most likely OK, but see below |
| |
| if Is_Others_Aggregate (Aggr) then |
| null; |
| |
| -- An aggregate with a single choice requires a qualified expression |
| -- whose subtype mark is an unconstrained type because we need it to |
| -- have the semantics of an "others" aggregate. |
| |
| elsif Nkind (Parent (N)) = N_Qualified_Expression |
| and then not Is_Constrained (Entity (Subtype_Mark (Parent (N)))) |
| and then Is_Single_Aggregate (Aggr) |
| then |
| null; |
| |
| -- The other cases are not OK |
| |
| else |
| return False; |
| end if; |
| |
| -- In any case we do not support an iterated association |
| |
| return Nkind (First (Assoc)) /= N_Iterated_Component_Association; |
| end Is_OK_Aggregate; |
| |
| Bounds : Range_Nodes; |
| Csiz : Uint := No_Uint; |
| Ctyp : Entity_Id; |
| Expr : Node_Id; |
| Index : Entity_Id; |
| Nunits : Int; |
| Remainder : Uint; |
| Value : Uint; |
| |
| -- Start of processing for Aggr_Assignment_OK_For_Backend |
| |
| begin |
| -- Back end doesn't know about <> |
| |
| if Has_Default_Init_Comps (N) then |
| return False; |
| end if; |
| |
| -- Recurse as far as possible to find the innermost component type |
| |
| Ctyp := Etype (N); |
| Expr := N; |
| while Is_Array_Type (Ctyp) loop |
| if Nkind (Expr) /= N_Aggregate |
| or else not Is_OK_Aggregate (Expr) |
| then |
| return False; |
| end if; |
| |
| Index := First_Index (Ctyp); |
| while Present (Index) loop |
| Bounds := Get_Index_Bounds (Index); |
| |
| if Is_Null_Range (Bounds.First, Bounds.Last) then |
| return False; |
| end if; |
| |
| Next_Index (Index); |
| end loop; |
| |
| Expr := Expression (First (Component_Associations (Expr))); |
| |
| for J in 1 .. Number_Dimensions (Ctyp) - 1 loop |
| if Nkind (Expr) /= N_Aggregate |
| or else not Is_OK_Aggregate (Expr) |
| then |
| return False; |
| end if; |
| |
| Expr := Expression (First (Component_Associations (Expr))); |
| end loop; |
| |
| if Has_Atomic_Components (Ctyp) then |
| return False; |
| end if; |
| |
| Csiz := Component_Size (Ctyp); |
| Ctyp := Component_Type (Ctyp); |
| |
| if Is_Full_Access (Ctyp) then |
| return False; |
| end if; |
| end loop; |
| |
| -- Access types need to be dealt with specially |
| |
| if Is_Access_Type (Ctyp) then |
| |
| -- Component_Size is not set by Layout_Type if the component |
| -- type is an access type ??? |
| |
| Csiz := Esize (Ctyp); |
| |
| -- Fat pointers are rejected as they are not really elementary |
| -- for the backend. |
| |
| if No (Csiz) or else Csiz /= System_Address_Size then |
| return False; |
| end if; |
| |
| -- The supported expressions are NULL and constants, others are |
| -- rejected upfront to avoid being analyzed below, which can be |
| -- problematic for some of them, for example allocators. |
| |
| if Nkind (Expr) /= N_Null and then not Is_Entity_Name (Expr) then |
| return False; |
| end if; |
| |
| -- Scalar types are OK if their size is a multiple of Storage_Unit |
| |
| elsif Is_Scalar_Type (Ctyp) and then Present (Csiz) then |
| |
| if Csiz mod System_Storage_Unit /= 0 then |
| return False; |
| end if; |
| |
| -- Composite types are rejected |
| |
| else |
| return False; |
| end if; |
| |
| -- If the expression has side effects (e.g. contains calls with |
| -- potential side effects) reject as well. We only preanalyze the |
| -- expression to prevent the removal of intended side effects. |
| |
| Preanalyze_And_Resolve (Expr, Ctyp); |
| |
| if not Side_Effect_Free (Expr) then |
| return False; |
| end if; |
| |
| -- The expression needs to be analyzed if True is returned |
| |
| Analyze_And_Resolve (Expr, Ctyp); |
| |
| -- Strip away any conversions from the expression as they simply |
| -- qualify the real expression. |
| |
| while Nkind (Expr) in N_Unchecked_Type_Conversion | N_Type_Conversion |
| loop |
| Expr := Expression (Expr); |
| end loop; |
| |
| Nunits := UI_To_Int (Csiz) / System_Storage_Unit; |
| |
| if Nunits = 1 then |
| return True; |
| end if; |
| |
| if not Compile_Time_Known_Value (Expr) then |
| return False; |
| end if; |
| |
| -- The only supported value for floating point is 0.0 |
| |
| if Is_Floating_Point_Type (Ctyp) then |
| return Expr_Value_R (Expr) = Ureal_0; |
| end if; |
| |
| -- For other types, we can look into the value as an integer, which |
| -- means the representation value for enumeration literals. |
| |
| Value := Expr_Rep_Value (Expr); |
| |
| if Has_Biased_Representation (Ctyp) then |
| Value := Value - Expr_Value (Type_Low_Bound (Ctyp)); |
| end if; |
| |
| -- Values 0 and -1 immediately satisfy the last check |
| |
| if Value = Uint_0 or else Value = Uint_Minus_1 then |
| return True; |
| end if; |
| |
| -- We need to work with an unsigned value |
| |
| if Value < 0 then |
| Value := Value + 2**(System_Storage_Unit * Nunits); |
| end if; |
| |
| Remainder := Value rem 2**System_Storage_Unit; |
| |
| for J in 1 .. Nunits - 1 loop |
| Value := Value / 2**System_Storage_Unit; |
| |
| if Value rem 2**System_Storage_Unit /= Remainder then |
| return False; |
| end if; |
| end loop; |
| |
| return True; |
| end Aggr_Assignment_OK_For_Backend; |
| |
| ------------------ |
| -- Aggr_Size_OK -- |
| ------------------ |
| |
| function Aggr_Size_OK (N : Node_Id) return Boolean is |
| Typ : constant Entity_Id := Etype (N); |
| Lo : Node_Id; |
| Hi : Node_Id; |
| Indx : Node_Id; |
| Size : Uint; |
| Lov : Uint; |
| Hiv : Uint; |
| |
| Max_Aggr_Size : Nat; |
| -- Determines the maximum size of an array aggregate produced by |
| -- converting named to positional notation (e.g. from others clauses). |
| -- This avoids running away with attempts to convert huge aggregates, |
| -- which hit memory limits in the backend. |
| |
| function Component_Count (T : Entity_Id) return Nat; |
| -- The limit is applied to the total number of subcomponents that the |
| -- aggregate will have, which is the number of static expressions |
| -- that will appear in the flattened array. This requires a recursive |
| -- computation of the number of scalar components of the structure. |
| |
| --------------------- |
| -- Component_Count -- |
| --------------------- |
| |
| function Component_Count (T : Entity_Id) return Nat is |
| Res : Nat := 0; |
| Comp : Entity_Id; |
| |
| begin |
| if Is_Scalar_Type (T) then |
| return 1; |
| |
| elsif Is_Record_Type (T) then |
| Comp := First_Component (T); |
| while Present (Comp) loop |
| Res := Res + Component_Count (Etype (Comp)); |
| Next_Component (Comp); |
| end loop; |
| |
| return Res; |
| |
| elsif Is_Array_Type (T) then |
| declare |
| Lo : constant Node_Id := |
| Type_Low_Bound (Etype (First_Index (T))); |
| Hi : constant Node_Id := |
| Type_High_Bound (Etype (First_Index (T))); |
| |
| Siz : constant Nat := Component_Count (Component_Type (T)); |
| |
| begin |
| -- Check for superflat arrays, i.e. arrays with such bounds |
| -- as 4 .. 2, to insure that this function never returns a |
| -- meaningless negative value. |
| |
| if not Compile_Time_Known_Value (Lo) |
| or else not Compile_Time_Known_Value (Hi) |
| or else Expr_Value (Hi) < Expr_Value (Lo) |
| then |
| return 0; |
| |
| else |
| -- If the number of components is greater than Int'Last, |
| -- then return Int'Last, so caller will return False (Aggr |
| -- size is not OK). Otherwise, UI_To_Int will crash. |
| |
| declare |
| UI : constant Uint := |
| Expr_Value (Hi) - Expr_Value (Lo) + 1; |
| begin |
| if UI_Is_In_Int_Range (UI) then |
| return Siz * UI_To_Int (UI); |
| else |
| return Int'Last; |
| end if; |
| end; |
| end if; |
| end; |
| |
| else |
| -- Can only be a null for an access type |
| |
| return 1; |
| end if; |
| end Component_Count; |
| |
| -- Start of processing for Aggr_Size_OK |
| |
| begin |
| -- We bump the maximum size unless the aggregate has a single component |
| -- association, which will be more efficient if implemented with a loop. |
| -- The -gnatd_g switch disables this bumping. |
| |
| if (No (Expressions (N)) |
| and then No (Next (First (Component_Associations (N))))) |
| or else Debug_Flag_Underscore_G |
| then |
| Max_Aggr_Size := Max_Aggregate_Size (N); |
| else |
| Max_Aggr_Size := Max_Aggregate_Size (N, 500_000); |
| end if; |
| |
| Size := UI_From_Int (Component_Count (Component_Type (Typ))); |
| |
| Indx := First_Index (Typ); |
| while Present (Indx) loop |
| Lo := Type_Low_Bound (Etype (Indx)); |
| Hi := Type_High_Bound (Etype (Indx)); |
| |
| -- Bounds need to be known at compile time |
| |
| if not Compile_Time_Known_Value (Lo) |
| or else not Compile_Time_Known_Value (Hi) |
| then |
| return False; |
| end if; |
| |
| Lov := Expr_Value (Lo); |
| Hiv := Expr_Value (Hi); |
| |
| -- A flat array is always safe |
| |
| if Hiv < Lov then |
| return True; |
| end if; |
| |
| -- One-component aggregates are suspicious, and if the context type |
| -- is an object declaration with nonstatic bounds it will trip gcc; |
| -- such an aggregate must be expanded into a single assignment. |
| |
| if Hiv = Lov and then Nkind (Parent (N)) = N_Object_Declaration then |
| declare |
| Index_Type : constant Entity_Id := |
| Etype |
| (First_Index (Etype (Defining_Identifier (Parent (N))))); |
| Indx : Node_Id; |
| |
| begin |
| if not Compile_Time_Known_Value (Type_Low_Bound (Index_Type)) |
| or else not Compile_Time_Known_Value |
| (Type_High_Bound (Index_Type)) |
| then |
| if Present (Component_Associations (N)) then |
| Indx := |
| First |
| (Choice_List (First (Component_Associations (N)))); |
| |
| if Is_Entity_Name (Indx) |
| and then not Is_Type (Entity (Indx)) |
| then |
| Error_Msg_N |
| ("single component aggregate in " |
| & "non-static context??", Indx); |
| Error_Msg_N ("\maybe subtype name was meant??", Indx); |
| end if; |
| end if; |
| |
| return False; |
| end if; |
| end; |
| end if; |
| |
| declare |
| Rng : constant Uint := Hiv - Lov + 1; |
| |
| begin |
| -- Check if size is too large |
| |
| if not UI_Is_In_Int_Range (Rng) then |
| return False; |
| end if; |
| |
| -- Compute the size using universal arithmetic to avoid the |
| -- possibility of overflow on very large aggregates. |
| |
| Size := Size * Rng; |
| |
| if Size <= 0 |
| or else Size > Max_Aggr_Size |
| then |
| return False; |
| end if; |
| end; |
| |
| -- Bounds must be in integer range, for later array construction |
| |
| if not UI_Is_In_Int_Range (Lov) |
| or else |
| not UI_Is_In_Int_Range (Hiv) |
| then |
| return False; |
| end if; |
| |
| Next_Index (Indx); |
| end loop; |
| |
| return True; |
| end Aggr_Size_OK; |
| |
| --------------------------------- |
| -- Backend_Processing_Possible -- |
| --------------------------------- |
| |
| -- Backend processing by Gigi/gcc is possible only if all the following |
| -- conditions are met: |
| |
| -- 1. N is fully positional |
| |
| -- 2. N is not a bit-packed array aggregate; |
| |
| -- 3. The size of N's array type must be known at compile time. Note |
| -- that this implies that the component size is also known |
| |
| -- 4. The array type of N does not follow the Fortran layout convention |
| -- or if it does it must be 1 dimensional. |
| |
| -- 5. The array component type may not be tagged (which could necessitate |
| -- reassignment of proper tags). |
| |
| -- 6. The array component type must not have unaligned bit components |
| |
| -- 7. None of the components of the aggregate may be bit unaligned |
| -- components. |
| |
| -- 8. There cannot be delayed components, since we do not know enough |
| -- at this stage to know if back end processing is possible. |
| |
| -- 9. There cannot be any discriminated record components, since the |
| -- back end cannot handle this complex case. |
| |
| -- 10. No controlled actions need to be generated for components |
| |
| -- 11. When generating C code, N must be part of a N_Object_Declaration |
| |
| -- 12. When generating C code, N must not include function calls |
| |
| function Backend_Processing_Possible (N : Node_Id) return Boolean is |
| Typ : constant Entity_Id := Etype (N); |
| -- Typ is the correct constrained array subtype of the aggregate |
| |
| function Component_Check (N : Node_Id; Index : Node_Id) return Boolean; |
| -- This routine checks components of aggregate N, enforcing checks |
| -- 1, 7, 8, 9, 11, and 12. In the multidimensional case, these checks |
| -- are performed on subaggregates. The Index value is the current index |
| -- being checked in the multidimensional case. |
| |
| --------------------- |
| -- Component_Check -- |
| --------------------- |
| |
| function Component_Check (N : Node_Id; Index : Node_Id) return Boolean is |
| function Ultimate_Original_Expression (N : Node_Id) return Node_Id; |
| -- Given a type conversion or an unchecked type conversion N, return |
| -- its innermost original expression. |
| |
| ---------------------------------- |
| -- Ultimate_Original_Expression -- |
| ---------------------------------- |
| |
| function Ultimate_Original_Expression (N : Node_Id) return Node_Id is |
| Expr : Node_Id := Original_Node (N); |
| |
| begin |
| while Nkind (Expr) in |
| N_Type_Conversion | N_Unchecked_Type_Conversion |
| loop |
| Expr := Original_Node (Expression (Expr)); |
| end loop; |
| |
| return Expr; |
| end Ultimate_Original_Expression; |
| |
| -- Local variables |
| |
| Expr : Node_Id; |
| |
| -- Start of processing for Component_Check |
| |
| begin |
| -- Checks 1: (no component associations) |
| |
| if Present (Component_Associations (N)) then |
| return False; |
| end if; |
| |
| -- Checks 11: The C code generator cannot handle aggregates that are |
| -- not part of an object declaration. |
| |
| if Modify_Tree_For_C and then not Is_CCG_Supported_Aggregate (N) then |
| return False; |
| end if; |
| |
| -- Checks on components |
| |
| -- Recurse to check subaggregates, which may appear in qualified |
| -- expressions. If delayed, the front-end will have to expand. |
| -- If the component is a discriminated record, treat as nonstatic, |
| -- as the back-end cannot handle this properly. |
| |
| Expr := First (Expressions (N)); |
| while Present (Expr) loop |
| |
| -- Checks 8: (no delayed components) |
| |
| if Is_Delayed_Aggregate (Expr) then |
| return False; |
| end if; |
| |
| -- Checks 9: (no discriminated records) |
| |
| if Present (Etype (Expr)) |
| and then Is_Record_Type (Etype (Expr)) |
| and then Has_Discriminants (Etype (Expr)) |
| then |
| return False; |
| end if; |
| |
| -- Checks 7. Component must not be bit aligned component |
| |
| if Possible_Bit_Aligned_Component (Expr) then |
| return False; |
| end if; |
| |
| -- Checks 12: (no function call) |
| |
| if Modify_Tree_For_C |
| and then |
| Nkind (Ultimate_Original_Expression (Expr)) = N_Function_Call |
| then |
| return False; |
| end if; |
| |
| -- Recursion to following indexes for multiple dimension case |
| |
| if Present (Next_Index (Index)) |
| and then not Component_Check (Expr, Next_Index (Index)) |
| then |
| return False; |
| end if; |
| |
| -- All checks for that component finished, on to next |
| |
| Next (Expr); |
| end loop; |
| |
| return True; |
| end Component_Check; |
| |
| -- Start of processing for Backend_Processing_Possible |
| |
| begin |
| -- Checks 2 (array not bit packed) and 10 (no controlled actions) |
| |
| if Is_Bit_Packed_Array (Typ) or else Needs_Finalization (Typ) then |
| return False; |
| end if; |
| |
| -- If component is limited, aggregate must be expanded because each |
| -- component assignment must be built in place. |
| |
| if Is_Limited_View (Component_Type (Typ)) then |
| return False; |
| end if; |
| |
| -- Checks 4 (array must not be multidimensional Fortran case) |
| |
| if Convention (Typ) = Convention_Fortran |
| and then Number_Dimensions (Typ) > 1 |
| then |
| return False; |
| end if; |
| |
| -- Checks 3 (size of array must be known at compile time) |
| |
| if not Size_Known_At_Compile_Time (Typ) then |
| return False; |
| end if; |
| |
| -- Checks on components |
| |
| if not Component_Check (N, First_Index (Typ)) then |
| return False; |
| end if; |
| |
| -- Checks 5 (if the component type is tagged, then we may need to do |
| -- tag adjustments. Perhaps this should be refined to check for any |
| -- component associations that actually need tag adjustment, similar |
| -- to the test in Component_OK_For_Backend for record aggregates with |
| -- tagged components, but not clear whether it's worthwhile ???; in the |
| -- case of virtual machines (no Tagged_Type_Expansion), object tags are |
| -- handled implicitly). |
| |
| if Is_Tagged_Type (Component_Type (Typ)) |
| and then Tagged_Type_Expansion |
| then |
| return False; |
| end if; |
| |
| -- Checks 6 (component type must not have bit aligned components) |
| |
| if Type_May_Have_Bit_Aligned_Components (Component_Type (Typ)) then |
| return False; |
| end if; |
| |
| -- Backend processing is possible |
| |
| return True; |
| end Backend_Processing_Possible; |
| |
| --------------------------- |
| -- Build_Array_Aggr_Code -- |
| --------------------------- |
| |
| -- The code that we generate from a one dimensional aggregate is |
| |
| -- 1. If the subaggregate contains discrete choices we |
| |
| -- (a) Sort the discrete choices |
| |
| -- (b) Otherwise for each discrete choice that specifies a range we |
| -- emit a loop. If a range specifies a maximum of three values, or |
| -- we are dealing with an expression we emit a sequence of |
| -- assignments instead of a loop. |
| |
| -- (c) Generate the remaining loops to cover the others choice if any |
| |
| -- 2. If the aggregate contains positional elements we |
| |
| -- (a) Translate the positional elements in a series of assignments |
| |
| -- (b) Generate a final loop to cover the others choice if any. |
| -- Note that this final loop has to be a while loop since the case |
| |
| -- L : Integer := Integer'Last; |
| -- H : Integer := Integer'Last; |
| -- A : array (L .. H) := (1, others =>0); |
| |
| -- cannot be handled by a for loop. Thus for the following |
| |
| -- array (L .. H) := (.. positional elements.., others => E); |
| |
| -- we always generate something like: |
| |
| -- J : Index_Type := Index_Of_Last_Positional_Element; |
| -- while J < H loop |
| -- J := Index_Base'Succ (J) |
| -- Tmp (J) := E; |
| -- end loop; |
| |
| function Build_Array_Aggr_Code |
| (N : Node_Id; |
| Ctype : Entity_Id; |
| Index : Node_Id; |
| Into : Node_Id; |
| Scalar_Comp : Boolean; |
| Indexes : List_Id := No_List) return List_Id |
| is |
| Loc : constant Source_Ptr := Sloc (N); |
| Index_Base : constant Entity_Id := Base_Type (Etype (Index)); |
| Index_Base_L : constant Node_Id := Type_Low_Bound (Index_Base); |
| Index_Base_H : constant Node_Id := Type_High_Bound (Index_Base); |
| |
| function Add (Val : Int; To : Node_Id) return Node_Id; |
| -- Returns an expression where Val is added to expression To, unless |
| -- To+Val is provably out of To's base type range. To must be an |
| -- already analyzed expression. |
| |
| function Empty_Range (L, H : Node_Id) return Boolean; |
| -- Returns True if the range defined by L .. H is certainly empty |
| |
| function Equal (L, H : Node_Id) return Boolean; |
| -- Returns True if L = H for sure |
| |
| function Index_Base_Name return Node_Id; |
| -- Returns a new reference to the index type name |
| |
| function Gen_Assign |
| (Ind : Node_Id; |
| Expr : Node_Id; |
| In_Loop : Boolean := False) return List_Id; |
| -- Ind must be a side-effect-free expression. If the input aggregate N |
| -- to Build_Loop contains no subaggregates, then this function returns |
| -- the assignment statement: |
| -- |
| -- Into (Indexes, Ind) := Expr; |
| -- |
| -- Otherwise we call Build_Code recursively. Flag In_Loop should be set |
| -- when the assignment appears within a generated loop. |
| -- |
| -- Ada 2005 (AI-287): In case of default initialized component, Expr |
| -- is empty and we generate a call to the corresponding IP subprogram. |
| |
| function Gen_Loop (L, H : Node_Id; Expr : Node_Id) return List_Id; |
| -- Nodes L and H must be side-effect-free expressions. If the input |
| -- aggregate N to Build_Loop contains no subaggregates, this routine |
| -- returns the for loop statement: |
| -- |
| -- for J in Index_Base'(L) .. Index_Base'(H) loop |
| -- Into (Indexes, J) := Expr; |
| -- end loop; |
| -- |
| -- Otherwise we call Build_Code recursively. As an optimization if the |
| -- loop covers 3 or fewer scalar elements we generate a sequence of |
| -- assignments. |
| -- If the component association that generates the loop comes from an |
| -- Iterated_Component_Association, the loop parameter has the name of |
| -- the corresponding parameter in the original construct. |
| |
| function Gen_While (L, H : Node_Id; Expr : Node_Id) return List_Id; |
| -- Nodes L and H must be side-effect-free expressions. If the input |
| -- aggregate N to Build_Loop contains no subaggregates, this routine |
| -- returns the while loop statement: |
| -- |
| -- J : Index_Base := L; |
| -- while J < H loop |
| -- J := Index_Base'Succ (J); |
| -- Into (Indexes, J) := Expr; |
| -- end loop; |
| -- |
| -- Otherwise we call Build_Code recursively |
| |
| function Get_Assoc_Expr (Assoc : Node_Id) return Node_Id; |
| -- For an association with a box, use value given by aspect |
| -- Default_Component_Value of array type if specified, else use |
| -- value given by aspect Default_Value for component type itself |
| -- if specified, else return Empty. |
| |
| function Local_Compile_Time_Known_Value (E : Node_Id) return Boolean; |
| function Local_Expr_Value (E : Node_Id) return Uint; |
| -- These two Local routines are used to replace the corresponding ones |
| -- in sem_eval because while processing the bounds of an aggregate with |
| -- discrete choices whose index type is an enumeration, we build static |
| -- expressions not recognized by Compile_Time_Known_Value as such since |
| -- they have not yet been analyzed and resolved. All the expressions in |
| -- question are things like Index_Base_Name'Val (Const) which we can |
| -- easily recognize as being constant. |
| |
| --------- |
| -- Add -- |
| --------- |
| |
| function Add (Val : Int; To : Node_Id) return Node_Id is |
| Expr_Pos : Node_Id; |
| Expr : Node_Id; |
| To_Pos : Node_Id; |
| U_To : Uint; |
| U_Val : constant Uint := UI_From_Int (Val); |
| |
| begin |
| -- Note: do not try to optimize the case of Val = 0, because |
| -- we need to build a new node with the proper Sloc value anyway. |
| |
| -- First test if we can do constant folding |
| |
| if Local_Compile_Time_Known_Value (To) then |
| U_To := Local_Expr_Value (To) + Val; |
| |
| -- Determine if our constant is outside the range of the index. |
| -- If so return an Empty node. This empty node will be caught |
| -- by Empty_Range below. |
| |
| if Compile_Time_Known_Value (Index_Base_L) |
| and then U_To < Expr_Value (Index_Base_L) |
| then |
| return Empty; |
| |
| elsif Compile_Time_Known_Value (Index_Base_H) |
| and then U_To > Expr_Value (Index_Base_H) |
| then |
| return Empty; |
| end if; |
| |
| Expr_Pos := Make_Integer_Literal (Loc, U_To); |
| Set_Is_Static_Expression (Expr_Pos); |
| |
| if not Is_Enumeration_Type (Index_Base) then |
| Expr := Expr_Pos; |
| |
| -- If we are dealing with enumeration return |
| -- Index_Base'Val (Expr_Pos) |
| |
| else |
| Expr := |
| Make_Attribute_Reference |
| (Loc, |
| Prefix => Index_Base_Name, |
| Attribute_Name => Name_Val, |
| Expressions => New_List (Expr_Pos)); |
| end if; |
| |
| return Expr; |
| end if; |
| |
| -- If we are here no constant folding possible |
| |
| if not Is_Enumeration_Type (Index_Base) then |
| Expr := |
| Make_Op_Add (Loc, |
| Left_Opnd => Duplicate_Subexpr (To), |
| Right_Opnd => Make_Integer_Literal (Loc, U_Val)); |
| |
| -- If we are dealing with enumeration return |
| -- Index_Base'Val (Index_Base'Pos (To) + Val) |
| |
| else |
| To_Pos := |
| Make_Attribute_Reference |
| (Loc, |
| Prefix => Index_Base_Name, |
| Attribute_Name => Name_Pos, |
| Expressions => New_List (Duplicate_Subexpr (To))); |
| |
| Expr_Pos := |
| Make_Op_Add (Loc, |
| Left_Opnd => To_Pos, |
| Right_Opnd => Make_Integer_Literal (Loc, U_Val)); |
| |
| Expr := |
| Make_Attribute_Reference |
| (Loc, |
| Prefix => Index_Base_Name, |
| Attribute_Name => Name_Val, |
| Expressions => New_List (Expr_Pos)); |
| end if; |
| |
| return Expr; |
| end Add; |
| |
| ----------------- |
| -- Empty_Range -- |
| ----------------- |
| |
| function Empty_Range (L, H : Node_Id) return Boolean is |
| Is_Empty : Boolean := False; |
| Low : Node_Id; |
| High : Node_Id; |
| |
| begin |
| -- First check if L or H were already detected as overflowing the |
| -- index base range type by function Add above. If this is so Add |
| -- returns the empty node. |
| |
| if No (L) or else No (H) then |
| return True; |
| end if; |
| |
| for J in 1 .. 3 loop |
| case J is |
| |
| -- L > H range is empty |
| |
| when 1 => |
| Low := L; |
| High := H; |
| |
| -- B_L > H range must be empty |
| |
| when 2 => |
| Low := Index_Base_L; |
| High := H; |
| |
| -- L > B_H range must be empty |
| |
| when 3 => |
| Low := L; |
| High := Index_Base_H; |
| end case; |
| |
| if Local_Compile_Time_Known_Value (Low) |
| and then |
| Local_Compile_Time_Known_Value (High) |
| then |
| Is_Empty := |
| UI_Gt (Local_Expr_Value (Low), Local_Expr_Value (High)); |
| end if; |
| |
| exit when Is_Empty; |
| end loop; |
| |
| return Is_Empty; |
| end Empty_Range; |
| |
| ----------- |
| -- Equal -- |
| ----------- |
| |
| function Equal (L, H : Node_Id) return Boolean is |
| begin |
| if L = H then |
| return True; |
| |
| elsif Local_Compile_Time_Known_Value (L) |
| and then |
| Local_Compile_Time_Known_Value (H) |
| then |
| return UI_Eq (Local_Expr_Value (L), Local_Expr_Value (H)); |
| end if; |
| |
| return False; |
| end Equal; |
| |
| ---------------- |
| -- Gen_Assign -- |
| ---------------- |
| |
| function Gen_Assign |
| (Ind : Node_Id; |
| Expr : Node_Id; |
| In_Loop : Boolean := False) return List_Id |
| is |
| function Add_Loop_Actions (Lis : List_Id) return List_Id; |
| -- Collect insert_actions generated in the construction of a loop, |
| -- and prepend them to the sequence of assignments to complete the |
| -- eventual body of the loop. |
| |
| procedure Initialize_Array_Component |
| (Arr_Comp : Node_Id; |
| Comp_Typ : Node_Id; |
| Init_Expr : Node_Id; |
| Stmts : List_Id); |
| -- Perform the initialization of array component Arr_Comp with |
| -- expected type Comp_Typ. Init_Expr denotes the initialization |
| -- expression of the array component. All generated code is added |
| -- to list Stmts. |
| |
| procedure Initialize_Ctrl_Array_Component |
| (Arr_Comp : Node_Id; |
| Comp_Typ : Entity_Id; |
| Init_Expr : Node_Id; |
| Stmts : List_Id); |
| -- Perform the initialization of array component Arr_Comp when its |
| -- expected type Comp_Typ needs finalization actions. Init_Expr is |
| -- the initialization expression of the array component. All hook- |
| -- related declarations are inserted prior to aggregate N. Remaining |
| -- code is added to list Stmts. |
| |
| ---------------------- |
| -- Add_Loop_Actions -- |
| ---------------------- |
| |
| function Add_Loop_Actions (Lis : List_Id) return List_Id is |
| Res : List_Id; |
| |
| begin |
| -- Ada 2005 (AI-287): Do nothing else in case of default |
| -- initialized component. |
| |
| if No (Expr) then |
| return Lis; |
| |
| elsif Nkind (Parent (Expr)) = N_Component_Association |
| and then Present (Loop_Actions (Parent (Expr))) |
| then |
| Append_List (Lis, Loop_Actions (Parent (Expr))); |
| Res := Loop_Actions (Parent (Expr)); |
| Set_Loop_Actions (Parent (Expr), No_List); |
| return Res; |
| |
| else |
| return Lis; |
| end if; |
| end Add_Loop_Actions; |
| |
| -------------------------------- |
| -- Initialize_Array_Component -- |
| -------------------------------- |
| |
| procedure Initialize_Array_Component |
| (Arr_Comp : Node_Id; |
| Comp_Typ : Node_Id; |
| Init_Expr : Node_Id; |
| Stmts : List_Id) |
| is |
| Exceptions_OK : constant Boolean := |
| not Restriction_Active |
| (No_Exception_Propagation); |
| |
| Finalization_OK : constant Boolean := |
| Present (Comp_Typ) |
| and then Needs_Finalization (Comp_Typ); |
| |
| Full_Typ : constant Entity_Id := Underlying_Type (Comp_Typ); |
| Adj_Call : Node_Id; |
| Blk_Stmts : List_Id; |
| Init_Stmt : Node_Id; |
| |
| begin |
| -- Protect the initialization statements from aborts. Generate: |
| |
| -- Abort_Defer; |
| |
| if Finalization_OK and Abort_Allowed then |
| if Exceptions_OK then |
| Blk_Stmts := New_List; |
| else |
| Blk_Stmts := Stmts; |
| end if; |
| |
| Append_To (Blk_Stmts, Build_Runtime_Call (Loc, RE_Abort_Defer)); |
| |
| -- Otherwise aborts are not allowed. All generated code is added |
| -- directly to the input list. |
| |
| else |
| Blk_Stmts := Stmts; |
| end if; |
| |
| -- Initialize the array element. Generate: |
| |
| -- Arr_Comp := Init_Expr; |
| |
| -- Note that the initialization expression is replicated because |
| -- it has to be reevaluated within a generated loop. |
| |
| Init_Stmt := |
| Make_OK_Assignment_Statement (Loc, |
| Name => New_Copy_Tree (Arr_Comp), |
| Expression => New_Copy_Tree (Init_Expr)); |
| Set_No_Ctrl_Actions (Init_Stmt); |
| |
| -- If this is an aggregate for an array of arrays, each |
| -- subaggregate will be expanded as well, and even with |
| -- No_Ctrl_Actions the assignments of inner components will |
| -- require attachment in their assignments to temporaries. These |
| -- temporaries must be finalized for each subaggregate. Generate: |
| |
| -- begin |
| -- Arr_Comp := Init_Expr; |
| -- end; |
| |
| if Finalization_OK and then Is_Array_Type (Comp_Typ) then |
| Init_Stmt := |
| Make_Block_Statement (Loc, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List (Init_Stmt))); |
| end if; |
| |
| Append_To (Blk_Stmts, Init_Stmt); |
| |
| -- Adjust the tag due to a possible view conversion. Generate: |
| |
| -- Arr_Comp._tag := Full_TypP; |
| |
| if Tagged_Type_Expansion |
| and then Present (Comp_Typ) |
| and then Is_Tagged_Type (Comp_Typ) |
| then |
| Append_To (Blk_Stmts, |
| Make_OK_Assignment_Statement (Loc, |
| Name => |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Arr_Comp), |
| Selector_Name => |
| New_Occurrence_Of |
| (First_Tag_Component (Full_Typ), Loc)), |
| |
| Expression => |
| Unchecked_Convert_To (RTE (RE_Tag), |
| New_Occurrence_Of |
| (Node (First_Elmt (Access_Disp_Table (Full_Typ))), |
| Loc)))); |
| end if; |
| |
| -- Adjust the array component. Controlled subaggregates are not |
| -- considered because each of their individual elements will |
| -- receive an adjustment of its own. Generate: |
| |
| -- [Deep_]Adjust (Arr_Comp); |
| |
| if Finalization_OK |
| and then not Is_Limited_Type (Comp_Typ) |
| and then not Is_Build_In_Place_Function_Call (Init_Expr) |
| and then not |
| (Is_Array_Type (Comp_Typ) |
| and then Is_Controlled (Component_Type (Comp_Typ)) |
| and then Nkind (Expr) = N_Aggregate) |
| then |
| Adj_Call := |
| Make_Adjust_Call |
| (Obj_Ref => New_Copy_Tree (Arr_Comp), |
| Typ => Comp_Typ); |
| |
| -- Guard against a missing [Deep_]Adjust when the component |
| -- type was not frozen properly. |
| |
| if Present (Adj_Call) then |
| Append_To (Blk_Stmts, Adj_Call); |
| end if; |
| end if; |
| |
| -- Complete the protection of the initialization statements |
| |
| if Finalization_OK and Abort_Allowed then |
| |
| -- Wrap the initialization statements in a block to catch a |
| -- potential exception. Generate: |
| |
| -- begin |
| -- Abort_Defer; |
| -- Arr_Comp := Init_Expr; |
| -- Arr_Comp._tag := Full_TypP; |
| -- [Deep_]Adjust (Arr_Comp); |
| -- at end |
| -- Abort_Undefer_Direct; |
| -- end; |
| |
| if Exceptions_OK then |
| Append_To (Stmts, |
| Build_Abort_Undefer_Block (Loc, |
| Stmts => Blk_Stmts, |
| Context => N)); |
| |
| -- Otherwise exceptions are not propagated. Generate: |
| |
| -- Abort_Defer; |
| -- Arr_Comp := Init_Expr; |
| -- Arr_Comp._tag := Full_TypP; |
| -- [Deep_]Adjust (Arr_Comp); |
| -- Abort_Undefer; |
| |
| else |
| Append_To (Blk_Stmts, |
| Build_Runtime_Call (Loc, RE_Abort_Undefer)); |
| end if; |
| end if; |
| end Initialize_Array_Component; |
| |
| ------------------------------------- |
| -- Initialize_Ctrl_Array_Component -- |
| ------------------------------------- |
| |
| procedure Initialize_Ctrl_Array_Component |
| (Arr_Comp : Node_Id; |
| Comp_Typ : Entity_Id; |
| Init_Expr : Node_Id; |
| Stmts : List_Id) |
| is |
| Act_Aggr : Node_Id; |
| Act_Stmts : List_Id; |
| Expr : Node_Id; |
| Fin_Call : Node_Id; |
| Hook_Clear : Node_Id; |
| |
| In_Place_Expansion : Boolean; |
| -- Flag set when a nonlimited controlled function call requires |
| -- in-place expansion. |
| |
| begin |
| -- Duplicate the initialization expression in case the context is |
| -- a multi choice list or an "others" choice which plugs various |
| -- holes in the aggregate. As a result the expression is no longer |
| -- shared between the various components and is reevaluated for |
| -- each such component. |
| |
| Expr := New_Copy_Tree (Init_Expr); |
| Set_Parent (Expr, Parent (Init_Expr)); |
| |
| -- Perform a preliminary analysis and resolution to determine what |
| -- the initialization expression denotes. An unanalyzed function |
| -- call may appear as an identifier or an indexed component. |
| |
| if Nkind (Expr) in N_Function_Call |
| | N_Identifier |
| | N_Indexed_Component |
| and then not Analyzed (Expr) |
| then |
| Preanalyze_And_Resolve (Expr, Comp_Typ); |
| end if; |
| |
| In_Place_Expansion := |
| Nkind (Expr) = N_Function_Call |
| and then not Is_Build_In_Place_Result_Type (Comp_Typ); |
| |
| -- The initialization expression is a controlled function call. |
| -- Perform in-place removal of side effects to avoid creating a |
| -- transient scope, which leads to premature finalization. |
| |
| -- This in-place expansion is not performed for limited transient |
| -- objects, because the initialization is already done in place. |
| |
| if In_Place_Expansion then |
| |
| -- Suppress the removal of side effects by general analysis, |
| -- because this behavior is emulated here. This avoids the |
| -- generation of a transient scope, which leads to out-of-order |
| -- adjustment and finalization. |
| |
| Set_No_Side_Effect_Removal (Expr); |
| |
| -- When the transient component initialization is related to a |
| -- range or an "others", keep all generated statements within |
| -- the enclosing loop. This way the controlled function call |
| -- will be evaluated at each iteration, and its result will be |
| -- finalized at the end of each iteration. |
| |
| if In_Loop then |
| Act_Aggr := Empty; |
| Act_Stmts := Stmts; |
| |
| -- Otherwise this is a single component initialization. Hook- |
| -- related statements are inserted prior to the aggregate. |
| |
| else |
| Act_Aggr := N; |
| Act_Stmts := No_List; |
| end if; |
| |
| -- Install all hook-related declarations and prepare the clean |
| -- up statements. |
| |
| Process_Transient_Component |
| (Loc => Loc, |
| Comp_Typ => Comp_Typ, |
| Init_Expr => Expr, |
| Fin_Call => Fin_Call, |
| Hook_Clear => Hook_Clear, |
| Aggr => Act_Aggr, |
| Stmts => Act_Stmts); |
| end if; |
| |
| -- Use the noncontrolled component initialization circuitry to |
| -- assign the result of the function call to the array element. |
| -- This also performs subaggregate wrapping, tag adjustment, and |
| -- [deep] adjustment of the array element. |
| |
| Initialize_Array_Component |
| (Arr_Comp => Arr_Comp, |
| Comp_Typ => Comp_Typ, |
| Init_Expr => Expr, |
| Stmts => Stmts); |
| |
| -- At this point the array element is fully initialized. Complete |
| -- the processing of the controlled array component by finalizing |
| -- the transient function result. |
| |
| if In_Place_Expansion then |
| Process_Transient_Component_Completion |
| (Loc => Loc, |
| Aggr => N, |
| Fin_Call => Fin_Call, |
| Hook_Clear => Hook_Clear, |
| Stmts => Stmts); |
| end if; |
| end Initialize_Ctrl_Array_Component; |
| |
| -- Local variables |
| |
| Stmts : constant List_Id := New_List; |
| |
| Comp_Typ : Entity_Id := Empty; |
| Expr_Q : Node_Id; |
| Indexed_Comp : Node_Id; |
| Init_Call : Node_Id; |
| New_Indexes : List_Id; |
| |
| -- Start of processing for Gen_Assign |
| |
| begin |
| if No (Indexes) then |
| New_Indexes := New_List; |
| else |
| New_Indexes := New_Copy_List_Tree (Indexes); |
| end if; |
| |
| Append_To (New_Indexes, Ind); |
| |
| if Present (Next_Index (Index)) then |
| return |
| Add_Loop_Actions ( |
| Build_Array_Aggr_Code |
| (N => Expr, |
| Ctype => Ctype, |
| Index => Next_Index (Index), |
| Into => Into, |
| Scalar_Comp => Scalar_Comp, |
| Indexes => New_Indexes)); |
| end if; |
| |
| -- If we get here then we are at a bottom-level (sub-)aggregate |
| |
| Indexed_Comp := |
| Checks_Off |
| (Make_Indexed_Component (Loc, |
| Prefix => New_Copy_Tree (Into), |
| Expressions => New_Indexes)); |
| |
| Set_Assignment_OK (Indexed_Comp); |
| |
| -- Ada 2005 (AI-287): In case of default initialized component, Expr |
| -- is not present (and therefore we also initialize Expr_Q to empty). |
| |
| if No (Expr) then |
| Expr_Q := Empty; |
| elsif Nkind (Expr) = N_Qualified_Expression then |
| Expr_Q := Expression (Expr); |
| else |
| Expr_Q := Expr; |
| end if; |
| |
| if Present (Etype (N)) and then Etype (N) /= Any_Composite then |
| Comp_Typ := Component_Type (Etype (N)); |
| pragma Assert (Comp_Typ = Ctype); -- AI-287 |
| |
| elsif Present (Next (First (New_Indexes))) then |
| |
| -- Ada 2005 (AI-287): Do nothing in case of default initialized |
| -- component because we have received the component type in |
| -- the formal parameter Ctype. |
| |
| -- ??? Some assert pragmas have been added to check if this new |
| -- formal can be used to replace this code in all cases. |
| |
| if Present (Expr) then |
| |
| -- This is a multidimensional array. Recover the component type |
| -- from the outermost aggregate, because subaggregates do not |
| -- have an assigned type. |
| |
| declare |
| P : Node_Id; |
| |
| begin |
| P := Parent (Expr); |
| while Present (P) loop |
| if Nkind (P) = N_Aggregate |
| and then Present (Etype (P)) |
| then |
| Comp_Typ := Component_Type (Etype (P)); |
| exit; |
| |
| else |
| P := Parent (P); |
| end if; |
| end loop; |
| |
| pragma Assert (Comp_Typ = Ctype); -- AI-287 |
| end; |
| end if; |
| end if; |
| |
| -- Ada 2005 (AI-287): We only analyze the expression in case of non- |
| -- default initialized components (otherwise Expr_Q is not present). |
| |
| if Present (Expr_Q) |
| and then Nkind (Expr_Q) in N_Aggregate | N_Extension_Aggregate |
| then |
| -- At this stage the Expression may not have been analyzed yet |
| -- because the array aggregate code has not been updated to use |
| -- the Expansion_Delayed flag and avoid analysis altogether to |
| -- solve the same problem (see Resolve_Aggr_Expr). So let us do |
| -- the analysis of non-array aggregates now in order to get the |
| -- value of Expansion_Delayed flag for the inner aggregate ??? |
| |
| -- In the case of an iterated component association, the analysis |
| -- of the generated loop will analyze the expression in the |
| -- proper context, in which the loop parameter is visible. |
| |
| if Present (Comp_Typ) and then not Is_Array_Type (Comp_Typ) then |
| if Nkind (Parent (Expr_Q)) = N_Iterated_Component_Association |
| or else Nkind (Parent (Parent ((Expr_Q)))) = |
| N_Iterated_Component_Association |
| then |
| null; |
| else |
| Analyze_And_Resolve (Expr_Q, Comp_Typ); |
| end if; |
| end if; |
| |
| if Is_Delayed_Aggregate (Expr_Q) then |
| |
| -- This is either a subaggregate of a multidimensional array, |
| -- or a component of an array type whose component type is |
| -- also an array. In the latter case, the expression may have |
| -- component associations that provide different bounds from |
| -- those of the component type, and sliding must occur. Instead |
| -- of decomposing the current aggregate assignment, force the |
| -- reanalysis of the assignment, so that a temporary will be |
| -- generated in the usual fashion, and sliding will take place. |
| |
| if Nkind (Parent (N)) = N_Assignment_Statement |
| and then Is_Array_Type (Comp_Typ) |
| and then Present (Component_Associations (Expr_Q)) |
| and then Must_Slide (N, Comp_Typ, Etype (Expr_Q)) |
| then |
| Set_Expansion_Delayed (Expr_Q, False); |
| Set_Analyzed (Expr_Q, False); |
| |
| else |
| return |
| Add_Loop_Actions ( |
| Late_Expansion (Expr_Q, Etype (Expr_Q), Indexed_Comp)); |
| end if; |
| end if; |
| end if; |
| |
| if Present (Expr) then |
| |
| -- Handle an initialization expression of a controlled type in |
| -- case it denotes a function call. In general such a scenario |
| -- will produce a transient scope, but this will lead to wrong |
| -- order of initialization, adjustment, and finalization in the |
| -- context of aggregates. |
| |
| -- Target (1) := Ctrl_Func_Call; |
| |
| -- begin -- scope |
| -- Trans_Obj : ... := Ctrl_Func_Call; -- object |
| -- Target (1) := Trans_Obj; |
| -- Finalize (Trans_Obj); |
| -- end; |
| -- Target (1)._tag := ...; |
| -- Adjust (Target (1)); |
| |
| -- In the example above, the call to Finalize occurs too early |
| -- and as a result it may leave the array component in a bad |
| -- state. Finalization of the transient object should really |
| -- happen after adjustment. |
| |
| -- To avoid this scenario, perform in-place side-effect removal |
| -- of the function call. This eliminates the transient property |
| -- of the function result and ensures correct order of actions. |
| |
| -- Res : ... := Ctrl_Func_Call; |
| -- Target (1) := Res; |
| -- Target (1)._tag := ...; |
| -- Adjust (Target (1)); |
| -- Finalize (Res); |
| |
| if Present (Comp_Typ) |
| and then Needs_Finalization (Comp_Typ) |
| and then Nkind (Expr) /= N_Aggregate |
| then |
| Initialize_Ctrl_Array_Component |
| (Arr_Comp => Indexed_Comp, |
| Comp_Typ => Comp_Typ, |
| Init_Expr => Expr, |
| Stmts => Stmts); |
| |
| -- Otherwise perform simple component initialization |
| |
| else |
| Initialize_Array_Component |
| (Arr_Comp => Indexed_Comp, |
| Comp_Typ => Comp_Typ, |
| Init_Expr => Expr, |
| Stmts => Stmts); |
| end if; |
| |
| -- Ada 2005 (AI-287): In case of default initialized component, call |
| -- the initialization subprogram associated with the component type. |
| -- If the component type is an access type, add an explicit null |
| -- assignment, because for the back-end there is an initialization |
| -- present for the whole aggregate, and no default initialization |
| -- will take place. |
| |
| -- In addition, if the component type is controlled, we must call |
| -- its Initialize procedure explicitly, because there is no explicit |
| -- object creation that will invoke it otherwise. |
| |
| else |
| if Present (Base_Init_Proc (Base_Type (Ctype))) |
| or else Has_Task (Base_Type (Ctype)) |
| then |
| Append_List_To (Stmts, |
| Build_Initialization_Call (Loc, |
| Id_Ref => Indexed_Comp, |
| Typ => Ctype, |
| With_Default_Init => True)); |
| |
| -- If the component type has invariants, add an invariant |
| -- check after the component is default-initialized. It will |
| -- be analyzed and resolved before the code for initialization |
| -- of other components. |
| |
| if Has_Invariants (Ctype) then |
| Set_Etype (Indexed_Comp, Ctype); |
| Append_To (Stmts, Make_Invariant_Call (Indexed_Comp)); |
| end if; |
| end if; |
| |
| if Needs_Finalization (Ctype) then |
| Init_Call := |
| Make_Init_Call |
| (Obj_Ref => New_Copy_Tree (Indexed_Comp), |
| Typ => Ctype); |
| |
| -- Guard against a missing [Deep_]Initialize when the component |
| -- type was not properly frozen. |
| |
| if Present (Init_Call) then |
| Append_To (Stmts, Init_Call); |
| end if; |
| end if; |
| |
| -- If Default_Initial_Condition applies to the component type, |
| -- add a DIC check after the component is default-initialized, |
| -- as well as after an Initialize procedure is called, in the |
| -- case of components of a controlled type. It will be analyzed |
| -- and resolved before the code for initialization of other |
| -- components. |
| |
| -- Theoretically this might also be needed for cases where Expr |
| -- is not empty, but a default init still applies, such as for |
| -- Default_Value cases, in which case we won't get here. ??? |
| |
| if Has_DIC (Ctype) and then Present (DIC_Procedure (Ctype)) then |
| Append_To (Stmts, |
| Build_DIC_Call (Loc, New_Copy_Tree (Indexed_Comp), Ctype)); |
| end if; |
| end if; |
| |
| return Add_Loop_Actions (Stmts); |
| end Gen_Assign; |
| |
| -------------- |
| -- Gen_Loop -- |
| -------------- |
| |
| function Gen_Loop (L, H : Node_Id; Expr : Node_Id) return List_Id is |
| Is_Iterated_Component : constant Boolean := |
| Parent_Kind (Expr) = N_Iterated_Component_Association; |
| |
| L_J : Node_Id; |
| |
| L_L : Node_Id; |
| -- Index_Base'(L) |
| |
| L_H : Node_Id; |
| -- Index_Base'(H) |
| |
| L_Range : Node_Id; |
| -- Index_Base'(L) .. Index_Base'(H) |
| |
| L_Iteration_Scheme : Node_Id; |
| -- L_J in Index_Base'(L) .. Index_Base'(H) |
| |
| L_Body : List_Id; |
| -- The statements to execute in the loop |
| |
| S : constant List_Id := New_List; |
| -- List of statements |
| |
| Tcopy : Node_Id; |
| -- Copy of expression tree, used for checking purposes |
| |
| begin |
| -- If loop bounds define an empty range return the null statement |
| |
| if Empty_Range (L, H) then |
| Append_To (S, Make_Null_Statement (Loc)); |
| |
| -- Ada 2005 (AI-287): Nothing else need to be done in case of |
| -- default initialized component. |
| |
| if No (Expr) then |
| null; |
| |
| else |
| -- The expression must be type-checked even though no component |
| -- of the aggregate will have this value. This is done only for |
| -- actual components of the array, not for subaggregates. Do |
| -- the check on a copy, because the expression may be shared |
| -- among several choices, some of which might be non-null. |
| |
| if Present (Etype (N)) |
| and then Is_Array_Type (Etype (N)) |
| and then No (Next_Index (Index)) |
| then |
| Expander_Mode_Save_And_Set (False); |
| Tcopy := New_Copy_Tree (Expr); |
| Set_Parent (Tcopy, N); |
| |
| -- For iterated_component_association analyze and resolve |
| -- the expression with name of the index parameter visible. |
| -- To manipulate scopes, we use entity of the implicit loop. |
| |
| if Is_Iterated_Component then |
| declare |
| Index_Parameter : constant Entity_Id := |
| Defining_Identifier (Parent (Expr)); |
| begin |
| Push_Scope (Scope (Index_Parameter)); |
| Enter_Name (Index_Parameter); |
| Analyze_And_Resolve |
| (Tcopy, Component_Type (Etype (N))); |
| End_Scope; |
| end; |
| |
| -- For ordinary component association, just analyze and |
| -- resolve the expression. |
| |
| else |
| Analyze_And_Resolve (Tcopy, Component_Type (Etype (N))); |
| end if; |
| |
| Expander_Mode_Restore; |
| end if; |
| end if; |
| |
| return S; |
| |
| -- If loop bounds are the same then generate an assignment, unless |
| -- the parent construct is an Iterated_Component_Association. |
| |
| elsif Equal (L, H) and then not Is_Iterated_Component then |
| return Gen_Assign (New_Copy_Tree (L), Expr); |
| |
| -- If H - L <= 2 then generate a sequence of assignments when we are |
| -- processing the bottom most aggregate and it contains scalar |
| -- components. |
| |
| elsif No (Next_Index (Index)) |
| and then Scalar_Comp |
| and then Local_Compile_Time_Known_Value (L) |
| and then Local_Compile_Time_Known_Value (H) |
| and then Local_Expr_Value (H) - Local_Expr_Value (L) <= 2 |
| and then not Is_Iterated_Component |
| then |
| Append_List_To (S, Gen_Assign (New_Copy_Tree (L), Expr)); |
| Append_List_To (S, Gen_Assign (Add (1, To => L), Expr)); |
| |
| if Local_Expr_Value (H) - Local_Expr_Value (L) = 2 then |
| Append_List_To (S, Gen_Assign (Add (2, To => L), Expr)); |
| end if; |
| |
| return S; |
| end if; |
| |
| -- Otherwise construct the loop, starting with the loop index L_J |
| |
| if Is_Iterated_Component then |
| L_J := |
| Make_Defining_Identifier (Loc, |
| Chars => (Chars (Defining_Identifier (Parent (Expr))))); |
| |
| else |
| L_J := Make_Temporary (Loc, 'J', L); |
| end if; |
| |
| -- Construct "L .. H" in Index_Base. We use a qualified expression |
| -- for the bound to convert to the index base, but we don't need |
| -- to do that if we already have the base type at hand. |
| |
| if Etype (L) = Index_Base then |
| L_L := L; |
| else |
| L_L := |
| Make_Qualified_Expression (Loc, |
| Subtype_Mark => Index_Base_Name, |
| Expression => New_Copy_Tree (L)); |
| end if; |
| |
| if Etype (H) = Index_Base then |
| L_H := H; |
| else |
| L_H := |
| Make_Qualified_Expression (Loc, |
| Subtype_Mark => Index_Base_Name, |
| Expression => New_Copy_Tree (H)); |
| end if; |
| |
| L_Range := |
| Make_Range (Loc, |
| Low_Bound => L_L, |
| High_Bound => L_H); |
| |
| -- Construct "for L_J in Index_Base range L .. H" |
| |
| L_Iteration_Scheme := |
| Make_Iteration_Scheme (Loc, |
| Loop_Parameter_Specification => |
| Make_Loop_Parameter_Specification (Loc, |
| Defining_Identifier => L_J, |
| Discrete_Subtype_Definition => L_Range)); |
| |
| -- Construct the statements to execute in the loop body |
| |
| L_Body := |
| Gen_Assign (New_Occurrence_Of (L_J, Loc), Expr, In_Loop => True); |
| |
| -- Construct the final loop |
| |
| Append_To (S, |
| Make_Implicit_Loop_Statement |
| (Node => N, |
| Identifier => Empty, |
| Iteration_Scheme => L_Iteration_Scheme, |
| Statements => L_Body)); |
| |
| -- A small optimization: if the aggregate is initialized with a box |
| -- and the component type has no initialization procedure, remove the |
| -- useless empty loop. |
| |
| if Nkind (First (S)) = N_Loop_Statement |
| and then Is_Empty_List (Statements (First (S))) |
| then |
| return New_List (Make_Null_Statement (Loc)); |
| else |
| return S; |
| end if; |
| end Gen_Loop; |
| |
| --------------- |
| -- Gen_While -- |
| --------------- |
| |
| -- The code built is |
| |
| -- W_J : Index_Base := L; |
| -- while W_J < H loop |
| -- W_J := Index_Base'Succ (W); |
| -- L_Body; |
| -- end loop; |
| |
| function Gen_While (L, H : Node_Id; Expr : Node_Id) return List_Id is |
| W_J : Node_Id; |
| |
| W_Decl : Node_Id; |
| -- W_J : Base_Type := L; |
| |
| W_Iteration_Scheme : Node_Id; |
| -- while W_J < H |
| |
| W_Index_Succ : Node_Id; |
| -- Index_Base'Succ (J) |
| |
| W_Increment : Node_Id; |
| -- W_J := Index_Base'Succ (W) |
| |
| W_Body : constant List_Id := New_List; |
| -- The statements to execute in the loop |
| |
| S : constant List_Id := New_List; |
| -- list of statement |
| |
| begin |
| -- If loop bounds define an empty range or are equal return null |
| |
| if Empty_Range (L, H) or else Equal (L, H) then |
| Append_To (S, Make_Null_Statement (Loc)); |
| return S; |
| end if; |
| |
| -- Build the decl of W_J |
| |
| W_J := Make_Temporary (Loc, 'J', L); |
| W_Decl := |
| Make_Object_Declaration |
| (Loc, |
| Defining_Identifier => W_J, |
| Object_Definition => Index_Base_Name, |
| Expression => L); |
| |
| -- Theoretically we should do a New_Copy_Tree (L) here, but we know |
| -- that in this particular case L is a fresh Expr generated by |
| -- Add which we are the only ones to use. |
| |
| Append_To (S, W_Decl); |
| |
| -- Construct " while W_J < H" |
| |
| W_Iteration_Scheme := |
| Make_Iteration_Scheme |
| (Loc, |
| Condition => Make_Op_Lt |
| (Loc, |
| Left_Opnd => New_Occurrence_Of (W_J, Loc), |
| Right_Opnd => New_Copy_Tree (H))); |
| |
| -- Construct the statements to execute in the loop body |
| |
| W_Index_Succ := |
| Make_Attribute_Reference |
| (Loc, |
| Prefix => Index_Base_Name, |
| Attribute_Name => Name_Succ, |
| Expressions => New_List (New_Occurrence_Of (W_J, Loc))); |
| |
| W_Increment := |
| Make_OK_Assignment_Statement |
| (Loc, |
| Name => New_Occurrence_Of (W_J, Loc), |
| Expression => W_Index_Succ); |
| |
| Append_To (W_Body, W_Increment); |
| |
| Append_List_To (W_Body, |
| Gen_Assign (New_Occurrence_Of (W_J, Loc), Expr, In_Loop => True)); |
| |
| -- Construct the final loop |
| |
| Append_To (S, |
| Make_Implicit_Loop_Statement |
| (Node => N, |
| Identifier => Empty, |
| Iteration_Scheme => W_Iteration_Scheme, |
| Statements => W_Body)); |
| |
| return S; |
| end Gen_While; |
| |
| -------------------- |
| -- Get_Assoc_Expr -- |
| -------------------- |
| |
| function Get_Assoc_Expr (Assoc : Node_Id) return Node_Id is |
| Typ : constant Entity_Id := Base_Type (Etype (N)); |
| |
| begin |
| if Box_Present (Assoc) then |
| if Present (Default_Aspect_Component_Value (Typ)) then |
| return Default_Aspect_Component_Value (Typ); |
| elsif Needs_Simple_Initialization (Ctype) then |
| return Get_Simple_Init_Val (Ctype, N); |
| else |
| return Empty; |
| end if; |
| |
| else |
| return Expression (Assoc); |
| end if; |
| end Get_Assoc_Expr; |
| |
| --------------------- |
| -- Index_Base_Name -- |
| --------------------- |
| |
| function Index_Base_Name return Node_Id is |
| begin |
| return New_Occurrence_Of (Index_Base, Sloc (N)); |
| end Index_Base_Name; |
| |
| ------------------------------------ |
| -- Local_Compile_Time_Known_Value -- |
| ------------------------------------ |
| |
| function Local_Compile_Time_Known_Value (E : Node_Id) return Boolean is |
| begin |
| return Compile_Time_Known_Value (E) |
| or else |
| (Nkind (E) = N_Attribute_Reference |
| and then Attribute_Name (E) = Name_Val |
| and then Compile_Time_Known_Value (First (Expressions (E)))); |
| end Local_Compile_Time_Known_Value; |
| |
| ---------------------- |
| -- Local_Expr_Value -- |
| ---------------------- |
| |
| function Local_Expr_Value (E : Node_Id) return Uint is |
| begin |
| if Compile_Time_Known_Value (E) then |
| return Expr_Value (E); |
| else |
| return Expr_Value (First (Expressions (E))); |
| end if; |
| end Local_Expr_Value; |
| |
| -- Local variables |
| |
| New_Code : constant List_Id := New_List; |
| |
| Aggr_L : constant Node_Id := Low_Bound (Aggregate_Bounds (N)); |
| Aggr_H : constant Node_Id := High_Bound (Aggregate_Bounds (N)); |
| -- The aggregate bounds of this specific subaggregate. Note that if the |
| -- code generated by Build_Array_Aggr_Code is executed then these bounds |
| -- are OK. Otherwise a Constraint_Error would have been raised. |
| |
| Aggr_Low : constant Node_Id := Duplicate_Subexpr_No_Checks (Aggr_L); |
| Aggr_High : constant Node_Id := Duplicate_Subexpr_No_Checks (Aggr_H); |
| -- After Duplicate_Subexpr these are side-effect free |
| |
| Assoc : Node_Id; |
| Choice : Node_Id; |
| Expr : Node_Id; |
| Typ : Entity_Id; |
| |
| Bounds : Range_Nodes; |
| Low : Node_Id renames Bounds.First; |
| High : Node_Id renames Bounds.Last; |
| |
| Nb_Choices : Nat := 0; |
| Table : Case_Table_Type (1 .. Number_Of_Choices (N)); |
| -- Used to sort all the different choice values |
| |
| Nb_Elements : Int; |
| -- Number of elements in the positional aggregate |
| |
| Others_Assoc : Node_Id := Empty; |
| |
| -- Start of processing for Build_Array_Aggr_Code |
| |
| begin |
| -- First before we start, a special case. if we have a bit packed |
| -- array represented as a modular type, then clear the value to |
| -- zero first, to ensure that unused bits are properly cleared. |
| |
| Typ := Etype (N); |
| |
| if Present (Typ) |
| and then Is_Bit_Packed_Array (Typ) |
| and then Is_Modular_Integer_Type (Packed_Array_Impl_Type (Typ)) |
| then |
| declare |
| Zero : constant Node_Id := Make_Integer_Literal (Loc, Uint_0); |
| begin |
| Analyze_And_Resolve (Zero, Packed_Array_Impl_Type (Typ)); |
| Append_To (New_Code, |
| Make_Assignment_Statement (Loc, |
| Name => New_Copy_Tree (Into), |
| Expression => Unchecked_Convert_To (Typ, Zero))); |
| end; |
| end if; |
| |
| -- If the component type contains tasks, we need to build a Master |
| -- entity in the current scope, because it will be needed if build- |
| -- in-place functions are called in the expanded code. |
| |
| if Nkind (Parent (N)) = N_Object_Declaration and then Has_Task (Typ) then |
| Build_Master_Entity (Defining_Identifier (Parent (N))); |
| end if; |
| |
| -- STEP 1: Process component associations |
| |
| -- For those associations that may generate a loop, initialize |
| -- Loop_Actions to collect inserted actions that may be crated. |
| |
| -- Skip this if no component associations |
| |
| if No (Expressions (N)) then |
| |
| -- STEP 1 (a): Sort the discrete choices |
| |
| Assoc := First (Component_Associations (N)); |
| while Present (Assoc) loop |
| Choice := First (Choice_List (Assoc)); |
| while Present (Choice) loop |
| if Nkind (Choice) = N_Others_Choice then |
| Others_Assoc := Assoc; |
| exit; |
| end if; |
| |
| Bounds := Get_Index_Bounds (Choice); |
| |
| if Low /= High then |
| Set_Loop_Actions (Assoc, New_List); |
| end if; |
| |
| Nb_Choices := Nb_Choices + 1; |
| |
| Table (Nb_Choices) := |
| (Choice_Lo => Low, |
| Choice_Hi => High, |
| Choice_Node => Get_Assoc_Expr (Assoc)); |
| |
| Next (Choice); |
| end loop; |
| |
| Next (Assoc); |
| end loop; |
| |
| -- If there is more than one set of choices these must be static |
| -- and we can therefore sort them. Remember that Nb_Choices does not |
| -- account for an others choice. |
| |
| if Nb_Choices > 1 then |
| Sort_Case_Table (Table); |
| end if; |
| |
| -- STEP 1 (b): take care of the whole set of discrete choices |
| |
| for J in 1 .. Nb_Choices loop |
| Low := Table (J).Choice_Lo; |
| High := Table (J).Choice_Hi; |
| Expr := Table (J).Choice_Node; |
| Append_List (Gen_Loop (Low, High, Expr), To => New_Code); |
| end loop; |
| |
| -- STEP 1 (c): generate the remaining loops to cover others choice |
| -- We don't need to generate loops over empty gaps, but if there is |
| -- a single empty range we must analyze the expression for semantics |
| |
| if Present (Others_Assoc) then |
| declare |
| First : Boolean := True; |
| Dup_Expr : Node_Id; |
| |
| begin |
| for J in 0 .. Nb_Choices loop |
| if J = 0 then |
| Low := Aggr_Low; |
| else |
| Low := Add (1, To => Table (J).Choice_Hi); |
| end if; |
| |
| if J = Nb_Choices then |
| High := Aggr_High; |
| else |
| High := Add (-1, To => Table (J + 1).Choice_Lo); |
| end if; |
| |
| -- If this is an expansion within an init proc, make |
| -- sure that discriminant references are replaced by |
| -- the corresponding discriminal. |
| |
| if Inside_Init_Proc then |
| if Is_Entity_Name (Low) |
| and then Ekind (Entity (Low)) = E_Discriminant |
| then |
| Set_Entity (Low, Discriminal (Entity (Low))); |
| end if; |
| |
| if Is_Entity_Name (High) |
| and then Ekind (Entity (High)) = E_Discriminant |
| then |
| Set_Entity (High, Discriminal (Entity (High))); |
| end if; |
| end if; |
| |
| if First |
| or else not Empty_Range (Low, High) |
| then |
| First := False; |
| |
| -- Duplicate the expression in case we will be generating |
| -- several loops. As a result the expression is no longer |
| -- shared between the loops and is reevaluated for each |
| -- such loop. |
| |
| Expr := Get_Assoc_Expr (Others_Assoc); |
| Dup_Expr := New_Copy_Tree (Expr); |
| Copy_Parent (To => Dup_Expr, From => Expr); |
| |
| Set_Loop_Actions (Others_Assoc, New_List); |
| Append_List |
| (Gen_Loop (Low, High, Dup_Expr), To => New_Code); |
| end if; |
| end loop; |
| end; |
| end if; |
| |
| -- STEP 2: Process positional components |
| |
| else |
| -- STEP 2 (a): Generate the assignments for each positional element |
| -- Note that here we have to use Aggr_L rather than Aggr_Low because |
| -- Aggr_L is analyzed and Add wants an analyzed expression. |
| |
| Expr := First (Expressions (N)); |
| Nb_Elements := -1; |
| while Present (Expr) loop |
| Nb_Elements := Nb_Elements + 1; |
| Append_List (Gen_Assign (Add (Nb_Elements, To => Aggr_L), Expr), |
| To => New_Code); |
| Next (Expr); |
| end loop; |
| |
| -- STEP 2 (b): Generate final loop if an others choice is present. |
| -- Here Nb_Elements gives the offset of the last positional element. |
| |
| if Present (Component_Associations (N)) then |
| Assoc := Last (Component_Associations (N)); |
| |
| if Nkind (Assoc) = N_Iterated_Component_Association then |
| -- Ada 2022: generate a loop to have a proper scope for |
| -- the identifier that typically appears in the expression. |
| -- The lower bound of the loop is the position after all |
| -- previous positional components. |
| |
| Append_List (Gen_Loop (Add (Nb_Elements + 1, To => Aggr_L), |
| Aggr_High, |
| Expression (Assoc)), |
| To => New_Code); |
| else |
| -- Ada 2005 (AI-287) |
| |
| Append_List (Gen_While (Add (Nb_Elements, To => Aggr_L), |
| Aggr_High, |
| Get_Assoc_Expr (Assoc)), |
| To => New_Code); |
| end if; |
| end if; |
| end if; |
| |
| return New_Code; |
| end Build_Array_Aggr_Code; |
| |
| ---------------------------- |
| -- Build_Record_Aggr_Code -- |
| ---------------------------- |
| |
| function Build_Record_Aggr_Code |
| (N : Node_Id; |
| Typ : Entity_Id; |
| Lhs : Node_Id) return List_Id |
| is |
| Loc : constant Source_Ptr := Sloc (N); |
| L : constant List_Id := New_List; |
| N_Typ : constant Entity_Id := Etype (N); |
| |
| Comp : Node_Id; |
| Instr : Node_Id; |
| Ref : Node_Id; |
| Target : Entity_Id; |
| Comp_Type : Entity_Id; |
| Selector : Entity_Id; |
| Comp_Expr : Node_Id; |
| Expr_Q : Node_Id; |
| |
| -- If this is an internal aggregate, the External_Final_List is an |
| -- expression for the controller record of the enclosing type. |
| |
| -- If the current aggregate has several controlled components, this |
| -- expression will appear in several calls to attach to the finali- |
| -- zation list, and it must not be shared. |
| |
| Ancestor_Is_Expression : Boolean := False; |
| Ancestor_Is_Subtype_Mark : Boolean := False; |
| |
| Init_Typ : Entity_Id := Empty; |
| |
| Finalization_Done : Boolean := False; |
| -- True if Generate_Finalization_Actions has already been called; calls |
| -- after the first do nothing. |
| |
| function Ancestor_Discriminant_Value (Disc : Entity_Id) return Node_Id; |
| -- Returns the value that the given discriminant of an ancestor type |
| -- should receive (in the absence of a conflict with the value provided |
| -- by an ancestor part of an extension aggregate). |
| |
| procedure Check_Ancestor_Discriminants (Anc_Typ : Entity_Id); |
| -- Check that each of the discriminant values defined by the ancestor |
| -- part of an extension aggregate match the corresponding values |
| -- provided by either an association of the aggregate or by the |
| -- constraint imposed by a parent type (RM95-4.3.2(8)). |
| |
| function Compatible_Int_Bounds |
| (Agg_Bounds : Node_Id; |
| Typ_Bounds : Node_Id) return Boolean; |
| -- Return true if Agg_Bounds are equal or within Typ_Bounds. It is |
| -- assumed that both bounds are integer ranges. |
| |
| procedure Generate_Finalization_Actions; |
| -- Deal with the various controlled type data structure initializations |
| -- (but only if it hasn't been done already). |
| |
| function Get_Constraint_Association (T : Entity_Id) return Node_Id; |
| -- Returns the first discriminant association in the constraint |
| -- associated with T, if any, otherwise returns Empty. |
| |
| function Get_Explicit_Discriminant_Value (D : Entity_Id) return Node_Id; |
| -- If the ancestor part is an unconstrained type and further ancestors |
| -- do not provide discriminants for it, check aggregate components for |
| -- values of the discriminants. |
| |
| procedure Init_Hidden_Discriminants (Typ : Entity_Id; List : List_Id); |
| -- If Typ is derived, and constrains discriminants of the parent type, |
| -- these discriminants are not components of the aggregate, and must be |
| -- initialized. The assignments are appended to List. The same is done |
| -- if Typ derives fron an already constrained subtype of a discriminated |
| -- parent type. |
| |
| procedure Init_Stored_Discriminants; |
| -- If the type is derived and has inherited discriminants, generate |
| -- explicit assignments for each, using the store constraint of the |
| -- type. Note that both visible and stored discriminants must be |
| -- initialized in case the derived type has some renamed and some |
| -- constrained discriminants. |
| |
| procedure Init_Visible_Discriminants; |
| -- If type has discriminants, retrieve their values from aggregate, |
| -- and generate explicit assignments for each. This does not include |
| -- discriminants inherited from ancestor, which are handled above. |
| -- The type of the aggregate is a subtype created ealier using the |
| -- given values of the discriminant components of the aggregate. |
| |
| procedure Initialize_Ctrl_Record_Component |
| (Rec_Comp : Node_Id; |
| Comp_Typ : Entity_Id; |
| Init_Expr : Node_Id; |
| Stmts : List_Id); |
| -- Perform the initialization of controlled record component Rec_Comp. |
| -- Comp_Typ is the component type. Init_Expr is the initialization |
| -- expression for the record component. Hook-related declarations are |
| -- inserted prior to aggregate N using Insert_Action. All remaining |
| -- generated code is added to list Stmts. |
| |
| procedure Initialize_Record_Component |
| (Rec_Comp : Node_Id; |
| Comp_Typ : Entity_Id; |
| Init_Expr : Node_Id; |
| Stmts : List_Id); |
| -- Perform the initialization of record component Rec_Comp. Comp_Typ |
| -- is the component type. Init_Expr is the initialization expression |
| -- of the record component. All generated code is added to list Stmts. |
| |
| function Is_Int_Range_Bounds (Bounds : Node_Id) return Boolean; |
| -- Check whether Bounds is a range node and its lower and higher bounds |
| -- are integers literals. |
| |
| function Replace_Type (Expr : Node_Id) return Traverse_Result; |
| -- If the aggregate contains a self-reference, traverse each expression |
| -- to replace a possible self-reference with a reference to the proper |
| -- component of the target of the assignment. |
| |
| function Rewrite_Discriminant (Expr : Node_Id) return Traverse_Result; |
| -- If default expression of a component mentions a discriminant of the |
| -- type, it must be rewritten as the discriminant of the target object. |
| |
| --------------------------------- |
| -- Ancestor_Discriminant_Value -- |
| --------------------------------- |
| |
| function Ancestor_Discriminant_Value (Disc : Entity_Id) return Node_Id is |
| Assoc : Node_Id; |
| Assoc_Elmt : Elmt_Id; |
| Aggr_Comp : Entity_Id; |
| Corresp_Disc : Entity_Id; |
| Current_Typ : Entity_Id := Base_Type (Typ); |
| Parent_Typ : Entity_Id; |
| Parent_Disc : Entity_Id; |
| Save_Assoc : Node_Id := Empty; |
| |
| begin |
| -- First check any discriminant associations to see if any of them |
| -- provide a value for the discriminant. |
| |
| if Present (Discriminant_Specifications (Parent (Current_Typ))) then |
| Assoc := First (Component_Associations (N)); |
| while Present (Assoc) loop |
| Aggr_Comp := Entity (First (Choices (Assoc))); |
| |
| if Ekind (Aggr_Comp) = E_Discriminant then |
| Save_Assoc := Expression (Assoc); |
| |
| Corresp_Disc := Corresponding_Discriminant (Aggr_Comp); |
| while Present (Corresp_Disc) loop |
| |
| -- If found a corresponding discriminant then return the |
| -- value given in the aggregate. (Note: this is not |
| -- correct in the presence of side effects. ???) |
| |
| if Disc = Corresp_Disc then |
| return Duplicate_Subexpr (Expression (Assoc)); |
| end if; |
| |
| Corresp_Disc := Corresponding_Discriminant (Corresp_Disc); |
| end loop; |
| end if; |
| |
| Next (Assoc); |
| end loop; |
| end if; |
| |
| -- No match found in aggregate, so chain up parent types to find |
| -- a constraint that defines the value of the discriminant. |
| |
| Parent_Typ := Etype (Current_Typ); |
| while Current_Typ /= Parent_Typ loop |
| if Has_Discriminants (Parent_Typ) |
| and then not Has_Unknown_Discriminants (Parent_Typ) |
| then |
| Parent_Disc := First_Discriminant (Parent_Typ); |
| |
| -- We either get the association from the subtype indication |
| -- of the type definition itself, or from the discriminant |
| -- constraint associated with the type entity (which is |
| -- preferable, but it's not always present ???) |
| |
| if Is_Empty_Elmt_List (Discriminant_Constraint (Current_Typ)) |
| then |
| Assoc := Get_Constraint_Association (Current_Typ); |
| Assoc_Elmt := No_Elmt; |
| else |
| Assoc_Elmt := |
| First_Elmt (Discriminant_Constraint (Current_Typ)); |
| Assoc := Node (Assoc_Elmt); |
| end if; |
| |
| -- Traverse the discriminants of the parent type looking |
| -- for one that corresponds. |
| |
| while Present (Parent_Disc) and then Present (Assoc) loop |
| Corresp_Disc := Parent_Disc; |
| while Present (Corresp_Disc) |
| and then Disc /= Corresp_Disc |
| loop |
| Corresp_Disc := Corresponding_Discriminant (Corresp_Disc); |
| end loop; |
| |
| if Disc = Corresp_Disc then |
| if Nkind (Assoc) = N_Discriminant_Association then |
| Assoc := Expression (Assoc); |
| end if; |
| |
| -- If the located association directly denotes |
| -- a discriminant, then use the value of a saved |
| -- association of the aggregate. This is an approach |
| -- used to handle certain cases involving multiple |
| -- discriminants mapped to a single discriminant of |
| -- a descendant. It's not clear how to locate the |
| -- appropriate discriminant value for such cases. ??? |
| |
| if Is_Entity_Name (Assoc) |
| and then Ekind (Entity (Assoc)) = E_Discriminant |
| then |
| Assoc := Save_Assoc; |
| end if; |
| |
| return Duplicate_Subexpr (Assoc); |
| end if; |
| |
| Next_Discriminant (Parent_Disc); |
| |
| if No (Assoc_Elmt) then |
| Next (Assoc); |
| |
| else |
| Next_Elmt (Assoc_Elmt); |
| |
| if Present (Assoc_Elmt) then |
| Assoc := Node (Assoc_Elmt); |
| else |
| Assoc := Empty; |
| end if; |
| end if; |
| end loop; |
| end if; |
| |
| Current_Typ := Parent_Typ; |
| Parent_Typ := Etype (Current_Typ); |
| end loop; |
| |
| -- In some cases there's no ancestor value to locate (such as |
| -- when an ancestor part given by an expression defines the |
| -- discriminant value). |
| |
| return Empty; |
| end Ancestor_Discriminant_Value; |
| |
| ---------------------------------- |
| -- Check_Ancestor_Discriminants -- |
| ---------------------------------- |
| |
| procedure Check_Ancestor_Discriminants (Anc_Typ : Entity_Id) is |
| Discr : Entity_Id; |
| Disc_Value : Node_Id; |
| Cond : Node_Id; |
| |
| begin |
| Discr := First_Discriminant (Base_Type (Anc_Typ)); |
| while Present (Discr) loop |
| Disc_Value := Ancestor_Discriminant_Value (Discr); |
| |
| if Present (Disc_Value) then |
| Cond := Make_Op_Ne (Loc, |
| Left_Opnd => |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => New_Occurrence_Of (Discr, Loc)), |
| Right_Opnd => Disc_Value); |
| |
| Append_To (L, |
| Make_Raise_Constraint_Error (Loc, |
| Condition => Cond, |
| Reason => CE_Discriminant_Check_Failed)); |
| end if; |
| |
| Next_Discriminant (Discr); |
| end loop; |
| end Check_Ancestor_Discriminants; |
| |
| --------------------------- |
| -- Compatible_Int_Bounds -- |
| --------------------------- |
| |
| function Compatible_Int_Bounds |
| (Agg_Bounds : Node_Id; |
| Typ_Bounds : Node_Id) return Boolean |
| is |
| Agg_Lo : constant Uint := Intval (Low_Bound (Agg_Bounds)); |
| Agg_Hi : constant Uint := Intval (High_Bound (Agg_Bounds)); |
| Typ_Lo : constant Uint := Intval (Low_Bound (Typ_Bounds)); |
| Typ_Hi : constant Uint := Intval (High_Bound (Typ_Bounds)); |
| begin |
| return Typ_Lo <= Agg_Lo and then Agg_Hi <= Typ_Hi; |
| end Compatible_Int_Bounds; |
| |
| ----------------------------------- |
| -- Generate_Finalization_Actions -- |
| ----------------------------------- |
| |
| procedure Generate_Finalization_Actions is |
| begin |
| -- Do the work only the first time this is called |
| |
| if Finalization_Done then |
| return; |
| end if; |
| |
| Finalization_Done := True; |
| |
| -- Determine the external finalization list. It is either the |
| -- finalization list of the outer scope or the one coming from an |
| -- outer aggregate. When the target is not a temporary, the proper |
| -- scope is the scope of the target rather than the potentially |
| -- transient current scope. |
| |
| if Is_Controlled (Typ) and then Ancestor_Is_Subtype_Mark then |
| Ref := Convert_To (Init_Typ, New_Copy_Tree (Target)); |
| Set_Assignment_OK (Ref); |
| |
| Append_To (L, |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of |
| (Find_Prim_Op (Init_Typ, Name_Initialize), Loc), |
| Parameter_Associations => New_List (New_Copy_Tree (Ref)))); |
| end if; |
| end Generate_Finalization_Actions; |
| |
| -------------------------------- |
| -- Get_Constraint_Association -- |
| -------------------------------- |
| |
| function Get_Constraint_Association (T : Entity_Id) return Node_Id is |
| Indic : Node_Id; |
| Typ : Entity_Id; |
| |
| begin |
| Typ := T; |
| |
| -- If type is private, get constraint from full view. This was |
| -- previously done in an instance context, but is needed whenever |
| -- the ancestor part has a discriminant, possibly inherited through |
| -- multiple derivations. |
| |
| if Is_Private_Type (Typ) and then Present (Full_View (Typ)) then |
| Typ := Full_View (Typ); |
| end if; |
| |
| Indic := Subtype_Indication (Type_Definition (Parent (Typ))); |
| |
| -- Verify that the subtype indication carries a constraint |
| |
| if Nkind (Indic) = N_Subtype_Indication |
| and then Present (Constraint (Indic)) |
| then |
| return First (Constraints (Constraint (Indic))); |
| end if; |
| |
| return Empty; |
| end Get_Constraint_Association; |
| |
| ------------------------------------- |
| -- Get_Explicit_Discriminant_Value -- |
| ------------------------------------- |
| |
| function Get_Explicit_Discriminant_Value |
| (D : Entity_Id) return Node_Id |
| is |
| Assoc : Node_Id; |
| Choice : Node_Id; |
| Val : Node_Id; |
| |
| begin |
| -- The aggregate has been normalized and all associations have a |
| -- single choice. |
| |
| Assoc := First (Component_Associations (N)); |
| while Present (Assoc) loop |
| Choice := First (Choices (Assoc)); |
| |
| if Chars (Choice) = Chars (D) then |
| Val := Expression (Assoc); |
| Remove (Assoc); |
| return Val; |
| end if; |
| |
| Next (Assoc); |
| end loop; |
| |
| return Empty; |
| end Get_Explicit_Discriminant_Value; |
| |
| ------------------------------- |
| -- Init_Hidden_Discriminants -- |
| ------------------------------- |
| |
| procedure Init_Hidden_Discriminants (Typ : Entity_Id; List : List_Id) is |
| function Is_Completely_Hidden_Discriminant |
| (Discr : Entity_Id) return Boolean; |
| -- Determine whether Discr is a completely hidden discriminant of |
| -- type Typ. |
| |
| --------------------------------------- |
| -- Is_Completely_Hidden_Discriminant -- |
| --------------------------------------- |
| |
| function Is_Completely_Hidden_Discriminant |
| (Discr : Entity_Id) return Boolean |
| is |
| Item : Entity_Id; |
| |
| begin |
| -- Use First/Next_Entity as First/Next_Discriminant do not yield |
| -- completely hidden discriminants. |
| |
| Item := First_Entity (Typ); |
| while Present (Item) loop |
| if Ekind (Item) = E_Discriminant |
| and then Is_Completely_Hidden (Item) |
| and then Chars (Original_Record_Component (Item)) = |
| Chars (Discr) |
| then |
| return True; |
| end if; |
| |
| Next_Entity (Item); |
| end loop; |
| |
| return False; |
| end Is_Completely_Hidden_Discriminant; |
| |
| -- Local variables |
| |
| Base_Typ : Entity_Id; |
| Discr : Entity_Id; |
| Discr_Constr : Elmt_Id; |
| Discr_Init : Node_Id; |
| Discr_Val : Node_Id; |
| In_Aggr_Type : Boolean; |
| Par_Typ : Entity_Id; |
| |
| -- Start of processing for Init_Hidden_Discriminants |
| |
| begin |
| -- The constraints on the hidden discriminants, if present, are kept |
| -- in the Stored_Constraint list of the type itself, or in that of |
| -- the base type. If not in the constraints of the aggregate itself, |
| -- we examine ancestors to find discriminants that are not renamed |
| -- by other discriminants but constrained explicitly. |
| |
| In_Aggr_Type := True; |
| |
| Base_Typ := Base_Type (Typ); |
| while Is_Derived_Type (Base_Typ) |
| and then |
| (Present (Stored_Constraint (Base_Typ)) |
| or else |
| (In_Aggr_Type and then Present (Stored_Constraint (Typ)))) |
| loop |
| Par_Typ := Etype (Base_Typ); |
| |
| if not Has_Discriminants (Par_Typ) then |
| return; |
| end if; |
| |
| Discr := First_Discriminant (Par_Typ); |
| |
| -- We know that one of the stored-constraint lists is present |
| |
| if Present (Stored_Constraint (Base_Typ)) then |
| Discr_Constr := First_Elmt (Stored_Constraint (Base_Typ)); |
| |
| -- For private extension, stored constraint may be on full view |
| |
| elsif Is_Private_Type (Base_Typ) |
| and then Present (Full_View (Base_Typ)) |
| and then Present (Stored_Constraint (Full_View (Base_Typ))) |
| then |
| Discr_Constr := |
| First_Elmt (Stored_Constraint (Full_View (Base_Typ))); |
| |
| -- Otherwise, no discriminant to process |
| |
| else |
| Discr_Constr := No_Elmt; |
| end if; |
| |
| while Present (Discr) and then Present (Discr_Constr) loop |
| Discr_Val := Node (Discr_Constr); |
| |
| -- The parent discriminant is renamed in the derived type, |
| -- nothing to initialize. |
| |
| -- type Deriv_Typ (Discr : ...) |
| -- is new Parent_Typ (Discr => Discr); |
| |
| if Is_Entity_Name (Discr_Val) |
| and then Ekind (Entity (Discr_Val)) = E_Discriminant |
| then |
| null; |
| |
| -- When the parent discriminant is constrained at the type |
| -- extension level, it does not appear in the derived type. |
| |
| -- type Deriv_Typ (Discr : ...) |
| -- is new Parent_Typ (Discr => Discr, |
| -- Hidden_Discr => Expression); |
| |
| elsif Is_Completely_Hidden_Discriminant (Discr) then |
| null; |
| |
| -- Otherwise initialize the discriminant |
| |
| else |
| Discr_Init := |
| Make_OK_Assignment_Statement (Loc, |
| Name => |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => New_Occurrence_Of (Discr, Loc)), |
| Expression => New_Copy_Tree (Discr_Val)); |
| |
| Append_To (List, Discr_Init); |
| end if; |
| |
| Next_Elmt (Discr_Constr); |
| Next_Discriminant (Discr); |
| end loop; |
| |
| In_Aggr_Type := False; |
| Base_Typ := Base_Type (Par_Typ); |
| end loop; |
| end Init_Hidden_Discriminants; |
| |
| -------------------------------- |
| -- Init_Visible_Discriminants -- |
| -------------------------------- |
| |
| procedure Init_Visible_Discriminants is |
| Discriminant : Entity_Id; |
| Discriminant_Value : Node_Id; |
| |
| begin |
| Discriminant := First_Discriminant (Typ); |
| while Present (Discriminant) loop |
| Comp_Expr := |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => New_Occurrence_Of (Discriminant, Loc)); |
| |
| Discriminant_Value := |
| Get_Discriminant_Value |
| (Discriminant, Typ, Discriminant_Constraint (N_Typ)); |
| |
| Instr := |
| Make_OK_Assignment_Statement (Loc, |
| Name => Comp_Expr, |
| Expression => New_Copy_Tree (Discriminant_Value)); |
| |
| Append_To (L, Instr); |
| |
| Next_Discriminant (Discriminant); |
| end loop; |
| end Init_Visible_Discriminants; |
| |
| ------------------------------- |
| -- Init_Stored_Discriminants -- |
| ------------------------------- |
| |
| procedure Init_Stored_Discriminants is |
| Discriminant : Entity_Id; |
| Discriminant_Value : Node_Id; |
| |
| begin |
| Discriminant := First_Stored_Discriminant (Typ); |
| while Present (Discriminant) loop |
| Comp_Expr := |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => New_Occurrence_Of (Discriminant, Loc)); |
| |
| Discriminant_Value := |
| Get_Discriminant_Value |
| (Discriminant, N_Typ, Discriminant_Constraint (N_Typ)); |
| |
| Instr := |
| Make_OK_Assignment_Statement (Loc, |
| Name => Comp_Expr, |
| Expression => New_Copy_Tree (Discriminant_Value)); |
| |
| Append_To (L, Instr); |
| |
| Next_Stored_Discriminant (Discriminant); |
| end loop; |
| end Init_Stored_Discriminants; |
| |
| -------------------------------------- |
| -- Initialize_Ctrl_Record_Component -- |
| -------------------------------------- |
| |
| procedure Initialize_Ctrl_Record_Component |
| (Rec_Comp : Node_Id; |
| Comp_Typ : Entity_Id; |
| Init_Expr : Node_Id; |
| Stmts : List_Id) |
| is |
| Fin_Call : Node_Id; |
| Hook_Clear : Node_Id; |
| |
| In_Place_Expansion : Boolean; |
| -- Flag set when a nonlimited controlled function call requires |
| -- in-place expansion. |
| |
| begin |
| -- Perform a preliminary analysis and resolution to determine what |
| -- the initialization expression denotes. Unanalyzed function calls |
| -- may appear as identifiers or indexed components. |
| |
| if Nkind (Init_Expr) in N_Function_Call |
| | N_Identifier |
| | N_Indexed_Component |
| and then not Analyzed (Init_Expr) |
| then |
| Preanalyze_And_Resolve (Init_Expr, Comp_Typ); |
| end if; |
| |
| In_Place_Expansion := |
| Nkind (Init_Expr) = N_Function_Call |
| and then not Is_Build_In_Place_Result_Type (Comp_Typ); |
| |
| -- The initialization expression is a controlled function call. |
| -- Perform in-place removal of side effects to avoid creating a |
| -- transient scope. |
| |
| -- This in-place expansion is not performed for limited transient |
| -- objects because the initialization is already done in place. |
| |
| if In_Place_Expansion then |
| |
| -- Suppress the removal of side effects by general analysis |
| -- because this behavior is emulated here. This avoids the |
| -- generation of a transient scope, which leads to out-of-order |
| -- adjustment and finalization. |
| |
| Set_No_Side_Effect_Removal (Init_Expr); |
| |
| -- Install all hook-related declarations and prepare the clean up |
| -- statements. The generated code follows the initialization order |
| -- of individual components and discriminants, rather than being |
| -- inserted prior to the aggregate. This ensures that a transient |
| -- component which mentions a discriminant has proper visibility |
| -- of the discriminant. |
| |
| Process_Transient_Component |
| (Loc => Loc, |
| Comp_Typ => Comp_Typ, |
| Init_Expr => Init_Expr, |
| Fin_Call => Fin_Call, |
| Hook_Clear => Hook_Clear, |
| Stmts => Stmts); |
| end if; |
| |
| -- Use the noncontrolled component initialization circuitry to |
| -- assign the result of the function call to the record component. |
| -- This also performs tag adjustment and [deep] adjustment of the |
| -- record component. |
| |
| Initialize_Record_Component |
| (Rec_Comp => Rec_Comp, |
| Comp_Typ => Comp_Typ, |
| Init_Expr => Init_Expr, |
| Stmts => Stmts); |
| |
| -- At this point the record component is fully initialized. Complete |
| -- the processing of the controlled record component by finalizing |
| -- the transient function result. |
| |
| if In_Place_Expansion then |
| Process_Transient_Component_Completion |
| (Loc => Loc, |
| Aggr => N, |
| Fin_Call => Fin_Call, |
| Hook_Clear => Hook_Clear, |
| Stmts => Stmts); |
| end if; |
| end Initialize_Ctrl_Record_Component; |
| |
| --------------------------------- |
| -- Initialize_Record_Component -- |
| --------------------------------- |
| |
| procedure Initialize_Record_Component |
| (Rec_Comp : Node_Id; |
| Comp_Typ : Entity_Id; |
| Init_Expr : Node_Id; |
| Stmts : List_Id) |
| is |
| Exceptions_OK : constant Boolean := |
| not Restriction_Active (No_Exception_Propagation); |
| |
| Finalization_OK : constant Boolean := Needs_Finalization (Comp_Typ); |
| |
| Full_Typ : constant Entity_Id := Underlying_Type (Comp_Typ); |
| Adj_Call : Node_Id; |
| Blk_Stmts : List_Id; |
| Init_Stmt : Node_Id; |
| |
| begin |
| pragma Assert (Nkind (Init_Expr) in N_Subexpr); |
| |
| -- Protect the initialization statements from aborts. Generate: |
| |
| -- Abort_Defer; |
| |
| if Finalization_OK and Abort_Allowed then |
| if Exceptions_OK then |
| Blk_Stmts := New_List; |
| else |
| Blk_Stmts := Stmts; |
| end if; |
| |
| Append_To (Blk_Stmts, Build_Runtime_Call (Loc, RE_Abort_Defer)); |
| |
| -- Otherwise aborts are not allowed. All generated code is added |
| -- directly to the input list. |
| |
| else |
| Blk_Stmts := Stmts; |
| end if; |
| |
| -- Initialize the record component. Generate: |
| |
| -- Rec_Comp := Init_Expr; |
| |
| -- Note that the initialization expression is NOT replicated because |
| -- only a single component may be initialized by it. |
| |
| Init_Stmt := |
| Make_OK_Assignment_Statement (Loc, |
| Name => New_Copy_Tree (Rec_Comp), |
| Expression => Init_Expr); |
| Set_No_Ctrl_Actions (Init_Stmt); |
| |
| Append_To (Blk_Stmts, Init_Stmt); |
| |
| -- Adjust the tag due to a possible view conversion. Generate: |
| |
| -- Rec_Comp._tag := Full_TypeP; |
| |
| if Tagged_Type_Expansion and then Is_Tagged_Type (Comp_Typ) then |
| Append_To (Blk_Stmts, |
| Make_OK_Assignment_Statement (Loc, |
| Name => |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Rec_Comp), |
| Selector_Name => |
| New_Occurrence_Of |
| (First_Tag_Component (Full_Typ), Loc)), |
| |
| Expression => |
| Unchecked_Convert_To (RTE (RE_Tag), |
| New_Occurrence_Of |
| (Node (First_Elmt (Access_Disp_Table (Full_Typ))), |
| Loc)))); |
| end if; |
| |
| -- Adjust the component. Generate: |
| |
| -- [Deep_]Adjust (Rec_Comp); |
| |
| if Finalization_OK |
| and then not Is_Limited_Type (Comp_Typ) |
| and then not Is_Build_In_Place_Function_Call (Init_Expr) |
| then |
| Adj_Call := |
| Make_Adjust_Call |
| (Obj_Ref => New_Copy_Tree (Rec_Comp), |
| Typ => Comp_Typ); |
| |
| -- Guard against a missing [Deep_]Adjust when the component type |
| -- was not properly frozen. |
| |
| if Present (Adj_Call) then |
| Append_To (Blk_Stmts, Adj_Call); |
| end if; |
| end if; |
| |
| -- Complete the protection of the initialization statements |
| |
| if Finalization_OK and Abort_Allowed then |
| |
| -- Wrap the initialization statements in a block to catch a |
| -- potential exception. Generate: |
| |
| -- begin |
| -- Abort_Defer; |
| -- Rec_Comp := Init_Expr; |
| -- Rec_Comp._tag := Full_TypP; |
| -- [Deep_]Adjust (Rec_Comp); |
| -- at end |
| -- Abort_Undefer_Direct; |
| -- end; |
| |
| if Exceptions_OK then |
| Append_To (Stmts, |
| Build_Abort_Undefer_Block (Loc, |
| Stmts => Blk_Stmts, |
| Context => N)); |
| |
| -- Otherwise exceptions are not propagated. Generate: |
| |
| -- Abort_Defer; |
| -- Rec_Comp := Init_Expr; |
| -- Rec_Comp._tag := Full_TypP; |
| -- [Deep_]Adjust (Rec_Comp); |
| -- Abort_Undefer; |
| |
| else |
| Append_To (Blk_Stmts, |
| Build_Runtime_Call (Loc, RE_Abort_Undefer)); |
| end if; |
| end if; |
| end Initialize_Record_Component; |
| |
| ------------------------- |
| -- Is_Int_Range_Bounds -- |
| ------------------------- |
| |
| function Is_Int_Range_Bounds (Bounds : Node_Id) return Boolean is |
| begin |
| return Nkind (Bounds) = N_Range |
| and then Nkind (Low_Bound (Bounds)) = N_Integer_Literal |
| and then Nkind (High_Bound (Bounds)) = N_Integer_Literal; |
| end Is_Int_Range_Bounds; |
| |
| ------------------ |
| -- Replace_Type -- |
| ------------------ |
| |
| function Replace_Type (Expr : Node_Id) return Traverse_Result is |
| begin |
| -- Note regarding the Root_Type test below: Aggregate components for |
| -- self-referential types include attribute references to the current |
| -- instance, of the form: Typ'access, etc.. These references are |
| -- rewritten as references to the target of the aggregate: the |
| -- left-hand side of an assignment, the entity in a declaration, |
| -- or a temporary. Without this test, we would improperly extended |
| -- this rewriting to attribute references whose prefix was not the |
| -- type of the aggregate. |
| |
| if Nkind (Expr) = N_Attribute_Reference |
| and then Is_Entity_Name (Prefix (Expr)) |
| and then Is_Type (Entity (Prefix (Expr))) |
| and then Root_Type (Etype (N)) = Root_Type (Entity (Prefix (Expr))) |
| then |
| if Is_Entity_Name (Lhs) then |
| Rewrite (Prefix (Expr), New_Occurrence_Of (Entity (Lhs), Loc)); |
| |
| else |
| Rewrite (Expr, |
| Make_Attribute_Reference (Loc, |
| Attribute_Name => Name_Unrestricted_Access, |
| Prefix => New_Copy_Tree (Lhs))); |
| Set_Analyzed (Parent (Expr), False); |
| end if; |
| end if; |
| |
| return OK; |
| end Replace_Type; |
| |
| -------------------------- |
| -- Rewrite_Discriminant -- |
| -------------------------- |
| |
| function Rewrite_Discriminant (Expr : Node_Id) return Traverse_Result is |
| begin |
| if Is_Entity_Name (Expr) |
| and then Present (Entity (Expr)) |
| and then Ekind (Entity (Expr)) = E_In_Parameter |
| and then Present (Discriminal_Link (Entity (Expr))) |
| and then Scope (Discriminal_Link (Entity (Expr))) = |
| Base_Type (Etype (N)) |
| then |
| Rewrite (Expr, |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Lhs), |
| Selector_Name => Make_Identifier (Loc, Chars (Expr)))); |
| |
| -- The generated code will be reanalyzed, but if the reference |
| -- to the discriminant appears within an already analyzed |
| -- expression (e.g. a conditional) we must set its proper entity |
| -- now. Context is an initialization procedure. |
| |
| Analyze (Expr); |
| end if; |
| |
| return OK; |
| end Rewrite_Discriminant; |
| |
| procedure Replace_Discriminants is |
| new Traverse_Proc (Rewrite_Discriminant); |
| |
| procedure Replace_Self_Reference is |
| new Traverse_Proc (Replace_Type); |
| |
| -- Start of processing for Build_Record_Aggr_Code |
| |
| begin |
| if Has_Self_Reference (N) then |
| Replace_Self_Reference (N); |
| end if; |
| |
| -- If the target of the aggregate is class-wide, we must convert it |
| -- to the actual type of the aggregate, so that the proper components |
| -- are visible. We know already that the types are compatible. |
| |
| if Present (Etype (Lhs)) |
| and then Is_Class_Wide_Type (Etype (Lhs)) |
| then |
| Target := Unchecked_Convert_To (Typ, Lhs); |
| else |
| Target := Lhs; |
| end if; |
| |
| -- Deal with the ancestor part of extension aggregates or with the |
| -- discriminants of the root type. |
| |
| if Nkind (N) = N_Extension_Aggregate then |
| declare |
| Ancestor : constant Node_Id := Ancestor_Part (N); |
| Adj_Call : Node_Id; |
| Assign : List_Id; |
| |
| begin |
| -- If the ancestor part is a subtype mark "T", we generate |
| |
| -- init-proc (T (tmp)); if T is constrained and |
| -- init-proc (S (tmp)); where S applies an appropriate |
| -- constraint if T is unconstrained |
| |
| if Is_Entity_Name (Ancestor) |
| and then Is_Type (Entity (Ancestor)) |
| then |
| Ancestor_Is_Subtype_Mark := True; |
| |
| if Is_Constrained (Entity (Ancestor)) then |
| Init_Typ := Entity (Ancestor); |
| |
| -- For an ancestor part given by an unconstrained type mark, |
| -- create a subtype constrained by appropriate corresponding |
| -- discriminant values coming from either associations of the |
| -- aggregate or a constraint on a parent type. The subtype will |
| -- be used to generate the correct default value for the |
| -- ancestor part. |
| |
| elsif Has_Discriminants (Entity (Ancestor)) then |
| declare |
| Anc_Typ : constant Entity_Id := Entity (Ancestor); |
| Anc_Constr : constant List_Id := New_List; |
| Discrim : Entity_Id; |
| Disc_Value : Node_Id; |
| New_Indic : Node_Id; |
| Subt_Decl : Node_Id; |
| |
| begin |
| Discrim := First_Discriminant (Anc_Typ); |
| while Present (Discrim) loop |
| Disc_Value := Ancestor_Discriminant_Value (Discrim); |
| |
| -- If no usable discriminant in ancestors, check |
| -- whether aggregate has an explicit value for it. |
| |
| if No (Disc_Value) then |
| Disc_Value := |
| Get_Explicit_Discriminant_Value (Discrim); |
| end if; |
| |
| Append_To (Anc_Constr, Disc_Value); |
| Next_Discriminant (Discrim); |
| end loop; |
| |
| New_Indic := |
| Make_Subtype_Indication (Loc, |
| Subtype_Mark => New_Occurrence_Of (Anc_Typ, Loc), |
| Constraint => |
| Make_Index_Or_Discriminant_Constraint (Loc, |
| Constraints => Anc_Constr)); |
| |
| Init_Typ := Create_Itype (Ekind (Anc_Typ), N); |
| |
| Subt_Decl := |
| Make_Subtype_Declaration (Loc, |
| Defining_Identifier => Init_Typ, |
| Subtype_Indication => New_Indic); |
| |
| -- Itypes must be analyzed with checks off Declaration |
| -- must have a parent for proper handling of subsidiary |
| -- actions. |
| |
| Set_Parent (Subt_Decl, N); |
| Analyze (Subt_Decl, Suppress => All_Checks); |
| end; |
| end if; |
| |
| Ref := Convert_To (Init_Typ, New_Copy_Tree (Target)); |
| Set_Assignment_OK (Ref); |
| |
| if not Is_Interface (Init_Typ) then |
| Append_List_To (L, |
| Build_Initialization_Call (Loc, |
| Id_Ref => Ref, |
| Typ => Init_Typ, |
| In_Init_Proc => Within_Init_Proc, |
| With_Default_Init => Has_Default_Init_Comps (N) |
| or else |
| Has_Task (Base_Type (Init_Typ)))); |
| |
| if Is_Constrained (Entity (Ancestor)) |
| and then Has_Discriminants (Entity (Ancestor)) |
| then |
| Check_Ancestor_Discriminants (Entity (Ancestor)); |
| end if; |
| |
| -- If ancestor type has Default_Initialization_Condition, |
| -- add a DIC check after the ancestor object is initialized |
| -- by default. |
| |
| if Has_DIC (Entity (Ancestor)) |
| and then Present (DIC_Procedure (Entity (Ancestor))) |
| then |
| Append_To (L, |
| Build_DIC_Call |
| (Loc, New_Copy_Tree (Ref), Entity (Ancestor))); |
| end if; |
| end if; |
| |
| -- Handle calls to C++ constructors |
| |
| elsif Is_CPP_Constructor_Call (Ancestor) then |
| Init_Typ := Etype (Ancestor); |
| Ref := Convert_To (Init_Typ, New_Copy_Tree (Target)); |
| Set_Assignment_OK (Ref); |
| |
| Append_List_To (L, |
| Build_Initialization_Call (Loc, |
| Id_Ref => Ref, |
| Typ => Init_Typ, |
| In_Init_Proc => Within_Init_Proc, |
| With_Default_Init => Has_Default_Init_Comps (N), |
| Constructor_Ref => Ancestor)); |
| |
| -- Ada 2005 (AI-287): If the ancestor part is an aggregate of |
| -- limited type, a recursive call expands the ancestor. Note that |
| -- in the limited case, the ancestor part must be either a |
| -- function call (possibly qualified) or aggregate (definitely |
| -- qualified). |
| |
| elsif Is_Limited_Type (Etype (Ancestor)) |
| and then Nkind (Unqualify (Ancestor)) in |
| N_Aggregate | N_Extension_Aggregate |
| then |
| Ancestor_Is_Expression := True; |
| |
| -- Set up finalization data for enclosing record, because |
| -- controlled subcomponents of the ancestor part will be |
| -- attached to it. |
| |
| Generate_Finalization_Actions; |
| |
| Append_List_To (L, |
| Build_Record_Aggr_Code |
| (N => Unqualify (Ancestor), |
| Typ => Etype (Unqualify (Ancestor)), |
| Lhs => Target)); |
| |
| -- If the ancestor part is an expression "E", we generate |
| |
| -- T (tmp) := E; |
| |
| -- In Ada 2005, this includes the case of a (possibly qualified) |
| -- limited function call. The assignment will turn into a |
| -- build-in-place function call (for further details, see |
| -- Make_Build_In_Place_Call_In_Assignment). |
| |
| else |
| Ancestor_Is_Expression := True; |
| Init_Typ := Etype (Ancestor); |
| |
| -- If the ancestor part is an aggregate, force its full |
| -- expansion, which was delayed. |
| |
| if Nkind (Unqualify (Ancestor)) in |
| N_Aggregate | N_Extension_Aggregate |
| then |
| Set_Analyzed (Ancestor, False); |
| Set_Analyzed (Expression (Ancestor), False); |
| end if; |
| |
| Ref := Convert_To (Init_Typ, New_Copy_Tree (Target)); |
| Set_Assignment_OK (Ref); |
| |
| -- Make the assignment without usual controlled actions, since |
| -- we only want to Adjust afterwards, but not to Finalize |
| -- beforehand. Add manual Adjust when necessary. |
| |
| Assign := New_List ( |
| Make_OK_Assignment_Statement (Loc, |
| Name => Ref, |
| Expression => Ancestor)); |
| Set_No_Ctrl_Actions (First (Assign)); |
| |
| -- Assign the tag now to make sure that the dispatching call in |
| -- the subsequent deep_adjust works properly (unless |
| -- Tagged_Type_Expansion where tags are implicit). |
| |
| if Tagged_Type_Expansion then |
| Instr := |
| Make_OK_Assignment_Statement (Loc, |
| Name => |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => |
| New_Occurrence_Of |
| (First_Tag_Component (Base_Type (Typ)), Loc)), |
| |
| Expression => |
| Unchecked_Convert_To (RTE (RE_Tag), |
| New_Occurrence_Of |
| (Node (First_Elmt |
| (Access_Disp_Table (Base_Type (Typ)))), |
| Loc))); |
| |
| Set_Assignment_OK (Name (Instr)); |
| Append_To (Assign, Instr); |
| |
| -- Ada 2005 (AI-251): If tagged type has progenitors we must |
| -- also initialize tags of the secondary dispatch tables. |
| |
| if Has_Interfaces (Base_Type (Typ)) then |
| Init_Secondary_Tags |
| (Typ => Base_Type (Typ), |
| Target => Target, |
| Stmts_List => Assign, |
| Init_Tags_List => Assign); |
| end if; |
| end if; |
| |
| -- Call Adjust manually |
| |
| if Needs_Finalization (Etype (Ancestor)) |
| and then not Is_Limited_Type (Etype (Ancestor)) |
| and then not Is_Build_In_Place_Function_Call (Ancestor) |
| then |
| Adj_Call := |
| Make_Adjust_Call |
| (Obj_Ref => New_Copy_Tree (Ref), |
| Typ => Etype (Ancestor)); |
| |
| -- Guard against a missing [Deep_]Adjust when the ancestor |
| -- type was not properly frozen. |
| |
| if Present (Adj_Call) then |
| Append_To (Assign, Adj_Call); |
| end if; |
| end if; |
| |
| Append_To (L, |
| Make_Unsuppress_Block (Loc, Name_Discriminant_Check, Assign)); |
| |
| if Has_Discriminants (Init_Typ) then |
| Check_Ancestor_Discriminants (Init_Typ); |
| end if; |
| end if; |
| |
| pragma Assert (Nkind (N) = N_Extension_Aggregate); |
| pragma Assert |
| (not (Ancestor_Is_Expression and Ancestor_Is_Subtype_Mark)); |
| end; |
| |
| -- Generate assignments of hidden discriminants. If the base type is |
| -- an unchecked union, the discriminants are unknown to the back-end |
| -- and absent from a value of the type, so assignments for them are |
| -- not emitted. |
| |
| if Has_Discriminants (Typ) |
| and then not Is_Unchecked_Union (Base_Type (Typ)) |
| then |
| Init_Hidden_Discriminants (Typ, L); |
| end if; |
| |
| -- Normal case (not an extension aggregate) |
| |
| else |
| -- Generate the discriminant expressions, component by component. |
| -- If the base type is an unchecked union, the discriminants are |
| -- unknown to the back-end and absent from a value of the type, so |
| -- assignments for them are not emitted. |
| |
| if Has_Discriminants (Typ) |
| and then not Is_Unchecked_Union (Base_Type (Typ)) |
| then |
| Init_Hidden_Discriminants (Typ, L); |
| |
| -- Generate discriminant init values for the visible discriminants |
| |
| Init_Visible_Discriminants; |
| |
| if Is_Derived_Type (N_Typ) then |
| Init_Stored_Discriminants; |
| end if; |
| end if; |
| end if; |
| |
| -- For CPP types we generate an implicit call to the C++ default |
| -- constructor to ensure the proper initialization of the _Tag |
| -- component. |
| |
| if Is_CPP_Class (Root_Type (Typ)) and then CPP_Num_Prims (Typ) > 0 then |
| Invoke_Constructor : declare |
| CPP_Parent : constant Entity_Id := Enclosing_CPP_Parent (Typ); |
| |
| procedure Invoke_IC_Proc (T : Entity_Id); |
| -- Recursive routine used to climb to parents. Required because |
| -- parents must be initialized before descendants to ensure |
| -- propagation of inherited C++ slots. |
| |
| -------------------- |
| -- Invoke_IC_Proc -- |
| -------------------- |
| |
| procedure Invoke_IC_Proc (T : Entity_Id) is |
| begin |
| -- Avoid generating extra calls. Initialization required |
| -- only for types defined from the level of derivation of |
| -- type of the constructor and the type of the aggregate. |
| |
| if T = CPP_Parent then |
| return; |
| end if; |
| |
| Invoke_IC_Proc (Etype (T)); |
| |
| -- Generate call to the IC routine |
| |
| if Present (CPP_Init_Proc (T)) then |
| Append_To (L, |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (CPP_Init_Proc (T), Loc))); |
| end if; |
| end Invoke_IC_Proc; |
| |
| -- Start of processing for Invoke_Constructor |
| |
| begin |
| -- Implicit invocation of the C++ constructor |
| |
| if Nkind (N) = N_Aggregate then |
| Append_To (L, |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (Base_Init_Proc (CPP_Parent), Loc), |
| Parameter_Associations => New_List ( |
| Unchecked_Convert_To (CPP_Parent, |
| New_Copy_Tree (Lhs))))); |
| end if; |
| |
| Invoke_IC_Proc (Typ); |
| end Invoke_Constructor; |
| end if; |
| |
| -- Generate the assignments, component by component |
| |
| -- tmp.comp1 := Expr1_From_Aggr; |
| -- tmp.comp2 := Expr2_From_Aggr; |
| -- .... |
| |
| Comp := First (Component_Associations (N)); |
| while Present (Comp) loop |
| Selector := Entity (First (Choices (Comp))); |
| |
| -- C++ constructors |
| |
| if Is_CPP_Constructor_Call (Expression (Comp)) then |
| Append_List_To (L, |
| Build_Initialization_Call (Loc, |
| Id_Ref => |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => New_Occurrence_Of (Selector, Loc)), |
| Typ => Etype (Selector), |
| Enclos_Type => Typ, |
| With_Default_Init => True, |
| Constructor_Ref => Expression (Comp))); |
| |
| elsif Box_Present (Comp) |
| and then Needs_Simple_Initialization (Etype (Selector)) |
| then |
| Comp_Expr := |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => New_Occurrence_Of (Selector, Loc)); |
| |
| Initialize_Record_Component |
| (Rec_Comp => Comp_Expr, |
| Comp_Typ => Etype (Selector), |
| Init_Expr => Get_Simple_Init_Val |
| (Typ => Etype (Selector), |
| N => Comp, |
| Size => |
| (if Known_Esize (Selector) |
| then Esize (Selector) |
| else Uint_0)), |
| Stmts => L); |
| |
| -- Ada 2005 (AI-287): For each default-initialized component generate |
| -- a call to the corresponding IP subprogram if available. |
| |
| elsif Box_Present (Comp) |
| and then Has_Non_Null_Base_Init_Proc (Etype (Selector)) |
| then |
| if Ekind (Selector) /= E_Discriminant then |
| Generate_Finalization_Actions; |
| end if; |
| |
| -- Ada 2005 (AI-287): If the component type has tasks then |
| -- generate the activation chain and master entities (except |
| -- in case of an allocator because in that case these entities |
| -- are generated by Build_Task_Allocate_Block_With_Init_Stmts). |
| |
| declare |
| Ctype : constant Entity_Id := Etype (Selector); |
| Inside_Allocator : Boolean := False; |
| P : Node_Id := Parent (N); |
| |
| begin |
| if Is_Task_Type (Ctype) or else Has_Task (Ctype) then |
| while Present (P) loop |
| if Nkind (P) = N_Allocator then |
| Inside_Allocator := True; |
| exit; |
| end if; |
| |
| P := Parent (P); |
| end loop; |
| |
| if not Inside_Init_Proc and not Inside_Allocator then |
| Build_Activation_Chain_Entity (N); |
| end if; |
| end if; |
| end; |
| |
| Append_List_To (L, |
| Build_Initialization_Call (Loc, |
| Id_Ref => Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => |
| New_Occurrence_Of (Selector, Loc)), |
| Typ => Etype (Selector), |
| Enclos_Type => Typ, |
| With_Default_Init => True)); |
| |
| -- Prepare for component assignment |
| |
| elsif Ekind (Selector) /= E_Discriminant |
| or else Nkind (N) = N_Extension_Aggregate |
| then |
| -- All the discriminants have now been assigned |
| |
| -- This is now a good moment to initialize and attach all the |
| -- controllers. Their position may depend on the discriminants. |
| |
| if Ekind (Selector) /= E_Discriminant then |
| Generate_Finalization_Actions; |
| end if; |
| |
| Comp_Type := Underlying_Type (Etype (Selector)); |
| Comp_Expr := |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => New_Occurrence_Of (Selector, Loc)); |
| |
| if Nkind (Expression (Comp)) = N_Qualified_Expression then |
| Expr_Q := Expression (Expression (Comp)); |
| else |
| Expr_Q := Expression (Comp); |
| end if; |
| |
| -- Now either create the assignment or generate the code for the |
| -- inner aggregate top-down. |
| |
| if Is_Delayed_Aggregate (Expr_Q) then |
| |
| -- We have the following case of aggregate nesting inside |
| -- an object declaration: |
| |
| -- type Arr_Typ is array (Integer range <>) of ...; |
| |
| -- type Rec_Typ (...) is record |
| -- Obj_Arr_Typ : Arr_Typ (A .. B); |
| -- end record; |
| |
| -- Obj_Rec_Typ : Rec_Typ := (..., |
| -- Obj_Arr_Typ => (X => (...), Y => (...))); |
| |
| -- The length of the ranges of the aggregate and Obj_Add_Typ |
| -- are equal (B - A = Y - X), but they do not coincide (X /= |
| -- A and B /= Y). This case requires array sliding which is |
| -- performed in the following manner: |
| |
| -- subtype Arr_Sub is Arr_Typ (X .. Y); |
| -- Temp : Arr_Sub; |
| -- Temp (X) := (...); |
| -- ... |
| -- Temp (Y) := (...); |
| -- Obj_Rec_Typ.Obj_Arr_Typ := Temp; |
| |
| if Ekind (Comp_Type) = E_Array_Subtype |
| and then Is_Int_Range_Bounds (Aggregate_Bounds (Expr_Q)) |
| and then Is_Int_Range_Bounds (First_Index (Comp_Type)) |
| and then not |
| Compatible_Int_Bounds |
| (Agg_Bounds => Aggregate_Bounds (Expr_Q), |
| Typ_Bounds => First_Index (Comp_Type)) |
| then |
| -- Create the array subtype with bounds equal to those of |
| -- the corresponding aggregate. |
| |
| declare |
| SubE : constant Entity_Id := Make_Temporary (Loc, 'T'); |
| |
| SubD : constant Node_Id := |
| Make_Subtype_Declaration (Loc, |
| Defining_Identifier => SubE, |
| Subtype_Indication => |
| Make_Subtype_Indication (Loc, |
| Subtype_Mark => |
| New_Occurrence_Of (Etype (Comp_Type), Loc), |
| Constraint => |
| Make_Index_Or_Discriminant_Constraint |
| (Loc, |
| Constraints => New_List ( |
| New_Copy_Tree |
| (Aggregate_Bounds (Expr_Q)))))); |
| |
| -- Create a temporary array of the above subtype which |
| -- will be used to capture the aggregate assignments. |
| |
| TmpE : constant Entity_Id := Make_Temporary (Loc, 'A', N); |
| |
| TmpD : constant Node_Id := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => TmpE, |
| Object_Definition => New_Occurrence_Of (SubE, Loc)); |
| |
| begin |
| Set_No_Initialization (TmpD); |
| Append_To (L, SubD); |
| Append_To (L, TmpD); |
| |
| -- Expand aggregate into assignments to the temp array |
| |
| Append_List_To (L, |
| Late_Expansion (Expr_Q, Comp_Type, |
| New_Occurrence_Of (TmpE, Loc))); |
| |
| -- Slide |
| |
| Append_To (L, |
| Make_Assignment_Statement (Loc, |
| Name => New_Copy_Tree (Comp_Expr), |
| Expression => New_Occurrence_Of (TmpE, Loc))); |
| end; |
| |
| -- Normal case (sliding not required) |
| |
| else |
| Append_List_To (L, |
| Late_Expansion (Expr_Q, Comp_Type, Comp_Expr)); |
| end if; |
| |
| -- Expr_Q is not delayed aggregate |
| |
| else |
| if Has_Discriminants (Typ) then |
| Replace_Discriminants (Expr_Q); |
| |
| -- If the component is an array type that depends on |
| -- discriminants, and the expression is a single Others |
| -- clause, create an explicit subtype for it because the |
| -- backend has troubles recovering the actual bounds. |
| |
| if Nkind (Expr_Q) = N_Aggregate |
| and then Is_Array_Type (Comp_Type) |
| and then Present (Component_Associations (Expr_Q)) |
| then |
| declare |
| Assoc : constant Node_Id := |
| First (Component_Associations (Expr_Q)); |
| Decl : Node_Id; |
| |
| begin |
| if Nkind (First (Choices (Assoc))) = N_Others_Choice |
| then |
| Decl := |
| Build_Actual_Subtype_Of_Component |
| (Comp_Type, Comp_Expr); |
| |
| -- If the component type does not in fact depend on |
| -- discriminants, the subtype declaration is empty. |
| |
| if Present (Decl) then |
| Append_To (L, Decl); |
| Set_Etype (Comp_Expr, Defining_Entity (Decl)); |
| end if; |
| end if; |
| end; |
| end if; |
| end if; |
| |
| if Modify_Tree_For_C |
| and then Nkind (Expr_Q) = N_Aggregate |
| and then Is_Array_Type (Etype (Expr_Q)) |
| and then Present (First_Index (Etype (Expr_Q))) |
| then |
| declare |
| Expr_Q_Type : constant Entity_Id := Etype (Expr_Q); |
| begin |
| Append_List_To (L, |
| Build_Array_Aggr_Code |
| (N => Expr_Q, |
| Ctype => Component_Type (Expr_Q_Type), |
| Index => First_Index (Expr_Q_Type), |
| Into => Comp_Expr, |
| Scalar_Comp => |
| Is_Scalar_Type (Component_Type (Expr_Q_Type)))); |
| end; |
| |
| else |
| -- Handle an initialization expression of a controlled type |
| -- in case it denotes a function call. In general such a |
| -- scenario will produce a transient scope, but this will |
| -- lead to wrong order of initialization, adjustment, and |
| -- finalization in the context of aggregates. |
| |
| -- Target.Comp := Ctrl_Func_Call; |
| |
| -- begin -- scope |
| -- Trans_Obj : ... := Ctrl_Func_Call; -- object |
| -- Target.Comp := Trans_Obj; |
| -- Finalize (Trans_Obj); |
| -- end |
| -- Target.Comp._tag := ...; |
| -- Adjust (Target.Comp); |
| |
| -- In the example above, the call to Finalize occurs too |
| -- early and as a result it may leave the record component |
| -- in a bad state. Finalization of the transient object |
| -- should really happen after adjustment. |
| |
| -- To avoid this scenario, perform in-place side-effect |
| -- removal of the function call. This eliminates the |
| -- transient property of the function result and ensures |
| -- correct order of actions. |
| |
| -- Res : ... := Ctrl_Func_Call; |
| -- Target.Comp := Res; |
| -- Target.Comp._tag := ...; |
| -- Adjust (Target.Comp); |
| -- Finalize (Res); |
| |
| if Needs_Finalization (Comp_Type) |
| and then Nkind (Expr_Q) /= N_Aggregate |
| then |
| Initialize_Ctrl_Record_Component |
| (Rec_Comp => Comp_Expr, |
| Comp_Typ => Etype (Selector), |
| Init_Expr => Expr_Q, |
| Stmts => L); |
| |
| -- Otherwise perform single component initialization |
| |
| else |
| Initialize_Record_Component |
| (Rec_Comp => Comp_Expr, |
| Comp_Typ => Etype (Selector), |
| Init_Expr => Expr_Q, |
| Stmts => L); |
| end if; |
| end if; |
| end if; |
| |
| -- comment would be good here ??? |
| |
| elsif Ekind (Selector) = E_Discriminant |
| and then Nkind (N) /= N_Extension_Aggregate |
| and then Nkind (Parent (N)) = N_Component_Association |
| and then Is_Constrained (Typ) |
| then |
| -- We must check that the discriminant value imposed by the |
| -- context is the same as the value given in the subaggregate, |
| -- because after the expansion into assignments there is no |
| -- record on which to perform a regular discriminant check. |
| |
| declare |
| D_Val : Elmt_Id; |
| Disc : Entity_Id; |
| |
| begin |
| D_Val := First_Elmt (Discriminant_Constraint (Typ)); |
| Disc := First_Discriminant (Typ); |
| while Chars (Disc) /= Chars (Selector) loop |
| Next_Discriminant (Disc); |
| Next_Elmt (D_Val); |
| end loop; |
| |
| pragma Assert (Present (D_Val)); |
| |
| -- This check cannot performed for components that are |
| -- constrained by a current instance, because this is not a |
| -- value that can be compared with the actual constraint. |
| |
| if Nkind (Node (D_Val)) /= N_Attribute_Reference |
| or else not Is_Entity_Name (Prefix (Node (D_Val))) |
| or else not Is_Type (Entity (Prefix (Node (D_Val)))) |
| then |
| Append_To (L, |
| Make_Raise_Constraint_Error (Loc, |
| Condition => |
| Make_Op_Ne (Loc, |
| Left_Opnd => New_Copy_Tree (Node (D_Val)), |
| Right_Opnd => Expression (Comp)), |
| Reason => CE_Discriminant_Check_Failed)); |
| |
| else |
| -- Find self-reference in previous discriminant assignment, |
| -- and replace with proper expression. |
| |
| declare |
| Ass : Node_Id; |
| |
| begin |
| Ass := First (L); |
| while Present (Ass) loop |
| if Nkind (Ass) = N_Assignment_Statement |
| and then Nkind (Name (Ass)) = N_Selected_Component |
| and then Chars (Selector_Name (Name (Ass))) = |
| Chars (Disc) |
| then |
| Set_Expression |
| (Ass, New_Copy_Tree (Expression (Comp))); |
| exit; |
| end if; |
| Next (Ass); |
| end loop; |
| end; |
| end if; |
| end; |
| end if; |
| |
| -- If the component association was specified with a box and the |
| -- component type has a Default_Initial_Condition, then generate |
| -- a call to the DIC procedure. |
| |
| if Has_DIC (Etype (Selector)) |
| and then Was_Default_Init_Box_Association (Comp) |
| and then Present (DIC_Procedure (Etype (Selector))) |
| then |
| Append_To (L, |
| Build_DIC_Call (Loc, |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => New_Occurrence_Of (Selector, Loc)), |
| Etype (Selector))); |
| end if; |
| |
| Next (Comp); |
| end loop; |
| |
| -- If the type is tagged, the tag needs to be initialized (unless we |
| -- are in VM-mode where tags are implicit). It is done late in the |
| -- initialization process because in some cases, we call the init |
| -- proc of an ancestor which will not leave out the right tag. |
| |
| if Ancestor_Is_Expression then |
| null; |
| |
| -- For CPP types we generated a call to the C++ default constructor |
| -- before the components have been initialized to ensure the proper |
| -- initialization of the _Tag component (see above). |
| |
| elsif Is_CPP_Class (Typ) then |
| null; |
| |
| elsif Is_Tagged_Type (Typ) and then Tagged_Type_Expansion then |
| Instr := |
| Make_OK_Assignment_Statement (Loc, |
| Name => |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => |
| New_Occurrence_Of |
| (First_Tag_Component (Base_Type (Typ)), Loc)), |
| |
| Expression => |
| Unchecked_Convert_To (RTE (RE_Tag), |
| New_Occurrence_Of |
| (Node (First_Elmt (Access_Disp_Table (Base_Type (Typ)))), |
| Loc))); |
| |
| Append_To (L, Instr); |
| |
| -- Ada 2005 (AI-251): If the tagged type has been derived from an |
| -- abstract interfaces we must also initialize the tags of the |
| -- secondary dispatch tables. |
| |
| if Has_Interfaces (Base_Type (Typ)) then |
| Init_Secondary_Tags |
| (Typ => Base_Type (Typ), |
| Target => Target, |
| Stmts_List => L, |
| Init_Tags_List => L); |
| end if; |
| end if; |
| |
| -- If the controllers have not been initialized yet (by lack of non- |
| -- discriminant components), let's do it now. |
| |
| Generate_Finalization_Actions; |
| |
| return L; |
| end Build_Record_Aggr_Code; |
| |
| ------------------------------- |
| -- Convert_Aggr_In_Allocator -- |
| ------------------------------- |
| |
| procedure Convert_Aggr_In_Allocator |
| (Alloc : Node_Id; |
| Decl : Node_Id; |
| Aggr : Node_Id) |
| is |
| Loc : constant Source_Ptr := Sloc (Aggr); |
| Typ : constant Entity_Id := Etype (Aggr); |
| Temp : constant Entity_Id := Defining_Identifier (Decl); |
| |
| Occ : constant Node_Id := |
| Unchecked_Convert_To (Typ, |
| Make_Explicit_Dereference (Loc, New_Occurrence_Of (Temp, Loc))); |
| |
| begin |
| if Is_Array_Type (Typ) then |
| Convert_Array_Aggr_In_Allocator (Decl, Aggr, Occ); |
| |
| elsif Has_Default_Init_Comps (Aggr) then |
| declare |
| L : constant List_Id := New_List; |
| Init_Stmts : List_Id; |
| |
| begin |
| Init_Stmts := Late_Expansion (Aggr, Typ, Occ); |
| |
| if Has_Task (Typ) then |
| Build_Task_Allocate_Block_With_Init_Stmts (L, Aggr, Init_Stmts); |
| Insert_Actions (Alloc, L); |
| else |
| Insert_Actions (Alloc, Init_Stmts); |
| end if; |
| end; |
| |
| else |
| Insert_Actions (Alloc, Late_Expansion (Aggr, Typ, Occ)); |
| end if; |
| end Convert_Aggr_In_Allocator; |
| |
| -------------------------------- |
| -- Convert_Aggr_In_Assignment -- |
| -------------------------------- |
| |
| procedure Convert_Aggr_In_Assignment (N : Node_Id) is |
| Aggr : Node_Id := Expression (N); |
| Typ : constant Entity_Id := Etype (Aggr); |
| Occ : constant Node_Id := New_Copy_Tree (Name (N)); |
|