| ------------------------------------------------------------------------------ |
| -- -- |
| -- 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) * Siz; |
| begin |
| if UI_Is_In_Int_Range (UI) then |
| return 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; |
| |
| Ent : Entity_Id; |
| |
| 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 |
| |
| -- Create a new scope for the loop variable so that the |
| -- following Gen_Assign (that ends up calling |
| -- Preanalyze_And_Resolve) can correctly find it. |
| |
| Ent := New_Internal_Entity (E_Loop, |
| Current_Scope, Loc, 'L'); |
| Set_Etype (Ent, Standard_Void_Type); |
| Set_Parent (Ent, Parent (Parent (Expr))); |
| Push_Scope (Ent); |
| |
| L_J := |
| Make_Defining_Identifier (Loc, |
| Chars => (Chars (Defining_Identifier (Parent (Expr))))); |
| |
| Enter_Name (L_J); |
| |
| -- The Etype will be set by a later Analyze call. |
| Set_Etype (L_J, Any_Type); |
| |
| Mutate_Ekind (L_J, E_Variable); |
| Set_Scope (L_J, Ent); |
| 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)); |
| |
| if Is_Iterated_Component then |
| End_Scope; |
| end if; |
| |
| -- 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_Bounds : constant Range_Nodes := |
| Get_Index_Bounds (Aggregate_Bounds (N)); |
| Aggr_L : Node_Id renames Aggr_Bounds.First; |
| Aggr_H : Node_Id renames Aggr_Bounds.Last; |
| -- 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 from 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)); |
| |
| begin |
| if Nkind (Aggr) = N_Qualified_Expression then |
| Aggr := Expression (Aggr); |
| end if; |
| |
| Insert_Actions_After (N, Late_Expansion (Aggr, Typ, Occ)); |
| end Convert_Aggr_In_Assignment; |
| |
| --------------------------------- |
| -- Convert_Aggr_In_Object_Decl -- |
| --------------------------------- |
| |
| procedure Convert_Aggr_In_Object_Decl (N : Node_Id) is |
| Obj : constant Entity_Id := Defining_Identifier (N); |
| Aggr : Node_Id := Expression (N); |
| Loc : constant Source_Ptr := Sloc (Aggr); |
| Typ : constant Entity_Id := Etype (Aggr); |
| Occ : constant Node_Id := New_Occurrence_Of (Obj, Loc); |
| |
| Has_Transient_Scope : Boolean := False; |
| |
| function Discriminants_Ok return Boolean; |
| -- If the object type is constrained, the discriminants in the |
| -- aggregate must be checked against the discriminants of the subtype. |
| -- This cannot be done using Apply_Discriminant_Checks because after |
| -- expansion there is no aggregate left to check. |
| |
| ---------------------- |
| -- Discriminants_Ok -- |
| ---------------------- |
| |
| function Discriminants_Ok return Boolean is |
| Cond : Node_Id := Empty; |
| Check : Node_Id; |
| D : Entity_Id; |
| Disc1 : Elmt_Id; |
| Disc2 : Elmt_Id; |
| Val1 : Node_Id; |
| Val2 : Node_Id; |
| |
| begin |
| D := First_Discriminant (Typ); |
| Disc1 := First_Elmt (Discriminant_Constraint (Typ)); |
| Disc2 := First_Elmt (Discriminant_Constraint (Etype (Obj))); |
| while Present (Disc1) and then Present (Disc2) loop |
| Val1 := Node (Disc1); |
| Val2 := Node (Disc2); |
| |
| if not Is_OK_Static_Expression (Val1) |
| or else not Is_OK_Static_Expression (Val2) |
| then |
| Check := Make_Op_Ne (Loc, |
| Left_Opnd => Duplicate_Subexpr (Val1), |
| Right_Opnd => Duplicate_Subexpr (Val2)); |
| |
| if No (Cond) then |
| Cond := Check; |
| |
| else |
| Cond := Make_Or_Else (Loc, |
| Left_Opnd => Cond, |
| Right_Opnd => Check); |
| end if; |
| |
| elsif Expr_Value (Val1) /= Expr_Value (Val2) then |
| Apply_Compile_Time_Constraint_Error (Aggr, |
| Msg => "incorrect value for discriminant&??", |
| Reason => CE_Discriminant_Check_Failed, |
| Ent => D); |
| return False; |
| end if; |
| |
| Next_Discriminant (D); |
| Next_Elmt (Disc1); |
| Next_Elmt (Disc2); |
| end loop; |
| |
| -- If any discriminant constraint is nonstatic, emit a check |
| |
| if Present (Cond) then |
| Insert_Action (N, |
| Make_Raise_Constraint_Error (Loc, |
| Condition => Cond, |
| Reason => CE_Discriminant_Check_Failed)); |
| end if; |
| |
| return True; |
| end Discriminants_Ok; |
| |
| -- Start of processing for Convert_Aggr_In_Object_Decl |
| |
| begin |
| Set_Assignment_OK (Occ); |
| |
| if Nkind (Aggr) = N_Qualified_Expression then |
| Aggr := Expression (Aggr); |
| end if; |
| |
| if Has_Discriminants (Typ) |
| and then Typ /= Etype (Obj) |
| and then Is_Constrained (Etype (Obj)) |
| and then not Discriminants_Ok |
| then |
| return; |
| end if; |
| |
| -- If the context is an extended return statement, it has its own |
| -- finalization machinery (i.e. works like a transient scope) and |
| -- we do not want to create an additional one, because objects on |
| -- the finalization list of the return must be moved to the caller's |
| -- finalization list to complete the return. |
| |
| -- Similarly if the aggregate is limited, it is built in place, and the |
| -- controlled components are not assigned to intermediate temporaries |
| -- so there is no need for a transient scope in this case either. |
| |
| if Requires_Transient_Scope (Typ) |
| and then Ekind (Current_Scope) /= E_Return_Statement |
| and then not Is_Limited_Type (Typ) |
| then |
| Establish_Transient_Scope (Aggr, Manage_Sec_Stack => False); |
| Has_Transient_Scope := True; |
| end if; |
| |
| declare |
| Stmts : constant List_Id := Late_Expansion (Aggr, Typ, Occ); |
| Stmt : Node_Id; |
| Param : Node_Id; |
| |
| begin |
| -- If Obj is already frozen or if N is wrapped in a transient scope, |
| -- Stmts do not need to be saved in Initialization_Statements since |
| -- there is no freezing issue. |
| |
| if Is_Frozen (Obj) or else Has_Transient_Scope then |
| Insert_Actions_After (N, Stmts); |
| else |
| Stmt := Make_Compound_Statement (Sloc (N), Actions => Stmts); |
| Insert_Action_After (N, Stmt); |
| |
| -- Insert_Action_After may freeze Obj in which case we should |
| -- remove the compound statement just created and simply insert |
| -- Stmts after N. |
| |
| if Is_Frozen (Obj) then |
| Remove (Stmt); |
| Insert_Actions_After (N, Stmts); |
| else |
| Set_Initialization_Statements (Obj, Stmt); |
| end if; |
| end if; |
| |
| -- If Typ has controlled components and a call to a Slice_Assign |
| -- procedure is part of the initialization statements, then we |
| -- need to initialize the array component since Slice_Assign will |
| -- need to adjust it. |
| |
| if Has_Controlled_Component (Typ) then |
| Stmt := First (Stmts); |
| |
| while Present (Stmt) loop |
| if Nkind (Stmt) = N_Procedure_Call_Statement |
| and then Get_TSS_Name (Entity (Name (Stmt))) |
| = TSS_Slice_Assign |
| then |
| Param := First (Parameter_Associations (Stmt)); |
| Insert_Actions |
| (Stmt, |
| Build_Initialization_Call |
| (Sloc (N), New_Copy_Tree (Param), Etype (Param))); |
| end if; |
| |
| Next (Stmt); |
| end loop; |
| end if; |
| end; |
| |
| Set_No_Initialization (N); |
| |
| -- After expansion the expression can be removed from the declaration |
| -- except if the object is class-wide, in which case the aggregate |
| -- provides the actual type. |
| |
| if not Is_Class_Wide_Type (Etype (Obj)) then |
| Set_Expression (N, Empty); |
| end if; |
| |
| Initialize_Discriminants (N, Typ); |
| end Convert_Aggr_In_Object_Decl; |
| |
| ------------------------------------- |
| -- Convert_Array_Aggr_In_Allocator -- |
| ------------------------------------- |
| |
| procedure Convert_Array_Aggr_In_Allocator |
| (Decl : Node_Id; |
| Aggr : Node_Id; |
| Target : Node_Id) |
| is |
| Typ : constant Entity_Id := Etype (Aggr); |
| Ctyp : constant Entity_Id := Component_Type (Typ); |
| Aggr_Code : List_Id; |
| New_Aggr : Node_Id; |
| |
| begin |
| -- The target is an explicit dereference of the allocated object |
| |
| -- If the assignment can be done directly by the back end, then |
| -- reset Set_Expansion_Delayed and do not expand further. |
| |
| if not CodePeer_Mode |
| and then not Modify_Tree_For_C |
| and then Aggr_Assignment_OK_For_Backend (Aggr) |
| then |
| New_Aggr := New_Copy_Tree (Aggr); |
| Set_Expansion_Delayed (New_Aggr, False); |
| |
| Aggr_Code := |
| New_List ( |
| Make_OK_Assignment_Statement (Sloc (New_Aggr), |
| Name => Target, |
| Expression => New_Aggr)); |
| |
| -- Or else, generate component assignments to it, as for an aggregate |
| -- that appears on the right-hand side of an assignment statement. |
| |
| else |
| Aggr_Code := |
| Build_Array_Aggr_Code (Aggr, |
| Ctype => Ctyp, |
| Index => First_Index (Typ), |
| Into => Target, |
| Scalar_Comp => Is_Scalar_Type (Ctyp)); |
| end if; |
| |
| Insert_Actions_After (Decl, Aggr_Code); |
| end Convert_Array_Aggr_In_Allocator; |
| |
| ------------------------ |
| -- In_Place_Assign_OK -- |
| ------------------------ |
| |
| function In_Place_Assign_OK |
| (N : Node_Id; |
| Target_Object : Entity_Id := Empty) return Boolean |
| is |
| Is_Array : constant Boolean := Is_Array_Type (Etype (N)); |
| |
| Aggr_In : Node_Id; |
| Aggr_Bounds : Range_Nodes; |
| Obj_In : Node_Id; |
| Obj_Bounds : Range_Nodes; |
| Parent_Kind : Node_Kind; |
| Parent_Node : Node_Id; |
| |
| function Safe_Aggregate (Aggr : Node_Id) return Boolean; |
| -- Check recursively that each component of a (sub)aggregate does not |
| -- depend on the variable being assigned to. |
| |
| function Safe_Component (Expr : Node_Id) return Boolean; |
| -- Verify that an expression cannot depend on the target being assigned |
| -- to. Return true for compile-time known values, stand-alone objects, |
| -- parameters passed by copy, calls to functions that return by copy, |
| -- selected components thereof only if the aggregate's type is an array, |
| -- indexed components and slices thereof only if the aggregate's type is |
| -- a record, and simple expressions involving only these as operands. |
| -- This is OK whatever the target because, for a component to overlap |
| -- with the target, it must be either a direct reference to a component |
| -- of the target, in which case there must be a matching selection or |
| -- indexation or slicing, or an indirect reference to such a component, |
| -- which is excluded by the above condition. Additionally, if the target |
| -- is statically known, return true for arbitrarily nested selections, |
| -- indexations or slicings, provided that their ultimate prefix is not |
| -- the target itself. |
| |
| -------------------- |
| -- Safe_Aggregate -- |
| -------------------- |
| |
| function Safe_Aggregate (Aggr : Node_Id) return Boolean is |
| Expr : Node_Id; |
| |
| begin |
| if Nkind (Parent (Aggr)) = N_Iterated_Component_Association then |
| return False; |
| end if; |
| |
| if Present (Expressions (Aggr)) then |
| Expr := First (Expressions (Aggr)); |
| while Present (Expr) loop |
| if Nkind (Expr) = N_Aggregate then |
| if not Safe_Aggregate (Expr) then |
| return False; |
| end if; |
| |
| elsif not Safe_Component (Expr) then |
| return False; |
| end if; |
| |
| Next (Expr); |
| end loop; |
| end if; |
| |
| if Present (Component_Associations (Aggr)) then |
| Expr := First (Component_Associations (Aggr)); |
| while Present (Expr) loop |
| if Nkind (Expression (Expr)) = N_Aggregate then |
| if not Safe_Aggregate (Expression (Expr)) then |
| return False; |
| end if; |
| |
| -- If association has a box, no way to determine yet whether |
| -- default can be assigned in place. |
| |
| elsif Box_Present (Expr) then |
| return False; |
| |
| elsif not Safe_Component (Expression (Expr)) then |
| return False; |
| end if; |
| |
| Next (Expr); |
| end loop; |
| end if; |
| |
| return True; |
| end Safe_Aggregate; |
| |
| -------------------- |
| -- Safe_Component -- |
| -------------------- |
| |
| function Safe_Component (Expr : Node_Id) return Boolean is |
| Comp : Node_Id := Expr; |
| |
| function Check_Component (C : Node_Id; T_OK : Boolean) return Boolean; |
| -- Do the recursive traversal, after copy. If T_OK is True, return |
| -- True for a stand-alone object only if the target is statically |
| -- known and distinct from the object. At the top level, we start |
| -- with T_OK set to False and set it to True at a deeper level only |
| -- if we cannot disambiguate the component here without statically |
| -- knowing the target. Note that this is not optimal, we should do |
| -- something along the lines of Denotes_Same_Prefix for that. |
| |
| --------------------- |
| -- Check_Component -- |
| --------------------- |
| |
| function Check_Component (C : Node_Id; T_OK : Boolean) return Boolean |
| is |
| |
| function SDO (E : Entity_Id) return Uint; |
| -- Return the Scope Depth Of the enclosing dynamic scope of E |
| |
| --------- |
| -- SDO -- |
| --------- |
| |
| function SDO (E : Entity_Id) return Uint is |
| begin |
| return Scope_Depth (Enclosing_Dynamic_Scope (E)); |
| end SDO; |
| |
| -- Start of processing for Check_Component |
| |
| begin |
| if Is_Overloaded (C) then |
| return False; |
| |
| elsif Compile_Time_Known_Value (C) then |
| return True; |
| end if; |
| |
| case Nkind (C) is |
| when N_Attribute_Reference => |
| return Check_Component (Prefix (C), T_OK); |
| |
| when N_Function_Call => |
| if Nkind (Name (C)) = N_Explicit_Dereference then |
| return not Returns_By_Ref (Etype (Name (C))); |
| else |
| return not Returns_By_Ref (Entity (Name (C))); |
| end if; |
| |
| when N_Indexed_Component | N_Slice => |
| -- In a target record, these operations cannot determine |
| -- alone a component so we can recurse whatever the target. |
| return Check_Component (Prefix (C), T_OK or else Is_Array); |
| |
| when N_Selected_Component => |
| -- In a target array, this operation cannot determine alone |
| -- a component so we can recurse whatever the target. |
| return |
| Check_Component (Prefix (C), T_OK or else not Is_Array); |
| |
| when N_Type_Conversion | N_Unchecked_Type_Conversion => |
| return Check_Component (Expression (C), T_OK); |
| |
| when N_Binary_Op => |
| return Check_Component (Left_Opnd (C), T_OK) |
| and then Check_Component (Right_Opnd (C), T_OK); |
| |
| when N_Unary_Op => |
| return Check_Component (Right_Opnd (C), T_OK); |
| |
| when others => |
| if Is_Entity_Name (C) and then Is_Object (Entity (C)) then |
| -- Case of a formal parameter component. It's either |
| -- trivial if passed by copy or very annoying if not, |
| -- because in the latter case it's almost equivalent |
| -- to a dereference, so the path-based disambiguation |
| -- logic is totally off and we always need the target. |
| |
| if Is_Formal (Entity (C)) then |
| |
| -- If it is passed by copy, then this is safe |
| |
| if Mechanism (Entity (C)) = By_Copy then |
| return True; |
| |
| -- Otherwise, this is safe if the target is present |
| -- and is at least as deeply nested as the component. |
| |
| else |
| return Present (Target_Object) |
| and then not Is_Formal (Target_Object) |
| and then SDO (Target_Object) >= SDO (Entity (C)); |
| end if; |
| |
| -- For a renamed object, recurse |
| |
| elsif Present (Renamed_Object (Entity (C))) then |
| return |
| Check_Component (Renamed_Object (Entity (C)), T_OK); |
| |
| -- If this is safe whatever the target, we are done |
| |
| elsif not T_OK then |
| return True; |
| |
| -- If there is no target or the component is the target, |
| -- this is not safe. |
| |
| elsif No (Target_Object) |
| or else Entity (C) = Target_Object |
| then |
| return False; |
| |
| -- Case of a formal parameter target. This is safe if it |
| -- is at most as deeply nested as the component. |
| |
| elsif Is_Formal (Target_Object) then |
| return SDO (Target_Object) <= SDO (Entity (C)); |
| |
| -- For distinct stand-alone objects, this is safe |
| |
| else |
| return True; |
| end if; |
| |
| -- For anything else than an object, this is not safe |
| |
| else |
| return False; |
| end if; |
| end case; |
| end Check_Component; |
| |
| -- Start of processing for Safe_Component |
| |
| begin |
| -- If the component appears in an association that may correspond |
| -- to more than one element, it is not analyzed before expansion |
| -- into assignments, to avoid side effects. We analyze, but do not |
| -- resolve the copy, to obtain sufficient entity information for |
| -- the checks that follow. If component is overloaded we assume |
| -- an unsafe function call. |
| |
| if not Analyzed (Comp) then |
| if Is_Overloaded (Expr) then |
| return False; |
| |
| elsif Nkind (Expr) = N_Allocator then |
| |
| -- For now, too complex to analyze |
| |
| return False; |
| |
| elsif Nkind (Parent (Expr)) = N_Iterated_Component_Association then |
| |
| -- Ditto for iterated component associations, which in general |
| -- require an enclosing loop and involve nonstatic expressions. |
| |
| return False; |
| end if; |
| |
| Comp := New_Copy_Tree (Expr); |
| Set_Parent (Comp, Parent (Expr)); |
| Analyze (Comp); |
| end if; |
| |
| if Nkind (Comp) = N_Aggregate then |
| return Safe_Aggregate (Comp); |
| else |
| return Check_Component (Comp, False); |
| end if; |
| end Safe_Component; |
| |
| -- Start of processing for In_Place_Assign_OK |
| |
| begin |
| -- By-copy semantic cannot be guaranteed for controlled objects |
| |
| if Needs_Finalization (Etype (N)) then |
| return False; |
| end if; |
| |
| Parent_Node := Parent (N); |
| Parent_Kind := Nkind (Parent_Node); |
| |
| if Parent_Kind = N_Qualified_Expression then |
| Parent_Node := Parent (Parent_Node); |
| Parent_Kind := Nkind (Parent_Node); |
| end if; |
| |
| -- On assignment, sliding can take place, so we cannot do the |
| -- assignment in place unless the bounds of the aggregate are |
| -- statically equal to those of the target. |
| |
| -- If the aggregate is given by an others choice, the bounds are |
| -- derived from the left-hand side, and the assignment is safe if |
| -- the expression is. |
| |
| if Is_Array |
| and then Present (Component_Associations (N)) |
| and then not Is_Others_Aggregate (N) |
| then |
| Aggr_In := First_Index (Etype (N)); |
| |
| -- Context is an assignment |
| |
| if Parent_Kind = N_Assignment_Statement then |
| Obj_In := First_Index (Etype (Name (Parent_Node))); |
| |
| -- Context is an allocator. Check the bounds of the aggregate against |
| -- those of the designated type, except in the case where the type is |
| -- unconstrained (and then we can directly return true, see below). |
| |
| else pragma Assert (Parent_Kind = N_Allocator); |
| declare |
| Desig_Typ : constant Entity_Id := |
| Designated_Type (Etype (Parent_Node)); |
| begin |
| if not Is_Constrained (Desig_Typ) then |
| return True; |
| end if; |
| |
| Obj_In := First_Index (Desig_Typ); |
| end; |
| end if; |
| |
| while Present (Aggr_In) loop |
| Aggr_Bounds := Get_Index_Bounds (Aggr_In); |
| Obj_Bounds := Get_Index_Bounds (Obj_In); |
| |
| -- We require static bounds for the target and a static matching |
| -- of low bound for the aggregate. |
| |
| if not Compile_Time_Known_Value (Obj_Bounds.First) |
| or else not Compile_Time_Known_Value (Obj_Bounds.Last) |
| or else not Compile_Time_Known_Value (Aggr_Bounds.First) |
| or else Expr_Value (Aggr_Bounds.First) /= |
| Expr_Value (Obj_Bounds.First) |
| then |
| return False; |
| |
| -- For an assignment statement we require static matching of |
| -- bounds. Ditto for an allocator whose qualified expression |
| -- is a constrained type. If the expression in the allocator |
| -- is an unconstrained array, we accept an upper bound that |
| -- is not static, to allow for nonstatic expressions of the |
| -- base type. Clearly there are further possibilities (with |
| -- diminishing returns) for safely building arrays in place |
| -- here. |
| |
| elsif Parent_Kind = N_Assignment_Statement |
| or else Is_Constrained (Etype (Parent_Node)) |
| then |
| if not Compile_Time_Known_Value (Aggr_Bounds.Last) |
| or else Expr_Value (Aggr_Bounds.Last) /= |
| Expr_Value (Obj_Bounds.Last) |
| then |
| return False; |
| end if; |
| end if; |
| |
| Next_Index (Aggr_In); |
| Next_Index (Obj_In); |
| end loop; |
| end if; |
| |
| -- Now check the component values themselves, except for an allocator |
| -- for which the target is newly allocated memory. |
| |
| if Parent_Kind = N_Allocator then |
| return True; |
| else |
| return Safe_Aggregate (N); |
| end if; |
| end In_Place_Assign_OK; |
| |
| ---------------------------- |
| -- Convert_To_Assignments -- |
| ---------------------------- |
| |
| procedure Convert_To_Assignments (N : Node_Id; Typ : Entity_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| T : Entity_Id; |
| Temp : Entity_Id; |
| |
| Aggr_Code : List_Id; |
| Instr : Node_Id; |
| Target_Expr : Node_Id; |
| Parent_Kind : Node_Kind; |
| Unc_Decl : Boolean := False; |
| Parent_Node : Node_Id; |
| |
| begin |
| pragma Assert (Nkind (N) in N_Aggregate | N_Extension_Aggregate); |
| pragma Assert (not Is_Static_Dispatch_Table_Aggregate (N)); |
| pragma Assert (Is_Record_Type (Typ)); |
| |
| Parent_Node := Parent (N); |
| Parent_Kind := Nkind (Parent_Node); |
| |
| if Parent_Kind = N_Qualified_Expression then |
| -- Check if we are in an unconstrained declaration because in this |
| -- case the current delayed expansion mechanism doesn't work when |
| -- the declared object size depends on the initializing expr. |
| |
| Parent_Node := Parent (Parent_Node); |
| Parent_Kind := Nkind (Parent_Node); |
| |
| if Parent_Kind = N_Object_Declaration then |
| Unc_Decl := |
| not Is_Entity_Name (Object_Definition (Parent_Node)) |
| or else (Nkind (N) = N_Aggregate |
| and then |
| Has_Discriminants |
| (Entity (Object_Definition (Parent_Node)))) |
| or else Is_Class_Wide_Type |
| (Entity (Object_Definition (Parent_Node))); |
| end if; |
| end if; |
| |
| -- Just set the Delay flag in the cases where the transformation will be |
| -- done top down from above. |
| |
| if |
| -- Internal aggregate (transformed when expanding the parent) |
| |
| Parent_Kind in |
| N_Aggregate | N_Extension_Aggregate | N_Component_Association |
| |
| -- Allocator (see Convert_Aggr_In_Allocator) |
| |
| or else Parent_Kind = N_Allocator |
| |
| -- Object declaration (see Convert_Aggr_In_Object_Decl) |
| |
| or else (Parent_Kind = N_Object_Declaration and then not Unc_Decl) |
| |
| -- Safe assignment (see Convert_Aggr_Assignments). So far only the |
| -- assignments in init procs are taken into account. |
| |
| or else (Parent_Kind = N_Assignment_Statement |
| and then Inside_Init_Proc) |
| |
| -- (Ada 2005) An inherently limited type in a return statement, which |
| -- will be handled in a build-in-place fashion, and may be rewritten |
| -- as an extended return and have its own finalization machinery. |
| -- In the case of a simple return, the aggregate needs to be delayed |
| -- until the scope for the return statement has been created, so |
| -- that any finalization chain will be associated with that scope. |
| -- For extended returns, we delay expansion to avoid the creation |
| -- of an unwanted transient scope that could result in premature |
| -- finalization of the return object (which is built in place |
| -- within the caller's scope). |
| |
| or else Is_Build_In_Place_Aggregate_Return (N) |
| then |
| Set_Expansion_Delayed (N); |
| return; |
| end if; |
| |
| -- Otherwise, if a transient scope is required, create it now. If we |
| -- are within an initialization procedure do not create such, because |
| -- the target of the assignment must not be declared within a local |
| -- block, and because cleanup will take place on return from the |
| -- initialization procedure. |
| |
| -- Should the condition be more restrictive ??? |
| |
| if Requires_Transient_Scope (Typ) and then not Inside_Init_Proc then |
| Establish_Transient_Scope (N, Manage_Sec_Stack => False); |
| end if; |
| |
| -- If the aggregate is nonlimited, create a temporary, since aggregates |
| -- have "by copy" semantics. If it is limited and context is an |
| -- assignment, this is a subaggregate for an enclosing aggregate being |
| -- expanded. It must be built in place, so use target of the current |
| -- assignment. |
| |
| if Is_Limited_Type (Typ) |
| and then Parent_Kind = N_Assignment_Statement |
| then |
| Target_Expr := New_Copy_Tree (Name (Parent_Node)); |
| Insert_Actions (Parent_Node, |
| Build_Record_Aggr_Code (N, Typ, Target_Expr)); |
| Rewrite (Parent_Node, Make_Null_Statement (Loc)); |
| |
| -- Do not declare a temporary to initialize an aggregate assigned to |
| -- a target when in-place assignment is possible, i.e. preserving the |
| -- by-copy semantic of aggregates. This avoids large stack usage and |
| -- generates more efficient code. |
| |
| elsif Parent_Kind = N_Assignment_Statement |
| and then In_Place_Assign_OK (N, Get_Base_Object (Name (Parent_Node))) |
| then |
| declare |
| Lhs : constant Node_Id := Name (Parent_Node); |
| begin |
| -- Apply discriminant check if required |
| |
| if Has_Discriminants (Etype (N)) then |
| Apply_Discriminant_Check (N, Etype (Lhs), Lhs); |
| end if; |
| |
| -- The check just above may have replaced the aggregate with a CE |
| |
| if Nkind (N) in N_Aggregate | N_Extension_Aggregate then |
| Target_Expr := New_Copy_Tree (Lhs); |
| Insert_Actions (Parent_Node, |
| Build_Record_Aggr_Code (N, Typ, Target_Expr)); |
| Rewrite (Parent_Node, Make_Null_Statement (Loc)); |
| end if; |
| end; |
| |
| else |
| Temp := Make_Temporary (Loc, 'A', N); |
| |
| -- If the type inherits unknown discriminants, use the view with |
| -- known discriminants if available. |
| |
| if Has_Unknown_Discriminants (Typ) |
| and then Present (Underlying_Record_View (Typ)) |
| then |
| T := Underlying_Record_View (Typ); |
| else |
| T := Typ; |
| end if; |
| |
| Instr := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Temp, |
| Object_Definition => New_Occurrence_Of (T, Loc)); |
| |
| Set_No_Initialization (Instr); |
| Insert_Action (N, Instr); |
| Initialize_Discriminants (Instr, T); |
| |
| Target_Expr := New_Occurrence_Of (Temp, Loc); |
| Aggr_Code := Build_Record_Aggr_Code (N, T, Target_Expr); |
| |
| -- Save the last assignment statement associated with the aggregate |
| -- when building a controlled object. This reference is utilized by |
| -- the finalization machinery when marking an object as successfully |
| -- initialized. |
| |
| if Needs_Finalization (T) then |
| Set_Last_Aggregate_Assignment (Temp, Last (Aggr_Code)); |
| end if; |
| |
| Insert_Actions (N, Aggr_Code); |
| Rewrite (N, New_Occurrence_Of (Temp, Loc)); |
| Analyze_And_Resolve (N, T); |
| end if; |
| end Convert_To_Assignments; |
| |
| --------------------------- |
| -- Convert_To_Positional -- |
| --------------------------- |
| |
| procedure Convert_To_Positional |
| (N : Node_Id; |
| Handle_Bit_Packed : Boolean := False) |
| is |
| Typ : constant Entity_Id := Etype (N); |
| Dims : constant Nat := Number_Dimensions (Typ); |
| Max_Others_Replicate : constant Nat := Max_Aggregate_Size (N); |
| |
| Static_Components : Boolean := True; |
| |
| procedure Check_Static_Components; |
| -- Check whether all components of the aggregate are compile-time known |
| -- values, and can be passed as is to the back-end without further |
| -- expansion. |
| |
| function Flatten |
| (N : Node_Id; |
| Dims : Nat; |
| Ix : Node_Id; |
| Ixb : Node_Id) return Boolean; |
| -- Convert the aggregate into a purely positional form if possible after |
| -- checking that the bounds of all dimensions are known to be static. |
| |
| function Is_Flat (N : Node_Id; Dims : Nat) return Boolean; |
| -- Return True if the aggregate N is flat (which is not trivial in the |
| -- case of multidimensional aggregates). |
| |
| function Is_Static_Element (N : Node_Id; Dims : Nat) return Boolean; |
| -- Return True if N, an element of a component association list, i.e. |
| -- N_Component_Association or N_Iterated_Component_Association, has a |
| -- compile-time known value and can be passed as is to the back-end |
| -- without further expansion. |
| -- An Iterated_Component_Association is treated as nonstatic in most |
| -- cases for now, so there are possibilities for optimization. |
| |
| ----------------------------- |
| -- Check_Static_Components -- |
| ----------------------------- |
| |
| -- Could use some comments in this body ??? |
| |
| procedure Check_Static_Components is |
| Assoc : Node_Id; |
| Expr : Node_Id; |
| |
| begin |
| Static_Components := True; |
| |
| if Nkind (N) = N_String_Literal then |
| null; |
| |
| elsif Present (Expressions (N)) then |
| Expr := First (Expressions (N)); |
| while Present (Expr) loop |
| if Nkind (Expr) /= N_Aggregate |
| or else not Compile_Time_Known_Aggregate (Expr) |
| or else Expansion_Delayed (Expr) |
| then |
| Static_Components := False; |
| exit; |
| end if; |
| |
| Next (Expr); |
| end loop; |
| end if; |
| |
| if Nkind (N) = N_Aggregate |
| and then Present (Component_Associations (N)) |
| then |
| Assoc := First (Component_Associations (N)); |
| while Present (Assoc) loop |
| if not Is_Static_Element (Assoc, Dims) then |
| Static_Components := False; |
| exit; |
| end if; |
| |
| Next (Assoc); |
| end loop; |
| end if; |
| end Check_Static_Components; |
| |
| ------------- |
| -- Flatten -- |
| ------------- |
| |
| function Flatten |
| (N : Node_Id; |
| Dims : Nat; |
| Ix : Node_Id; |
| Ixb : Node_Id) return Boolean |
| is |
| Loc : constant Source_Ptr := Sloc (N); |
| Blo : constant Node_Id := Type_Low_Bound (Etype (Ixb)); |
| Lo : constant Node_Id := Type_Low_Bound (Etype (Ix)); |
| Hi : constant Node_Id := Type_High_Bound (Etype (Ix)); |
| |
| function Cannot_Flatten_Next_Aggr (Expr : Node_Id) return Boolean; |
| -- Return true if Expr is an aggregate for the next dimension that |
| -- cannot be recursively flattened. |
| |
| ------------------------------ |
| -- Cannot_Flatten_Next_Aggr -- |
| ------------------------------ |
| |
| function Cannot_Flatten_Next_Aggr (Expr : Node_Id) return Boolean is |
| begin |
| return Nkind (Expr) = N_Aggregate |
| and then Present (Next_Index (Ix)) |
| and then not |
| Flatten (Expr, Dims - 1, Next_Index (Ix), Next_Index (Ixb)); |
| end Cannot_Flatten_Next_Aggr; |
| |
| -- Local variables |
| |
| Lov : Uint; |
| Hiv : Uint; |
| Others_Present : Boolean; |
| |
| -- Start of processing for Flatten |
| |
| begin |
| if Nkind (Original_Node (N)) = N_String_Literal then |
| return True; |
| end if; |
| |
| 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); |
| |
| -- Check if there is an others choice |
| |
| Others_Present := False; |
| |
| if Present (Component_Associations (N)) then |
| if Is_Empty_List (Component_Associations (N)) then |
| -- an expanded null array aggregate |
| return False; |
| end if; |
| |
| declare |
| Assoc : Node_Id; |
| Choice : Node_Id; |
| |
| begin |
| Assoc := First (Component_Associations (N)); |
| while Present (Assoc) loop |
| |
| -- If this is a box association, flattening is in general |
| -- not possible because at this point we cannot tell if the |
| -- default is static or even exists. |
| |
| if Box_Present (Assoc) then |
| return False; |
| |
| elsif Nkind (Assoc) = N_Iterated_Component_Association then |
| return False; |
| end if; |
| |
| Choice := First (Choice_List (Assoc)); |
| |
| while Present (Choice) loop |
| if Nkind (Choice) = N_Others_Choice then |
| Others_Present := True; |
| end if; |
| |
| Next (Choice); |
| end loop; |
| |
| Next (Assoc); |
| end loop; |
| end; |
| end if; |
| |
| -- If the low bound is not known at compile time and others is not |
| -- present we can proceed since the bounds can be obtained from the |
| -- aggregate. |
| |
| if Hiv < Lov |
| or else (not Compile_Time_Known_Value (Blo) and then Others_Present) |
| then |
| return False; |
| end if; |
| |
| -- Determine if set of alternatives is suitable for conversion and |
| -- build an array containing the values in sequence. |
| |
| declare |
| Vals : array (UI_To_Int (Lov) .. UI_To_Int (Hiv)) |
| of Node_Id := (others => Empty); |
| -- The values in the aggregate sorted appropriately |
| |
| Vlist : List_Id; |
| -- Same data as Vals in list form |
| |
| Rep_Count : Nat; |
| -- Used to validate Max_Others_Replicate limit |
| |
| Elmt : Node_Id; |
| Expr : Node_Id; |
| Num : Int := UI_To_Int (Lov); |
| Choice_Index : Int; |
| Choice : Node_Id; |
| Lo, Hi : Node_Id; |
| |
| begin |
| if Present (Expressions (N)) then |
| Elmt := First (Expressions (N)); |
| while Present (Elmt) loop |
| -- In the case of a multidimensional array, check that the |
| -- aggregate can be recursively flattened. |
| |
| if Cannot_Flatten_Next_Aggr (Elmt) then |
| return False; |
| end if; |
| |
| -- Duplicate expression for each index it covers |
| |
| Vals (Num) := New_Copy_Tree (Elmt); |
| Num := Num + 1; |
| |
| Next (Elmt); |
| end loop; |
| end if; |
| |
| if No (Component_Associations (N)) then |
| return True; |
| end if; |
| |
| Elmt := First (Component_Associations (N)); |
| |
| Component_Loop : while Present (Elmt) loop |
| Expr := Expression (Elmt); |
| |
| -- In the case of a multidimensional array, check that the |
| -- aggregate can be recursively flattened. |
| |
| if Cannot_Flatten_Next_Aggr (Expr) then |
| return False; |
| end if; |
| |
| Choice := First (Choice_List (Elmt)); |
| Choice_Loop : while Present (Choice) loop |
| |
| -- If we have an others choice, fill in the missing elements |
| -- subject to the limit established by Max_Others_Replicate. |
| |
| if Nkind (Choice) = N_Others_Choice then |
| Rep_Count := 0; |
| |
| -- If the expression involves a construct that generates |
| -- a loop, we must generate individual assignments and |
| -- no flattening is possible. |
| |
| if Nkind (Expr) = N_Quantified_Expression then |
| return False; |
| end if; |
| |
| for J in Vals'Range loop |
| if No (Vals (J)) then |
| Vals (J) := New_Copy_Tree (Expr); |
| Rep_Count := Rep_Count + 1; |
| |
| -- Check for maximum others replication. Note that |
| -- we skip this test if either of the restrictions |
| -- No_Implicit_Loops or No_Elaboration_Code is |
| -- active, if this is a preelaborable unit or |
| -- a predefined unit, or if the unit must be |
| -- placed in data memory. This also ensures that |
| -- predefined units get the same level of constant |
| -- folding in Ada 95 and Ada 2005, where their |
| -- categorization has changed. |
| |
| declare |
| P : constant Entity_Id := |
| Cunit_Entity (Current_Sem_Unit); |
| |
| begin |
| -- Check if duplication is always OK and, if so, |
| -- continue processing. |
| |
| if Restriction_Active (No_Implicit_Loops) then |
| null; |
| |
| -- If duplication is not always OK, continue |
| -- only if either the element is static or is |
| -- an aggregate (we already know it is OK). |
| |
| elsif not Is_Static_Element (Elmt, Dims) |
| and then Nkind (Expr) /= N_Aggregate |
| then |
| return False; |
| |
| -- Check if duplication is OK for elaboration |
| -- purposes and, if so, continue processing. |
| |
| elsif Restriction_Active (No_Elaboration_Code) |
| or else |
| (Ekind (Current_Scope) = E_Package |
| and then |
| Static_Elaboration_Desired (Current_Scope)) |
| or else Is_Preelaborated (P) |
| or else (Ekind (P) = E_Package_Body |
| and then |
| Is_Preelaborated (Spec_Entity (P))) |
| or else |
| Is_Predefined_Unit (Get_Source_Unit (P)) |
| then |
| null; |
| |
| -- Otherwise, check that the replication count |
| -- is not too high. |
| |
| elsif Rep_Count > Max_Others_Replicate then |
| return False; |
| end if; |
| end; |
| end if; |
| end loop; |
| |
| if Rep_Count = 0 |
| and then Warn_On_Redundant_Constructs |
| then |
| Error_Msg_N ("there are no others?r?", Elmt); |
| end if; |
| |
| exit Component_Loop; |
| |
| -- Case of a subtype mark, identifier or expanded name |
| |
| elsif Is_Entity_Name (Choice) |
| and then Is_Type (Entity (Choice)) |
| then |
| Lo := Type_Low_Bound (Etype (Choice)); |
| Hi := Type_High_Bound (Etype (Choice)); |
| |
| -- Case of subtype indication |
| |
| elsif Nkind (Choice) = N_Subtype_Indication then |
| Lo := Low_Bound (Range_Expression (Constraint (Choice))); |
| Hi := High_Bound (Range_Expression (Constraint (Choice))); |
| |
| -- Case of a range |
| |
| elsif Nkind (Choice) = N_Range then |
| Lo := Low_Bound (Choice); |
| Hi := High_Bound (Choice); |
| |
| -- Normal subexpression case |
| |
| else pragma Assert (Nkind (Choice) in N_Subexpr); |
| if not Compile_Time_Known_Value (Choice) then |
| return False; |
| |
| else |
| Choice_Index := UI_To_Int (Expr_Value (Choice)); |
| |
| if Choice_Index in Vals'Range then |
| Vals (Choice_Index) := New_Copy_Tree (Expr); |
| goto Continue; |
| |
| -- Choice is statically out-of-range, will be |
| -- rewritten to raise Constraint_Error. |
| |
| else |
| return False; |
| end if; |
| end if; |
| end if; |
| |
| -- Range cases merge with Lo,Hi set |
| |
| if not Compile_Time_Known_Value (Lo) |
| or else |
| not Compile_Time_Known_Value (Hi) |
| then |
| return False; |
| |
| else |
| for J in UI_To_Int (Expr_Value (Lo)) .. |
| UI_To_Int (Expr_Value (Hi)) |
| loop |
| Vals (J) := New_Copy_Tree (Expr); |
| end loop; |
| end if; |
| |
| <<Continue>> |
| Next (Choice); |
| end loop Choice_Loop; |
| |
| Next (Elmt); |
| end loop Component_Loop; |
| |
| -- If we get here the conversion is possible |
| |
| Vlist := New_List; |
| for J in Vals'Range loop |
| Append (Vals (J), Vlist); |
| end loop; |
| |
| Rewrite (N, Make_Aggregate (Loc, Expressions => Vlist)); |
| Set_Aggregate_Bounds (N, Aggregate_Bounds (Original_Node (N))); |
| return True; |
| end; |
| end Flatten; |
| |
| ------------- |
| -- Is_Flat -- |
| ------------- |
| |
| function Is_Flat (N : Node_Id; Dims : Nat) return Boolean is |
| Elmt : Node_Id; |
| |
| begin |
| if Dims = 0 then |
| return True; |
| |
| elsif Nkind (N) = N_Aggregate then |
| if Present (Component_Associations (N)) then |
| return False; |
| |
| else |
| Elmt := First (Expressions (N)); |
| while Present (Elmt) loop |
| if not Is_Flat (Elmt, Dims - 1) then |
| return False; |
| end if; |
| |
| Next (Elmt); |
| end loop; |
| |
| return True; |
| end if; |
| else |
| return True; |
| end if; |
| end Is_Flat; |
| |
| ------------------------- |
| -- Is_Static_Element -- |
| ------------------------- |
| |
| function Is_Static_Element (N : Node_Id; Dims : Nat) return Boolean is |
| Expr : constant Node_Id := Expression (N); |
| |
| begin |
| -- In most cases the interesting expressions are unambiguously static |
| |
| if Compile_Time_Known_Value (Expr) then |
| return True; |
| |
| elsif Nkind (N) = N_Iterated_Component_Association then |
| return False; |
| |
| elsif Nkind (Expr) = N_Aggregate |
| and then Compile_Time_Known_Aggregate (Expr) |
| and then not Expansion_Delayed (Expr) |
| then |
| return True; |
| |
| -- However, one may write static expressions that are syntactically |
| -- ambiguous, so preanalyze the expression before checking it again, |
| -- but only at the innermost level for a multidimensional array. |
| |
| elsif Dims = 1 then |
| Preanalyze_And_Resolve (Expr, Component_Type (Typ)); |
| return Compile_Time_Known_Value (Expr); |
| |
| else |
| return False; |
| end if; |
| end Is_Static_Element; |
| |
| -- Start of processing for Convert_To_Positional |
| |
| begin |
| -- Only convert to positional when generating C in case of an |
| -- object declaration, this is the only case where aggregates are |
| -- supported in C. |
| |
| if Modify_Tree_For_C and then not Is_CCG_Supported_Aggregate (N) then |
| return; |
| end if; |
| |
| -- Ada 2005 (AI-287): Do not convert in case of default initialized |
| -- components because in this case will need to call the corresponding |
| -- IP procedure. |
| |
| if Has_Default_Init_Comps (N) then |
| return; |
| end if; |
| |
| -- A subaggregate may have been flattened but is not known to be |
| -- Compile_Time_Known. Set that flag in cases that cannot require |
| -- elaboration code, so that the aggregate can be used as the |
| -- initial value of a thread-local variable. |
| |
| if Is_Flat (N, Dims) then |
| if Static_Array_Aggregate (N) then |
| Set_Compile_Time_Known_Aggregate (N); |
| end if; |
| |
| return; |
| end if; |
| |
| if Is_Bit_Packed_Array (Typ) and then not Handle_Bit_Packed then |
| return; |
| end if; |
| |
| -- Do not convert to positional if controlled components are involved |
| -- since these require special processing |
| |
| if Has_Controlled_Component (Typ) then |
| return; |
| end if; |
| |
| Check_Static_Components; |
| |
| -- If the size is known, or all the components are static, try to |
| -- build a fully positional aggregate. |
| |
| -- The size of the type may not be known for an aggregate with |
| -- discriminated array components, but if the components are static |
| -- it is still possible to verify statically that the length is |
| -- compatible with the upper bound of the type, and therefore it is |
| -- worth flattening such aggregates as well. |
| |
| if Aggr_Size_OK (N) |
| and then |
| Flatten (N, Dims, First_Index (Typ), First_Index (Base_Type (Typ))) |
| then |
| if Static_Components then |
| Set_Compile_Time_Known_Aggregate (N); |
| Set_Expansion_Delayed (N, False); |
| end if; |
| |
| Analyze_And_Resolve (N, Typ); |
| end if; |
| |
| -- If Static_Elaboration_Desired has been specified, diagnose aggregates |
| -- that will still require initialization code. |
| |
| if (Ekind (Current_Scope) = E_Package |
| and then Static_Elaboration_Desired (Current_Scope)) |
| and then Nkind (Parent (N)) = N_Object_Declaration |
| then |
| declare |
| Expr : Node_Id; |
| |
| begin |
| if Nkind (N) = N_Aggregate and then Present (Expressions (N)) then |
| Expr := First (Expressions (N)); |
| while Present (Expr) loop |
| if not Compile_Time_Known_Value (Expr) then |
| Error_Msg_N |
| ("non-static object requires elaboration code??", N); |
| exit; |
| end if; |
| |
| Next (Expr); |
| end loop; |
| |
| if Present (Component_Associations (N)) then |
| Error_Msg_N ("object requires elaboration code??", N); |
| end if; |
| end if; |
| end; |
| end if; |
| end Convert_To_Positional; |
| |
| ---------------------------- |
| -- Expand_Array_Aggregate -- |
| ---------------------------- |
| |
| -- Array aggregate expansion proceeds as follows: |
| |
| -- 1. If requested we generate code to perform all the array aggregate |
| -- bound checks, specifically |
| |
| -- (a) Check that the index range defined by aggregate bounds is |
| -- compatible with corresponding index subtype. |
| |
| -- (b) If an others choice is present check that no aggregate |
| -- index is outside the bounds of the index constraint. |
| |
| -- (c) For multidimensional arrays make sure that all subaggregates |
| -- corresponding to the same dimension have the same bounds. |
| |
| -- 2. Check for packed array aggregate which can be converted to a |
| -- constant so that the aggregate disappears completely. |
| |
| -- 3. Check case of nested aggregate. Generally nested aggregates are |
| -- handled during the processing of the parent aggregate. |
| |
| -- 4. Check if the aggregate can be statically processed. If this is the |
| -- case pass it as is to Gigi. Note that a necessary condition for |
| -- static processing is that the aggregate be fully positional. |
| |
| -- 5. If in-place aggregate expansion is possible (i.e. no need to create |
| -- a temporary) then mark the aggregate as such and return. Otherwise |
| -- create a new temporary and generate the appropriate initialization |
| -- code. |
| |
| procedure Expand_Array_Aggregate (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| |
| Typ : constant Entity_Id := Etype (N); |
| Ctyp : constant Entity_Id := Component_Type (Typ); |
| -- Typ is the correct constrained array subtype of the aggregate |
| -- Ctyp is the corresponding component type. |
| |
| Aggr_Dimension : constant Pos := Number_Dimensions (Typ); |
| -- Number of aggregate index dimensions |
| |
| Aggr_Low : array (1 .. Aggr_Dimension) of Node_Id; |
| Aggr_High : array (1 .. Aggr_Dimension) of Node_Id; |
| -- Low and High bounds of the constraint for each aggregate index |
| |
| Aggr_Index_Typ : array (1 .. Aggr_Dimension) of Entity_Id; |
| -- The type of each index |
| |
| In_Place_Assign_OK_For_Declaration : Boolean := False; |
| -- True if we are to generate an in-place assignment for a declaration |
| |
| Maybe_In_Place_OK : Boolean; |
| -- If the type is neither controlled nor packed and the aggregate |
| -- is the expression in an assignment, assignment in place may be |
| -- possible, provided other conditions are met on the LHS. |
| |
| Others_Present : array (1 .. Aggr_Dimension) of Boolean := |
| (others => False); |
| -- If Others_Present (J) is True, then there is an others choice in one |
| -- of the subaggregates of N at dimension J. |
| |
| procedure Build_Constrained_Type (Positional : Boolean); |
| -- If the subtype is not static or unconstrained, build a constrained |
| -- type using the computable sizes of the aggregate and its sub- |
| -- aggregates. |
| |
| procedure Check_Bounds (Aggr_Bounds_Node, Index_Bounds_Node : Node_Id); |
| -- Checks that the bounds of Aggr_Bounds are within the bounds defined |
| -- by Index_Bounds. |
| |
| procedure Check_Same_Aggr_Bounds (Sub_Aggr : Node_Id; Dim : Pos); |
| -- Checks that in a multidimensional array aggregate all subaggregates |
| -- corresponding to the same dimension have the same bounds. Sub_Aggr is |
| -- an array subaggregate. Dim is the dimension corresponding to the |
| -- subaggregate. |
| |
| procedure Compute_Others_Present (Sub_Aggr : Node_Id; Dim : Pos); |
| -- Computes the values of array Others_Present. Sub_Aggr is the array |
| -- subaggregate we start the computation from. Dim is the dimension |
| -- corresponding to the subaggregate. |
| |
| procedure Others_Check (Sub_Aggr : Node_Id; Dim : Pos); |
| -- Checks that if an others choice is present in any subaggregate, no |
| -- aggregate index is outside the bounds of the index constraint. |
| -- Sub_Aggr is an array subaggregate. Dim is the dimension corresponding |
| -- to the subaggregate. |
| |
| function Safe_Left_Hand_Side (N : Node_Id) return Boolean; |
| -- In addition to Maybe_In_Place_OK, in order for an aggregate to be |
| -- built directly into the target of the assignment it must be free |
| -- of side effects. N is the LHS of an assignment. |
| |
| procedure Two_Pass_Aggregate_Expansion (N : Node_Id); |
| -- If the aggregate consists only of iterated associations then the |
| -- aggregate is constructed in two steps: |
| -- a) Build an expression to compute the number of elements |
| -- generated by each iterator, and use the expression to allocate |
| -- the destination aggregate. |
| -- b) Generate the loops corresponding to each iterator to insert |
| -- the elements in their proper positions. |
| |
| ---------------------------- |
| -- Build_Constrained_Type -- |
| ---------------------------- |
| |
| procedure Build_Constrained_Type (Positional : Boolean) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Agg_Type : constant Entity_Id := Make_Temporary (Loc, 'A'); |
| Comp : Node_Id; |
| Decl : Node_Id; |
| Typ : constant Entity_Id := Etype (N); |
| Indexes : constant List_Id := New_List; |
| Num : Nat; |
| Sub_Agg : Node_Id; |
| |
| begin |
| -- If the aggregate is purely positional, all its subaggregates |
| -- have the same size. We collect the dimensions from the first |
| -- subaggregate at each level. |
| |
| if Positional then |
| Sub_Agg := N; |
| |
| for D in 1 .. Number_Dimensions (Typ) loop |
| Sub_Agg := First (Expressions (Sub_Agg)); |
| |
| Comp := Sub_Agg; |
| Num := 0; |
| while Present (Comp) loop |
| Num := Num + 1; |
| Next (Comp); |
| end loop; |
| |
| Append_To (Indexes, |
| Make_Range (Loc, |
| Low_Bound => Make_Integer_Literal (Loc, 1), |
| High_Bound => Make_Integer_Literal (Loc, Num))); |
| end loop; |
| |
| else |
| -- We know the aggregate type is unconstrained and the aggregate |
| -- is not processable by the back end, therefore not necessarily |
| -- positional. Retrieve each dimension bounds (computed earlier). |
| |
| for D in 1 .. Number_Dimensions (Typ) loop |
| Append_To (Indexes, |
| Make_Range (Loc, |
| Low_Bound => Aggr_Low (D), |
| High_Bound => Aggr_High (D))); |
| end loop; |
| end if; |
| |
| Decl := |
| Make_Full_Type_Declaration (Loc, |
| Defining_Identifier => Agg_Type, |
| Type_Definition => |
| Make_Constrained_Array_Definition (Loc, |
| Discrete_Subtype_Definitions => Indexes, |
| Component_Definition => |
| Make_Component_Definition (Loc, |
| Aliased_Present => False, |
| Subtype_Indication => |
| New_Occurrence_Of (Component_Type (Typ), Loc)))); |
| |
| Insert_Action (N, Decl); |
| Analyze (Decl); |
| Set_Etype (N, Agg_Type); |
| Set_Is_Itype (Agg_Type); |
| Freeze_Itype (Agg_Type, N); |
| end Build_Constrained_Type; |
| |
| ------------------ |
| -- Check_Bounds -- |
| ------------------ |
| |
| procedure Check_Bounds (Aggr_Bounds_Node, Index_Bounds_Node : Node_Id) is |
| Aggr_Bounds : constant Range_Nodes := |
| Get_Index_Bounds (Aggr_Bounds_Node); |
| Ind_Bounds : constant Range_Nodes := |
| Get_Index_Bounds (Index_Bounds_Node); |
| |
| Cond : Node_Id := Empty; |
| |
| begin |
| -- Generate the following test: |
| |
| -- [constraint_error when |
| -- Aggr_Bounds.First <= Aggr_Bounds.Last and then |
| -- (Aggr_Bounds.First < Ind_Bounds.First |
| -- or else Aggr_Bounds.Last > Ind_Bounds.Last)] |
| |
| -- As an optimization try to see if some tests are trivially vacuous |
| -- because we are comparing an expression against itself. |
| |
| if Aggr_Bounds.First = Ind_Bounds.First |
| and then Aggr_Bounds.Last = Ind_Bounds.Last |
| then |
| Cond := Empty; |
| |
| elsif Aggr_Bounds.Last = Ind_Bounds.Last then |
| Cond := |
| Make_Op_Lt (Loc, |
| Left_Opnd => |
| Duplicate_Subexpr_Move_Checks (Aggr_Bounds.First), |
| Right_Opnd => |
| Duplicate_Subexpr_Move_Checks (Ind_Bounds.First)); |
| |
| elsif Aggr_Bounds.First = Ind_Bounds.First then |
| Cond := |
| Make_Op_Gt (Loc, |
| Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Bounds.Last), |
| Right_Opnd => Duplicate_Subexpr_Move_Checks (Ind_Bounds.Last)); |
| |
| else |
| Cond := |
| Make_Or_Else (Loc, |
| Left_Opnd => |
| Make_Op_Lt (Loc, |
| Left_Opnd => |
| Duplicate_Subexpr_Move_Checks (Aggr_Bounds.First), |
| Right_Opnd => |
| Duplicate_Subexpr_Move_Checks (Ind_Bounds.First)), |
| |
| Right_Opnd => |
| Make_Op_Gt (Loc, |
| Left_Opnd => Duplicate_Subexpr (Aggr_Bounds.Last), |
| Right_Opnd => Duplicate_Subexpr (Ind_Bounds.Last))); |
| end if; |
| |
| if Present (Cond) then |
| Cond := |
| Make_And_Then (Loc, |
| Left_Opnd => |
| Make_Op_Le (Loc, |
| Left_Opnd => |
| Duplicate_Subexpr_Move_Checks (Aggr_Bounds.First), |
| Right_Opnd => |
| Duplicate_Subexpr_Move_Checks (Aggr_Bounds.Last)), |
| |
| Right_Opnd => Cond); |
| |
| Set_Analyzed (Left_Opnd (Left_Opnd (Cond)), False); |
| Set_Analyzed (Right_Opnd (Left_Opnd (Cond)), False); |
| Insert_Action (N, |
| Make_Raise_Constraint_Error (Loc, |
| Condition => Cond, |
| Reason => CE_Range_Check_Failed)); |
| end if; |
| end Check_Bounds; |
| |
| ---------------------------- |
| -- Check_Same_Aggr_Bounds -- |
| ---------------------------- |
| |
| procedure Check_Same_Aggr_Bounds (Sub_Aggr : Node_Id; Dim : Pos) is |
| Sub_Bounds : constant Range_Nodes |
| := Get_Index_Bounds (Aggregate_Bounds (Sub_Aggr)); |
| Sub_Lo : Node_Id renames Sub_Bounds.First; |
| Sub_Hi : Node_Id renames Sub_Bounds.Last; |
| -- The bounds of this specific subaggregate |
| |
| Aggr_Lo : constant Node_Id := Aggr_Low (Dim); |
| Aggr_Hi : constant Node_Id := Aggr_High (Dim); |
| -- The bounds of the aggregate for this dimension |
| |
| Ind_Typ : constant Entity_Id := Aggr_Index_Typ (Dim); |
| -- The index type for this dimension.xxx |
| |
| Cond : Node_Id := Empty; |
| Assoc : Node_Id; |
| Expr : Node_Id; |
| |
| begin |
| -- If index checks are on generate the test |
| |
| -- [constraint_error when |
| -- Aggr_Lo /= Sub_Lo or else Aggr_Hi /= Sub_Hi] |
| |
| -- As an optimization try to see if some tests are trivially vacuos |
| -- because we are comparing an expression against itself. Also for |
| -- the first dimension the test is trivially vacuous because there |
| -- is just one aggregate for dimension 1. |
| |
| if Index_Checks_Suppressed (Ind_Typ) then |
| Cond := Empty; |
| |
| elsif Dim = 1 or else (Aggr_Lo = Sub_Lo and then Aggr_Hi = Sub_Hi) |
| then |
| Cond := Empty; |
| |
| elsif Aggr_Hi = Sub_Hi then |
| Cond := |
| Make_Op_Ne (Loc, |
| Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo), |
| Right_Opnd => Duplicate_Subexpr_Move_Checks (Sub_Lo)); |
| |
| elsif Aggr_Lo = Sub_Lo then |
| Cond := |
| Make_Op_Ne (Loc, |
| Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Hi), |
| Right_Opnd => Duplicate_Subexpr_Move_Checks (Sub_Hi)); |
| |
| else |
| Cond := |
| Make_Or_Else (Loc, |
| Left_Opnd => |
| Make_Op_Ne (Loc, |
| Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo), |
| Right_Opnd => Duplicate_Subexpr_Move_Checks (Sub_Lo)), |
| |
| Right_Opnd => |
| Make_Op_Ne (Loc, |
| Left_Opnd => Duplicate_Subexpr (Aggr_Hi), |
| Right_Opnd => Duplicate_Subexpr (Sub_Hi))); |
| end if; |
| |
| if Present (Cond) then |
| Insert_Action (N, |
| Make_Raise_Constraint_Error (Loc, |
| Condition => Cond, |
| Reason => CE_Length_Check_Failed)); |
| end if; |
| |
| -- Now look inside the subaggregate to see if there is more work |
| |
| if Dim < Aggr_Dimension then |
| |
| -- Process positional components |
| |
| if Present (Expressions (Sub_Aggr)) then |
| Expr := First (Expressions (Sub_Aggr)); |
| while Present (Expr) loop |
| Check_Same_Aggr_Bounds (Expr, Dim + 1); |
| Next (Expr); |
| end loop; |
| end if; |
| |
| -- Process component associations |
| |
| if Present (Component_Associations (Sub_Aggr)) then |
| Assoc := First (Component_Associations (Sub_Aggr)); |
| while Present (Assoc) loop |
| Expr := Expression (Assoc); |
| Check_Same_Aggr_Bounds (Expr, Dim + 1); |
| Next (Assoc); |
| end loop; |
| end if; |
| end if; |
| end Check_Same_Aggr_Bounds; |
| |
| ---------------------------- |
| -- Compute_Others_Present -- |
| ---------------------------- |
| |
| procedure Compute_Others_Present (Sub_Aggr : Node_Id; Dim : Pos) is |
| Assoc : Node_Id; |
| Expr : Node_Id; |
| |
| begin |
| if Present (Component_Associations (Sub_Aggr)) then |
| Assoc := Last (Component_Associations (Sub_Aggr)); |
| |
| if Present (Assoc) |
| and then Nkind (First (Choice_List (Assoc))) = N_Others_Choice |
| then |
| Others_Present (Dim) := True; |
| |
| -- An others_clause may be superfluous if previous components |
| -- cover the full given range of a constrained array. In such |
| -- a case an others_clause does not contribute any additional |
| -- components and has not been analyzed. We analyze it now to |
| -- detect type errors in the expression, even though no code |
| -- will be generated for it. |
| |
| if Dim = Aggr_Dimension |
| and then Nkind (Assoc) /= N_Iterated_Component_Association |
| and then not Analyzed (Expression (Assoc)) |
| and then not Box_Present (Assoc) |
| then |
| Preanalyze_And_Resolve (Expression (Assoc), Ctyp); |
| end if; |
| end if; |
| end if; |
| |
| -- Now look inside the subaggregate to see if there is more work |
| |
| if Dim < Aggr_Dimension then |
| |
| -- Process positional components |
| |
| if Present (Expressions (Sub_Aggr)) then |
| Expr := First (Expressions (Sub_Aggr)); |
| while Present (Expr) loop |
| Compute_Others_Present (Expr, Dim + 1); |
| Next (Expr); |
| end loop; |
| end if; |
| |
| -- Process component associations |
| |
| if Present (Component_Associations (Sub_Aggr)) then |
| Assoc := First (Component_Associations (Sub_Aggr)); |
| while Present (Assoc) loop |
| Expr := Expression (Assoc); |
| Compute_Others_Present (Expr, Dim + 1); |
| Next (Assoc); |
| end loop; |
| end if; |
| end if; |
| end Compute_Others_Present; |
| |
| ------------------ |
| -- Others_Check -- |
| ------------------ |
| |
| procedure Others_Check (Sub_Aggr : Node_Id; Dim : Pos) is |
| Aggr_Lo : constant Node_Id := Aggr_Low (Dim); |
| Aggr_Hi : constant Node_Id := Aggr_High (Dim); |
| -- The bounds of the aggregate for this dimension |
| |
| Ind_Typ : constant Entity_Id := Aggr_Index_Typ (Dim); |
| -- The index type for this dimension |
| |
| Need_To_Check : Boolean := False; |
| |
| Choices_Lo : Node_Id := Empty; |
| Choices_Hi : Node_Id := Empty; |
| -- The lowest and highest discrete choices for a named subaggregate |
| |
| Nb_Choices : Int := -1; |
| -- The number of discrete non-others choices in this subaggregate |
| |
| Nb_Elements : Uint := Uint_0; |
| -- The number of elements in a positional aggregate |
| |
| Cond : Node_Id := Empty; |
| |
| Assoc : Node_Id; |
| Choice : Node_Id; |
| Expr : Node_Id; |
| |
| begin |
| -- Check if we have an others choice. If we do make sure that this |
| -- subaggregate contains at least one element in addition to the |
| -- others choice. |
| |
| if Range_Checks_Suppressed (Ind_Typ) then |
| Need_To_Check := False; |
| |
| elsif Present (Expressions (Sub_Aggr)) |
| and then Present (Component_Associations (Sub_Aggr)) |
| then |
| Need_To_Check := |
| not (Is_Empty_List (Expressions (Sub_Aggr)) |
| and then Is_Empty_List |
| (Component_Associations (Sub_Aggr))); |
| |
| elsif Present (Component_Associations (Sub_Aggr)) then |
| Assoc := Last (Component_Associations (Sub_Aggr)); |
| |
| if Nkind (First (Choice_List (Assoc))) /= N_Others_Choice then |
| Need_To_Check := False; |
| |
| else |
| -- Count the number of discrete choices. Start with -1 because |
| -- the others choice does not count. |
| |
| -- Is there some reason we do not use List_Length here ??? |
| |
| Nb_Choices := -1; |
| Assoc := First (Component_Associations (Sub_Aggr)); |
| while Present (Assoc) loop |
| Choice := First (Choice_List (Assoc)); |
| while Present (Choice) loop |
| Nb_Choices := Nb_Choices + 1; |
| Next (Choice); |
| end loop; |
| |
| Next (Assoc); |
| end loop; |
| |
| -- If there is only an others choice nothing to do |
| |
| Need_To_Check := (Nb_Choices > 0); |
| end if; |
| |
| else |
| Need_To_Check := False; |
| end if; |
| |
| -- If we are dealing with a positional subaggregate with an others |
| -- choice then compute the number or positional elements. |
| |
| if Need_To_Check and then Present (Expressions (Sub_Aggr)) then |
| Expr := First (Expressions (Sub_Aggr)); |
| Nb_Elements := Uint_0; |
| while Present (Expr) loop |
| Nb_Elements := Nb_Elements + 1; |
| Next (Expr); |
| end loop; |
| |
| -- If the aggregate contains discrete choices and an others choice |
| -- compute the smallest and largest discrete choice values. |
| |
| elsif Need_To_Check then |
| Compute_Choices_Lo_And_Choices_Hi : declare |
| |
| Table : Case_Table_Type (1 .. Nb_Choices); |
| -- Used to sort all the different choice values |
| |
| J : Pos := 1; |
| |
| begin |
| Assoc := First (Component_Associations (Sub_Aggr)); |
| while Present (Assoc) loop |
| Choice := First (Choice_List (Assoc)); |
| while Present (Choice) loop |
| if Nkind (Choice) = N_Others_Choice then |
| exit; |
| end if; |
| |
| declare |
| Bounds : constant Range_Nodes := |
| Get_Index_Bounds (Choice); |
| begin |
| Table (J).Choice_Lo := Bounds.First; |
| Table (J).Choice_Hi := Bounds.Last; |
| end; |
| |
| J := J + 1; |
| Next (Choice); |
| end loop; |
| |
| Next (Assoc); |
| end loop; |
| |
| -- Sort the discrete choices |
| |
| Sort_Case_Table (Table); |
| |
| Choices_Lo := Table (1).Choice_Lo; |
| Choices_Hi := Table (Nb_Choices).Choice_Hi; |
| end Compute_Choices_Lo_And_Choices_Hi; |
| end if; |
| |
| -- If no others choice in this subaggregate, or the aggregate |
| -- comprises only an others choice, nothing to do. |
| |
| if not Need_To_Check then |
| Cond := Empty; |
| |
| -- If we are dealing with an aggregate containing an others choice |
| -- and positional components, we generate the following test: |
| |
| -- if Ind_Typ'Pos (Aggr_Lo) + (Nb_Elements - 1) > |
| -- Ind_Typ'Pos (Aggr_Hi) |
| -- then |
| -- raise Constraint_Error; |
| -- end if; |
| |
| -- in the general case, but the following simpler test: |
| |
| -- [constraint_error when |
| -- Aggr_Lo + (Nb_Elements - 1) > Aggr_Hi]; |
| |
| -- instead if the index type is a signed integer. |
| |
| elsif Nb_Elements > Uint_0 then |
| if Nb_Elements = Uint_1 then |
| Cond := |
| Make_Op_Gt (Loc, |
| Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo), |
| Right_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Hi)); |
| |
| elsif Is_Signed_Integer_Type (Ind_Typ) then |
| Cond := |
| Make_Op_Gt (Loc, |
| Left_Opnd => |
| Make_Op_Add (Loc, |
| Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo), |
| Right_Opnd => |
| Make_Integer_Literal (Loc, Nb_Elements - 1)), |
| Right_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Hi)); |
| |
| else |
| Cond := |
| Make_Op_Gt (Loc, |
| Left_Opnd => |
| Make_Op_Add (Loc, |
| Left_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Ind_Typ, Loc), |
| Attribute_Name => Name_Pos, |
| Expressions => |
| New_List |
| (Duplicate_Subexpr_Move_Checks (Aggr_Lo))), |
| Right_Opnd => Make_Integer_Literal (Loc, Nb_Elements - 1)), |
| |
| Right_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Ind_Typ, Loc), |
| Attribute_Name => Name_Pos, |
| Expressions => New_List ( |
| Duplicate_Subexpr_Move_Checks (Aggr_Hi)))); |
| end if; |
| |
| -- If we are dealing with an aggregate containing an others choice |
| -- and discrete choices we generate the following test: |
| |
| -- [constraint_error when |
| -- Choices_Lo < Aggr_Lo or else Choices_Hi > Aggr_Hi]; |
| |
| else |
| Cond := |
| Make_Or_Else (Loc, |
| Left_Opnd => |
| Make_Op_Lt (Loc, |
| Left_Opnd => Duplicate_Subexpr_Move_Checks (Choices_Lo), |
| Right_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo)), |
| |
| Right_Opnd => |
| Make_Op_Gt (Loc, |
| Left_Opnd => Duplicate_Subexpr (Choices_Hi), |
| Right_Opnd => Duplicate_Subexpr (Aggr_Hi))); |
| end if; |
| |
| if Present (Cond) then |
| Insert_Action (N, |
| Make_Raise_Constraint_Error (Loc, |
| Condition => Cond, |
| Reason => CE_Length_Check_Failed)); |
| -- Questionable reason code, shouldn't that be a |
| -- CE_Range_Check_Failed ??? |
| end if; |
| |
| -- Now look inside the subaggregate to see if there is more work |
| |
| if Dim < Aggr_Dimension then |
| |
| -- Process positional components |
| |
| if Present (Expressions (Sub_Aggr)) then |
| Expr := First (Expressions (Sub_Aggr)); |
| while Present (Expr) loop |
| Others_Check (Expr, Dim + 1); |
| Next (Expr); |
| end loop; |
| end if; |
| |
| -- Process component associations |
| |
| if Present (Component_Associations (Sub_Aggr)) then |
| Assoc := First (Component_Associations (Sub_Aggr)); |
| while Present (Assoc) loop |
| Expr := Expression (Assoc); |
| Others_Check (Expr, Dim + 1); |
| Next (Assoc); |
| end loop; |
| end if; |
| end if; |
| end Others_Check; |
| |
| ------------------------- |
| -- Safe_Left_Hand_Side -- |
| ------------------------- |
| |
| function Safe_Left_Hand_Side (N : Node_Id) return Boolean is |
| function Is_Safe_Index (Indx : Node_Id) return Boolean; |
| -- If the left-hand side includes an indexed component, check that |
| -- the indexes are free of side effects. |
| |
| ------------------- |
| -- Is_Safe_Index -- |
| ------------------- |
| |
| function Is_Safe_Index (Indx : Node_Id) return Boolean is |
| begin |
| if Is_Entity_Name (Indx) then |
| return True; |
| |
| elsif Nkind (Indx) = N_Integer_Literal then |
| return True; |
| |
| elsif Nkind (Indx) = N_Function_Call |
| and then Is_Entity_Name (Name (Indx)) |
| and then Has_Pragma_Pure_Function (Entity (Name (Indx))) |
| then |
| return True; |
| |
| elsif Nkind (Indx) = N_Type_Conversion |
| and then Is_Safe_Index (Expression (Indx)) |
| then |
| return True; |
| |
| else |
| return False; |
| end if; |
| end Is_Safe_Index; |
| |
| -- Start of processing for Safe_Left_Hand_Side |
| |
| begin |
| if Is_Entity_Name (N) then |
| return True; |
| |
| elsif Nkind (N) in N_Explicit_Dereference | N_Selected_Component |
| and then Safe_Left_Hand_Side (Prefix (N)) |
| then |
| return True; |
| |
| elsif Nkind (N) = N_Indexed_Component |
| and then Safe_Left_Hand_Side (Prefix (N)) |
| and then Is_Safe_Index (First (Expressions (N))) |
| then |
| return True; |
| |
| elsif Nkind (N) = N_Unchecked_Type_Conversion then |
| return Safe_Left_Hand_Side (Expression (N)); |
| |
| else |
| return False; |
| end if; |
| end Safe_Left_Hand_Side; |
| |
| ---------------------------------- |
| -- Two_Pass_Aggregate_Expansion -- |
| ---------------------------------- |
| |
| procedure Two_Pass_Aggregate_Expansion (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Comp_Type : constant Entity_Id := Etype (N); |
| Index_Id : constant Entity_Id := Make_Temporary (Loc, 'I', N); |
| Index_Type : constant Entity_Id := Etype (First_Index (Etype (N))); |
| Size_Id : constant Entity_Id := Make_Temporary (Loc, 'I', N); |
| TmpE : constant Entity_Id := Make_Temporary (Loc, 'A', N); |
| |
| Assoc : Node_Id := First (Component_Associations (N)); |
| Incr : Node_Id; |
| Iter : Node_Id; |
| New_Comp : Node_Id; |
| One_Loop : Node_Id; |
| |
| Size_Expr_Code : List_Id; |
| Insertion_Code : List_Id := New_List; |
| |
| begin |
| Size_Expr_Code := New_List ( |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Size_Id, |
| Object_Definition => New_Occurrence_Of (Standard_Integer, Loc), |
| Expression => Make_Integer_Literal (Loc, 0))); |
| |
| -- First pass: execute the iterators to count the number of elements |
| -- that will be generated. |
| |
| while Present (Assoc) loop |
| Iter := Iterator_Specification (Assoc); |
| Incr := Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of (Size_Id, Loc), |
| Expression => |
| Make_Op_Add (Loc, |
| Left_Opnd => New_Occurrence_Of (Size_Id, Loc), |
| Right_Opnd => Make_Integer_Literal (Loc, 1))); |
| |
| One_Loop := Make_Loop_Statement (Loc, |
| Iteration_Scheme => |
| Make_Iteration_Scheme (Loc, |
| Iterator_Specification => New_Copy_Tree (Iter)), |
| Statements => New_List (Incr)); |
| |
| Append (One_Loop, Size_Expr_Code); |
| Next (Assoc); |
| end loop; |
| |
| Insert_Actions (N, Size_Expr_Code); |
| |
| -- Build a constrained subtype with the calculated length |
| -- and declare the proper bounded aggregate object. |
| -- The index type is some discrete type, so the bounds of the |
| -- constructed array are computed as T'Val (T'Pos (ineger bound)); |
| |
| declare |
| Pos_Lo : constant Node_Id := |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Index_Type, Loc), |
| Attribute_Name => Name_Pos, |
| Expressions => New_List ( |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Index_Type, Loc), |
| Attribute_Name => Name_First))); |
| |
| Aggr_Lo : constant Node_Id := |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Index_Type, Loc), |
| Attribute_Name => Name_Val, |
| Expressions => New_List (New_Copy_Tree (Pos_Lo))); |
| |
| -- Hi = Index_type'Pos (Lo + Size -1). |
| |
| Pos_Hi : constant Node_Id := |
| Make_Op_Add (Loc, |
| Left_Opnd => New_Copy_Tree (Pos_Lo), |
| Right_Opnd => |
| Make_Op_Subtract (Loc, |
| Left_Opnd => New_Occurrence_Of (Size_Id, Loc), |
| Right_Opnd => Make_Integer_Literal (Loc, 1))); |
| |
| -- Corresponding index value |
| |
| Aggr_Hi : constant Node_Id := |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Index_Type, Loc), |
| Attribute_Name => Name_Val, |
| Expressions => New_List (New_Copy_Tree (Pos_Hi))); |
| |
| 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 (Make_Range (Loc, Aggr_Lo, Aggr_Hi))))); |
| |
| -- Create a temporary array of the above subtype which |
| -- will be used to capture the aggregate assignments. |
| |
| TmpD : constant Node_Id := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => TmpE, |
| Object_Definition => New_Occurrence_Of (SubE, Loc)); |
| begin |
| Insert_Actions (N, New_List (SubD, TmpD)); |
| end; |
| |
| -- Second pass: use the iterators to generate the elements of the |
| -- aggregate. Insertion index starts at Index_Type'First. We |
| -- assume that the second evaluation of each iterator generates |
| -- the same number of elements as the first pass, and consider |
| -- that the execution is erroneous (even if the RM does not state |
| -- this explicitly) if the number of elements generated differs |
| -- between first and second pass. |
| |
| Assoc := First (Component_Associations (N)); |
| |
| -- Initialize insertion position to first array component. |
| |
| Insertion_Code := New_List ( |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Index_Id, |
| Object_Definition => |
| New_Occurrence_Of (Index_Type, Loc), |
| Expression => |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Index_Type, Loc), |
| Attribute_Name => Name_First))); |
| |
| while Present (Assoc) loop |
| Iter := Iterator_Specification (Assoc); |
| New_Comp := Make_Assignment_Statement (Loc, |
| Name => |
| Make_Indexed_Component (Loc, |
| Prefix => New_Occurrence_Of (TmpE, Loc), |
| Expressions => |
| New_List (New_Occurrence_Of (Index_Id, Loc))), |
| Expression => New_Copy_Tree (Expression (Assoc))); |
| |
| -- Advance index position for insertion. |
| |
| Incr := Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of (Index_Id, Loc), |
| Expression => |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of (Index_Type, Loc), |
| Attribute_Name => Name_Succ, |
| Expressions => |
| New_List (New_Occurrence_Of (Index_Id, Loc)))); |
| |
| -- Add guard to skip last increment when upper bound is reached. |
| |
| Incr := Make_If_Statement (Loc, |
| Condition => |
| Make_Op_Ne (Loc, |
| Left_Opnd => New_Occurrence_Of (Index_Id, Loc), |
| Right_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Index_Type, Loc), |
| Attribute_Name => Name_Last)), |
| Then_Statements => New_List (Incr)); |
| |
| One_Loop := Make_Loop_Statement (Loc, |
| Iteration_Scheme => |
| Make_Iteration_Scheme (Loc, |
| Iterator_Specification => Copy_Separate_Tree (Iter)), |
| Statements => New_List (New_Comp, Incr)); |
| |
| Append (One_Loop, Insertion_Code); |
| Next (Assoc); |
| end loop; |
| |
| Insert_Actions (N, Insertion_Code); |
| |
| -- Depending on context this may not work for build-in-place |
| -- arrays ??? |
| |
| Rewrite (N, New_Occurrence_Of (TmpE, Loc)); |
| |
| end Two_Pass_Aggregate_Expansion; |
| |
| -- Local variables |
| |
| Tmp : Entity_Id; |
| -- Holds the temporary aggregate value |
| |
| Tmp_Decl : Node_Id; |
| -- Holds the declaration of Tmp |
| |
| Aggr_Code : List_Id; |
| Parent_Node : Node_Id; |
| Parent_Kind : Node_Kind; |
| |
| -- Start of processing for Expand_Array_Aggregate |
| |
| begin |
| -- Do not touch the special aggregates of attributes used for Asm calls |
| |
| if Is_RTE (Ctyp, RE_Asm_Input_Operand) |
| or else Is_RTE (Ctyp, RE_Asm_Output_Operand) |
| then |
| return; |
| |
| -- Do not expand an aggregate for an array type which contains tasks if |
| -- the aggregate is associated with an unexpanded return statement of a |
| -- build-in-place function. The aggregate is expanded when the related |
| -- return statement (rewritten into an extended return) is processed. |
| -- This delay ensures that any temporaries and initialization code |
| -- generated for the aggregate appear in the proper return block and |
| -- use the correct _chain and _master. |
| |
| elsif Has_Task (Base_Type (Etype (N))) |
| and then Nkind (Parent (N)) = N_Simple_Return_Statement |
| and then Is_Build_In_Place_Function |
| (Return_Applies_To (Return_Statement_Entity (Parent (N)))) |
| then |
| return; |
| |
| elsif Present (Component_Associations (N)) |
| and then Nkind (First (Component_Associations (N))) = |
| N_Iterated_Component_Association |
| and then |
| Present (Iterator_Specification (First (Component_Associations (N)))) |
| then |
| Two_Pass_Aggregate_Expansion (N); |
| return; |
| |
| -- Do not attempt expansion if error already detected. We may reach this |
| -- point in spite of previous errors when compiling with -gnatq, to |
| -- force all possible errors (this is the usual ACATS mode). |
| |
| elsif Error_Posted (N) then |
| return; |
| end if; |
| |
| -- If the semantic analyzer has determined that aggregate N will raise |
| -- Constraint_Error at run time, then the aggregate node has been |
| -- replaced with an N_Raise_Constraint_Error node and we should |
| -- never get here. |
| |
| pragma Assert (not Raises_Constraint_Error (N)); |
| |
| -- STEP 1a |
| |
| -- Check that the index range defined by aggregate bounds is |
| -- compatible with corresponding index subtype. |
| |
| Index_Compatibility_Check : declare |
| Aggr_Index_Range : Node_Id := First_Index (Typ); |
| -- The current aggregate index range |
| |
| Index_Constraint : Node_Id := First_Index (Etype (Typ)); |
| -- The corresponding index constraint against which we have to |
| -- check the above aggregate index range. |
| |
| begin |
| Compute_Others_Present (N, 1); |
| |
| for J in 1 .. Aggr_Dimension loop |
| -- There is no need to emit a check if an others choice is present |
| -- for this array aggregate dimension since in this case one of |
| -- N's subaggregates has taken its bounds from the context and |
| -- these bounds must have been checked already. In addition all |
| -- subaggregates corresponding to the same dimension must all have |
| -- the same bounds (checked in (c) below). |
| |
| if not Range_Checks_Suppressed (Etype (Index_Constraint)) |
| and then not Others_Present (J) |
| then |
| -- We don't use Checks.Apply_Range_Check here because it emits |
| -- a spurious check. Namely it checks that the range defined by |
| -- the aggregate bounds is nonempty. But we know this already |
| -- if we get here. |
| |
| Check_Bounds (Aggr_Index_Range, Index_Constraint); |
| end if; |
| |
| -- Save the low and high bounds of the aggregate index as well as |
| -- the index type for later use in checks (b) and (c) below. |
| |
| Get_Index_Bounds |
| (Aggr_Index_Range, L => Aggr_Low (J), H => Aggr_High (J)); |
| |
| Aggr_Index_Typ (J) := Etype (Index_Constraint); |
| |
| Next_Index (Aggr_Index_Range); |
| Next_Index (Index_Constraint); |
| end loop; |
| end Index_Compatibility_Check; |
| |
| -- STEP 1b |
| |
| -- If an others choice is present check that no aggregate index is |
| -- outside the bounds of the index constraint. |
| |
| Others_Check (N, 1); |
| |
| -- STEP 1c |
| |
| -- For multidimensional arrays make sure that all subaggregates |
| -- corresponding to the same dimension have the same bounds. |
| |
| if Aggr_Dimension > 1 then |
| Check_Same_Aggr_Bounds (N, 1); |
| end if; |
| |
| -- STEP 1d |
| |
| -- If we have a default component value, or simple initialization is |
| -- required for the component type, then we replace <> in component |
| -- associations by the required default value. |
| |
| declare |
| Default_Val : Node_Id; |
| Assoc : Node_Id; |
| |
| begin |
| if (Present (Default_Aspect_Component_Value (Typ)) |
| or else Needs_Simple_Initialization (Ctyp)) |
| and then Present (Component_Associations (N)) |
| then |
| Assoc := First (Component_Associations (N)); |
| while Present (Assoc) loop |
| if Nkind (Assoc) = N_Component_Association |
| and then Box_Present (Assoc) |
| then |
| Set_Box_Present (Assoc, False); |
| |
| if Present (Default_Aspect_Component_Value (Typ)) then |
| Default_Val := Default_Aspect_Component_Value (Typ); |
| else |
| Default_Val := Get_Simple_Init_Val (Ctyp, N); |
| end if; |
| |
| Set_Expression (Assoc, New_Copy_Tree (Default_Val)); |
| Analyze_And_Resolve (Expression (Assoc), Ctyp); |
| end if; |
| |
| Next (Assoc); |
| end loop; |
| end if; |
| end; |
| |
| -- STEP 2 |
| |
| -- Here we test for is packed array aggregate that we can handle at |
| -- compile time. If so, return with transformation done. Note that we do |
| -- this even if the aggregate is nested, because once we have done this |
| -- processing, there is no more nested aggregate. |
| |
| if Packed_Array_Aggregate_Handled (N) then |
| return; |
| end if; |
| |
| -- At this point we try to convert to positional form |
| |
| Convert_To_Positional (N); |
| |
| -- If the result is no longer an aggregate (e.g. it may be a string |
| -- literal, or a temporary which has the needed value), then we are |
| -- done, since there is no longer a nested aggregate. |
| |
| if Nkind (N) /= N_Aggregate then |
| return; |
| |
| -- We are also done if the result is an analyzed aggregate, indicating |
| -- that Convert_To_Positional succeeded and reanalyzed the rewritten |
| -- aggregate. |
| |
| elsif Analyzed (N) and then Is_Rewrite_Substitution (N) then |
| return; |
| end if; |
| |
| -- If all aggregate components are compile-time known and the aggregate |
| -- has been flattened, nothing left to do. The same occurs if the |
| -- aggregate is used to initialize the components of a statically |
| -- allocated dispatch table. |
| |
| if Compile_Time_Known_Aggregate (N) |
| or else Is_Static_Dispatch_Table_Aggregate (N) |
| then |
| Set_Expansion_Delayed (N, False); |
| return; |
| end if; |
| |
| -- Now see if back end processing is possible |
| |
| if Backend_Processing_Possible (N) then |
| |
| -- If the aggregate is static but the constraints are not, build |
| -- a static subtype for the aggregate, so that Gigi can place it |
| -- in static memory. Perform an unchecked_conversion to the non- |
| -- static type imposed by the context. |
| |
| declare |
| Itype : constant Entity_Id := Etype (N); |
| Index : Node_Id; |
| Needs_Type : Boolean := False; |
| |
| begin |
| Index := First_Index (Itype); |
| while Present (Index) loop |
| if not Is_OK_Static_Subtype (Etype (Index)) then |
| Needs_Type := True; |
| exit; |
| else |
| Next_Index (Index); |
| end if; |
| end loop; |
| |
| if Needs_Type then |
| Build_Constrained_Type (Positional => True); |
| Rewrite (N, Unchecked_Convert_To (Itype, N)); |
| Analyze (N); |
| end if; |
| end; |
| |
| return; |
| end if; |
| |
| -- STEP 3 |
| |
| -- Delay expansion for nested aggregates: it will be taken care of when |
| -- the parent aggregate is expanded. |
| |
| Parent_Node := Parent (N); |
| Parent_Kind := Nkind (Parent_Node); |
| |
| if Parent_Kind = N_Qualified_Expression then |
| Parent_Node := Parent (Parent_Node); |
| Parent_Kind := Nkind (Parent_Node); |
| end if; |
| |
| if Parent_Kind = N_Aggregate |
| or else Parent_Kind = N_Extension_Aggregate |
| or else Parent_Kind = N_Component_Association |
| or else (Parent_Kind = N_Object_Declaration |
| and then Needs_Finalization (Typ)) |
| or else (Parent_Kind = N_Assignment_Statement |
| and then Inside_Init_Proc) |
| then |
| Set_Expansion_Delayed (N, not Static_Array_Aggregate (N)); |
| return; |
| end if; |
| |
| -- STEP 4 |
| |
| -- Check whether in-place aggregate expansion is possible |
| |
| -- For object declarations we build the aggregate in place, unless |
| -- the array is bit-packed. |
| |
| -- For assignments we do the assignment in place if all the component |
| -- associations have compile-time known values, or are default- |
| -- initialized limited components, e.g. tasks. For other cases we |
| -- create a temporary. A full analysis for safety of in-place assignment |
| -- is delicate. |
| |
| -- For allocators we assign to the designated object in place if the |
| -- aggregate meets the same conditions as other in-place assignments. |
| -- In this case the aggregate may not come from source but was created |
| -- for default initialization, e.g. with Initialize_Scalars. |
| |
| if Requires_Transient_Scope (Typ) then |
| Establish_Transient_Scope (N, Manage_Sec_Stack => False); |
| end if; |
| |
| -- An array of limited components is built in place |
| |
| if Is_Limited_Type (Typ) then |
| Maybe_In_Place_OK := True; |
| |
| elsif Has_Default_Init_Comps (N) then |
| Maybe_In_Place_OK := False; |
| |
| elsif Is_Bit_Packed_Array (Typ) |
| or else Has_Controlled_Component (Typ) |
| then |
| Maybe_In_Place_OK := False; |
| |
| elsif Parent_Kind = N_Assignment_Statement then |
| Maybe_In_Place_OK := |
| In_Place_Assign_OK (N, Get_Base_Object (Name (Parent_Node))); |
| |
| elsif Parent_Kind = N_Allocator then |
| Maybe_In_Place_OK := In_Place_Assign_OK (N); |
| |
| else |
| Maybe_In_Place_OK := False; |
| end if; |
| |
| -- If this is an array of tasks, it will be expanded into build-in-place |
| -- assignments. Build an activation chain for the tasks now. |
| |
| if Has_Task (Etype (N)) then |
| Build_Activation_Chain_Entity (N); |
| end if; |
| |
| -- Perform in-place expansion of aggregate in an object declaration. |
| -- Note: actions generated for the aggregate will be captured in an |
| -- expression-with-actions statement so that they can be transferred |
| -- to freeze actions later if there is an address clause for the |
| -- object. (Note: we don't use a block statement because this would |
| -- cause generated freeze nodes to be elaborated in the wrong scope). |
| |
| -- Arrays of limited components must be built in place. The code |
| -- previously excluded controlled components but this is an old |
| -- oversight: the rules in 7.6 (17) are clear. |
| |
| if Comes_From_Source (Parent_Node) |
| and then Parent_Kind = N_Object_Declaration |
| and then Present (Expression (Parent_Node)) |
| and then not |
| Must_Slide (N, Etype (Defining_Identifier (Parent_Node)), Typ) |
| and then not Is_Bit_Packed_Array (Typ) |
| then |
| In_Place_Assign_OK_For_Declaration := True; |
| Tmp := Defining_Identifier (Parent_Node); |
| Set_No_Initialization (Parent_Node); |
| Set_Expression (Parent_Node, Empty); |
| |
| -- Set kind and type of the entity, for use in the analysis |
| -- of the subsequent assignments. If the nominal type is not |
| -- constrained, build a subtype from the known bounds of the |
| -- aggregate. If the declaration has a subtype mark, use it, |
| -- otherwise use the itype of the aggregate. |
| |
| Mutate_Ekind (Tmp, E_Variable); |
| |
| if not Is_Constrained (Typ) then |
| Build_Constrained_Type (Positional => False); |
| |
| elsif Is_Entity_Name (Object_Definition (Parent_Node)) |
| and then Is_Constrained (Entity (Object_Definition (Parent_Node))) |
| then |
| Set_Etype (Tmp, Entity (Object_Definition (Parent_Node))); |
| |
| else |
| Set_Size_Known_At_Compile_Time (Typ, False); |
| Set_Etype (Tmp, Typ); |
| end if; |
| |
| elsif Maybe_In_Place_OK and then Parent_Kind = N_Allocator then |
| Set_Expansion_Delayed (N); |
| return; |
| |
| -- Limited arrays in return statements are expanded when |
| -- enclosing construct is expanded. |
| |
| elsif Maybe_In_Place_OK |
| and then Parent_Kind = N_Simple_Return_Statement |
| then |
| Set_Expansion_Delayed (N); |
| return; |
| |
| -- In the remaining cases the aggregate appears in the RHS of an |
| -- assignment, which may be part of the expansion of an object |
| -- declaration. If the aggregate is an actual in a call, itself |
| -- possibly in a RHS, building it in the target is not possible. |
| |
| elsif Maybe_In_Place_OK |
| and then Nkind (Parent_Node) not in N_Subprogram_Call |
| and then Safe_Left_Hand_Side (Name (Parent_Node)) |
| then |
| Tmp := Name (Parent_Node); |
| |
| if Etype (Tmp) /= Etype (N) then |
| Apply_Length_Check (N, Etype (Tmp)); |
| |
| if Nkind (N) = N_Raise_Constraint_Error then |
| |
| -- Static error, nothing further to expand |
| |
| return; |
| end if; |
| end if; |
| |
| -- If a slice assignment has an aggregate with a single others_choice, |
| -- the assignment can be done in place even if bounds are not static, |
| -- by converting it into a loop over the discrete range of the slice. |
| |
| elsif Maybe_In_Place_OK |
| and then Nkind (Name (Parent_Node)) = N_Slice |
| and then Is_Others_Aggregate (N) |
| then |
| Tmp := Name (Parent_Node); |
| |
| -- Set type of aggregate to be type of lhs in assignment, in order |
| -- to suppress redundant length checks. |
| |
| Set_Etype (N, Etype (Tmp)); |
| |
| -- Step 5 |
| |
| -- In-place aggregate expansion is not possible |
| |
| else |
| Maybe_In_Place_OK := False; |
| Tmp := Make_Temporary (Loc, 'A', N); |
| Tmp_Decl := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Tmp, |
| Object_Definition => New_Occurrence_Of (Typ, Loc)); |
| Set_No_Initialization (Tmp_Decl, True); |
| Set_Warnings_Off (Tmp); |
| |
| -- If we are within a loop, the temporary will be pushed on the |
| -- stack at each iteration. If the aggregate is the expression |
| -- for an allocator, it will be immediately copied to the heap |
| -- and can be reclaimed at once. We create a transient scope |
| -- around the aggregate for this purpose. |
| |
| if Ekind (Current_Scope) = E_Loop |
| and then Parent_Kind = N_Allocator |
| then |
| Establish_Transient_Scope (N, Manage_Sec_Stack => False); |
| end if; |
| |
| Insert_Action (N, Tmp_Decl); |
| end if; |
| |
| -- Construct and insert the aggregate code. We can safely suppress index |
| -- checks because this code is guaranteed not to raise CE on index |
| -- checks. However we should *not* suppress all checks. |
| |
| declare |
| Target : Node_Id; |
| |
| begin |
| if Nkind (Tmp) = N_Defining_Identifier then |
| Target := New_Occurrence_Of (Tmp, Loc); |
| |
| else |
| if Has_Default_Init_Comps (N) |
| and then not Maybe_In_Place_OK |
| then |
| -- Ada 2005 (AI-287): This case has not been analyzed??? |
| |
| raise Program_Error; |
| end if; |
| |
| -- Name in assignment is explicit dereference |
| |
| Target := New_Copy (Tmp); |
| end if; |
| |
| -- If we are to generate an in-place assignment for a declaration or |
| -- an assignment statement, and the assignment can be done directly |
| -- by the back end, then do not expand further. |
| |
| -- ??? We can also do that if in-place expansion is not possible but |
| -- then we could go into an infinite recursion. |
| |
| if (In_Place_Assign_OK_For_Declaration or else Maybe_In_Place_OK) |
| and then not CodePeer_Mode |
| and then not Modify_Tree_For_C |
| and then not Possible_Bit_Aligned_Component (Target) |
| and then not Is_Possibly_Unaligned_Slice (Target) |
| and then Aggr_Assignment_OK_For_Backend (N) |
| then |
| if Maybe_In_Place_OK then |
| return; |
| end if; |
| |
| Aggr_Code := |
| New_List ( |
| Make_Assignment_Statement (Loc, |
| Name => Target, |
| Expression => New_Copy_Tree (N))); |
| |
| else |
| Aggr_Code := |
| Build_Array_Aggr_Code (N, |
| Ctype => Ctyp, |
| Index => First_Index (Typ), |
| Into => Target, |
| Scalar_Comp => Is_Scalar_Type (Ctyp)); |
| end if; |
| |
| -- Save the last assignment statement associated with the aggregate |
| -- when building a controlled object. This reference is utilized by |
| -- the finalization machinery when marking an object as successfully |
| -- initialized. |
| |
| if Needs_Finalization (Typ) |
| and then Is_Entity_Name (Target) |
| and then Present (Entity (Target)) |
| and then Ekind (Entity (Target)) in E_Constant | E_Variable |
| then |
| Set_Last_Aggregate_Assignment (Entity (Target), Last (Aggr_Code)); |
| end if; |
| end; |
| |
| -- If the aggregate is the expression in a declaration, the expanded |
| -- code must be inserted after it. The defining entity might not come |
| -- from source if this is part of an inlined body, but the declaration |
| -- itself will. |
| -- The test below looks very specialized and kludgy??? |
| |
| if Comes_From_Source (Tmp) |
| or else |
| (Nkind (Parent (N)) = N_Object_Declaration |
| and then Comes_From_Source (Parent (N)) |
| and then Tmp = Defining_Entity (Parent (N))) |
| then |
| if Parent_Kind /= N_Object_Declaration or else Is_Frozen (Tmp) then |
| Insert_Actions_After (Parent_Node, Aggr_Code); |
| else |
| declare |
| Comp_Stmt : constant Node_Id := |
| Make_Compound_Statement |
| (Sloc (Parent_Node), Actions => Aggr_Code); |
| begin |
| Insert_Action_After (Parent_Node, Comp_Stmt); |
| Set_Initialization_Statements (Tmp, Comp_Stmt); |
| end; |
| end if; |
| else |
| Insert_Actions (N, Aggr_Code); |
| end if; |
| |
| -- If the aggregate has been assigned in place, remove the original |
| -- assignment. |
| |
| if Parent_Kind = N_Assignment_Statement and then Maybe_In_Place_OK then |
| Rewrite (Parent_Node, Make_Null_Statement (Loc)); |
| |
| -- Or else, if a temporary was created, replace the aggregate with it |
| |
| elsif Parent_Kind /= N_Object_Declaration |
| or else Tmp /= Defining_Identifier (Parent_Node) |
| then |
| Rewrite (N, New_Occurrence_Of (Tmp, Loc)); |
| Analyze_And_Resolve (N, Typ); |
| end if; |
| end Expand_Array_Aggregate; |
| |
| ------------------------ |
| -- Expand_N_Aggregate -- |
| ------------------------ |
| |
| procedure Expand_N_Aggregate (N : Node_Id) is |
| T : constant Entity_Id := Etype (N); |
| begin |
| -- Record aggregate case |
| |
| if Is_Record_Type (T) |
| and then not Is_Private_Type (T) |
| then |
| Expand_Record_Aggregate (N); |
| |
| elsif Has_Aspect (T, Aspect_Aggregate) then |
| Expand_Container_Aggregate (N); |
| |
| -- Array aggregate case |
| |
| else |
| -- A special case, if we have a string subtype with bounds 1 .. N, |
| -- where N is known at compile time, and the aggregate is of the |
| -- form (others => 'x'), with a single choice and no expressions, |
| -- and N is less than 80 (an arbitrary limit for now), then replace |
| -- the aggregate by the equivalent string literal (but do not mark |
| -- it as static since it is not). |
| |
| -- Note: this entire circuit is redundant with respect to code in |
| -- Expand_Array_Aggregate that collapses others choices to positional |
| -- form, but there are two problems with that circuit: |
| |
| -- a) It is limited to very small cases due to ill-understood |
| -- interactions with bootstrapping. That limit is removed by |
| -- use of the No_Implicit_Loops restriction. |
| |
| -- b) It incorrectly ends up with the resulting expressions being |
| -- considered static when they are not. For example, the |
| -- following test should fail: |
| |
| -- pragma Restrictions (No_Implicit_Loops); |
| -- package NonSOthers4 is |
| -- B : constant String (1 .. 6) := (others => 'A'); |
| -- DH : constant String (1 .. 8) := B & "BB"; |
| -- X : Integer; |
| -- pragma Export (C, X, Link_Name => DH); |
| -- end; |
| |
| -- But it succeeds (DH looks static to pragma Export) |
| |
| -- To be sorted out ??? |
| |
| if Present (Component_Associations (N)) then |
| declare |
| CA : constant Node_Id := First (Component_Associations (N)); |
| MX : constant := 80; |
| |
| begin |
| if Present (CA) |
| and then Nkind (First (Choice_List (CA))) = N_Others_Choice |
| and then Nkind (Expression (CA)) = N_Character_Literal |
| and then No (Expressions (N)) |
| then |
| declare |
| X : constant Node_Id := First_Index (T); |
| EC : constant Node_Id := Expression (CA); |
| CV : constant Uint := Char_Literal_Value (EC); |
| CC : constant Char_Code := UI_To_CC (CV); |
| |
| begin |
| if Nkind (X) = N_Range |
| and then Compile_Time_Known_Value (Low_Bound (X)) |
| and then Expr_Value (Low_Bound (X)) = 1 |
| and then Compile_Time_Known_Value (High_Bound (X)) |
| then |
| declare |
| Hi : constant Uint := Expr_Value (High_Bound (X)); |
| |
| begin |
| if Hi <= MX then |
| Start_String; |
| |
| for J in 1 .. UI_To_Int (Hi) loop |
| Store_String_Char (CC); |
| end loop; |
| |
| Rewrite (N, |
| Make_String_Literal (Sloc (N), |
| Strval => End_String)); |
| |
| if In_Character_Range (CC) then |
| null; |
| elsif In_Wide_Character_Range (CC) then |
| Set_Has_Wide_Character (N); |
| else |
| Set_Has_Wide_Wide_Character (N); |
| end if; |
| |
| Analyze_And_Resolve (N, T); |
| Set_Is_Static_Expression (N, False); |
| return; |
| end if; |
| end; |
| end if; |
| end; |
| end if; |
| end; |
| end if; |
| |
| -- Not that special case, so normal expansion of array aggregate |
| |
| Expand_Array_Aggregate (N); |
| end if; |
| |
| exception |
| when RE_Not_Available => |
| return; |
| end Expand_N_Aggregate; |
| |
| -------------------------------- |
| -- Expand_Container_Aggregate -- |
| -------------------------------- |
| |
| procedure Expand_Container_Aggregate (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Typ : constant Entity_Id := Etype (N); |
| Asp : constant Node_Id := Find_Value_Of_Aspect (Typ, Aspect_Aggregate); |
| |
| Empty_Subp : Node_Id := Empty; |
| Add_Named_Subp : Node_Id := Empty; |
| Add_Unnamed_Subp : Node_Id := Empty; |
| New_Indexed_Subp : Node_Id := Empty; |
| Assign_Indexed_Subp : Node_Id := Empty; |
| |
| Aggr_Code : constant List_Id := New_List; |
| Temp : constant Entity_Id := Make_Temporary (Loc, 'C', N); |
| |
| Comp : Node_Id; |
| Decl : Node_Id; |
| Default : Node_Id; |
| Init_Stat : Node_Id; |
| Siz : Int; |
| |
| -- The following are used when the size of the aggregate is not |
| -- static and requires a dynamic evaluation. |
| Siz_Decl : Node_Id; |
| Siz_Exp : Node_Id := Empty; |
| Count_Type : Entity_Id; |
| |
| function Aggregate_Size return Int; |
| -- Compute number of entries in aggregate, including choices |
| -- that cover a range or subtype, as well as iterated constructs. |
| -- Return -1 if the size is not known statically, in which case |
| -- allocate a default size for the aggregate, or build an expression |
| -- to estimate the size dynamically. |
| |
| function Build_Siz_Exp (Comp : Node_Id) return Int; |
| -- When the aggregate contains a single Iterated_Component_Association |
| -- or Element_Association with non-static bounds, build an expression |
| -- to be used as the allocated size of the container. This may be an |
| -- overestimate if a filter is present, but is a safe approximation. |
| -- If bounds are dynamic the aggregate is created in two passes, and |
| -- the first generates a loop for the sole purpose of computing the |
| -- number of elements that will be generated on the second pass. |
| |
| procedure Expand_Iterated_Component (Comp : Node_Id); |
| -- Handle iterated_component_association and iterated_Element |
| -- association by generating a loop over the specified range, |
| -- given either by a loop parameter specification or an iterator |
| -- specification. |
| |
| -------------------- |
| -- Aggregate_Size -- |
| -------------------- |
| |
| function Aggregate_Size return Int is |
| Comp : Node_Id; |
| Choice : Node_Id; |
| Lo, Hi : Node_Id; |
| Siz : Int := 0; |
| |
| procedure Add_Range_Size; |
| -- Compute number of components specified by a component association |
| -- given by a range or subtype name. |
| |
| -------------------- |
| -- Add_Range_Size -- |
| -------------------- |
| |
| procedure Add_Range_Size is |
| begin |
| -- The bounds of the discrete range are integers or enumeration |
| -- literals |
| |
| if Nkind (Lo) = N_Integer_Literal then |
| Siz := Siz + UI_To_Int (Intval (Hi)) |
| - UI_To_Int (Intval (Lo)) + 1; |
| else |
| Siz := Siz + UI_To_Int (Enumeration_Pos (Hi)) |
| - UI_To_Int (Enumeration_Pos (Lo)) + 1; |
| end if; |
| end Add_Range_Size; |
| |
| begin |
| -- Aggregate is either all positional or all named. |
| |
| if Present (Expressions (N)) then |
| Siz := List_Length (Expressions (N)); |
| end if; |
| |
| if Present (Component_Associations (N)) then |
| Comp := First (Component_Associations (N)); |
| -- If there is a single component association it can be |
| -- an iterated component with dynamic bounds or an element |
| -- iterator over an iterable object. If it is an array |
| -- we can use the attribute Length to get its size; |
| -- for a predefined container the function Length plays |
| -- the same role. There is no available mechanism for |
| -- user-defined containers. For now we treat all of these |
| -- as dynamic. |
| |
| if List_Length (Component_Associations (N)) = 1 |
| and then Nkind (Comp) in N_Iterated_Component_Association | |
| N_Iterated_Element_Association |
| then |
| return Build_Siz_Exp (Comp); |
| end if; |
| |
| -- Otherwise all associations must specify static sizes. |
| |
| while Present (Comp) loop |
| Choice := First (Choice_List (Comp)); |
| |
| while Present (Choice) loop |
| Analyze (Choice); |
| |
| if Nkind (Choice) = N_Range then |
| Lo := Low_Bound (Choice); |
| Hi := High_Bound (Choice); |
| Add_Range_Size; |
| |
| elsif Is_Entity_Name (Choice) |
| and then Is_Type (Entity (Choice)) |
| then |
| Lo := Type_Low_Bound (Entity (Choice)); |
| Hi := Type_High_Bound (Entity (Choice)); |
| Add_Range_Size; |
| |
| Rewrite (Choice, |
| Make_Range (Loc, |
| New_Copy_Tree (Lo), |
| New_Copy_Tree (Hi))); |
| |
| else |
| -- Single choice (syntax excludes a subtype |
| -- indication). |
| |
| Siz := Siz + 1; |
| end if; |
| |
| Next (Choice); |
| end loop; |
| Next (Comp); |
| end loop; |
| end if; |
| |
| return Siz; |
| end Aggregate_Size; |
| |
| ------------------- |
| -- Build_Siz_Exp -- |
| ------------------- |
| |
| function Build_Siz_Exp (Comp : Node_Id) return Int is |
| Lo, Hi : Node_Id; |
| begin |
| if Nkind (Comp) = N_Range then |
| Lo := Low_Bound (Comp); |
| Hi := High_Bound (Comp); |
| Analyze (Lo); |
| Analyze (Hi); |
| |
| -- Compute static size when possible. |
| |
| if Is_Static_Expression (Lo) |
| and then Is_Static_Expression (Hi) |
| then |
| if Nkind (Lo) = N_Integer_Literal then |
| Siz := UI_To_Int (Intval (Hi)) - UI_To_Int (Intval (Lo)) + 1; |
| else |
| Siz := UI_To_Int (Enumeration_Pos (Hi)) |
| - UI_To_Int (Enumeration_Pos (Lo)) + 1; |
| end if; |
| return Siz; |
| |
| else |
| Siz_Exp := |
| Make_Op_Add (Sloc (Comp), |
| Left_Opnd => |
| Make_Op_Subtract (Sloc (Comp), |
| Left_Opnd => New_Copy_Tree (Hi), |
| Right_Opnd => New_Copy_Tree (Lo)), |
| Right_Opnd => |
| Make_Integer_Literal (Loc, 1)); |
| return -1; |
| end if; |
| |
| elsif Nkind (Comp) = N_Iterated_Component_Association then |
| return Build_Siz_Exp (First (Discrete_Choices (Comp))); |
| |
| elsif Nkind (Comp) = N_Iterated_Element_Association then |
| return -1; |
| |
| -- ??? Need to create code for a loop and add to generated code, |
| -- as is done for array aggregates with iterated element |
| -- associations, instead of using Append operations. |
| |
| else |
| return -1; |
| end if; |
| end Build_Siz_Exp; |
| |
| ------------------------------- |
| -- Expand_Iterated_Component -- |
| ------------------------------- |
| |
| procedure Expand_Iterated_Component (Comp : Node_Id) is |
| Expr : constant Node_Id := Expression (Comp); |
| |
| Key_Expr : Node_Id := Empty; |
| Loop_Id : Entity_Id; |
| L_Range : Node_Id; |
| L_Iteration_Scheme : Node_Id; |
| Loop_Stat : Node_Id; |
| Params : List_Id; |
| Stats : List_Id; |
| |
| begin |
| if Nkind (Comp) = N_Iterated_Element_Association then |
| Key_Expr := Key_Expression (Comp); |
| |
| -- We create a new entity as loop identifier in all cases, |
| -- as is done for generated loops elsewhere, as the loop |
| -- structure has been previously analyzed. |
| |
| if Present (Iterator_Specification (Comp)) then |
| |
| -- Either an Iterator_Specification or a Loop_Parameter_ |
| -- Specification is present. |
| |
| L_Iteration_Scheme := |
| Make_Iteration_Scheme (Loc, |
| Iterator_Specification => Iterator_Specification (Comp)); |
| Loop_Id := |
| Make_Defining_Identifier (Loc, |
| Chars => Chars (Defining_Identifier |
| (Iterator_Specification (Comp)))); |
| Set_Defining_Identifier |
| (Iterator_Specification (L_Iteration_Scheme), Loop_Id); |
| |
| else |
| L_Iteration_Scheme := |
| Make_Iteration_Scheme (Loc, |
| Loop_Parameter_Specification => |
| Loop_Parameter_Specification (Comp)); |
| Loop_Id := |
| Make_Defining_Identifier (Loc, |
| Chars => Chars (Defining_Identifier |
| (Loop_Parameter_Specification (Comp)))); |
| Set_Defining_Identifier |
| (Loop_Parameter_Specification |
| (L_Iteration_Scheme), Loop_Id); |
| end if; |
| else |
| |
| -- Iterated_Component_Association. |
| |
| Loop_Id := |
| Make_Defining_Identifier (Loc, |
| Chars => Chars (Defining_Identifier (Comp))); |
| |
| if Present (Iterator_Specification (Comp)) then |
| L_Iteration_Scheme := |
| Make_Iteration_Scheme (Loc, |
| Iterator_Specification => Iterator_Specification (Comp)); |
| |
| else |
| -- Loop_Parameter_Specification is parsed with a choice list. |
| -- where the range is the first (and only) choice. |
| |
| L_Range := Relocate_Node (First (Discrete_Choices (Comp))); |
| |
| L_Iteration_Scheme := |
| Make_Iteration_Scheme (Loc, |
| Loop_Parameter_Specification => |
| Make_Loop_Parameter_Specification (Loc, |
| Defining_Identifier => Loop_Id, |
| Discrete_Subtype_Definition => L_Range)); |
| end if; |
| end if; |
| |
| -- Build insertion statement. For a positional aggregate, only the |
| -- expression is needed. For a named aggregate, the loop variable, |
| -- whose type is that of the key, is an additional parameter for |
| -- the insertion operation. |
| -- If a Key_Expression is present, it serves as the additional |
| -- parameter. Otherwise the key is given by the loop parameter |
| -- itself. |
| |
| if Present (Add_Unnamed_Subp) |
| and then No (Add_Named_Subp) |
| then |
| Stats := New_List |
| (Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Entity (Add_Unnamed_Subp), Loc), |
| Parameter_Associations => |
| New_List (New_Occurrence_Of (Temp, Loc), |
| New_Copy_Tree (Expr)))); |
| else |
| -- Named or indexed aggregate, for which a key is present, |
| -- possibly with a specified key_expression. |
| |
| if Present (Key_Expr) then |
| Params := New_List (New_Occurrence_Of (Temp, Loc), |
| New_Copy_Tree (Key_Expr), |
| New_Copy_Tree (Expr)); |
| else |
| Params := New_List (New_Occurrence_Of (Temp, Loc), |
| New_Occurrence_Of (Loop_Id, Loc), |
| New_Copy_Tree (Expr)); |
| end if; |
| |
| Stats := New_List |
| (Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Entity (Add_Named_Subp), Loc), |
| Parameter_Associations => Params)); |
| end if; |
| |
| Loop_Stat := Make_Implicit_Loop_Statement |
| (Node => N, |
| Identifier => Empty, |
| Iteration_Scheme => L_Iteration_Scheme, |
| Statements => Stats); |
| Append (Loop_Stat, Aggr_Code); |
| |
| end Expand_Iterated_Component; |
| |
| -- Start of processing for Expand_Container_Aggregate |
| |
| begin |
| Parse_Aspect_Aggregate (Asp, |
| Empty_Subp, Add_Named_Subp, Add_Unnamed_Subp, |
| New_Indexed_Subp, Assign_Indexed_Subp); |
| |
| -- The constructor for bounded containers is a function with |
| -- a parameter that sets the size of the container. If the |
| -- size cannot be determined statically we use a default value |
| -- or a dynamic expression. |
| |
| Siz := Aggregate_Size; |
| |
| if Ekind (Entity (Empty_Subp)) = E_Function |
| and then Present (First_Formal (Entity (Empty_Subp))) |
| then |
| Default := Default_Value (First_Formal (Entity (Empty_Subp))); |
| |
| -- If aggregate size is not static, we can use default value |
| -- of formal parameter for allocation. We assume that this |
| -- (implementation-dependent) value is static, even though |
| -- the AI does not require it. |
| |
| -- Create declaration for size: a constant literal in the simple |
| -- case, an expression if iterated component associations may be |
| -- involved, the default otherwise. |
| |
| Count_Type := Etype (First_Formal (Entity (Empty_Subp))); |
| if Siz = -1 then |
| if No (Siz_Exp) then |
| Siz := UI_To_Int (Intval (Default)); |
| Siz_Exp := Make_Integer_Literal (Loc, Siz); |
| |
| else |
| Siz_Exp := Make_Type_Conversion (Loc, |
| Subtype_Mark => |
| New_Occurrence_Of (Count_Type, Loc), |
| Expression => Siz_Exp); |
| end if; |
| |
| else |
| Siz_Exp := Make_Integer_Literal (Loc, Siz); |
| end if; |
| |
| Siz_Decl := Make_Object_Declaration (Loc, |
| Defining_Identifier => Make_Temporary (Loc, 'S', N), |
| Object_Definition => |
| New_Occurrence_Of (Count_Type, Loc), |
| Expression => Siz_Exp); |
| Append (Siz_Decl, Aggr_Code); |
| |
| if Nkind (Siz_Exp) = N_Integer_Literal then |
| Init_Stat := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Temp, |
| Object_Definition => New_Occurrence_Of (Typ, Loc), |
| Expression => Make_Function_Call (Loc, |
| Name => New_Occurrence_Of (Entity (Empty_Subp), Loc), |
| Parameter_Associations => |
| New_List |
| (New_Occurrence_Of |
| (Defining_Identifier (Siz_Decl), Loc)))); |
| |
| else |
| Init_Stat := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Temp, |
| Object_Definition => New_Occurrence_Of (Typ, Loc), |
| Expression => Make_Function_Call (Loc, |
| Name => |
| New_Occurrence_Of (Entity (New_Indexed_Subp), Loc), |
| Parameter_Associations => |
| New_List ( |
| Make_Integer_Literal (Loc, 1), |
| New_Occurrence_Of |
| (Defining_Identifier (Siz_Decl), Loc)))); |
| end if; |
| |
| Append (Init_Stat, Aggr_Code); |
| |
| -- Size is dynamic: Create declaration for object, and intitialize |
| -- with a call to the null container, or an assignment to it. |
| |
| else |
| Decl := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Temp, |
| Object_Definition => New_Occurrence_Of (Typ, Loc)); |
| |
| Insert_Action (N, Decl); |
| |
| -- The Empty entity is either a parameterless function, or |
| -- a constant. |
| |
| if Ekind (Entity (Empty_Subp)) = E_Function then |
| Init_Stat := Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of (Temp, Loc), |
| Expression => Make_Function_Call (Loc, |
| Name => New_Occurrence_Of (Entity (Empty_Subp), Loc))); |
| |
| else |
| Init_Stat := Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of (Temp, Loc), |
| Expression => New_Occurrence_Of (Entity (Empty_Subp), Loc)); |
| end if; |
| |
| Append (Init_Stat, Aggr_Code); |
| end if; |
| |
| --------------------------- |
| -- Positional aggregate -- |
| --------------------------- |
| |
| -- If the aggregate is positional the aspect must include |
| -- an Add_Unnamed subprogram. |
| |
| if Present (Add_Unnamed_Subp) then |
| if Present (Expressions (N)) then |
| declare |
| Insert : constant Entity_Id := Entity (Add_Unnamed_Subp); |
| Comp : Node_Id; |
| Stat : Node_Id; |
| |
| begin |
| Comp := First (Expressions (N)); |
| while Present (Comp) loop |
| Stat := Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Insert, Loc), |
| Parameter_Associations => |
| New_List (New_Occurrence_Of (Temp, Loc), |
| New_Copy_Tree (Comp))); |
| Append (Stat, Aggr_Code); |
| Next (Comp); |
| end loop; |
| end; |
| end if; |
| |
| -- Indexed aggregates are handled below. Unnamed aggregates |
| -- such as sets may include iterated component associations. |
| |
| if No (New_Indexed_Subp) then |
| Comp := First (Component_Associations (N)); |
| while Present (Comp) loop |
| if Nkind (Comp) = N_Iterated_Component_Association then |
| Expand_Iterated_Component (Comp); |
| end if; |
| Next (Comp); |
| end loop; |
| end if; |
| |
| --------------------- |
| -- Named_Aggregate -- |
| --------------------- |
| |
| elsif Present (Add_Named_Subp) then |
| declare |
| Insert : constant Entity_Id := Entity (Add_Named_Subp); |
| Stat : Node_Id; |
| Key : Node_Id; |
| begin |
| Comp := First (Component_Associations (N)); |
| |
| -- Each component association may contain several choices; |
| -- generate an insertion statement for each. |
| |
| while Present (Comp) loop |
| if Nkind (Comp) in N_Iterated_Component_Association |
| | N_Iterated_Element_Association |
| then |
| Expand_Iterated_Component (Comp); |
| else |
| Key := First (Choices (Comp)); |
| |
| while Present (Key) loop |
| Stat := Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Insert, Loc), |
| Parameter_Associations => |
| New_List (New_Occurrence_Of (Temp, Loc), |
| New_Copy_Tree (Key), |
| New_Copy_Tree (Expression (Comp)))); |
| Append (Stat, Aggr_Code); |
| |
| Next (Key); |
| end loop; |
| end if; |
| |
| Next (Comp); |
| end loop; |
| end; |
| end if; |
| |
| ----------------------- |
| -- Indexed_Aggregate -- |
| ----------------------- |
| |
| -- For an indexed aggregate there must be an Assigned_Indexeed |
| -- subprogram. Note that unlike array aggregates, a container |
| -- aggregate must be fully positional or fully indexed. In the |
| -- first case the expansion has already taken place. |
| -- TBA: the keys for an indexed aggregate must provide a dense |
| -- range with no repetitions. |
| |
| if Present (Assign_Indexed_Subp) |
| and then Present (Component_Associations (N)) |
| then |
| declare |
| Insert : constant Entity_Id := Entity (Assign_Indexed_Subp); |
| Index_Type : constant Entity_Id := |
| Etype (Next_Formal (First_Formal (Insert))); |
| |
| function Expand_Range_Component |
| (Rng : Node_Id; |
| Expr : Node_Id) return Node_Id; |
| -- Transform a component assoication with a range into an |
| -- explicit loop. If the choice is a subtype name, it is |
| -- rewritten as a range with the corresponding bounds, which |
| -- are known to be static. |
| |
| Comp : Node_Id; |
| Index : Node_Id; |
| Pos : Int := 0; |
| Stat : Node_Id; |
| Key : Node_Id; |
| |
| ----------------------------- |
| -- Expand_Raange_Component -- |
| ----------------------------- |
| |
| function Expand_Range_Component |
| (Rng : Node_Id; |
| Expr : Node_Id) return Node_Id |
| is |
| Loop_Id : constant Entity_Id := |
| Make_Temporary (Loc, 'T'); |
| |
| L_Iteration_Scheme : Node_Id; |
| Stats : List_Id; |
| |
| begin |
| L_Iteration_Scheme := |
| Make_Iteration_Scheme (Loc, |
| Loop_Parameter_Specification => |
| Make_Loop_Parameter_Specification (Loc, |
| Defining_Identifier => Loop_Id, |
| Discrete_Subtype_Definition => New_Copy_Tree (Rng))); |
| |
| Stats := New_List |
| (Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (Entity (Assign_Indexed_Subp), Loc), |
| Parameter_Associations => |
| New_List (New_Occurrence_Of (Temp, Loc), |
| New_Occurrence_Of (Loop_Id, Loc), |
| New_Copy_Tree (Expr)))); |
| |
| return Make_Implicit_Loop_Statement |
| (Node => N, |
| Identifier => Empty, |
| Iteration_Scheme => L_Iteration_Scheme, |
| Statements => Stats); |
| end Expand_Range_Component; |
| |
| begin |
| if Siz > 0 then |
| |
| -- Modify the call to the constructor to allocate the |
| -- required size for the aggregwte : call the provided |
| -- constructor rather than the Empty aggregate. |
| |
| Index := Make_Op_Add (Loc, |
| Left_Opnd => New_Copy_Tree (Type_Low_Bound (Index_Type)), |
| Right_Opnd => Make_Integer_Literal (Loc, Siz - 1)); |
| |
| Set_Expression (Init_Stat, |
| Make_Function_Call (Loc, |
| Name => |
| New_Occurrence_Of (Entity (New_Indexed_Subp), Loc), |
| Parameter_Associations => |
| New_List ( |
| New_Copy_Tree (Type_Low_Bound (Index_Type)), |
| Index))); |
| end if; |
| |
| if Present (Expressions (N)) then |
| Comp := First (Expressions (N)); |
| |
| while Present (Comp) loop |
| |
| -- Compute index position for successive components |
| -- in the list of expressions, and use the indexed |
| -- assignment procedure for each. |
| |
| Index := Make_Op_Add (Loc, |
| Left_Opnd => Type_Low_Bound (Index_Type), |
| Right_Opnd => Make_Integer_Literal (Loc, Pos)); |
| |
| Stat := Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Insert, Loc), |
| Parameter_Associations => |
| New_List (New_Occurrence_Of (Temp, Loc), |
| Index, |
| New_Copy_Tree (Comp))); |
| |
| Pos := Pos + 1; |
| |
| Append (Stat, Aggr_Code); |
| Next (Comp); |
| end loop; |
| end if; |
| |
| if Present (Component_Associations (N)) then |
| Comp := First (Component_Associations (N)); |
| |
| -- The choice may be a static value, or a range with |
| -- static bounds. |
| |
| while Present (Comp) loop |
| if Nkind (Comp) = N_Component_Association then |
| Key := First (Choices (Comp)); |
| while Present (Key) loop |
| |
| -- If the expression is a box, the corresponding |
| -- component (s) is left uninitialized. |
| |
| if Box_Present (Comp) then |
| goto Next_Key; |
| |
| elsif Nkind (Key) = N_Range then |
| |
| -- Create loop for tne specified range, |
| -- with copies of the expression. |
| |
| Stat := |
| Expand_Range_Component (Key, Expression (Comp)); |
| |
| else |
| Stat := Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of |
| (Entity (Assign_Indexed_Subp), Loc), |
| Parameter_Associations => |
| New_List (New_Occurrence_Of (Temp, Loc), |
| New_Copy_Tree (Key), |
| New_Copy_Tree (Expression (Comp)))); |
| end if; |
| |
| Append (Stat, Aggr_Code); |
| |
| <<Next_Key>> |
| Next (Key); |
| end loop; |
| |
| else |
| -- Iterated component association. Discard |
| -- positional insertion procedure. |
| |
| Add_Named_Subp := Assign_Indexed_Subp; |
| Add_Unnamed_Subp := Empty; |
| Expand_Iterated_Component (Comp); |
| end if; |
| |
| Next (Comp); |
| end loop; |
| end if; |
| end; |
| end if; |
| |
| Insert_Actions (N, Aggr_Code); |
| Rewrite (N, New_Occurrence_Of (Temp, Loc)); |
| Analyze_And_Resolve (N, Typ); |
| end Expand_Container_Aggregate; |
| |
| ------------------------------ |
| -- Expand_N_Delta_Aggregate -- |
| ------------------------------ |
| |
| procedure Expand_N_Delta_Aggregate (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Typ : constant Entity_Id := Etype (Expression (N)); |
| Decl : Node_Id; |
| |
| begin |
| Decl := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Make_Temporary (Loc, 'T'), |
| Object_Definition => New_Occurrence_Of (Typ, Loc), |
| Expression => New_Copy_Tree (Expression (N))); |
| |
| if Is_Array_Type (Etype (N)) then |
| Expand_Delta_Array_Aggregate (N, New_List (Decl)); |
| else |
| Expand_Delta_Record_Aggregate (N, New_List (Decl)); |
| end if; |
| end Expand_N_Delta_Aggregate; |
| |
| ---------------------------------- |
| -- Expand_Delta_Array_Aggregate -- |
| ---------------------------------- |
| |
| procedure Expand_Delta_Array_Aggregate (N : Node_Id; Deltas : List_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Temp : constant Entity_Id := Defining_Identifier (First (Deltas)); |
| Assoc : Node_Id; |
| |
| function Generate_Loop (C : Node_Id) return Node_Id; |
| -- Generate a loop containing individual component assignments for |
| -- choices that are ranges, subtype indications, subtype names, and |
| -- iterated component associations. |
| |
| ------------------- |
| -- Generate_Loop -- |
| ------------------- |
| |
| function Generate_Loop (C : Node_Id) return Node_Id is |
| Sl : constant Source_Ptr := Sloc (C); |
| Ix : Entity_Id; |
| |
| begin |
| if Nkind (Parent (C)) = N_Iterated_Component_Association then |
| Ix := |
| Make_Defining_Identifier (Loc, |
| Chars => (Chars (Defining_Identifier (Parent (C))))); |
| else |
| Ix := Make_Temporary (Sl, 'I'); |
| end if; |
| |
| return |
| Make_Loop_Statement (Loc, |
| Iteration_Scheme => |
| Make_Iteration_Scheme (Sl, |
| Loop_Parameter_Specification => |
| Make_Loop_Parameter_Specification (Sl, |
| Defining_Identifier => Ix, |
| Discrete_Subtype_Definition => New_Copy_Tree (C))), |
| |
| Statements => New_List ( |
| Make_Assignment_Statement (Sl, |
| Name => |
| Make_Indexed_Component (Sl, |
| Prefix => New_Occurrence_Of (Temp, Sl), |
| Expressions => New_List (New_Occurrence_Of (Ix, Sl))), |
| Expression => New_Copy_Tree (Expression (Assoc)))), |
| End_Label => Empty); |
| end Generate_Loop; |
| |
| -- Local variables |
| |
| Choice : Node_Id; |
| |
| -- Start of processing for Expand_Delta_Array_Aggregate |
| |
| begin |
| Assoc := First (Component_Associations (N)); |
| while Present (Assoc) loop |
| Choice := First (Choice_List (Assoc)); |
| if Nkind (Assoc) = N_Iterated_Component_Association then |
| while Present (Choice) loop |
| Append_To (Deltas, Generate_Loop (Choice)); |
| Next (Choice); |
| end loop; |
| |
| else |
| while Present (Choice) loop |
| |
| -- Choice can be given by a range, a subtype indication, a |
| -- subtype name, a scalar value, or an entity. |
| |
| if Nkind (Choice) = N_Range |
| or else (Is_Entity_Name (Choice) |
| and then Is_Type (Entity (Choice))) |
| then |
| Append_To (Deltas, Generate_Loop (Choice)); |
| |
| elsif Nkind (Choice) = N_Subtype_Indication then |
| Append_To (Deltas, |
| Generate_Loop (Range_Expression (Constraint (Choice)))); |
| |
| else |
| Append_To (Deltas, |
| Make_Assignment_Statement (Sloc (Choice), |
| Name => |
| Make_Indexed_Component (Sloc (Choice), |
| Prefix => New_Occurrence_Of (Temp, Loc), |
| Expressions => New_List (New_Copy_Tree (Choice))), |
| Expression => New_Copy_Tree (Expression (Assoc)))); |
| end if; |
| |
| Next (Choice); |
| end loop; |
| end if; |
| |
| Next (Assoc); |
| end loop; |
| |
| Insert_Actions (N, Deltas); |
| Rewrite (N, New_Occurrence_Of (Temp, Loc)); |
| end Expand_Delta_Array_Aggregate; |
| |
| ----------------------------------- |
| -- Expand_Delta_Record_Aggregate -- |
| ----------------------------------- |
| |
| procedure Expand_Delta_Record_Aggregate (N : Node_Id; Deltas : List_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Temp : constant Entity_Id := Defining_Identifier (First (Deltas)); |
| Assoc : Node_Id; |
| Choice : Node_Id; |
| |
| begin |
| Assoc := First (Component_Associations (N)); |
| |
| while Present (Assoc) loop |
| Choice := First (Choice_List (Assoc)); |
| while Present (Choice) loop |
| Append_To (Deltas, |
| Make_Assignment_Statement (Sloc (Choice), |
| Name => |
| Make_Selected_Component (Sloc (Choice), |
| Prefix => New_Occurrence_Of (Temp, Loc), |
| Selector_Name => Make_Identifier (Loc, Chars (Choice))), |
| Expression => New_Copy_Tree (Expression (Assoc)))); |
| Next (Choice); |
| end loop; |
| |
| Next (Assoc); |
| end loop; |
| |
| Insert_Actions (N, Deltas); |
| Rewrite (N, New_Occurrence_Of (Temp, Loc)); |
| end Expand_Delta_Record_Aggregate; |
| |
| ---------------------------------- |
| -- Expand_N_Extension_Aggregate -- |
| ---------------------------------- |
| |
| -- If the ancestor part is an expression, add a component association for |
| -- the parent field. If the type of the ancestor part is not the direct |
| -- parent of the expected type, build recursively the needed ancestors. |
| -- If the ancestor part is a subtype_mark, replace aggregate with a |
| -- declaration for a temporary of the expected type, followed by |
| -- individual assignments to the given components. |
| |
| procedure Expand_N_Extension_Aggregate (N : Node_Id) is |
| A : constant Node_Id := Ancestor_Part (N); |
| Loc : constant Source_Ptr := Sloc (N); |
| Typ : constant Entity_Id := Etype (N); |
| |
| begin |
| -- If the ancestor is a subtype mark, an init proc must be called |
| -- on the resulting object which thus has to be materialized in |
| -- the front-end |
| |
| if Is_Entity_Name (A) and then Is_Type (Entity (A)) then |
| Convert_To_Assignments (N, Typ); |
| |
| -- The extension aggregate is transformed into a record aggregate |
| -- of the following form (c1 and c2 are inherited components) |
| |
| -- (Exp with c3 => a, c4 => b) |
| -- ==> (c1 => Exp.c1, c2 => Exp.c2, c3 => a, c4 => b) |
| |
| else |
| Set_Etype (N, Typ); |
| |
| if Tagged_Type_Expansion then |
| Expand_Record_Aggregate (N, |
| Orig_Tag => |
| New_Occurrence_Of |
| (Node (First_Elmt (Access_Disp_Table (Typ))), Loc), |
| Parent_Expr => A); |
| |
| -- No tag is needed in the case of a VM |
| |
| else |
| Expand_Record_Aggregate (N, Parent_Expr => A); |
| end if; |
| end if; |
| |
| exception |
| when RE_Not_Available => |
| return; |
| end Expand_N_Extension_Aggregate; |
| |
| ----------------------------- |
| -- Expand_Record_Aggregate -- |
| ----------------------------- |
| |
| procedure Expand_Record_Aggregate |
| (N : Node_Id; |
| Orig_Tag : Node_Id := Empty; |
| Parent_Expr : Node_Id := Empty) |
| is |
| Loc : constant Source_Ptr := Sloc (N); |
| Comps : constant List_Id := Component_Associations (N); |
| Typ : constant Entity_Id := Etype (N); |
| Base_Typ : constant Entity_Id := Base_Type (Typ); |
| |
| Static_Components : Boolean := True; |
| -- Flag to indicate whether all components are compile-time known, |
| -- and the aggregate can be constructed statically and handled by |
| -- the back-end. Set to False by Component_OK_For_Backend. |
| |
| procedure Build_Back_End_Aggregate; |
| -- Build a proper aggregate to be handled by the back-end |
| |
| function Compile_Time_Known_Composite_Value (N : Node_Id) return Boolean; |
| -- Returns true if N is an expression of composite type which can be |
| -- fully evaluated at compile time without raising constraint error. |
| -- Such expressions can be passed as is to Gigi without any expansion. |
| -- |
| -- This returns true for N_Aggregate with Compile_Time_Known_Aggregate |
| -- set and constants whose expression is such an aggregate, recursively. |
| |
| function Component_OK_For_Backend return Boolean; |
| -- Check for presence of a component which makes it impossible for the |
| -- backend to process the aggregate, thus requiring the use of a series |
| -- of assignment statements. Cases checked for are a nested aggregate |
| -- needing Late_Expansion, the presence of a tagged component which may |
| -- need tag adjustment, and a bit unaligned component reference. |
| -- |
| -- We also force expansion into assignments if a component is of a |
| -- mutable type (including a private type with discriminants) because |
| -- in that case the size of the component to be copied may be smaller |
| -- than the side of the target, and there is no simple way for gigi |
| -- to compute the size of the object to be copied. |
| -- |
| -- NOTE: This is part of the ongoing work to define precisely the |
| -- interface between front-end and back-end handling of aggregates. |
| -- In general it is desirable to pass aggregates as they are to gigi, |
| -- in order to minimize elaboration code. This is one case where the |
| -- semantics of Ada complicate the analysis and lead to anomalies in |
| -- the gcc back-end if the aggregate is not expanded into assignments. |
| -- |
| -- NOTE: This sets the global Static_Components to False in most, but |
| -- not all, cases when it returns False. |
| |
| function Has_Per_Object_Constraint (L : List_Id) return Boolean; |
| -- Return True if any element of L has Has_Per_Object_Constraint set. |
| -- L should be the Choices component of an N_Component_Association. |
| |
| function Has_Visible_Private_Ancestor (Id : E) return Boolean; |
| -- If any ancestor of the current type is private, the aggregate |
| -- cannot be built in place. We cannot rely on Has_Private_Ancestor, |
| -- because it will not be set when type and its parent are in the |
| -- same scope, and the parent component needs expansion. |
| |
| function Top_Level_Aggregate (N : Node_Id) return Node_Id; |
| -- For nested aggregates return the ultimate enclosing aggregate; for |
| -- non-nested aggregates return N. |
| |
| ------------------------------ |
| -- Build_Back_End_Aggregate -- |
| ------------------------------ |
| |
| procedure Build_Back_End_Aggregate is |
| Comp : Entity_Id; |
| New_Comp : Node_Id; |
| Tag_Value : Node_Id; |
| |
| begin |
| if Nkind (N) = N_Aggregate then |
| |
| -- If the aggregate is static and can be handled by the back-end, |
| -- nothing left to do. |
| |
| if Static_Components then |
| Set_Compile_Time_Known_Aggregate (N); |
| Set_Expansion_Delayed (N, False); |
| end if; |
| end if; |
| |
| -- If no discriminants, nothing special to do |
| |
| if not Has_Discriminants (Typ) then |
| null; |
| |
| -- Case of discriminants present |
| |
| elsif Is_Derived_Type (Typ) then |
| |
| -- For untagged types, non-stored discriminants are replaced with |
| -- stored discriminants, which are the ones that gigi uses to |
| -- describe the type and its components. |
| |
| Generate_Aggregate_For_Derived_Type : declare |
| procedure Prepend_Stored_Values (T : Entity_Id); |
| -- Scan the list of stored discriminants of the type, and add |
| -- their values to the aggregate being built. |
| |
| --------------------------- |
| -- Prepend_Stored_Values -- |
| --------------------------- |
| |
| procedure Prepend_Stored_Values (T : Entity_Id) is |
| Discr : Entity_Id; |
| First_Comp : Node_Id := Empty; |
| |
| begin |
| Discr := First_Stored_Discriminant (T); |
| while Present (Discr) loop |
| New_Comp := |
| Make_Component_Association (Loc, |
| Choices => New_List ( |
| New_Occurrence_Of (Discr, Loc)), |
| Expression => |
| New_Copy_Tree |
| (Get_Discriminant_Value |
| (Discr, |
| Typ, |
| Discriminant_Constraint (Typ)))); |
| |
| if No (First_Comp) then |
| Prepend_To (Component_Associations (N), New_Comp); |
| else |
| Insert_After (First_Comp, New_Comp); |
| end if; |
| |
| First_Comp := New_Comp; |
| Next_Stored_Discriminant (Discr); |
| end loop; |
| end Prepend_Stored_Values; |
| |
| -- Local variables |
| |
| Constraints : constant List_Id := New_List; |
| |
| Discr : Entity_Id; |
| Decl : Node_Id; |
| Num_Disc : Nat := 0; |
| Num_Stor : Nat := 0; |
| |
| -- Start of processing for Generate_Aggregate_For_Derived_Type |
| |
| begin |
| -- Remove the associations for the discriminant of derived type |
| |
| declare |
| First_Comp : Node_Id; |
| |
| begin |
| First_Comp := First (Component_Associations (N)); |
| while Present (First_Comp) loop |
| Comp := First_Comp; |
| Next (First_Comp); |
| |
| if Ekind (Entity (First (Choices (Comp)))) = |
| E_Discriminant |
| then |
| Remove (Comp); |
| Num_Disc := Num_Disc + 1; |
| end if; |
| end loop; |
| end; |
| |
| -- Insert stored discriminant associations in the correct |
| -- order. If there are more stored discriminants than new |
| -- discriminants, there is at least one new discriminant that |
| -- constrains more than one of the stored discriminants. In |
| -- this case we need to construct a proper subtype of the |
| -- parent type, in order to supply values to all the |
| -- components. Otherwise there is one-one correspondence |
| -- between the constraints and the stored discriminants. |
| |
| Discr := First_Stored_Discriminant (Base_Type (Typ)); |
| while Present (Discr) loop |
| Num_Stor := Num_Stor + 1; |
| Next_Stored_Discriminant (Discr); |
| end loop; |
| |
| -- Case of more stored discriminants than new discriminants |
| |
| if Num_Stor > Num_Disc then |
| |
| -- Create a proper subtype of the parent type, which is the |
| -- proper implementation type for the aggregate, and convert |
| -- it to the intended target type. |
| |
| Discr := First_Stored_Discriminant (Base_Type (Typ)); |
| while Present (Discr) loop |
| New_Comp := |
| New_Copy_Tree |
| (Get_Discriminant_Value |
| (Discr, |
| Typ, |
| Discriminant_Constraint (Typ))); |
| |
| Append (New_Comp, Constraints); |
| Next_Stored_Discriminant (Discr); |
| end loop; |
| |
| Decl := |
| Make_Subtype_Declaration (Loc, |
| Defining_Identifier => Make_Temporary (Loc, 'T'), |
| Subtype_Indication => |
| Make_Subtype_Indication (Loc, |
| Subtype_Mark => |
| New_Occurrence_Of (Etype (Base_Type (Typ)), Loc), |
| Constraint => |
| Make_Index_Or_Discriminant_Constraint |
| (Loc, Constraints))); |
| |
| Insert_Action (N, Decl); |
| Prepend_Stored_Values (Base_Type (Typ)); |
| |
| Set_Etype (N, Defining_Identifier (Decl)); |
| Set_Analyzed (N); |
| |
| Rewrite (N, Unchecked_Convert_To (Typ, N)); |
| Analyze (N); |
| |
| -- Case where we do not have fewer new discriminants than |
| -- stored discriminants, so in this case we can simply use the |
| -- stored discriminants of the subtype. |
| |
| else |
| Prepend_Stored_Values (Typ); |
| end if; |
| end Generate_Aggregate_For_Derived_Type; |
| end if; |
| |
| if Is_Tagged_Type (Typ) then |
| |
| -- In the tagged case, _parent and _tag component must be created |
| |
| -- Reset Null_Present unconditionally. Tagged records always have |
| -- at least one field (the tag or the parent). |
| |
| Set_Null_Record_Present (N, False); |
| |
| -- When the current aggregate comes from the expansion of an |
| -- extension aggregate, the parent expr is replaced by an |
| -- aggregate formed by selected components of this expr. |
| |
| if Present (Parent_Expr) and then Is_Empty_List (Comps) then |
| Comp := First_Component_Or_Discriminant (Typ); |
| while Present (Comp) loop |
| |
| -- Skip all expander-generated components |
| |
| if not Comes_From_Source (Original_Record_Component (Comp)) |
| then |
| null; |
| |
| else |
| New_Comp := |
| Make_Selected_Component (Loc, |
| Prefix => |
| Unchecked_Convert_To (Typ, |
| Duplicate_Subexpr (Parent_Expr, True)), |
| Selector_Name => New_Occurrence_Of (Comp, Loc)); |
| |
| Append_To (Comps, |
| Make_Component_Association (Loc, |
| Choices => New_List ( |
| New_Occurrence_Of (Comp, Loc)), |
| Expression => New_Comp)); |
| |
| Analyze_And_Resolve (New_Comp, Etype (Comp)); |
| end if; |
| |
| Next_Component_Or_Discriminant (Comp); |
| end loop; |
| end if; |
| |
| -- Compute the value for the Tag now, if the type is a root it |
| -- will be included in the aggregate right away, otherwise it will |
| -- be propagated to the parent aggregate. |
| |
| if Present (Orig_Tag) then |
| Tag_Value := Orig_Tag; |
| |
| elsif not Tagged_Type_Expansion then |
| Tag_Value := Empty; |
| |
| else |
| Tag_Value := |
| New_Occurrence_Of |
| (Node (First_Elmt (Access_Disp_Table (Typ))), Loc); |
| end if; |
| |
| -- For a derived type, an aggregate for the parent is formed with |
| -- all the inherited components. |
| |
| if Is_Derived_Type (Typ) then |
| declare |
| First_Comp : Node_Id; |
| Parent_Comps : List_Id; |
| Parent_Aggr : Node_Id; |
| Parent_Name : Node_Id; |
| |
| begin |
| First_Comp := First (Component_Associations (N)); |
| Parent_Comps := New_List; |
| |
| -- First skip the discriminants |
| |
| while Present (First_Comp) |
| and then Ekind (Entity (First (Choices (First_Comp)))) |
| = E_Discriminant |
| loop |
| Next (First_Comp); |
| end loop; |
| |
| -- Then remove the inherited component association from the |
| -- aggregate and store them in the parent aggregate |
| |
| while Present (First_Comp) |
| and then |
| Scope (Original_Record_Component |
| (Entity (First (Choices (First_Comp))))) /= |
| Base_Typ |
| loop |
| Comp := First_Comp; |
| Next (First_Comp); |
| Remove (Comp); |
| Append (Comp, Parent_Comps); |
| end loop; |
| |
| Parent_Aggr := |
| Make_Aggregate (Loc, |
| Component_Associations => Parent_Comps); |
| Set_Etype (Parent_Aggr, Etype (Base_Type (Typ))); |
| |
| -- Find the _parent component |
| |
| Comp := First_Component (Typ); |
| while Chars (Comp) /= Name_uParent loop |
| Next_Component (Comp); |
| end loop; |
| |
| Parent_Name := New_Occurrence_Of (Comp, Loc); |
| |
| -- Insert the parent aggregate |
| |
| Prepend_To (Component_Associations (N), |
| Make_Component_Association (Loc, |
| Choices => New_List (Parent_Name), |
| Expression => Parent_Aggr)); |
| |
| -- Expand recursively the parent propagating the right Tag |
| |
| Expand_Record_Aggregate |
| (Parent_Aggr, Tag_Value, Parent_Expr); |
| |
| -- The ancestor part may be a nested aggregate that has |
| -- delayed expansion: recheck now. |
| |
| if not Component_OK_For_Backend then |
| Convert_To_Assignments (N, Typ); |
| end if; |
| end; |
| |
| -- For a root type, the tag component is added (unless compiling |
| -- for the VMs, where tags are implicit). |
| |
| elsif Tagged_Type_Expansion then |
| declare |
| Tag_Name : constant Node_Id := |
| New_Occurrence_Of |
| (First_Tag_Component (Typ), Loc); |
| Typ_Tag : constant Entity_Id := RTE (RE_Tag); |
| Conv_Node : constant Node_Id := |
| Unchecked_Convert_To (Typ_Tag, Tag_Value); |
| |
| begin |
| Set_Etype (Conv_Node, Typ_Tag); |
| Prepend_To (Component_Associations (N), |
| Make_Component_Association (Loc, |
| Choices => New_List (Tag_Name), |
| Expression => Conv_Node)); |
| end; |
| end if; |
| end if; |
| end Build_Back_End_Aggregate; |
| |
| ---------------------------------------- |
| -- Compile_Time_Known_Composite_Value -- |
| ---------------------------------------- |
| |
| function Compile_Time_Known_Composite_Value |
| (N : Node_Id) return Boolean |
| is |
| begin |
| -- If we have an entity name, then see if it is the name of a |
| -- constant and if so, test the corresponding constant value. |
| |
| if Is_Entity_Name (N) then |
| declare |
| E : constant Entity_Id := Entity (N); |
| V : Node_Id; |
| begin |
| if Ekind (E) /= E_Constant then |
| return False; |
| else |
| V := Constant_Value (E); |
| return Present (V) |
| and then Compile_Time_Known_Composite_Value (V); |
| end if; |
| end; |
| |
| -- We have a value, see if it is compile time known |
| |
| else |
| if Nkind (N) = N_Aggregate then |
| return Compile_Time_Known_Aggregate (N); |
| end if; |
| |
| -- All other types of values are not known at compile time |
| |
| return False; |
| end if; |
| |
| end Compile_Time_Known_Composite_Value; |
| |
| ------------------------------ |
| -- Component_OK_For_Backend -- |
| ------------------------------ |
| |
| function Component_OK_For_Backend return Boolean is |
| C : Node_Id; |
| Expr_Q : Node_Id; |
| |
| begin |
| C := First (Comps); |
| while Present (C) loop |
| |
| -- If the component has box initialization, expansion is needed |
| -- and component is not ready for backend. |
| |
| if Box_Present (C) then |
| return False; |
| end if; |
| |
| if Nkind (Expression (C)) = N_Qualified_Expression then |
| Expr_Q := Expression (Expression (C)); |
| else |
| Expr_Q := Expression (C); |
| end if; |
| |
| -- Return False for array components whose bounds raise |
| -- constraint error. |
| |
| declare |
| Comp : constant Entity_Id := First (Choices (C)); |
| Indx : Node_Id; |
| |
| begin |
| if Present (Etype (Comp)) |
| and then Is_Array_Type (Etype (Comp)) |
| then |
| Indx := First_Index (Etype (Comp)); |
| while Present (Indx) loop |
| if Nkind (Type_Low_Bound (Etype (Indx))) = |
| N_Raise_Constraint_Error |
| or else Nkind (Type_High_Bound (Etype (Indx))) = |
| N_Raise_Constraint_Error |
| then |
| return False; |
| end if; |
| |
| Next_Index (Indx); |
| end loop; |
| end if; |
| end; |
| |
| -- Return False if the aggregate has any associations for tagged |
| -- components that may require tag adjustment. |
| |
| -- These are cases where the source expression may have a tag that |
| -- could differ from the component tag (e.g., can occur for type |
| -- conversions and formal parameters). (Tag adjustment not needed |
| -- if Tagged_Type_Expansion because object tags are implicit in |
| -- the machine.) |
| |
| if Is_Tagged_Type (Etype (Expr_Q)) |
| and then |
| (Nkind (Expr_Q) = N_Type_Conversion |
| or else |
| (Is_Entity_Name (Expr_Q) |
| and then Is_Formal (Entity (Expr_Q)))) |
| and then Tagged_Type_Expansion |
| then |
| Static_Components := False; |
| return False; |
| |
| elsif Is_Delayed_Aggregate (Expr_Q) then |
| Static_Components := False; |
| return False; |
| |
| elsif Nkind (Expr_Q) = N_Quantified_Expression then |
| Static_Components := False; |
| return False; |
| |
| elsif Possible_Bit_Aligned_Component (Expr_Q) then |
| Static_Components := False; |
| return False; |
| |
| elsif Modify_Tree_For_C |
| and then Nkind (C) = N_Component_Association |
| and then Has_Per_Object_Constraint (Choices (C)) |
| then |
| Static_Components := False; |
| return False; |
| |
| elsif Modify_Tree_For_C |
| and then Nkind (Expr_Q) = N_Identifier |
| and then Is_Array_Type (Etype (Expr_Q)) |
| then |
| Static_Components := False; |
| return False; |
| |
| elsif Modify_Tree_For_C |
| and then Nkind (Expr_Q) = N_Type_Conversion |
| and then Is_Array_Type (Etype (Expr_Q)) |
| then |
| Static_Components := False; |
| return False; |
| end if; |
| |
| if Is_Elementary_Type (Etype (Expr_Q)) then |
| if not Compile_Time_Known_Value (Expr_Q) then |
| Static_Components := False; |
| end if; |
| |
| elsif not Compile_Time_Known_Composite_Value (Expr_Q) then |
| Static_Components := False; |
| |
| if Is_Private_Type (Etype (Expr_Q)) |
| and then Has_Discriminants (Etype (Expr_Q)) |
| then |
| return False; |
| end if; |
| end if; |
| |
| Next (C); |
| end loop; |
| |
| return True; |
| end Component_OK_For_Backend; |
| |
| ------------------------------- |
| -- Has_Per_Object_Constraint -- |
| ------------------------------- |
| |
| function Has_Per_Object_Constraint (L : List_Id) return Boolean is |
| N : Node_Id := First (L); |
| begin |
| while Present (N) loop |
| if Is_Entity_Name (N) |
| and then Present (Entity (N)) |
| and then Has_Per_Object_Constraint (Entity (N)) |
| then |
| return True; |
| end if; |
| |
| Next (N); |
| end loop; |
| |
| return False; |
| end Has_Per_Object_Constraint; |
| |
| ----------------------------------- |
| -- Has_Visible_Private_Ancestor -- |
| ----------------------------------- |
| |
| function Has_Visible_Private_Ancestor (Id : E) return Boolean is |
| R : constant Entity_Id := Root_Type (Id); |
| T1 : Entity_Id := Id; |
| |
| begin |
| loop |
| if Is_Private_Type (T1) then |
| return True; |
| |
| elsif T1 = R then |
| return False; |
| |
| else |
| T1 := Etype (T1); |
| end if; |
| end loop; |
| end Has_Visible_Private_Ancestor; |
| |
| ------------------------- |
| -- Top_Level_Aggregate -- |
| ------------------------- |
| |
| function Top_Level_Aggregate (N : Node_Id) return Node_Id is |
| Aggr : Node_Id; |
| |
| begin |
| Aggr := N; |
| while Present (Parent (Aggr)) |
| and then Nkind (Parent (Aggr)) in |
| N_Aggregate | N_Component_Association |
| loop |
| Aggr := Parent (Aggr); |
| end loop; |
| |
| return Aggr; |
| end Top_Level_Aggregate; |
| |
| -- Local variables |
| |
| Top_Level_Aggr : constant Node_Id := Top_Level_Aggregate (N); |
| |
| -- Start of processing for Expand_Record_Aggregate |
| |
| begin |
| -- If the aggregate is to be assigned to a full access variable, we have |
| -- to prevent a piecemeal assignment even if the aggregate is to be |
| -- expanded. We create a temporary for the aggregate, and assign the |
| -- temporary instead, so that the back end can generate an atomic move |
| -- for it. |
| |
| if Is_Full_Access_Aggregate (N) then |
| return; |
| |
| -- No special management required for aggregates used to initialize |
| -- statically allocated dispatch tables |
| |
| elsif Is_Static_Dispatch_Table_Aggregate (N) then |
| return; |
| |
| -- Case pattern aggregates need to remain as aggregates |
| |
| elsif Is_Case_Choice_Pattern (N) then |
| return; |
| end if; |
| |
| -- If the pragma Aggregate_Individually_Assign is set, always convert to |
| -- assignments. |
| |
| if Aggregate_Individually_Assign then |
| Convert_To_Assignments (N, Typ); |
| |
| -- Ada 2005 (AI-318-2): We need to convert to assignments if components |
| -- are build-in-place function calls. The assignments will each turn |
| -- into a build-in-place function call. If components are all static, |
| -- we can pass the aggregate to the back end regardless of limitedness. |
| |
| -- Extension aggregates, aggregates in extended return statements, and |
| -- aggregates for C++ imported types must be expanded. |
| |
| elsif Ada_Version >= Ada_2005 and then Is_Limited_View (Typ) then |
| if Nkind (Parent (N)) not in |
| N_Component_Association | N_Object_Declaration |
| then |
| Convert_To_Assignments (N, Typ); |
| |
| elsif Nkind (N) = N_Extension_Aggregate |
| or else Convention (Typ) = Convention_CPP |
| then |
| Convert_To_Assignments (N, Typ); |
| |
| elsif not Size_Known_At_Compile_Time (Typ) |
| or else not Component_OK_For_Backend |
| or else not Static_Components |
| then |
| Convert_To_Assignments (N, Typ); |
| |
| -- In all other cases, build a proper aggregate to be handled by |
| -- the back-end. |
| |
| else |
| Build_Back_End_Aggregate; |
| end if; |
| |
| -- Gigi doesn't properly handle temporaries of variable size so we |
| -- generate it in the front-end |
| |
| elsif not Size_Known_At_Compile_Time (Typ) |
| and then Tagged_Type_Expansion |
| then |
| Convert_To_Assignments (N, Typ); |
| |
| -- An aggregate used to initialize a controlled object must be turned |
| -- into component assignments as the components themselves may require |
| -- finalization actions such as adjustment. |
| |
| elsif Needs_Finalization (Typ) then |
| Convert_To_Assignments (N, Typ); |
| |
| -- Ada 2005 (AI-287): In case of default initialized components we |
| -- convert the aggregate into assignments. |
| |
| elsif Has_Default_Init_Comps (N) then |
| Convert_To_Assignments (N, Typ); |
| |
| -- Check components |
| |
| elsif not Component_OK_For_Backend then |
| Convert_To_Assignments (N, Typ); |
| |
| -- If an ancestor is private, some components are not inherited and we |
| -- cannot expand into a record aggregate. |
| |
| elsif Has_Visible_Private_Ancestor (Typ) then |
| Convert_To_Assignments (N, Typ); |
| |
| -- ??? The following was done to compile fxacc00.ads in the ACVCs. Gigi |
| -- is not able to handle the aggregate for Late_Request. |
| |
| elsif Is_Tagged_Type (Typ) and then Has_Discriminants (Typ) then |
| Convert_To_Assignments (N, Typ); |
| |
| -- If the tagged types covers interface types we need to initialize all |
| -- hidden components containing pointers to secondary dispatch tables. |
| |
| elsif Is_Tagged_Type (Typ) and then Has_Interfaces (Typ) then |
| Convert_To_Assignments (N, Typ); |
| |
| -- If some components are mutable, the size of the aggregate component |
| -- may be distinct from the default size of the type component, so |
| -- we need to expand to insure that the back-end copies the proper |
| -- size of the data. However, if the aggregate is the initial value of |
| -- a constant, the target is immutable and might be built statically |
| -- if components are appropriate. |
| |
| elsif Has_Mutable_Components (Typ) |
| and then |
| (Nkind (Parent (Top_Level_Aggr)) /= N_Object_Declaration |
| or else not Constant_Present (Parent (Top_Level_Aggr)) |
| or else not Static_Components) |
| then |
| Convert_To_Assignments (N, Typ); |
| |
| -- If the type involved has bit aligned components, then we are not sure |
| -- that the back end can handle this case correctly. |
| |
| elsif Type_May_Have_Bit_Aligned_Components (Typ) then |
| Convert_To_Assignments (N, Typ); |
| |
| -- When generating C, only generate an aggregate when declaring objects |
| -- since C does not support aggregates in e.g. assignment statements. |
| |
| elsif Modify_Tree_For_C and then not Is_CCG_Supported_Aggregate (N) then |
| Convert_To_Assignments (N, Typ); |
| |
| -- In all other cases, build a proper aggregate to be handled by gigi |
| |
| else |
| Build_Back_End_Aggregate; |
| end if; |
| end Expand_Record_Aggregate; |
| |
| --------------------- |
| -- Get_Base_Object -- |
| --------------------- |
| |
| function Get_Base_Object (N : Node_Id) return Entity_Id is |
| R : Node_Id; |
| |
| begin |
| R := Get_Referenced_Object (N); |
| |
| while Nkind (R) in N_Indexed_Component | N_Selected_Component | N_Slice |
| loop |
| R := Get_Referenced_Object (Prefix (R)); |
| end loop; |
| |
| if Is_Entity_Name (R) and then Is_Object (Entity (R)) then |
| return Entity (R); |
| else |
| return Empty; |
| end if; |
| end Get_Base_Object; |
| |
| ---------------------------- |
| -- Has_Default_Init_Comps -- |
| ---------------------------- |
| |
| function Has_Default_Init_Comps (N : Node_Id) return Boolean is |
| Assoc : Node_Id; |
| Expr : Node_Id; |
| -- Component association and expression, respectively |
| |
| begin |
| pragma Assert (Nkind (N) in N_Aggregate | N_Extension_Aggregate); |
| |
| if Has_Self_Reference (N) then |
| return True; |
| end if; |
| |
| Assoc := First (Component_Associations (N)); |
| while Present (Assoc) loop |
| -- Each component association has either a box or an expression |
| |
| pragma Assert (Box_Present (Assoc) xor Present (Expression (Assoc))); |
| |
| -- Check if any direct component has default initialized components |
| |
| if Box_Present (Assoc) then |
| return True; |
| |
| -- Recursive call in case of aggregate expression |
| |
| else |
| Expr := Expression (Assoc); |
| |
| if Nkind (Expr) in N_Aggregate | N_Extension_Aggregate |
| and then Has_Default_Init_Comps (Expr) |
| then |
| return True; |
| end if; |
| end if; |
| |
| Next (Assoc); |
| end loop; |
| |
| return False; |
| end Has_Default_Init_Comps; |
| |
| ---------------------------------------- |
| -- Is_Build_In_Place_Aggregate_Return -- |
| ---------------------------------------- |
| |
| function Is_Build_In_Place_Aggregate_Return (N : Node_Id) return Boolean is |
| P : Node_Id := Parent (N); |
| |
| begin |
| while Nkind (P) = N_Qualified_Expression loop |
| P := Parent (P); |
| end loop; |
| |
| if Nkind (P) = N_Simple_Return_Statement then |
| null; |
| |
| elsif Nkind (Parent (P)) = N_Extended_Return_Statement then |
| P := Parent (P); |
| |
| else |
| return False; |
| end if; |
| |
| return |
| Is_Build_In_Place_Function |
| (Return_Applies_To (Return_Statement_Entity (P))); |
| end Is_Build_In_Place_Aggregate_Return; |
| |
| -------------------------- |
| -- Is_Delayed_Aggregate -- |
| -------------------------- |
| |
| function Is_Delayed_Aggregate (N : Node_Id) return Boolean is |
| Node : Node_Id := N; |
| Kind : Node_Kind := Nkind (Node); |
| |
| begin |
| if Kind = N_Qualified_Expression then |
| Node := Expression (Node); |
| Kind := Nkind (Node); |
| end if; |
| |
| return Kind in N_Aggregate | N_Extension_Aggregate |
| and then Expansion_Delayed (Node); |
| end Is_Delayed_Aggregate; |
| |
| -------------------------------- |
| -- Is_CCG_Supported_Aggregate -- |
| -------------------------------- |
| |
| function Is_CCG_Supported_Aggregate |
| (N : Node_Id) return Boolean |
| is |
| P : Node_Id := Parent (N); |
| |
| begin |
| -- Aggregates are not supported for nonstandard rep clauses, since they |
| -- may lead to extra padding fields in CCG. |
| |
| if Is_Record_Type (Etype (N)) |
| and then Has_Non_Standard_Rep (Etype (N)) |
| then |
| return False; |
| end if; |
| |
| while Present (P) and then Nkind (P) = N_Aggregate loop |
| P := Parent (P); |
| end loop; |
| |
| -- Check cases where aggregates are supported by the CCG backend |
| |
| if Nkind (P) = N_Object_Declaration then |
| declare |
| P_Typ : constant Entity_Id := Etype (Defining_Identifier (P)); |
| |
| begin |
| if Is_Record_Type (P_Typ) then |
| return True; |
| else |
| return Compile_Time_Known_Bounds (P_Typ); |
| end if; |
| end; |
| |
| elsif Nkind (P) = N_Qualified_Expression then |
| if Nkind (Parent (P)) = N_Object_Declaration then |
| declare |
| P_Typ : constant Entity_Id := |
| Etype (Defining_Identifier (Parent (P))); |
| begin |
| if Is_Record_Type (P_Typ) then |
| return True; |
| else |
| return Compile_Time_Known_Bounds (P_Typ); |
| end if; |
| end; |
| |
| elsif Nkind (Parent (P)) = N_Allocator then |
| return True; |
| end if; |
| end if; |
| |
| return False; |
| end Is_CCG_Supported_Aggregate; |
| |
| ---------------------------------------- |
| -- Is_Static_Dispatch_Table_Aggregate -- |
| ---------------------------------------- |
| |
| function Is_Static_Dispatch_Table_Aggregate (N : Node_Id) return Boolean is |
| Typ : constant Entity_Id := Base_Type (Etype (N)); |
| |
| begin |
| return Building_Static_Dispatch_Tables |
| and then Tagged_Type_Expansion |
| |
| -- Avoid circularity when rebuilding the compiler |
| |
| and then not Is_RTU (Cunit_Entity (Get_Source_Unit (N)), Ada_Tags) |
| and then (Is_RTE (Typ, RE_Dispatch_Table_Wrapper) |
| or else |
| Is_RTE (Typ, RE_Address_Array) |
| or else |
| Is_RTE (Typ, RE_Type_Specific_Data) |
| or else |
| Is_RTE (Typ, RE_Tag_Table) |
| or else |
| Is_RTE (Typ, RE_Object_Specific_Data) |
| or else |
| Is_RTE (Typ, RE_Interface_Data) |
| or else |
| Is_RTE (Typ, RE_Interfaces_Array) |
| or else |
| Is_RTE (Typ, RE_Interface_Data_Element)); |
| end Is_Static_Dispatch_Table_Aggregate; |
| |
| ----------------------------- |
| -- Is_Two_Dim_Packed_Array -- |
| ----------------------------- |
| |
| function Is_Two_Dim_Packed_Array (Typ : Entity_Id) return Boolean is |
| C : constant Uint := Component_Size (Typ); |
| begin |
| return Number_Dimensions (Typ) = 2 |
| and then Is_Bit_Packed_Array (Typ) |
| and then C in Uint_1 | Uint_2 | Uint_4; -- False if No_Uint |
| end Is_Two_Dim_Packed_Array; |
| |
| -------------------- |
| -- Late_Expansion -- |
| -------------------- |
| |
| function Late_Expansion |
| (N : Node_Id; |
| Typ : Entity_Id; |
| Target : Node_Id) return List_Id |
| is |
| Aggr_Code : List_Id; |
| New_Aggr : Node_Id; |
| |
| begin |
| if Is_Array_Type (Typ) then |
| -- If the assignment can be done directly by the back end, then |
| -- reset Set_Expansion_Delayed and do not expand further. |
| |
| if not CodePeer_Mode |
| and then not Modify_Tree_For_C |
| and then not Possible_Bit_Aligned_Component (Target) |
| and then not Is_Possibly_Unaligned_Slice (Target) |
| and then Aggr_Assignment_OK_For_Backend (N) |
| then |
| New_Aggr := New_Copy_Tree (N); |
| Set_Expansion_Delayed (New_Aggr, False); |
| |
| Aggr_Code := |
| New_List ( |
| Make_OK_Assignment_Statement (Sloc (New_Aggr), |
| Name => Target, |
| Expression => New_Aggr)); |
| |
| -- Or else, generate component assignments to it |
| |
| else |
| Aggr_Code := |
| Build_Array_Aggr_Code |
| (N => N, |
| Ctype => Component_Type (Typ), |
| Index => First_Index (Typ), |
| Into => Target, |
| Scalar_Comp => Is_Scalar_Type (Component_Type (Typ)), |
| Indexes => No_List); |
| end if; |
| |
| -- Directly or indirectly (e.g. access protected procedure) a record |
| |
| else |
| Aggr_Code := Build_Record_Aggr_Code (N, Typ, Target); |
| end if; |
| |
| -- Save the last assignment statement associated with the aggregate |
| -- when building a controlled object. This reference is utilized by |
| -- the finalization machinery when marking an object as successfully |
| -- initialized. |
| |
| if Needs_Finalization (Typ) |
| and then Is_Entity_Name (Target) |
| and then Present (Entity (Target)) |
| and then Ekind (Entity (Target)) in E_Constant | E_Variable |
| then |
| Set_Last_Aggregate_Assignment (Entity (Target), Last (Aggr_Code)); |
| end if; |
| |
| return Aggr_Code; |
| end Late_Expansion; |
| |
| ---------------------------------- |
| -- Make_OK_Assignment_Statement -- |
| ---------------------------------- |
| |
| function Make_OK_Assignment_Statement |
| (Sloc : Source_Ptr; |
| Name : Node_Id; |
| Expression : Node_Id) return Node_Id |
| is |
| begin |
| Set_Assignment_OK (Name); |
| return Make_Assignment_Statement (Sloc, Name, Expression); |
| end Make_OK_Assignment_Statement; |
| |
| ------------------------ |
| -- Max_Aggregate_Size -- |
| ------------------------ |
| |
| function Max_Aggregate_Size |
| (N : Node_Id; |
| Default_Size : Nat := 5000) return Nat |
| is |
| function Use_Small_Size (N : Node_Id) return Boolean; |
| -- True if we should return a very small size, which means large |
| -- aggregates will be implemented as a loop when possible (potentially |
| -- transformed to memset calls). |
| |
| function Aggr_Context (N : Node_Id) return Node_Id; |
| -- Return the context in which the aggregate appears, not counting |
| -- qualified expressions and similar. |
| |
| ------------------ |
| -- Aggr_Context -- |
| ------------------ |
| |
| function Aggr_Context (N : Node_Id) return Node_Id is |
| Result : Node_Id := Parent (N); |
| begin |
| if Nkind (Result) in N_Qualified_Expression |
| | N_Type_Conversion |
| | N_Unchecked_Type_Conversion |
| | N_If_Expression |
| | N_Case_Expression |
| | N_Component_Association |
| | N_Aggregate |
| then |
| Result := Aggr_Context (Result); |
| end if; |
| |
| return Result; |
| end Aggr_Context; |
| |
| -------------------- |
| -- Use_Small_Size -- |
| -------------------- |
| |
| function Use_Small_Size (N : Node_Id) return Boolean is |
| C : constant Node_Id := Aggr_Context (N); |
| -- The decision depends on the context in which the aggregate occurs, |
| -- and for variable declarations, whether we are nested inside a |
| -- subprogram. |
| begin |
| case Nkind (C) is |
| -- True for assignment statements and similar |
| |
| when N_Assignment_Statement |
| | N_Simple_Return_Statement |
| | N_Allocator |
| | N_Attribute_Reference |
| => |
| return True; |
| |
| -- True for nested variable declarations. False for library level |
| -- variables, and for constants (whether or not nested). |
| |
| when N_Object_Declaration => |
| return not Constant_Present (C) |
| and then Is_Subprogram (Current_Scope); |
| |
| -- False for all other contexts |
| |
| when others => |
| return False; |
| end case; |
| end Use_Small_Size; |
| |
| -- Local variables |
| |
| Typ : constant Entity_Id := Etype (N); |
| |
| -- Start of processing for Max_Aggregate_Size |
| |
| begin |
| -- We use a small limit in CodePeer mode where we favor loops instead of |
| -- thousands of single assignments (from large aggregates). |
| |
| -- We also increase the limit to 2**24 (about 16 million) if |
| -- Restrictions (No_Elaboration_Code) or Restrictions |
| -- (No_Implicit_Loops) is specified, since in either case we are at risk |
| -- of declaring the program illegal because of this limit. We also |
| -- increase the limit when Static_Elaboration_Desired, given that this |
| -- means that objects are intended to be placed in data memory. |
| |
| -- Same if the aggregate is for a packed two-dimensional array, because |
| -- if components are static it is much more efficient to construct a |
| -- one-dimensional equivalent array with static components. |
| |
| if CodePeer_Mode then |
| return 100; |
| elsif Restriction_Active (No_Elaboration_Code) |
| or else Restriction_Active (No_Implicit_Loops) |
| or else Is_Two_Dim_Packed_Array (Typ) |
| or else (Ekind (Current_Scope) = E_Package |
| and then Static_Elaboration_Desired (Current_Scope)) |
| then |
| return 2 ** 24; |
| elsif Use_Small_Size (N) then |
| return 64; |
| end if; |
| |
| return Default_Size; |
| end Max_Aggregate_Size; |
| |
| ----------------------- |
| -- Number_Of_Choices -- |
| ----------------------- |
| |
| function Number_Of_Choices (N : Node_Id) return Nat is |
| Assoc : Node_Id; |
| Choice : Node_Id; |
| |
| Nb_Choices : Nat := 0; |
| |
| begin |
| if Present (Expressions (N)) then |
| return 0; |
| end if; |
| |
| 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 |
| Nb_Choices := Nb_Choices + 1; |
| end if; |
| |
| Next (Choice); |
| end loop; |
| |
| Next (Assoc); |
| end loop; |
| |
| return Nb_Choices; |
| end Number_Of_Choices; |
| |
| ------------------------------------ |
| -- Packed_Array_Aggregate_Handled -- |
| ------------------------------------ |
| |
| -- The current version of this procedure will handle at compile time |
| -- any array aggregate that meets these conditions: |
| |
| -- One and two dimensional, bit packed |
| -- Underlying packed type is modular type |
| -- Bounds are within 32-bit Int range |
| -- All bounds and values are static |
| |
| -- Note: for now, in the 2-D case, we only handle component sizes of |
| -- 1, 2, 4 (cases where an integral number of elements occupies a byte). |
| |
| function Packed_Array_Aggregate_Handled (N : Node_Id) return Boolean is |
| Loc : constant Source_Ptr := Sloc (N); |
| Typ : constant Entity_Id := Etype (N); |
| Ctyp : constant Entity_Id := Component_Type (Typ); |
| |
| Not_Handled : exception; |
| -- Exception raised if this aggregate cannot be handled |
| |
| begin |
| -- Handle one- or two dimensional bit packed array |
| |
| if not Is_Bit_Packed_Array (Typ) |
| or else Number_Dimensions (Typ) > 2 |
| then |
| return False; |
| end if; |
| |
| -- If two-dimensional, check whether it can be folded, and transformed |
| -- into a one-dimensional aggregate for the Packed_Array_Impl_Type of |
| -- the original type. |
| |
| if Number_Dimensions (Typ) = 2 then |
| return Two_Dim_Packed_Array_Handled (N); |
| end if; |
| |
| if not Is_Modular_Integer_Type (Packed_Array_Impl_Type (Typ)) then |
| return False; |
| end if; |
| |
| if not Is_Scalar_Type (Ctyp) then |
| return False; |
| end if; |
| |
| declare |
| Csiz : constant Nat := UI_To_Int (Component_Size (Typ)); |
| |
| function Get_Component_Val (N : Node_Id) return Uint; |
| -- Given a expression value N of the component type Ctyp, returns a |
| -- value of Csiz (component size) bits representing this value. If |
| -- the value is nonstatic or any other reason exists why the value |
| -- cannot be returned, then Not_Handled is raised. |
| |
| ----------------------- |
| -- Get_Component_Val -- |
| ----------------------- |
| |
| function Get_Component_Val (N : Node_Id) return Uint is |
| Val : Uint; |
| |
| begin |
| -- We have to analyze the expression here before doing any further |
| -- processing here. The analysis of such expressions is deferred |
| -- till expansion to prevent some problems of premature analysis. |
| |
| Analyze_And_Resolve (N, Ctyp); |
| |
| -- Must have a compile time value. String literals have to be |
| -- converted into temporaries as well, because they cannot easily |
| -- be converted into their bit representation. |
| |
| if not Compile_Time_Known_Value (N) |
| or else Nkind (N) = N_String_Literal |
| then |
| raise Not_Handled; |
| end if; |
| |
| Val := Expr_Rep_Value (N); |
| |
| -- Adjust for bias, and strip proper number of bits |
| |
| if Has_Biased_Representation (Ctyp) then |
| Val := Val - Expr_Value (Type_Low_Bound (Ctyp)); |
| end if; |
| |
| return Val mod Uint_2 ** Csiz; |
| end Get_Component_Val; |
| |
| Bounds : constant Range_Nodes := Get_Index_Bounds (First_Index (Typ)); |
| |
| -- Here we know we have a one dimensional bit packed array |
| |
| begin |
| -- Cannot do anything if bounds are dynamic |
| |
| if not (Compile_Time_Known_Value (Bounds.First) |
| and then |
| Compile_Time_Known_Value (Bounds.Last)) |
| then |
| return False; |
| end if; |
| |
| declare |
| Bounds_Vals : Range_Values; |
| -- Compile-time known values of bounds |
| begin |
| -- Or are silly out of range of int bounds |
| |
| Bounds_Vals.First := Expr_Value (Bounds.First); |
| Bounds_Vals.Last := Expr_Value (Bounds.Last); |
| |
| if not UI_Is_In_Int_Range (Bounds_Vals.First) |
| or else |
| not UI_Is_In_Int_Range (Bounds_Vals.Last) |
| then |
| return False; |
| end if; |
| |
| -- At this stage we have a suitable aggregate for handling at |
| -- compile time. The only remaining checks are that the values of |
| -- expressions in the aggregate are compile-time known (checks are |
| -- performed by Get_Component_Val), and that any subtypes or |
| -- ranges are statically known. |
| |
| -- If the aggregate is not fully positional at this stage, then |
| -- convert it to positional form. Either this will fail, in which |
| -- case we can do nothing, or it will succeed, in which case we |
| -- have succeeded in handling the aggregate and transforming it |
| -- into a modular value, or it will stay an aggregate, in which |
| -- case we have failed to create a packed value for it. |
| |
| if Present (Component_Associations (N)) then |
| Convert_To_Positional (N, Handle_Bit_Packed => True); |
| return Nkind (N) /= N_Aggregate; |
| end if; |
| |
| -- Otherwise we are all positional, so convert to proper value |
| |
| declare |
| Len : constant Nat := |
| Int'Max (0, UI_To_Int (Bounds_Vals.Last) - |
| UI_To_Int (Bounds_Vals.First) + 1); |
| -- The length of the array (number of elements) |
| |
| Aggregate_Val : Uint; |
| -- Value of aggregate. The value is set in the low order bits |
| -- of this value. For the little-endian case, the values are |
| -- stored from low-order to high-order and for the big-endian |
| -- case the values are stored from high order to low order. |
| -- Note that gigi will take care of the conversions to left |
| -- justify the value in the big endian case (because of left |
| -- justified modular type processing), so we do not have to |
| -- worry about that here. |
| |
| Lit : Node_Id; |
| -- Integer literal for resulting constructed value |
| |
| Shift : Nat; |
| -- Shift count from low order for next value |
| |
| Incr : Int; |
| -- Shift increment for loop |
| |
| Expr : Node_Id; |
| -- Next expression from positional parameters of aggregate |
| |
| Left_Justified : Boolean; |
| -- Set True if we are filling the high order bits of the target |
| -- value (i.e. the value is left justified). |
| |
| begin |
| -- For little endian, we fill up the low order bits of the |
| -- target value. For big endian we fill up the high order bits |
| -- of the target value (which is a left justified modular |
| -- value). |
| |
| Left_Justified := Bytes_Big_Endian; |
| |
| -- Switch justification if using -gnatd8 |
| |
| if Debug_Flag_8 then |
| Left_Justified := not Left_Justified; |
| end if; |
| |
| -- Switch justfification if reverse storage order |
| |
| if Reverse_Storage_Order (Base_Type (Typ)) then |
| Left_Justified := not Left_Justified; |
| end if; |
| |
| if Left_Justified then |
| Shift := Csiz * (Len - 1); |
| Incr := -Csiz; |
| else |
| Shift := 0; |
| Incr := +Csiz; |
| end if; |
| |
| -- Loop to set the values |
| |
| if Len = 0 then |
| Aggregate_Val := Uint_0; |
| else |
| Expr := First (Expressions (N)); |
| Aggregate_Val := Get_Component_Val (Expr) * Uint_2 ** Shift; |
| |
| for J in 2 .. Len loop |
| Shift := Shift + Incr; |
| Next (Expr); |
| Aggregate_Val := |
| Aggregate_Val + |
| Get_Component_Val (Expr) * Uint_2 ** Shift; |
| end loop; |
| end if; |
| |
| -- Now we can rewrite with the proper value |
| |
| Lit := Make_Integer_Literal (Loc, Intval => Aggregate_Val); |
| Set_Print_In_Hex (Lit); |
| |
| -- Construct the expression using this literal. Note that it |
| -- is important to qualify the literal with its proper modular |
| -- type since universal integer does not have the required |
| -- range and also this is a left justified modular type, |
| -- which is important in the big-endian case. |
| |
| Rewrite (N, |
| Unchecked_Convert_To (Typ, |
| Make_Qualified_Expression (Loc, |
| Subtype_Mark => |
| New_Occurrence_Of (Packed_Array_Impl_Type (Typ), Loc), |
| Expression => Lit))); |
| |
| Analyze_And_Resolve (N, Typ); |
| return True; |
| end; |
| end; |
| end; |
| |
| exception |
| when Not_Handled => |
| return False; |
| end Packed_Array_Aggregate_Handled; |
| |
| ---------------------------- |
| -- Has_Mutable_Components -- |
| ---------------------------- |
| |
| function Has_Mutable_Components (Typ : Entity_Id) return Boolean is |
| Comp : Entity_Id; |
| Ctyp : Entity_Id; |
| |
| begin |
| Comp := First_Component (Typ); |
| while Present (Comp) loop |
| Ctyp := Underlying_Type (Etype (Comp)); |
| if Is_Record_Type (Ctyp) |
| and then Has_Discriminants (Ctyp) |
| and then not Is_Constrained (Ctyp) |
| then |
| return True; |
| end if; |
| |
| Next_Component (Comp); |
| end loop; |
| |
| return False; |
| end Has_Mutable_Components; |
| |
| ------------------------------ |
| -- Initialize_Discriminants -- |
| ------------------------------ |
| |
| procedure Initialize_Discriminants (N : Node_Id; Typ : Entity_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Bas : constant Entity_Id := Base_Type (Typ); |
| Par : constant Entity_Id := Etype (Bas); |
| Decl : constant Node_Id := Parent (Par); |
| Ref : Node_Id; |
| |
| begin |
| if Is_Tagged_Type (Bas) |
| and then Is_Derived_Type (Bas) |
| and then Has_Discriminants (Par) |
| and then Has_Discriminants (Bas) |
| and then Number_Discriminants (Bas) /= Number_Discriminants (Par) |
| and then Nkind (Decl) = N_Full_Type_Declaration |
| and then Nkind (Type_Definition (Decl)) = N_Record_Definition |
| and then |
| Present (Variant_Part (Component_List (Type_Definition (Decl)))) |
| and then Nkind (N) /= N_Extension_Aggregate |
| then |
| |
| -- Call init proc to set discriminants. |
| -- There should eventually be a special procedure for this ??? |
| |
| Ref := New_Occurrence_Of (Defining_Identifier (N), Loc); |
| Insert_Actions_After (N, |
| Build_Initialization_Call (Sloc (N), Ref, Typ)); |
| end if; |
| end Initialize_Discriminants; |
| |
| ---------------- |
| -- Must_Slide -- |
| ---------------- |
| |
| function Must_Slide |
| (Aggr : Node_Id; |
| Obj_Type : Entity_Id; |
| Typ : Entity_Id) return Boolean |
| is |
| begin |
| -- No sliding if the type of the object is not established yet, if it is |
| -- an unconstrained type whose actual subtype comes from the aggregate, |
| -- or if the two types are identical. If the aggregate contains only |
| -- an Others_Clause it gets its type from the context and no sliding |
| -- is involved either. |
| |
| if not Is_Array_Type (Obj_Type) then |
| return False; |
| |
| elsif not Is_Constrained (Obj_Type) then |
| return False; |
| |
| elsif Typ = Obj_Type then |
| return False; |
| |
| elsif Is_Others_Aggregate (Aggr) then |
| return False; |
| |
| else |
| -- Sliding can only occur along the first dimension |
| -- If any the bounds of non-static sliding is required |
| -- to force potential range checks. |
| |
| declare |
| Bounds1 : constant Range_Nodes := |
| Get_Index_Bounds (First_Index (Typ)); |
| Bounds2 : constant Range_Nodes := |
| Get_Index_Bounds (First_Index (Obj_Type)); |
| |
| begin |
| if not Is_OK_Static_Expression (Bounds1.First) or else |
| not Is_OK_Static_Expression (Bounds2.First) or else |
| not Is_OK_Static_Expression (Bounds1.Last) or else |
| not Is_OK_Static_Expression (Bounds2.Last) |
| then |
| return True; |
| |
| else |
| return Expr_Value (Bounds1.First) /= Expr_Value (Bounds2.First) |
| or else |
| Expr_Value (Bounds1.Last) /= Expr_Value (Bounds2.Last); |
| end if; |
| end; |
| end if; |
| end Must_Slide; |
| |
| --------------------------------- |
| -- Process_Transient_Component -- |
| --------------------------------- |
| |
| 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) |
| is |
| procedure Add_Item (Item : Node_Id); |
| -- Insert arbitrary node Item into the tree depending on the values of |
| -- Aggr and Stmts. |
| |
| -------------- |
| -- Add_Item -- |
| -------------- |
| |
| procedure Add_Item (Item : Node_Id) is |
| begin |
| if Present (Aggr) then |
| Insert_Action (Aggr, Item); |
| else |
| pragma Assert (Present (Stmts)); |
| Append_To (Stmts, Item); |
| end if; |
| end Add_Item; |
| |
| -- Local variables |
| |
| Hook_Assign : Node_Id; |
| Hook_Decl : Node_Id; |
| Ptr_Decl : Node_Id; |
| Res_Decl : Node_Id; |
| Res_Id : Entity_Id; |
| Res_Typ : Entity_Id; |
| |
| -- Start of processing for Process_Transient_Component |
| |
| begin |
| -- Add the access type, which provides a reference to the function |
| -- result. Generate: |
| |
| -- type Res_Typ is access all Comp_Typ; |
| |
| Res_Typ := Make_Temporary (Loc, 'A'); |
| Mutate_Ekind (Res_Typ, E_General_Access_Type); |
| Set_Directly_Designated_Type (Res_Typ, Comp_Typ); |
| |
| Add_Item |
| (Make_Full_Type_Declaration (Loc, |
| Defining_Identifier => Res_Typ, |
| Type_Definition => |
| Make_Access_To_Object_Definition (Loc, |
| All_Present => True, |
| Subtype_Indication => New_Occurrence_Of (Comp_Typ, Loc)))); |
| |
| -- Add the temporary which captures the result of the function call. |
| -- Generate: |
| |
| -- Res : constant Res_Typ := Init_Expr'Reference; |
| |
| -- Note that this temporary is effectively a transient object because |
| -- its lifetime is bounded by the current array or record component. |
| |
| Res_Id := Make_Temporary (Loc, 'R'); |
| Mutate_Ekind (Res_Id, E_Constant); |
| Set_Etype (Res_Id, Res_Typ); |
| |
| -- Mark the transient object as successfully processed to avoid double |
| -- finalization. |
| |
| Set_Is_Finalized_Transient (Res_Id); |
| |
| -- Signal the general finalization machinery that this transient object |
| -- should not be considered for finalization actions because its cleanup |
| -- will be performed by Process_Transient_Component_Completion. |
| |
| Set_Is_Ignored_Transient (Res_Id); |
| |
| Res_Decl := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Res_Id, |
| Constant_Present => True, |
| Object_Definition => New_Occurrence_Of (Res_Typ, Loc), |
| Expression => |
| Make_Reference (Loc, New_Copy_Tree (Init_Expr))); |
| |
| Add_Item (Res_Decl); |
| |
| -- Construct all pieces necessary to hook and finalize the transient |
| -- result. |
| |
| Build_Transient_Object_Statements |
| (Obj_Decl => Res_Decl, |
| Fin_Call => Fin_Call, |
| Hook_Assign => Hook_Assign, |
| Hook_Clear => Hook_Clear, |
| Hook_Decl => Hook_Decl, |
| Ptr_Decl => Ptr_Decl); |
| |
| -- Add the access type which provides a reference to the transient |
| -- result. Generate: |
| |
| -- type Ptr_Typ is access all Comp_Typ; |
| |
| Add_Item (Ptr_Decl); |
| |
| -- Add the temporary which acts as a hook to the transient result. |
| -- Generate: |
| |
| -- Hook : Ptr_Typ := null; |
| |
| Add_Item (Hook_Decl); |
| |
| -- Attach the transient result to the hook. Generate: |
| |
| -- Hook := Ptr_Typ (Res); |
| |
| Add_Item (Hook_Assign); |
| |
| -- The original initialization expression now references the value of |
| -- the temporary function result. Generate: |
| |
| -- Res.all |
| |
| Rewrite (Init_Expr, |
| Make_Explicit_Dereference (Loc, |
| Prefix => New_Occurrence_Of (Res_Id, Loc))); |
| end Process_Transient_Component; |
| |
| -------------------------------------------- |
| -- Process_Transient_Component_Completion -- |
| -------------------------------------------- |
| |
| procedure Process_Transient_Component_Completion |
| (Loc : Source_Ptr; |
| Aggr : Node_Id; |
| Fin_Call : Node_Id; |
| Hook_Clear : Node_Id; |
| Stmts : List_Id) |
| is |
| Exceptions_OK : constant Boolean := |
| not Restriction_Active (No_Exception_Propagation); |
| |
| begin |
| pragma Assert (Present (Hook_Clear)); |
| |
| -- Generate the following code if exception propagation is allowed: |
| |
| -- declare |
| -- Abort : constant Boolean := Triggered_By_Abort; |
| -- <or> |
| -- Abort : constant Boolean := False; -- no abort |
| |
| -- E : Exception_Occurrence; |
| -- Raised : Boolean := False; |
| |
| -- begin |
| -- [Abort_Defer;] |
| |
| -- begin |
| -- Hook := null; |
| -- [Deep_]Finalize (Res.all); |
| |
| -- exception |
| -- when others => |
| -- if not Raised then |
| -- Raised := True; |
| -- Save_Occurrence (E, |
| -- Get_Curent_Excep.all.all); |
| -- end if; |
| -- end; |
| |
| -- [Abort_Undefer;] |
| |
| -- if Raised and then not Abort then |
| -- Raise_From_Controlled_Operation (E); |
| -- end if; |
| -- end; |
| |
| if Exceptions_OK then |
| Abort_And_Exception : declare |
| Blk_Decls : constant List_Id := New_List; |
| Blk_Stmts : constant List_Id := New_List; |
| Fin_Stmts : constant List_Id := New_List; |
| |
| Fin_Data : Finalization_Exception_Data; |
| |
| begin |
| -- Create the declarations of the two flags and the exception |
| -- occurrence. |
| |
| Build_Object_Declarations (Fin_Data, Blk_Decls, Loc); |
| |
| -- Generate: |
| -- Abort_Defer; |
| |
| if Abort_Allowed then |
| Append_To (Blk_Stmts, |
| Build_Runtime_Call (Loc, RE_Abort_Defer)); |
| end if; |
| |
| -- Wrap the hook clear and the finalization call in order to trap |
| -- a potential exception. |
| |
| Append_To (Fin_Stmts, Hook_Clear); |
| |
| if Present (Fin_Call) then |
| Append_To (Fin_Stmts, Fin_Call); |
| end if; |
| |
| Append_To (Blk_Stmts, |
| Make_Block_Statement (Loc, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => Fin_Stmts, |
| Exception_Handlers => New_List ( |
| Build_Exception_Handler (Fin_Data))))); |
| |
| -- Generate: |
| -- Abort_Undefer; |
| |
| if Abort_Allowed then |
| Append_To (Blk_Stmts, |
| Build_Runtime_Call (Loc, RE_Abort_Undefer)); |
| end if; |
| |
| -- Reraise the potential exception with a proper "upgrade" to |
| -- Program_Error if needed. |
| |
| Append_To (Blk_Stmts, Build_Raise_Statement (Fin_Data)); |
| |
| -- Wrap everything in a block |
| |
| Append_To (Stmts, |
| Make_Block_Statement (Loc, |
| Declarations => Blk_Decls, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => Blk_Stmts))); |
| end Abort_And_Exception; |
| |
| -- Generate the following code if exception propagation is not allowed |
| -- and aborts are allowed: |
| |
| -- begin |
| -- Abort_Defer; |
| -- Hook := null; |
| -- [Deep_]Finalize (Res.all); |
| -- at end |
| -- Abort_Undefer_Direct; |
| -- end; |
| |
| elsif Abort_Allowed then |
| Abort_Only : declare |
| Blk_Stmts : constant List_Id := New_List; |
| |
| begin |
| Append_To (Blk_Stmts, Build_Runtime_Call (Loc, RE_Abort_Defer)); |
| Append_To (Blk_Stmts, Hook_Clear); |
| |
| if Present (Fin_Call) then |
| Append_To (Blk_Stmts, Fin_Call); |
| end if; |
| |
| Append_To (Stmts, |
| Build_Abort_Undefer_Block (Loc, |
| Stmts => Blk_Stmts, |
| Context => Aggr)); |
| end Abort_Only; |
| |
| -- Otherwise generate: |
| |
| -- Hook := null; |
| -- [Deep_]Finalize (Res.all); |
| |
| else |
| Append_To (Stmts, Hook_Clear); |
| |
| if Present (Fin_Call) then |
| Append_To (Stmts, Fin_Call); |
| end if; |
| end if; |
| end Process_Transient_Component_Completion; |
| |
| --------------------- |
| -- Sort_Case_Table -- |
| --------------------- |
| |
| procedure Sort_Case_Table (Case_Table : in out Case_Table_Type) is |
| L : constant Int := Case_Table'First; |
| U : constant Int := Case_Table'Last; |
| K : Int; |
| J : Int; |
| T : Case_Bounds; |
| |
| begin |
| K := L; |
| while K /= U loop |
| T := Case_Table (K + 1); |
| |
| J := K + 1; |
| while J /= L |
| and then Expr_Value (Case_Table (J - 1).Choice_Lo) > |
| Expr_Value (T.Choice_Lo) |
| loop |
| Case_Table (J) := Case_Table (J - 1); |
| J := J - 1; |
| end loop; |
| |
| Case_Table (J) := T; |
| K := K + 1; |
| end loop; |
| end Sort_Case_Table; |
| |
| ---------------------------- |
| -- Static_Array_Aggregate -- |
| ---------------------------- |
| |
| function Static_Array_Aggregate (N : Node_Id) return Boolean is |
| function Is_Static_Component (Nod : Node_Id) return Boolean; |
| -- Return True if Nod has a compile-time known value and can be passed |
| -- as is to the back-end without further expansion. |
| |
| --------------------------- |
| -- Is_Static_Component -- |
| --------------------------- |
| |
| function Is_Static_Component (Nod : Node_Id) return Boolean is |
| begin |
| if Nkind (Nod) in N_Integer_Literal | N_Real_Literal then |
| return True; |
| |
| elsif Is_Entity_Name (Nod) |
| and then Present (Entity (Nod)) |
| and then Ekind (Entity (Nod)) = E_Enumeration_Literal |
| then |
| return True; |
| |
| elsif Nkind (Nod) = N_Aggregate |
| and then Compile_Time_Known_Aggregate (Nod) |
| then |
| return True; |
| |
| else |
| return False; |
| end if; |
| end Is_Static_Component; |
| |
| -- Local variables |
| |
| Bounds : constant Node_Id := Aggregate_Bounds (N); |
| Typ : constant Entity_Id := Etype (N); |
| |
| Agg : Node_Id; |
| Expr : Node_Id; |
| Lo : Node_Id; |
| Hi : Node_Id; |
| |
| -- Start of processing for Static_Array_Aggregate |
| |
| begin |
| if Is_Packed (Typ) or else Has_Discriminants (Component_Type (Typ)) then |
| return False; |
| end if; |
| |
| if Present (Bounds) |
| and then Nkind (Bounds) = N_Range |
| and then Nkind (Low_Bound (Bounds)) = N_Integer_Literal |
| and then Nkind (High_Bound (Bounds)) = N_Integer_Literal |
| then |
| Lo := Low_Bound (Bounds); |
| Hi := High_Bound (Bounds); |
| |
| if No (Component_Associations (N)) then |
| |
| -- Verify that all components are static |
| |
| Expr := First (Expressions (N)); |
| while Present (Expr) loop |
| if not Is_Static_Component (Expr) then |
| return False; |
| end if; |
| |
| Next (Expr); |
| end loop; |
| |
| return True; |
| |
| else |
| -- We allow only a single named association, either a static |
| -- range or an others_clause, with a static expression. |
| |
| Expr := First (Component_Associations (N)); |
| |
| if Present (Expressions (N)) then |
| return False; |
| |
| elsif Present (Next (Expr)) then |
| return False; |
| |
| elsif Present (Next (First (Choice_List (Expr)))) then |
| return False; |
| |
| else |
| -- The aggregate is static if all components are literals, |
| -- or else all its components are static aggregates for the |
| -- component type. We also limit the size of a static aggregate |
| -- to prevent runaway static expressions. |
| |
| if not Is_Static_Component (Expression (Expr)) then |
| return False; |
| end if; |
| |
| if not Aggr_Size_OK (N) then |
| return False; |
| end if; |
| |
| -- Create a positional aggregate with the right number of |
| -- copies of the expression. |
| |
| Agg := Make_Aggregate (Sloc (N), New_List, No_List); |
| |
| for I in UI_To_Int (Intval (Lo)) .. UI_To_Int (Intval (Hi)) |
| loop |
| Append_To (Expressions (Agg), New_Copy (Expression (Expr))); |
| |
| -- The copied expression must be analyzed and resolved. |
| -- Besides setting the type, this ensures that static |
| -- expressions are appropriately marked as such. |
| |
| Analyze_And_Resolve |
| (Last (Expressions (Agg)), Component_Type (Typ)); |
| end loop; |
| |
| Set_Aggregate_Bounds (Agg, Bounds); |
| Set_Etype (Agg, Typ); |
| Set_Analyzed (Agg); |
| Rewrite (N, Agg); |
| Set_Compile_Time_Known_Aggregate (N); |
| |
| return True; |
| end if; |
| end if; |
| |
| else |
| return False; |
| end if; |
| end Static_Array_Aggregate; |
| |
| ---------------------------------- |
| -- Two_Dim_Packed_Array_Handled -- |
| ---------------------------------- |
| |
| function Two_Dim_Packed_Array_Handled (N : Node_Id) return Boolean is |
| Loc : constant Source_Ptr := Sloc (N); |
| Typ : constant Entity_Id := Etype (N); |
| Ctyp : constant Entity_Id := Component_Type (Typ); |
| Comp_Size : constant Int := UI_To_Int (Component_Size (Typ)); |
| Packed_Array : constant Entity_Id := |
| Packed_Array_Impl_Type (Base_Type (Typ)); |
| |
| One_Comp : Node_Id; |
| -- Expression in original aggregate |
| |
| One_Dim : Node_Id; |
| -- One-dimensional subaggregate |
| |
| begin |
| |
| -- For now, only deal with cases where an integral number of elements |
| -- fit in a single byte. This includes the most common boolean case. |
| |
| if not (Comp_Size = 1 or else |
| Comp_Size = 2 or else |
| Comp_Size = 4) |
| then |
| return False; |
| end if; |
| |
| Convert_To_Positional (N, Handle_Bit_Packed => True); |
| |
| -- Verify that all components are static |
| |
| if Nkind (N) = N_Aggregate |
| and then Compile_Time_Known_Aggregate (N) |
| then |
| null; |
| |
| -- The aggregate may have been reanalyzed and converted already |
| |
| elsif Nkind (N) /= N_Aggregate then |
| return True; |
| |
| -- If component associations remain, the aggregate is not static |
| |
| elsif Present (Component_Associations (N)) then |
| return False; |
| |
| else |
| One_Dim := First (Expressions (N)); |
| while Present (One_Dim) loop |
| if Present (Component_Associations (One_Dim)) then |
| return False; |
| end if; |
| |
| One_Comp := First (Expressions (One_Dim)); |
| while Present (One_Comp) loop |
| if not Is_OK_Static_Expression (One_Comp) then |
| return False; |
| end if; |
| |
| Next (One_Comp); |
| end loop; |
| |
| Next (One_Dim); |
| end loop; |
| end if; |
| |
| -- Two-dimensional aggregate is now fully positional so pack one |
| -- dimension to create a static one-dimensional array, and rewrite |
| -- as an unchecked conversion to the original type. |
| |
| declare |
| Byte_Size : constant Int := UI_To_Int (Component_Size (Packed_Array)); |
| -- The packed array type is a byte array |
| |
| Packed_Num : Nat; |
| -- Number of components accumulated in current byte |
| |
| Comps : List_Id; |
| -- Assembled list of packed values for equivalent aggregate |
| |
| Comp_Val : Uint; |
| -- Integer value of component |
| |
| Incr : Int; |
| -- Step size for packing |
| |
| Init_Shift : Int; |
| -- Endian-dependent start position for packing |
| |
| Shift : Int; |
| -- Current insertion position |
| |
| Val : Int; |
| -- Component of packed array being assembled |
| |
| begin |
| Comps := New_List; |
| Val := 0; |
| Packed_Num := 0; |
| |
| -- Account for endianness. See corresponding comment in |
| -- Packed_Array_Aggregate_Handled concerning the following. |
| |
| if Bytes_Big_Endian |
| xor Debug_Flag_8 |
| xor Reverse_Storage_Order (Base_Type (Typ)) |
| then |
| Init_Shift := Byte_Size - Comp_Size; |
| Incr := -Comp_Size; |
| else |
| Init_Shift := 0; |
| Incr := +Comp_Size; |
| end if; |
| |
| -- Iterate over each subaggregate |
| |
| Shift := Init_Shift; |
| One_Dim := First (Expressions (N)); |
| while Present (One_Dim) loop |
| One_Comp := First (Expressions (One_Dim)); |
| while Present (One_Comp) loop |
| if Packed_Num = Byte_Size / Comp_Size then |
| |
| -- Byte is complete, add to list of expressions |
| |
| Append (Make_Integer_Literal (Sloc (One_Dim), Val), Comps); |
| Val := 0; |
| Shift := Init_Shift; |
| Packed_Num := 0; |
| |
| else |
| Comp_Val := Expr_Rep_Value (One_Comp); |
| |
| -- Adjust for bias, and strip proper number of bits |
| |
| if Has_Biased_Representation (Ctyp) then |
| Comp_Val := Comp_Val - Expr_Value (Type_Low_Bound (Ctyp)); |
| end if; |
| |
| Comp_Val := Comp_Val mod Uint_2 ** Comp_Size; |
| Val := UI_To_Int (Val + Comp_Val * Uint_2 ** Shift); |
| Shift := Shift + Incr; |
| Next (One_Comp); |
| Packed_Num := Packed_Num + 1; |
| end if; |
| end loop; |
| |
| Next (One_Dim); |
| end loop; |
| |
| if Packed_Num > 0 then |
| |
| -- Add final incomplete byte if present |
| |
| Append (Make_Integer_Literal (Sloc (One_Dim), Val), Comps); |
| end if; |
| |
| Rewrite (N, |
| Unchecked_Convert_To (Typ, |
| Make_Qualified_Expression (Loc, |
| Subtype_Mark => New_Occurrence_Of (Packed_Array, Loc), |
| Expression => Make_Aggregate (Loc, Expressions => Comps)))); |
| Analyze_And_Resolve (N); |
| return True; |
| end; |
| end Two_Dim_Packed_Array_Handled; |
| |
| end Exp_Aggr; |