| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- E X P _ A G G R -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2015, 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 Atree; use Atree; |
| with Checks; use Checks; |
| with Debug; use Debug; |
| with Einfo; use Einfo; |
| 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 Fname; use Fname; |
| 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_Ch3; use Sem_Ch3; |
| with Sem_Eval; use Sem_Eval; |
| with Sem_Res; use Sem_Res; |
| with Sem_Util; use Sem_Util; |
| with Sinfo; use Sinfo; |
| with Snames; use Snames; |
| with Stand; use Stand; |
| with Stringt; use Stringt; |
| with Targparm; use Targparm; |
| with Tbuild; use Tbuild; |
| with Uintp; use Uintp; |
| |
| 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 Collect_Initialization_Statements |
| (Obj : Entity_Id; |
| N : Node_Id; |
| Node_After : Node_Id); |
| -- If Obj is not frozen, collect actions inserted after N until, but not |
| -- including, Node_After, for initialization of Obj, and move them to an |
| -- expression with actions, which becomes the Initialization_Statements for |
| -- Obj. |
| |
| 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_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 Must_Slide |
| (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. |
| |
| 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 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); |
| -- N is an N_Aggregate or an N_Extension_Aggregate. Typ is the type of the |
| -- aggregate (which can only be a record type, this procedure is only used |
| -- for record types). Transform the given aggregate into a sequence of |
| -- assignments performed component by component. |
| |
| 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. |
| |
| 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_Size_OK (N : Node_Id; Typ : Entity_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 non-static 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 sub-aggregate 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; |
| Max_Others_Replicate : Nat := 5; |
| 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 Max_Others_Replicate controls the maximum number of |
| -- values corresponding to an others choice that will be converted to |
| -- positional notation (the default of 5 is the normal limit, and reflects |
| -- the fact that normally the loop is better than a lot of separate |
| -- assignments). Note that this limit gets overridden in any case if |
| -- either of the restrictions No_Elaboration_Code or No_Implicit_Loops is |
| -- set. 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. |
| |
| -- It would seem useful to have a higher default for Max_Others_Replicate, |
| -- but aggregates in the compiler make this impossible: the compiler |
| -- bootstrap fails if Max_Others_Replicate is greater than 25. This |
| -- is unexpected ??? |
| |
| 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 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 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. |
| |
| 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_Size_OK -- |
| ------------------ |
| |
| function Aggr_Size_OK (N : Node_Id; Typ : Entity_Id) return Boolean is |
| Lo : Node_Id; |
| Hi : Node_Id; |
| Indx : Node_Id; |
| Siz : Int; |
| 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 Int; |
| -- The limit is applied to the total number of components 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 Int is |
| Res : Int := 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 Int := Component_Count (Component_Type (T)); |
| |
| begin |
| if not Compile_Time_Known_Value (Lo) |
| or else not Compile_Time_Known_Value (Hi) |
| then |
| return 0; |
| else |
| return |
| Siz * UI_To_Int (Expr_Value (Hi) - Expr_Value (Lo) + 1); |
| 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 |
| -- The normal aggregate limit is 50000, but we increase this 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. |
| |
| -- We also increase the limit 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. |
| |
| -- Conversely, we decrease the maximum size if none of the above |
| -- requirements apply, and if the aggregate has a single component |
| -- association, which will be more efficient if implemented with a loop. |
| |
| -- Finally, we use a small limit in CodePeer mode where we favor loops |
| -- instead of thousands of single assignments (from large aggregates). |
| |
| Max_Aggr_Size := 50000; |
| |
| if CodePeer_Mode then |
| Max_Aggr_Size := 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 |
| Max_Aggr_Size := 2 ** 24; |
| |
| elsif No (Expressions (N)) |
| and then No (Next (First (Component_Associations (N)))) |
| then |
| Max_Aggr_Size := 5000; |
| end if; |
| |
| Siz := 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 non-static 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 (Choices (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; |
| |
| Siz := Siz * UI_To_Int (Rng); |
| end; |
| |
| if Siz <= 0 |
| or else Siz > Max_Aggr_Size |
| then |
| return False; |
| end if; |
| |
| -- 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. For a VM back end, the array should have no aliased components |
| |
| 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, and 9. In the multi-dimensional case, these checks are |
| -- performed on subaggregates. The Index value is the current index |
| -- being checked in the multi-dimensional case. |
| |
| --------------------- |
| -- Component_Check -- |
| --------------------- |
| |
| function Component_Check (N : Node_Id; Index : Node_Id) return Boolean is |
| Expr : Node_Id; |
| |
| begin |
| -- Checks 1: (no component associations) |
| |
| if Present (Component_Associations (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 non-static, |
| -- 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; |
| |
| -- 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 multi-dimensional 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_Not_OK_For_Backend for record aggregates |
| -- with tagged components, but not clear whether it's worthwhile ???; |
| -- in the case of the JVM, 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; |
| |
| -- Checks 11: Array aggregates with aliased components are currently |
| -- not well supported by the VM backend; disable temporarily this |
| -- backend processing until it is definitely supported. |
| |
| if VM_Target /= No_VM |
| and then Has_Aliased_Components (Base_Type (Typ)) |
| then |
| return False; |
| end if; |
| |
| -- Backend processing is possible |
| |
| Set_Size_Known_At_Compile_Time (Etype (N), True); |
| return True; |
| end Backend_Processing_Possible; |
| |
| --------------------------- |
| -- Build_Array_Aggr_Code -- |
| --------------------------- |
| |
| -- The code that we generate from a one dimensional aggregate is |
| |
| -- 1. If the sub-aggregate 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) return List_Id; |
| -- Ind must be a side-effect free expression. If the input aggregate |
| -- N to Build_Loop contains no sub-aggregates, then this function |
| -- returns the assignment statement: |
| -- |
| -- Into (Indexes, Ind) := Expr; |
| -- |
| -- Otherwise we call Build_Code recursively |
| -- |
| -- 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 sub-aggregates, |
| -- 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 less scalar elements we |
| -- generate a sequence of assignments. |
| |
| 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 sub-aggregates, |
| -- 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) return List_Id is |
| L : constant List_Id := New_List; |
| A : Node_Id; |
| |
| New_Indexes : List_Id; |
| Indexed_Comp : Node_Id; |
| Expr_Q : Node_Id; |
| Comp_Type : Entity_Id := Empty; |
| |
| 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. |
| |
| ---------------------- |
| -- 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; |
| |
| -- 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_Type := Component_Type (Etype (N)); |
| pragma Assert (Comp_Type = 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_Type := Component_Type (Etype (P)); |
| exit; |
| |
| else |
| P := Parent (P); |
| end if; |
| end loop; |
| |
| pragma Assert (Comp_Type = 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_In (Expr_Q, 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 ??? |
| |
| if Present (Comp_Type) and then not Is_Array_Type (Comp_Type) then |
| Analyze_And_Resolve (Expr_Q, Comp_Type); |
| 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 |
| -- re-analysis 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_Type) |
| and then Present (Component_Associations (Expr_Q)) |
| and then Must_Slide (Comp_Type, 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; |
| |
| -- 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. |
| |
| if No (Expr) then |
| if Present (Base_Init_Proc (Base_Type (Ctype))) |
| or else Has_Task (Base_Type (Ctype)) |
| then |
| Append_List_To (L, |
| Build_Initialization_Call (Loc, |
| Id_Ref => Indexed_Comp, |
| Typ => Ctype, |
| With_Default_Init => True)); |
| |
| elsif Is_Access_Type (Ctype) then |
| Append_To (L, |
| Make_Assignment_Statement (Loc, |
| Name => Indexed_Comp, |
| Expression => Make_Null (Loc))); |
| end if; |
| |
| if Needs_Finalization (Ctype) then |
| Append_To (L, |
| Make_Init_Call |
| (Obj_Ref => New_Copy_Tree (Indexed_Comp), |
| Typ => Ctype)); |
| end if; |
| |
| else |
| A := |
| Make_OK_Assignment_Statement (Loc, |
| Name => Indexed_Comp, |
| Expression => New_Copy_Tree (Expr)); |
| |
| -- The target of the assignment may not have been initialized, |
| -- so it is not possible to call Finalize as expected in normal |
| -- controlled assignments. We must also avoid using the primitive |
| -- _assign (which depends on a valid target, and may for example |
| -- perform discriminant checks on it). |
| |
| -- Both Finalize and usage of _assign are disabled by setting |
| -- No_Ctrl_Actions on the assignment. The rest of the controlled |
| -- actions are done manually with the proper finalization list |
| -- coming from the context. |
| |
| Set_No_Ctrl_Actions (A); |
| |
| -- If this is an aggregate for an array of arrays, each |
| -- sub-aggregate 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, to prevent |
| -- multiple attachments of the same temporary location to same |
| -- finalization chain (and consequently circular lists). To ensure |
| -- that finalization takes place for each subaggregate we wrap the |
| -- assignment in a block. |
| |
| if Present (Comp_Type) |
| and then Needs_Finalization (Comp_Type) |
| and then Is_Array_Type (Comp_Type) |
| and then Present (Expr) |
| then |
| A := |
| Make_Block_Statement (Loc, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List (A))); |
| end if; |
| |
| Append_To (L, A); |
| |
| -- Adjust the tag if tagged (because of possible view |
| -- conversions), unless compiling for a VM where tags |
| -- are implicit. |
| |
| if Present (Comp_Type) |
| and then Is_Tagged_Type (Comp_Type) |
| and then Tagged_Type_Expansion |
| then |
| declare |
| Full_Typ : constant Entity_Id := Underlying_Type (Comp_Type); |
| |
| begin |
| A := |
| Make_OK_Assignment_Statement (Loc, |
| Name => |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Indexed_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))); |
| |
| Append_To (L, A); |
| end; |
| end if; |
| |
| -- Adjust and attach the component to the proper final list, which |
| -- can be the controller of the outer record object or the final |
| -- list associated with the scope. |
| |
| -- If the component is itself an array of controlled types, whose |
| -- value is given by a sub-aggregate, then the attach calls have |
| -- been generated when individual subcomponent are assigned, and |
| -- must not be done again to prevent malformed finalization chains |
| -- (see comments above, concerning the creation of a block to hold |
| -- inner finalization actions). |
| |
| if Present (Comp_Type) |
| and then Needs_Finalization (Comp_Type) |
| and then not Is_Limited_Type (Comp_Type) |
| and then not |
| (Is_Array_Type (Comp_Type) |
| and then Is_Controlled (Component_Type (Comp_Type)) |
| and then Nkind (Expr) = N_Aggregate) |
| then |
| Append_To (L, |
| Make_Adjust_Call |
| (Obj_Ref => New_Copy_Tree (Indexed_Comp), |
| Typ => Comp_Type)); |
| end if; |
| end if; |
| |
| return Add_Loop_Actions (L); |
| end Gen_Assign; |
| |
| -------------- |
| -- Gen_Loop -- |
| -------------- |
| |
| function Gen_Loop (L, H : Node_Id; Expr : Node_Id) return List_Id is |
| 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); |
| Analyze_And_Resolve (Tcopy, Component_Type (Etype (N))); |
| Expander_Mode_Restore; |
| end if; |
| end if; |
| |
| return S; |
| |
| -- If loop bounds are the same then generate an assignment |
| |
| elsif Equal (L, H) 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 |
| 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 |
| |
| L_J := Make_Temporary (Loc, 'J', L); |
| |
| -- 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 => 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 => 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); |
| |
| -- Construct the final loop |
| |
| Append_To (S, |
| Make_Implicit_Loop_Statement |
| (Node => N, |
| Identifier => Empty, |
| Iteration_Scheme => L_Iteration_Scheme, |
| Statements => L_Body)); |
| |
| -- A small optimization: if the aggregate is initialized with a box |
| -- and the component type has no initialization procedure, remove the |
| -- useless empty loop. |
| |
| if Nkind (First (S)) = N_Loop_Statement |
| and then Is_Empty_List (Statements (First (S))) |
| then |
| return New_List (Make_Null_Statement (Loc)); |
| else |
| return S; |
| end if; |
| end Gen_Loop; |
| |
| --------------- |
| -- Gen_While -- |
| --------------- |
| |
| -- The code built is |
| |
| -- W_J : Index_Base := L; |
| -- while W_J < H loop |
| -- W_J := Index_Base'Succ (W); |
| -- L_Body; |
| -- end loop; |
| |
| function Gen_While (L, H : Node_Id; Expr : Node_Id) return List_Id is |
| W_J : Node_Id; |
| |
| W_Decl : Node_Id; |
| -- W_J : Base_Type := L; |
| |
| W_Iteration_Scheme : Node_Id; |
| -- while W_J < H |
| |
| W_Index_Succ : Node_Id; |
| -- Index_Base'Succ (J) |
| |
| W_Increment : Node_Id; |
| -- W_J := Index_Base'Succ (W) |
| |
| W_Body : constant List_Id := New_List; |
| -- The statements to execute in the loop |
| |
| S : constant List_Id := New_List; |
| -- list of statement |
| |
| begin |
| -- If loop bounds define an empty range or are equal return null |
| |
| if Empty_Range (L, H) or else Equal (L, H) then |
| Append_To (S, Make_Null_Statement (Loc)); |
| return S; |
| end if; |
| |
| -- Build the decl of W_J |
| |
| W_J := Make_Temporary (Loc, 'J', L); |
| W_Decl := |
| Make_Object_Declaration |
| (Loc, |
| Defining_Identifier => W_J, |
| Object_Definition => Index_Base_Name, |
| Expression => L); |
| |
| -- Theoretically we should do a New_Copy_Tree (L) here, but we know |
| -- that in this particular case L is a fresh Expr generated by |
| -- Add which we are the only ones to use. |
| |
| Append_To (S, W_Decl); |
| |
| -- Construct " while W_J < H" |
| |
| W_Iteration_Scheme := |
| Make_Iteration_Scheme |
| (Loc, |
| Condition => Make_Op_Lt |
| (Loc, |
| Left_Opnd => New_Occurrence_Of (W_J, Loc), |
| Right_Opnd => New_Copy_Tree (H))); |
| |
| -- Construct the statements to execute in the loop body |
| |
| W_Index_Succ := |
| Make_Attribute_Reference |
| (Loc, |
| Prefix => Index_Base_Name, |
| Attribute_Name => Name_Succ, |
| Expressions => New_List (New_Occurrence_Of (W_J, Loc))); |
| |
| W_Increment := |
| Make_OK_Assignment_Statement |
| (Loc, |
| Name => New_Occurrence_Of (W_J, Loc), |
| Expression => W_Index_Succ); |
| |
| Append_To (W_Body, W_Increment); |
| Append_List_To (W_Body, |
| Gen_Assign (New_Occurrence_Of (W_J, Loc), Expr)); |
| |
| -- 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 Is_Scalar_Type (Ctype) then |
| if Present (Default_Aspect_Component_Value (Typ)) then |
| return Default_Aspect_Component_Value (Typ); |
| elsif Present (Default_Aspect_Value (Ctype)) then |
| return Default_Aspect_Value (Ctype); |
| else |
| return Empty; |
| end if; |
| |
| 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; |
| |
| -- Build_Array_Aggr_Code Variables |
| |
| Assoc : Node_Id; |
| Choice : Node_Id; |
| Expr : Node_Id; |
| Typ : Entity_Id; |
| |
| Others_Assoc : Node_Id := Empty; |
| |
| Aggr_L : constant Node_Id := Low_Bound (Aggregate_Bounds (N)); |
| Aggr_H : constant Node_Id := High_Bound (Aggregate_Bounds (N)); |
| -- The aggregate bounds of this specific sub-aggregate. 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 |
| |
| Low : Node_Id; |
| High : Node_Id; |
| |
| 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 |
| |
| New_Code : constant List_Id := New_List; |
| |
| -- 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 |
| Append_To (New_Code, |
| Make_Assignment_Statement (Loc, |
| Name => New_Copy_Tree (Into), |
| Expression => |
| Unchecked_Convert_To (Typ, |
| Make_Integer_Literal (Loc, Uint_0)))); |
| 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 (Choices (Assoc)); |
| while Present (Choice) loop |
| if Nkind (Choice) = N_Others_Choice then |
| Set_Loop_Actions (Assoc, New_List); |
| Others_Assoc := Assoc; |
| exit; |
| end if; |
| |
| Get_Index_Bounds (Choice, Low, High); |
| |
| 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; |
| |
| 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; |
| Append_List |
| (Gen_Loop (Low, High, |
| Get_Assoc_Expr (Others_Assoc)), 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)); |
| |
| -- Ada 2005 (AI-287) |
| |
| Append_List (Gen_While (Add (Nb_Elements, To => Aggr_L), |
| Aggr_High, |
| Get_Assoc_Expr (Assoc)), -- AI-287 |
| To => New_Code); |
| 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. |
| |
| procedure Init_Hidden_Discriminants (Typ : Entity_Id; List : List_Id); |
| -- If Typ is derived, and constrains discriminants of the parent type, |
| -- these discriminants are not components of the aggregate, and must be |
| -- initialized. The assignments are appended to List. The same is done |
| -- if Typ derives fron an already constrained subtype of a discriminated |
| -- parent type. |
| |
| 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. |
| |
| 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. |
| |
| --------------------------------- |
| -- 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; |
| |
| -------------------------------- |
| -- Get_Constraint_Association -- |
| -------------------------------- |
| |
| function Get_Constraint_Association (T : Entity_Id) return Node_Id is |
| Indic : Node_Id; |
| Typ : Entity_Id; |
| |
| begin |
| Typ := T; |
| |
| -- Handle private types in instances |
| |
| if In_Instance |
| and then Is_Private_Type (Typ) |
| and then Present (Full_View (Typ)) |
| then |
| Typ := Full_View (Typ); |
| end if; |
| |
| Indic := Subtype_Indication (Type_Definition (Parent (Typ))); |
| |
| -- ??? Also need to cover case of a type mark denoting a subtype |
| -- with 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 |
| Btype : Entity_Id; |
| Parent_Type : Entity_Id; |
| Disc : Entity_Id; |
| Discr_Val : Elmt_Id; |
| In_Aggr_Type : Boolean; |
| |
| 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; |
| |
| Btype := Base_Type (Typ); |
| while Is_Derived_Type (Btype) |
| and then |
| (Present (Stored_Constraint (Btype)) |
| or else |
| (In_Aggr_Type and then Present (Stored_Constraint (Typ)))) |
| loop |
| Parent_Type := Etype (Btype); |
| |
| if not Has_Discriminants (Parent_Type) then |
| return; |
| end if; |
| |
| Disc := First_Discriminant (Parent_Type); |
| |
| -- We know that one of the stored-constraint lists is present |
| |
| if Present (Stored_Constraint (Btype)) then |
| Discr_Val := First_Elmt (Stored_Constraint (Btype)); |
| |
| -- For private extension, stored constraint may be on full view |
| |
| elsif Is_Private_Type (Btype) |
| and then Present (Full_View (Btype)) |
| and then Present (Stored_Constraint (Full_View (Btype))) |
| then |
| Discr_Val := First_Elmt (Stored_Constraint (Full_View (Btype))); |
| |
| else |
| Discr_Val := First_Elmt (Stored_Constraint (Typ)); |
| end if; |
| |
| while Present (Discr_Val) and then Present (Disc) loop |
| |
| -- Only those discriminants of the parent that are not |
| -- renamed by discriminants of the derived type need to |
| -- be added explicitly. |
| |
| if not Is_Entity_Name (Node (Discr_Val)) |
| or else Ekind (Entity (Node (Discr_Val))) /= E_Discriminant |
| then |
| Comp_Expr := |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Target), |
| Selector_Name => New_Occurrence_Of (Disc, Loc)); |
| |
| Instr := |
| Make_OK_Assignment_Statement (Loc, |
| Name => Comp_Expr, |
| Expression => New_Copy_Tree (Node (Discr_Val))); |
| |
| Set_No_Ctrl_Actions (Instr); |
| Append_To (List, Instr); |
| end if; |
| |
| Next_Discriminant (Disc); |
| Next_Elmt (Discr_Val); |
| end loop; |
| |
| In_Aggr_Type := False; |
| Btype := Base_Type (Parent_Type); |
| end loop; |
| end Init_Hidden_Discriminants; |
| |
| ------------------------- |
| -- 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; |
| |
| ----------------------------------- |
| -- 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; |
| |
| 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. |
| |
| 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. |
| |
| -------------------------- |
| -- 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)))); |
| end if; |
| |
| return OK; |
| end Rewrite_Discriminant; |
| |
| ------------------ |
| -- 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)); |
| |
| elsif Nkind (Lhs) = N_Selected_Component then |
| Rewrite (Expr, |
| Make_Attribute_Reference (Loc, |
| Attribute_Name => Name_Unrestricted_Access, |
| Prefix => New_Copy_Tree (Lhs))); |
| Set_Analyzed (Parent (Expr), False); |
| |
| 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; |
| |
| procedure Replace_Self_Reference is |
| new Traverse_Proc (Replace_Type); |
| |
| procedure Replace_Discriminants is |
| new Traverse_Proc (Rewrite_Discriminant); |
| |
| -- 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); |
| 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; |
| 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 wrapped in an unchecked |
| -- conversion) or aggregate (definitely qualified). |
| |
| -- The ancestor part can also be a function call (that may be |
| -- transformed into an explicit dereference) or a qualification |
| -- of one such. |
| |
| elsif Is_Limited_Type (Etype (Ancestor)) |
| and then Nkind_In (Unqualify (Ancestor), 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_In (Unqualify (Ancestor), 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 VM_Target, |
| -- 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); |
| end if; |
| end if; |
| |
| -- Call Adjust manually |
| |
| if Needs_Finalization (Etype (Ancestor)) |
| and then not Is_Limited_Type (Etype (Ancestor)) |
| then |
| Append_To (Assign, |
| Make_Adjust_Call |
| (Obj_Ref => New_Copy_Tree (Ref), |
| Typ => Etype (Ancestor))); |
| 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; |
| 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 |
| |
| declare |
| 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)); |
| |
| Set_No_Ctrl_Actions (Instr); |
| Append_To (L, Instr); |
| |
| Next_Stored_Discriminant (Discriminant); |
| end loop; |
| end; |
| 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, |
| 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))); |
| |
| -- 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; |
| |
| Instr := |
| Make_OK_Assignment_Statement (Loc, |
| Name => Comp_Expr, |
| Expression => Expr_Q); |
| |
| Set_No_Ctrl_Actions (Instr); |
| Append_To (L, Instr); |
| |
| -- Adjust the tag if tagged (because of possible view |
| -- conversions), unless compiling for a VM where tags are |
| -- implicit. |
| |
| -- tmp.comp._tag := comp_typ'tag; |
| |
| if Is_Tagged_Type (Comp_Type) |
| and then Tagged_Type_Expansion |
| then |
| Instr := |
| Make_OK_Assignment_Statement (Loc, |
| Name => |
| Make_Selected_Component (Loc, |
| Prefix => New_Copy_Tree (Comp_Expr), |
| Selector_Name => |
| New_Occurrence_Of |
| (First_Tag_Component (Comp_Type), Loc)), |
| |
| Expression => |
| Unchecked_Convert_To (RTE (RE_Tag), |
| New_Occurrence_Of |
| (Node (First_Elmt (Access_Disp_Table (Comp_Type))), |
| Loc))); |
| |
| Append_To (L, Instr); |
| end if; |
| |
| -- Generate: |
| -- Adjust (tmp.comp); |
| |
| if Needs_Finalization (Comp_Type) |
| and then not Is_Limited_Type (Comp_Type) |
| then |
| Append_To (L, |
| Make_Adjust_Call |
| (Obj_Ref => New_Copy_Tree (Comp_Expr), |
| Typ => Comp_Type)); |
| 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; |
| |
| 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); |
| 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; |
| |
| --------------------------------------- |
| -- Collect_Initialization_Statements -- |
| --------------------------------------- |
| |
| procedure Collect_Initialization_Statements |
| (Obj : Entity_Id; |
| N : Node_Id; |
| Node_After : Node_Id) |
| is |
| Loc : constant Source_Ptr := Sloc (N); |
| Init_Actions : constant List_Id := New_List; |
| Init_Node : Node_Id; |
| Comp_Stmt : Node_Id; |
| |
| begin |
| -- Nothing to do if Obj is already frozen, as in this case we known we |
| -- won't need to move the initialization statements about later on. |
| |
| if Is_Frozen (Obj) then |
| return; |
| end if; |
| |
| Init_Node := N; |
| while Next (Init_Node) /= Node_After loop |
| Append_To (Init_Actions, Remove_Next (Init_Node)); |
| end loop; |
| |
| if not Is_Empty_List (Init_Actions) then |
| Comp_Stmt := Make_Compound_Statement (Loc, Actions => Init_Actions); |
| Insert_Action_After (Init_Node, Comp_Stmt); |
| Set_Initialization_Statements (Obj, Comp_Stmt); |
| end if; |
| end Collect_Initialization_Statements; |
| |
| ------------------------------- |
| -- 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); |
| |
| 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 non-static, 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. |
| |
| -- However, 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, |
| Sec_Stack => |
| Is_Controlled (Typ) or else Has_Controlled_Component (Typ)); |
| end if; |
| |
| declare |
| Node_After : constant Node_Id := Next (N); |
| begin |
| Insert_Actions_After (N, Late_Expansion (Aggr, Typ, Occ)); |
| Collect_Initialization_Statements (Obj, N, Node_After); |
| end; |
| Set_No_Initialization (N); |
| 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 |
| Aggr_Code : List_Id; |
| Typ : constant Entity_Id := Etype (Aggr); |
| Ctyp : constant Entity_Id := Component_Type (Typ); |
| |
| begin |
| -- The target is an explicit dereference of the allocated object. |
| -- Generate component assignments to it, as for an aggregate that |
| -- appears on the right-hand side of an assignment statement. |
| |
| Aggr_Code := |
| Build_Array_Aggr_Code (Aggr, |
| Ctype => Ctyp, |
| Index => First_Index (Typ), |
| Into => Target, |
| Scalar_Comp => Is_Scalar_Type (Ctyp)); |
| |
| Insert_Actions_After (Decl, Aggr_Code); |
| end Convert_Array_Aggr_In_Allocator; |
| |
| ---------------------------- |
| -- 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 (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 a unconstrained declaration because in this |
| -- case the current delayed expansion mechanism doesn't work when |
| -- the declared object size depend on the initializing expr. |
| |
| begin |
| 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 Has_Discriminants |
| (Entity (Object_Definition (Parent_Node))) |
| or else Is_Class_Wide_Type |
| (Entity (Object_Definition (Parent_Node))); |
| end if; |
| end; |
| end if; |
| |
| -- Just set the Delay flag in the cases where the transformation will be |
| -- done top down from above. |
| |
| if False |
| |
| -- Internal aggregate (transformed when expanding the parent) |
| |
| or else Parent_Kind = N_Aggregate |
| or else Parent_Kind = N_Extension_Aggregate |
| or else Parent_Kind = 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_Limited_View (Typ) |
| and then |
| (Nkind (Parent (Parent_Node)) = N_Extended_Return_Statement |
| or else Nkind (Parent_Node) = N_Simple_Return_Statement)) |
| 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, Sec_Stack => Needs_Finalization (Typ)); |
| end if; |
| |
| -- If the aggregate is non-limited, create a temporary. 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 Nkind (Parent (N)) = N_Assignment_Statement |
| then |
| Target_Expr := New_Copy_Tree (Name (Parent (N))); |
| Insert_Actions (Parent (N), |
| Build_Record_Aggr_Code (N, Typ, Target_Expr)); |
| Rewrite (Parent (N), Make_Null_Statement (Loc)); |
| |
| 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; |
| Max_Others_Replicate : Nat := 5; |
| Handle_Bit_Packed : Boolean := False) |
| is |
| Typ : constant Entity_Id := Etype (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; |
| Ix : Node_Id; |
| Ixb : Node_Id) return Boolean; |
| -- Convert the aggregate into a purely positional form if possible. On |
| -- entry the bounds of all dimensions are known to be static, and the |
| -- total number of components is safe enough to expand. |
| |
| function Is_Flat (N : Node_Id; Dims : Int) return Boolean; |
| -- Return True iff the array N is flat (which is not trivial in the case |
| -- of multidimensional aggregates). |
| |
| ----------------------------- |
| -- Check_Static_Components -- |
| ----------------------------- |
| |
| -- Could use some comments in this body ??? |
| |
| procedure Check_Static_Components is |
| 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 |
| Expr := First (Component_Associations (N)); |
| while Present (Expr) loop |
| if Nkind_In (Expression (Expr), N_Integer_Literal, |
| N_Real_Literal) |
| then |
| null; |
| |
| elsif Is_Entity_Name (Expression (Expr)) |
| and then Present (Entity (Expression (Expr))) |
| and then Ekind (Entity (Expression (Expr))) = |
| E_Enumeration_Literal |
| then |
| null; |
| |
| elsif Nkind (Expression (Expr)) /= N_Aggregate |
| or else not Compile_Time_Known_Aggregate (Expression (Expr)) |
| or else Expansion_Delayed (Expression (Expr)) |
| then |
| Static_Components := False; |
| exit; |
| end if; |
| |
| Next (Expr); |
| end loop; |
| end if; |
| end Check_Static_Components; |
| |
| ------------- |
| -- Flatten -- |
| ------------- |
| |
| function Flatten |
| (N : Node_Id; |
| 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)); |
| Lov : Uint; |
| Hiv : Uint; |
| |
| Others_Present : Boolean := False; |
| |
| 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 |
| |
| if Present (Component_Associations (N)) then |
| 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; |
| end if; |
| |
| Choice := First (Choices (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. |
| |
| -- Note: This case is required in VM platforms since their backends |
| -- normalize array indexes in the range 0 .. N-1. Hence, if we do |
| -- not flat an array whose bounds cannot be obtained from the type |
| -- of the index the backend has no way to properly generate the code. |
| -- See ACATS c460010 for an example. |
| |
| 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; |
| 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 |
| if Nkind (Elmt) = N_Aggregate |
| and then Present (Next_Index (Ix)) |
| and then |
| not Flatten (Elmt, Next_Index (Ix), Next_Index (Ixb)) |
| then |
| return False; |
| end if; |
| |
| Vals (Num) := Relocate_Node (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)); |
| |
| if Nkind (Expression (Elmt)) = N_Aggregate then |
| if Present (Next_Index (Ix)) |
| and then |
| not Flatten |
| (Expression (Elmt), Next_Index (Ix), Next_Index (Ixb)) |
| then |
| return False; |
| end if; |
| end if; |
| |
| Component_Loop : while Present (Elmt) loop |
| Choice := First (Choices (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; |
| |
| for J in Vals'Range loop |
| if No (Vals (J)) then |
| Vals (J) := New_Copy_Tree (Expression (Elmt)); |
| Rep_Count := Rep_Count + 1; |
| |
| -- Check for maximum others replication. Note that |
| -- we skip this test if either of the restrictions |
| -- No_Elaboration_Code or No_Implicit_Loops 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 OK and if so continue |
| -- processing. |
| |
| if Restriction_Active (No_Elaboration_Code) |
| or else Restriction_Active (No_Implicit_Loops) |
| 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_File_Name |
| (Unit_File_Name (Get_Source_Unit (P))) |
| then |
| null; |
| |
| -- If duplication not OK, then we return False |
| -- if the replication count is too high |
| |
| elsif Rep_Count > Max_Others_Replicate then |
| return False; |
| |
| -- Continue on if duplication not OK, but the |
| -- replication count is not excessive. |
| |
| else |
| null; |
| end if; |
| end; |
| end if; |
| end loop; |
| |
| 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 (Expression (Elmt)); |
| 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 (Expression (Elmt)); |
| 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 : Int) 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; |
| |
| -- Start of processing for Convert_To_Positional |
| |
| begin |
| -- 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; |
| |
| if Is_Flat (N, Number_Dimensions (Typ)) then |
| 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. |
| |
| -- For now the back-end expands these aggregates into individual |
| -- assignments to the target anyway, but it is conceivable that |
| -- it will eventually be able to treat such aggregates statically??? |
| |
| if Aggr_Size_OK (N, Typ) |
| and then Flatten (N, 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; |
| |
| -- Is Static_Eaboration_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 Nkind_In (Expr, N_Integer_Literal, N_Real_Literal) |
| or else |
| (Is_Entity_Name (Expr) |
| and then Ekind (Entity (Expr)) = E_Enumeration_Literal) |
| then |
| null; |
| |
| else |
| 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 sub-aggregates of N at dimension J. |
| |
| function Aggr_Assignment_OK_For_Backend (N : Node_Id) return Boolean; |
| -- Returns true if an aggregate assignment can be done by the back end |
| |
| 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_Id; Index_Bounds : 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 multi-dimensional array aggregate all subaggregates |
| -- corresponding to the same dimension have the same bounds. |
| -- Sub_Aggr is an array sub-aggregate. Dim is the dimension |
| -- corresponding to the sub-aggregate. |
| |
| procedure Compute_Others_Present (Sub_Aggr : Node_Id; Dim : Pos); |
| -- Computes the values of array Others_Present. Sub_Aggr is the |
| -- array sub-aggregate we start the computation from. Dim is the |
| -- dimension corresponding to the sub-aggregate. |
| |
| function In_Place_Assign_OK return Boolean; |
| -- Simple 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 Others_Check (Sub_Aggr : Node_Id; Dim : Pos); |
| -- Checks that if an others choice is present in any sub-aggregate no |
| -- aggregate index is outside the bounds of the index constraint. |
| -- Sub_Aggr is an array sub-aggregate. Dim is the dimension |
| -- corresponding to the sub-aggregate. |
| |
| 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. |
| |
| ------------------------------------ |
| -- Aggr_Assignment_OK_For_Backend -- |
| ------------------------------------ |
| |
| -- Backend processing by Gigi/gcc is possible only if all the following |
| -- conditions are met: |
| |
| -- 1. N consists of a single OTHERS choice, possibly recursively |
| |
| -- 2. The array type is not packed |
| |
| -- 3. The array type has no atomic components |
| |
| -- 4. The array type has no null ranges (the purpose of this is to |
| -- avoid a bogus warning for an out-of-range value). |
| |
| -- 5. The component type is discrete |
| |
| -- 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 1 .. A-1. This can also be viewed as K occurrences of |
| -- the 8-bit 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 |
| Ctyp : Entity_Id; |
| Index : Entity_Id; |
| Expr : Node_Id := N; |
| Low : Node_Id; |
| High : Node_Id; |
| Remainder : Uint; |
| Value : Uint; |
| Nunits : Nat; |
| |
| begin |
| -- Recurse as far as possible to find the innermost component type |
| |
| Ctyp := Etype (N); |
| while Is_Array_Type (Ctyp) loop |
| if Nkind (Expr) /= N_Aggregate |
| or else not Is_Others_Aggregate (Expr) |
| then |
| return False; |
| end if; |
| |
| if Present (Packed_Array_Impl_Type (Ctyp)) then |
| return False; |
| end if; |
| |
| if Has_Atomic_Components (Ctyp) then |
| return False; |
| end if; |
| |
| Index := First_Index (Ctyp); |
| while Present (Index) loop |
| Get_Index_Bounds (Index, Low, High); |
| |
| if Is_Null_Range (Low, High) 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_Others_Aggregate (Expr) |
| then |
| return False; |
| end if; |
| |
| Expr := Expression (First (Component_Associations (Expr))); |
| end loop; |
| |
| Ctyp := Component_Type (Ctyp); |
| |
| if Is_Atomic (Ctyp) then |
| return False; |
| end if; |
| end loop; |
| |
| if not Is_Discrete_Type (Ctyp) then |
| return False; |
| end if; |
| |
| -- The expression needs to be analyzed if True is returned |
| |
| Analyze_And_Resolve (Expr, Ctyp); |
| |
| -- The back end uses the Esize as the precision of the type |
| |
| Nunits := UI_To_Int (Esize (Ctyp)) / System_Storage_Unit; |
| |
| if Nunits = 1 then |
| return True; |
| end if; |
| |
| if not Compile_Time_Known_Value (Expr) then |
| return False; |
| end if; |
| |
| Value := Expr_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; |
| |
| ---------------------------- |
| -- 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 : Int; |
| 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, |
|