8sa1-gcc/gcc/ada/exp_aggr.adb
Gary Dismukes 097826df0c [Ada] Additional fixes for Default_Initial_Condition
gcc/ada/

	* exp_aggr.adb (Build_Array_Aggr_Code.Gen_Assign): Move
	generation of the call for DIC check past the optional
	generation of calls to controlled Initialize procedures.
	* exp_ch3.adb
	(Build_Array_Init_Proc.Init_One_Dimension.Possible_DIC_Call):
	Suppress generation of a DIC call when the array component type
	is controlled.  The call will now be generated later inside the
	array's DI (Deep_Initialize) procedure.
	* exp_ch7.adb
	(Make_Deep_Array_Body.Build_Initialize_Statements): Generate a
	DIC call (when needed by the array component type) after any
	call to the component type's controlled Initialize procedure, or
	generate the DIC call by itself if there's no Initialize to
	call.
	* sem_aggr.adb (Resolve_Record_Aggregate.Add_Association):
	Simplify condition to only test Is_Box_Init_By_Default (previous
	condition was overkill, as well as incorrect in some cases).
	* sem_elab.adb (Active_Scenarios.Output_Call): For
	Default_Initial_Condition, suppress call to
	Output_Verification_Call when the subprogram is a partial DIC
	procedure.
2020-12-14 10:51:51 -05:00

9896 lines
348 KiB
Ada

------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- E X P _ A G G R --
-- --
-- B o d y --
-- --
-- Copyright (C) 1992-2020, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
-- for more details. You should have received a copy of the GNU General --
-- Public License distributed with GNAT; see file COPYING3. If not, go to --
-- http://www.gnu.org/licenses for a complete copy of the license. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
with Aspects; use Aspects;
with Atree; use Atree;
with Checks; use Checks;
with Debug; use Debug;
with Einfo; use Einfo;
with Elists; use Elists;
with Errout; use Errout;
with Expander; use Expander;
with Exp_Util; use Exp_Util;
with Exp_Ch3; use Exp_Ch3;
with Exp_Ch6; use Exp_Ch6;
with Exp_Ch7; use Exp_Ch7;
with Exp_Ch9; use Exp_Ch9;
with Exp_Disp; use Exp_Disp;
with Exp_Tss; use Exp_Tss;
with Freeze; use Freeze;
with Itypes; use Itypes;
with Lib; use Lib;
with Namet; use Namet;
with Nmake; use Nmake;
with Nlists; use Nlists;
with Opt; use Opt;
with Restrict; use Restrict;
with Rident; use Rident;
with Rtsfind; use Rtsfind;
with Ttypes; use Ttypes;
with Sem; use Sem;
with Sem_Aggr; use Sem_Aggr;
with Sem_Aux; use Sem_Aux;
with Sem_Ch3; use Sem_Ch3;
with Sem_Ch8; use Sem_Ch8;
with Sem_Ch13; use Sem_Ch13;
with Sem_Eval; use Sem_Eval;
with Sem_Mech; use Sem_Mech;
with Sem_Res; use Sem_Res;
with Sem_Util; use Sem_Util;
with Sinfo; use Sinfo;
with Snames; use Snames;
with Stand; use Stand;
with Stringt; use Stringt;
with Tbuild; use Tbuild;
with Uintp; use Uintp;
with Urealp; use Urealp;
package body Exp_Aggr is
type Case_Bounds is record
Choice_Lo : Node_Id;
Choice_Hi : Node_Id;
Choice_Node : Node_Id;
end record;
type Case_Table_Type is array (Nat range <>) of Case_Bounds;
-- Table type used by Check_Case_Choices procedure
procedure 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.
procedure Expand_Delta_Array_Aggregate (N : Node_Id; Deltas : List_Id);
procedure Expand_Delta_Record_Aggregate (N : Node_Id; Deltas : List_Id);
procedure Expand_Container_Aggregate (N : Node_Id);
function Get_Base_Object (N : Node_Id) return Entity_Id;
-- Return the base object, i.e. the outermost prefix object, that N refers
-- to statically, or Empty if it cannot be determined. The assumption is
-- that all dereferences are explicit in the tree rooted at N.
function Has_Default_Init_Comps (N : Node_Id) return Boolean;
-- N is an aggregate (record or array). Checks the presence of default
-- initialization (<>) in any component (Ada 2005: AI-287).
function Is_CCG_Supported_Aggregate (N : Node_Id) return Boolean;
-- Return True if aggregate N is located in a context supported by the
-- CCG backend; False otherwise.
function Is_Static_Dispatch_Table_Aggregate (N : Node_Id) return Boolean;
-- Returns true if N is an aggregate used to initialize the components
-- of a statically allocated dispatch table.
function Late_Expansion
(N : Node_Id;
Typ : Entity_Id;
Target : Node_Id) return List_Id;
-- This routine implements top-down expansion of nested aggregates. In
-- doing so, it avoids the generation of temporaries at each level. N is
-- a nested record or array aggregate with the Expansion_Delayed flag.
-- Typ is the expected type of the aggregate. Target is a (duplicatable)
-- expression that will hold the result of the aggregate expansion.
function Make_OK_Assignment_Statement
(Sloc : Source_Ptr;
Name : Node_Id;
Expression : Node_Id) return Node_Id;
-- This is like Make_Assignment_Statement, except that Assignment_OK
-- is set in the left operand. All assignments built by this unit use
-- this routine. This is needed to deal with assignments to initialized
-- constants that are done in place.
function Must_Slide
(Obj_Type : Entity_Id;
Typ : Entity_Id) return Boolean;
-- A static array aggregate in an object declaration can in most cases be
-- expanded in place. The one exception is when the aggregate is given
-- with component associations that specify different bounds from those of
-- the type definition in the object declaration. In this pathological
-- case the aggregate must slide, and we must introduce an intermediate
-- temporary to hold it.
--
-- The same holds in an assignment to one-dimensional array of arrays,
-- when a component may be given with bounds that differ from those of the
-- component type.
function Number_Of_Choices (N : Node_Id) return Nat;
-- Returns the number of discrete choices (not including the others choice
-- if present) contained in (sub-)aggregate N.
procedure Process_Transient_Component
(Loc : Source_Ptr;
Comp_Typ : Entity_Id;
Init_Expr : Node_Id;
Fin_Call : out Node_Id;
Hook_Clear : out Node_Id;
Aggr : Node_Id := Empty;
Stmts : List_Id := No_List);
-- Subsidiary to the expansion of array and record aggregates. Generate
-- part of the necessary code to finalize a transient component. Comp_Typ
-- is the component type. Init_Expr is the initialization expression of the
-- component which is always a function call. Fin_Call is the finalization
-- call used to clean up the transient function result. Hook_Clear is the
-- hook reset statement. Aggr and Stmts both control the placement of the
-- generated code. Aggr is the related aggregate. If present, all code is
-- inserted prior to Aggr using Insert_Action. Stmts is the initialization
-- statements of the component. If present, all code is added to Stmts.
procedure Process_Transient_Component_Completion
(Loc : Source_Ptr;
Aggr : Node_Id;
Fin_Call : Node_Id;
Hook_Clear : Node_Id;
Stmts : List_Id);
-- Subsidiary to the expansion of array and record aggregates. Generate
-- part of the necessary code to finalize a transient component. Aggr is
-- the related aggregate. Fin_Clear is the finalization call used to clean
-- up the transient component. Hook_Clear is the hook reset statment. Stmts
-- is the initialization statement list for the component. All generated
-- code is added to Stmts.
procedure Sort_Case_Table (Case_Table : in out Case_Table_Type);
-- Sort the Case Table using the Lower Bound of each Choice as the key.
-- A simple insertion sort is used since the number of choices in a case
-- statement of variant part will usually be small and probably in near
-- sorted order.
------------------------------------------------------
-- Local subprograms for Record Aggregate Expansion --
------------------------------------------------------
function Is_Build_In_Place_Aggregate_Return (N : Node_Id) return Boolean;
-- True if N is an aggregate (possibly qualified or converted) that is
-- being returned from a build-in-place function.
function Build_Record_Aggr_Code
(N : Node_Id;
Typ : Entity_Id;
Lhs : Node_Id) return List_Id;
-- N is an N_Aggregate or an N_Extension_Aggregate. Typ is the type of the
-- aggregate. Target is an expression containing the location on which the
-- component by component assignments will take place. Returns the list of
-- assignments plus all other adjustments needed for tagged and controlled
-- types.
procedure Convert_To_Assignments (N : Node_Id; Typ : Entity_Id);
-- Transform a record aggregate into a sequence of assignments performed
-- component by component. N is an N_Aggregate or N_Extension_Aggregate.
-- Typ is the type of the record aggregate.
procedure Expand_Record_Aggregate
(N : Node_Id;
Orig_Tag : Node_Id := Empty;
Parent_Expr : Node_Id := Empty);
-- This is the top level procedure for record aggregate expansion.
-- Expansion for record aggregates needs expand aggregates for tagged
-- record types. Specifically Expand_Record_Aggregate adds the Tag
-- field in front of the Component_Association list that was created
-- during resolution by Resolve_Record_Aggregate.
--
-- N is the record aggregate node.
-- Orig_Tag is the value of the Tag that has to be provided for this
-- specific aggregate. It carries the tag corresponding to the type
-- of the outermost aggregate during the recursive expansion
-- Parent_Expr is the ancestor part of the original extension
-- aggregate
function Has_Mutable_Components (Typ : Entity_Id) return Boolean;
-- Return true if one of the components is of a discriminated type with
-- defaults. An aggregate for a type with mutable components must be
-- expanded into individual assignments.
function In_Place_Assign_OK
(N : Node_Id;
Target_Object : Entity_Id := Empty) return Boolean;
-- Predicate to determine whether an aggregate assignment can be done in
-- place, because none of the new values can depend on the components of
-- the target of the assignment.
procedure Initialize_Discriminants (N : Node_Id; Typ : Entity_Id);
-- If the type of the aggregate is a type extension with renamed discrimi-
-- nants, we must initialize the hidden discriminants of the parent.
-- Otherwise, the target object must not be initialized. The discriminants
-- are initialized by calling the initialization procedure for the type.
-- This is incorrect if the initialization of other components has any
-- side effects. We restrict this call to the case where the parent type
-- has a variant part, because this is the only case where the hidden
-- discriminants are accessed, namely when calling discriminant checking
-- functions of the parent type, and when applying a stream attribute to
-- an object of the derived type.
-----------------------------------------------------
-- Local Subprograms for Array Aggregate Expansion --
-----------------------------------------------------
function Aggr_Assignment_OK_For_Backend (N : Node_Id) return Boolean;
-- Returns true if an aggregate assignment can be done by the back end
function Aggr_Size_OK (N : Node_Id) return Boolean;
-- Very large static aggregates present problems to the back-end, and are
-- transformed into assignments and loops. This function verifies that the
-- total number of components of an aggregate is acceptable for rewriting
-- into a purely positional static form. Aggr_Size_OK must be called before
-- calling Flatten.
--
-- This function also detects and warns about one-component aggregates that
-- appear in a nonstatic context. Even if the component value is static,
-- such an aggregate must be expanded into an assignment.
function Backend_Processing_Possible (N : Node_Id) return Boolean;
-- This function checks if array aggregate N can be processed directly
-- by the backend. If this is the case, True is returned.
function Build_Array_Aggr_Code
(N : Node_Id;
Ctype : Entity_Id;
Index : Node_Id;
Into : Node_Id;
Scalar_Comp : Boolean;
Indexes : List_Id := No_List) return List_Id;
-- This recursive routine returns a list of statements containing the
-- loops and assignments that are needed for the expansion of the array
-- aggregate N.
--
-- N is the (sub-)aggregate node to be expanded into code. This node has
-- been fully analyzed, and its Etype is properly set.
--
-- Index is the index node corresponding to the array subaggregate N
--
-- Into is the target expression into which we are copying the aggregate.
-- Note that this node may not have been analyzed yet, and so the Etype
-- field may not be set.
--
-- Scalar_Comp is True if the component type of the aggregate is scalar
--
-- Indexes is the current list of expressions used to index the object we
-- are writing into.
procedure Convert_Array_Aggr_In_Allocator
(Decl : Node_Id;
Aggr : Node_Id;
Target : Node_Id);
-- If the aggregate appears within an allocator and can be expanded in
-- place, this routine generates the individual assignments to components
-- of the designated object. This is an optimization over the general
-- case, where a temporary is first created on the stack and then used to
-- construct the allocated object on the heap.
procedure Convert_To_Positional
(N : Node_Id;
Handle_Bit_Packed : Boolean := False);
-- If possible, convert named notation to positional notation. This
-- conversion is possible only in some static cases. If the conversion is
-- possible, then N is rewritten with the analyzed converted aggregate.
-- The parameter Handle_Bit_Packed is usually set False (since we do
-- not expect the back end to handle bit packed arrays, so the normal case
-- of conversion is pointless), but in the special case of a call from
-- Packed_Array_Aggregate_Handled, we set this parameter to True, since
-- these are cases we handle in there.
procedure Expand_Array_Aggregate (N : Node_Id);
-- This is the top-level routine to perform array aggregate expansion.
-- N is the N_Aggregate node to be expanded.
function Is_Two_Dim_Packed_Array (Typ : Entity_Id) return Boolean;
-- For two-dimensional packed aggregates with constant bounds and constant
-- components, it is preferable to pack the inner aggregates because the
-- whole matrix can then be presented to the back-end as a one-dimensional
-- list of literals. This is much more efficient than expanding into single
-- component assignments. This function determines if the type Typ is for
-- an array that is suitable for this optimization: it returns True if Typ
-- is a two dimensional bit packed array with component size 1, 2, or 4.
function Max_Aggregate_Size
(N : Node_Id;
Default_Size : Nat := 5000) return Nat;
-- Return the max size for a static aggregate N. Return Default_Size if no
-- other special criteria trigger.
function Packed_Array_Aggregate_Handled (N : Node_Id) return Boolean;
-- Given an array aggregate, this function handles the case of a packed
-- array aggregate with all constant values, where the aggregate can be
-- evaluated at compile time. If this is possible, then N is rewritten
-- to be its proper compile time value with all the components properly
-- assembled. The expression is analyzed and resolved and True is returned.
-- If this transformation is not possible, N is unchanged and False is
-- returned.
function Two_Dim_Packed_Array_Handled (N : Node_Id) return Boolean;
-- If the type of the aggregate is a two-dimensional bit_packed array
-- it may be transformed into an array of bytes with constant values,
-- and presented to the back-end as a static value. The function returns
-- false if this transformation cannot be performed. THis is similar to,
-- and reuses part of the machinery in Packed_Array_Aggregate_Handled.
------------------------------------
-- Aggr_Assignment_OK_For_Backend --
------------------------------------
-- Back-end processing by Gigi/gcc is possible only if all the following
-- conditions are met:
-- 1. N consists of a single OTHERS choice, possibly recursively, or
-- of a single choice, possibly recursively, if it is surrounded by
-- a qualified expression whose subtype mark is unconstrained.
-- 2. The array type has no null ranges (the purpose of this is to
-- avoid a bogus warning for an out-of-range value).
-- 3. The array type has no atomic components
-- 4. The component type is elementary
-- 5. The component size is a multiple of Storage_Unit
-- 6. The component size is Storage_Unit or the value is of the form
-- M * (1 + A**1 + A**2 + .. A**(K-1)) where A = 2**(Storage_Unit)
-- and M in 0 .. A-1. This can also be viewed as K occurrences of
-- the Storage_Unit value M, concatenated together.
-- The ultimate goal is to generate a call to a fast memset routine
-- specifically optimized for the target.
function Aggr_Assignment_OK_For_Backend (N : Node_Id) return Boolean is
Csiz : Uint := No_Uint;
Ctyp : Entity_Id;
Expr : Node_Id;
High : Node_Id;
Index : Entity_Id;
Low : Node_Id;
Nunits : Int;
Remainder : Uint;
Value : Uint;
function Is_OK_Aggregate (Aggr : Node_Id) return Boolean;
-- Return true if Aggr is suitable for back-end assignment
---------------------
-- Is_OK_Aggregate --
---------------------
function Is_OK_Aggregate (Aggr : Node_Id) return Boolean is
Assoc : constant List_Id := Component_Associations (Aggr);
begin
-- An "others" aggregate is most likely OK, but see below
if Is_Others_Aggregate (Aggr) then
null;
-- An aggregate with a single choice requires a qualified expression
-- whose subtype mark is an unconstrained type because we need it to
-- have the semantics of an "others" aggregate.
elsif Nkind (Parent (N)) = N_Qualified_Expression
and then not Is_Constrained (Entity (Subtype_Mark (Parent (N))))
and then Is_Single_Aggregate (Aggr)
then
null;
-- The other cases are not OK
else
return False;
end if;
-- In any case we do not support an iterated association
return Nkind (First (Assoc)) /= N_Iterated_Component_Association;
end Is_OK_Aggregate;
-- Start of processing for Aggr_Assignment_OK_For_Backend
begin
-- Back end doesn't know about <>
if Has_Default_Init_Comps (N) then
return False;
end if;
-- Recurse as far as possible to find the innermost component type
Ctyp := Etype (N);
Expr := N;
while Is_Array_Type (Ctyp) loop
if Nkind (Expr) /= N_Aggregate
or else not Is_OK_Aggregate (Expr)
then
return False;
end if;
Index := First_Index (Ctyp);
while Present (Index) loop
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_OK_Aggregate (Expr)
then
return False;
end if;
Expr := Expression (First (Component_Associations (Expr)));
end loop;
if Has_Atomic_Components (Ctyp) then
return False;
end if;
Csiz := Component_Size (Ctyp);
Ctyp := Component_Type (Ctyp);
if Is_Full_Access (Ctyp) then
return False;
end if;
end loop;
-- Access types need to be dealt with specially
if Is_Access_Type (Ctyp) then
-- Component_Size is not set by Layout_Type if the component
-- type is an access type ???
Csiz := Esize (Ctyp);
-- Fat pointers are rejected as they are not really elementary
-- for the backend.
if Csiz /= System_Address_Size then
return False;
end if;
-- The supported expressions are NULL and constants, others are
-- rejected upfront to avoid being analyzed below, which can be
-- problematic for some of them, for example allocators.
if Nkind (Expr) /= N_Null and then not Is_Entity_Name (Expr) then
return False;
end if;
-- Scalar types are OK if their size is a multiple of Storage_Unit
elsif Is_Scalar_Type (Ctyp) then
pragma Assert (Csiz /= No_Uint);
if Csiz mod System_Storage_Unit /= 0 then
return False;
end if;
-- Composite types are rejected
else
return False;
end if;
-- If the expression has side effects (e.g. contains calls with
-- potential side effects) reject as well. We only preanalyze the
-- expression to prevent the removal of intended side effects.
Preanalyze_And_Resolve (Expr, Ctyp);
if not Side_Effect_Free (Expr) then
return False;
end if;
-- The expression needs to be analyzed if True is returned
Analyze_And_Resolve (Expr, Ctyp);
-- Strip away any conversions from the expression as they simply
-- qualify the real expression.
while Nkind (Expr) in N_Unchecked_Type_Conversion | N_Type_Conversion
loop
Expr := Expression (Expr);
end loop;
Nunits := UI_To_Int (Csiz) / System_Storage_Unit;
if Nunits = 1 then
return True;
end if;
if not Compile_Time_Known_Value (Expr) then
return False;
end if;
-- The only supported value for floating point is 0.0
if Is_Floating_Point_Type (Ctyp) then
return Expr_Value_R (Expr) = Ureal_0;
end if;
-- For other types, we can look into the value as an integer, which
-- means the representation value for enumeration literals.
Value := Expr_Rep_Value (Expr);
if Has_Biased_Representation (Ctyp) then
Value := Value - Expr_Value (Type_Low_Bound (Ctyp));
end if;
-- Values 0 and -1 immediately satisfy the last check
if Value = Uint_0 or else Value = Uint_Minus_1 then
return True;
end if;
-- We need to work with an unsigned value
if Value < 0 then
Value := Value + 2**(System_Storage_Unit * Nunits);
end if;
Remainder := Value rem 2**System_Storage_Unit;
for J in 1 .. Nunits - 1 loop
Value := Value / 2**System_Storage_Unit;
if Value rem 2**System_Storage_Unit /= Remainder then
return False;
end if;
end loop;
return True;
end Aggr_Assignment_OK_For_Backend;
------------------
-- Aggr_Size_OK --
------------------
function Aggr_Size_OK (N : Node_Id) return Boolean is
Typ : constant Entity_Id := Etype (N);
Lo : Node_Id;
Hi : Node_Id;
Indx : Node_Id;
Size : Uint;
Lov : Uint;
Hiv : Uint;
Max_Aggr_Size : Nat;
-- Determines the maximum size of an array aggregate produced by
-- converting named to positional notation (e.g. from others clauses).
-- This avoids running away with attempts to convert huge aggregates,
-- which hit memory limits in the backend.
function Component_Count (T : Entity_Id) return Nat;
-- The limit is applied to the total number of subcomponents that the
-- aggregate will have, which is the number of static expressions
-- that will appear in the flattened array. This requires a recursive
-- computation of the number of scalar components of the structure.
---------------------
-- Component_Count --
---------------------
function Component_Count (T : Entity_Id) return Nat is
Res : Nat := 0;
Comp : Entity_Id;
begin
if Is_Scalar_Type (T) then
return 1;
elsif Is_Record_Type (T) then
Comp := First_Component (T);
while Present (Comp) loop
Res := Res + Component_Count (Etype (Comp));
Next_Component (Comp);
end loop;
return Res;
elsif Is_Array_Type (T) then
declare
Lo : constant Node_Id :=
Type_Low_Bound (Etype (First_Index (T)));
Hi : constant Node_Id :=
Type_High_Bound (Etype (First_Index (T)));
Siz : constant Nat := Component_Count (Component_Type (T));
begin
-- Check for superflat arrays, i.e. arrays with such bounds
-- as 4 .. 2, to insure that this function never returns a
-- meaningless negative value.
if not Compile_Time_Known_Value (Lo)
or else not Compile_Time_Known_Value (Hi)
or else Expr_Value (Hi) < Expr_Value (Lo)
then
return 0;
else
-- If the number of components is greater than Int'Last,
-- then return Int'Last, so caller will return False (Aggr
-- size is not OK). Otherwise, UI_To_Int will crash.
declare
UI : constant Uint :=
Expr_Value (Hi) - Expr_Value (Lo) + 1;
begin
if UI_Is_In_Int_Range (UI) then
return Siz * UI_To_Int (UI);
else
return Int'Last;
end if;
end;
end if;
end;
else
-- Can only be a null for an access type
return 1;
end if;
end Component_Count;
-- Start of processing for Aggr_Size_OK
begin
-- We bump the maximum size unless the aggregate has a single component
-- association, which will be more efficient if implemented with a loop.
if No (Expressions (N))
and then No (Next (First (Component_Associations (N))))
then
Max_Aggr_Size := Max_Aggregate_Size (N);
else
Max_Aggr_Size := Max_Aggregate_Size (N, 500_000);
end if;
Size := UI_From_Int (Component_Count (Component_Type (Typ)));
Indx := First_Index (Typ);
while Present (Indx) loop
Lo := Type_Low_Bound (Etype (Indx));
Hi := Type_High_Bound (Etype (Indx));
-- Bounds need to be known at compile time
if not Compile_Time_Known_Value (Lo)
or else not Compile_Time_Known_Value (Hi)
then
return False;
end if;
Lov := Expr_Value (Lo);
Hiv := Expr_Value (Hi);
-- A flat array is always safe
if Hiv < Lov then
return True;
end if;
-- One-component aggregates are suspicious, and if the context type
-- is an object declaration with nonstatic bounds it will trip gcc;
-- such an aggregate must be expanded into a single assignment.
if Hiv = Lov and then Nkind (Parent (N)) = N_Object_Declaration then
declare
Index_Type : constant Entity_Id :=
Etype
(First_Index (Etype (Defining_Identifier (Parent (N)))));
Indx : Node_Id;
begin
if not Compile_Time_Known_Value (Type_Low_Bound (Index_Type))
or else not Compile_Time_Known_Value
(Type_High_Bound (Index_Type))
then
if Present (Component_Associations (N)) then
Indx :=
First
(Choice_List (First (Component_Associations (N))));
if Is_Entity_Name (Indx)
and then not Is_Type (Entity (Indx))
then
Error_Msg_N
("single component aggregate in "
& "non-static context??", Indx);
Error_Msg_N ("\maybe subtype name was meant??", Indx);
end if;
end if;
return False;
end if;
end;
end if;
declare
Rng : constant Uint := Hiv - Lov + 1;
begin
-- Check if size is too large
if not UI_Is_In_Int_Range (Rng) then
return False;
end if;
-- Compute the size using universal arithmetic to avoid the
-- possibility of overflow on very large aggregates.
Size := Size * Rng;
if Size <= 0
or else Size > Max_Aggr_Size
then
return False;
end if;
end;
-- Bounds must be in integer range, for later array construction
if not UI_Is_In_Int_Range (Lov)
or else
not UI_Is_In_Int_Range (Hiv)
then
return False;
end if;
Next_Index (Indx);
end loop;
return True;
end Aggr_Size_OK;
---------------------------------
-- Backend_Processing_Possible --
---------------------------------
-- Backend processing by Gigi/gcc is possible only if all the following
-- conditions are met:
-- 1. N is fully positional
-- 2. N is not a bit-packed array aggregate;
-- 3. The size of N's array type must be known at compile time. Note
-- that this implies that the component size is also known
-- 4. The array type of N does not follow the Fortran layout convention
-- or if it does it must be 1 dimensional.
-- 5. The array component type may not be tagged (which could necessitate
-- reassignment of proper tags).
-- 6. The array component type must not have unaligned bit components
-- 7. None of the components of the aggregate may be bit unaligned
-- components.
-- 8. There cannot be delayed components, since we do not know enough
-- at this stage to know if back end processing is possible.
-- 9. There cannot be any discriminated record components, since the
-- back end cannot handle this complex case.
-- 10. No controlled actions need to be generated for components
-- 11. When generating C code, N must be part of a N_Object_Declaration
-- 12. When generating C code, N must not include function calls
function Backend_Processing_Possible (N : Node_Id) return Boolean is
Typ : constant Entity_Id := Etype (N);
-- Typ is the correct constrained array subtype of the aggregate
function Component_Check (N : Node_Id; Index : Node_Id) return Boolean;
-- This routine checks components of aggregate N, enforcing checks
-- 1, 7, 8, 9, 11, and 12. In the multidimensional case, these checks
-- are performed on subaggregates. The Index value is the current index
-- being checked in the multidimensional case.
---------------------
-- Component_Check --
---------------------
function Component_Check (N : Node_Id; Index : Node_Id) return Boolean is
function Ultimate_Original_Expression (N : Node_Id) return Node_Id;
-- Given a type conversion or an unchecked type conversion N, return
-- its innermost original expression.
----------------------------------
-- Ultimate_Original_Expression --
----------------------------------
function Ultimate_Original_Expression (N : Node_Id) return Node_Id is
Expr : Node_Id := Original_Node (N);
begin
while Nkind (Expr) in
N_Type_Conversion | N_Unchecked_Type_Conversion
loop
Expr := Original_Node (Expression (Expr));
end loop;
return Expr;
end Ultimate_Original_Expression;
-- Local variables
Expr : Node_Id;
-- Start of processing for Component_Check
begin
-- Checks 1: (no component associations)
if Present (Component_Associations (N)) then
return False;
end if;
-- Checks 11: The C code generator cannot handle aggregates that are
-- not part of an object declaration.
if Modify_Tree_For_C and then not Is_CCG_Supported_Aggregate (N) then
return False;
end if;
-- Checks on components
-- Recurse to check subaggregates, which may appear in qualified
-- expressions. If delayed, the front-end will have to expand.
-- If the component is a discriminated record, treat as nonstatic,
-- as the back-end cannot handle this properly.
Expr := First (Expressions (N));
while Present (Expr) loop
-- Checks 8: (no delayed components)
if Is_Delayed_Aggregate (Expr) then
return False;
end if;
-- Checks 9: (no discriminated records)
if Present (Etype (Expr))
and then Is_Record_Type (Etype (Expr))
and then Has_Discriminants (Etype (Expr))
then
return False;
end if;
-- Checks 7. Component must not be bit aligned component
if Possible_Bit_Aligned_Component (Expr) then
return False;
end if;
-- Checks 12: (no function call)
if Modify_Tree_For_C
and then
Nkind (Ultimate_Original_Expression (Expr)) = N_Function_Call
then
return False;
end if;
-- Recursion to following indexes for multiple dimension case
if Present (Next_Index (Index))
and then not Component_Check (Expr, Next_Index (Index))
then
return False;
end if;
-- All checks for that component finished, on to next
Next (Expr);
end loop;
return True;
end Component_Check;
-- Start of processing for Backend_Processing_Possible
begin
-- Checks 2 (array not bit packed) and 10 (no controlled actions)
if Is_Bit_Packed_Array (Typ) or else Needs_Finalization (Typ) then
return False;
end if;
-- If component is limited, aggregate must be expanded because each
-- component assignment must be built in place.
if Is_Limited_View (Component_Type (Typ)) then
return False;
end if;
-- Checks 4 (array must not be multidimensional Fortran case)
if Convention (Typ) = Convention_Fortran
and then Number_Dimensions (Typ) > 1
then
return False;
end if;
-- Checks 3 (size of array must be known at compile time)
if not Size_Known_At_Compile_Time (Typ) then
return False;
end if;
-- Checks on components
if not Component_Check (N, First_Index (Typ)) then
return False;
end if;
-- Checks 5 (if the component type is tagged, then we may need to do
-- tag adjustments. Perhaps this should be refined to check for any
-- component associations that actually need tag adjustment, similar
-- to the test in Component_OK_For_Backend for record aggregates with
-- tagged components, but not clear whether it's worthwhile ???; in the
-- case of virtual machines (no Tagged_Type_Expansion), object tags are
-- handled implicitly).
if Is_Tagged_Type (Component_Type (Typ))
and then Tagged_Type_Expansion
then
return False;
end if;
-- Checks 6 (component type must not have bit aligned components)
if Type_May_Have_Bit_Aligned_Components (Component_Type (Typ)) then
return False;
end if;
-- Backend processing is possible
return True;
end Backend_Processing_Possible;
---------------------------
-- Build_Array_Aggr_Code --
---------------------------
-- The code that we generate from a one dimensional aggregate is
-- 1. If the subaggregate contains discrete choices we
-- (a) Sort the discrete choices
-- (b) Otherwise for each discrete choice that specifies a range we
-- emit a loop. If a range specifies a maximum of three values, or
-- we are dealing with an expression we emit a sequence of
-- assignments instead of a loop.
-- (c) Generate the remaining loops to cover the others choice if any
-- 2. If the aggregate contains positional elements we
-- (a) translate the positional elements in a series of assignments
-- (b) Generate a final loop to cover the others choice if any.
-- Note that this final loop has to be a while loop since the case
-- L : Integer := Integer'Last;
-- H : Integer := Integer'Last;
-- A : array (L .. H) := (1, others =>0);
-- cannot be handled by a for loop. Thus for the following
-- array (L .. H) := (.. positional elements.., others =>E);
-- we always generate something like:
-- J : Index_Type := Index_Of_Last_Positional_Element;
-- while J < H loop
-- J := Index_Base'Succ (J)
-- Tmp (J) := E;
-- end loop;
function Build_Array_Aggr_Code
(N : Node_Id;
Ctype : Entity_Id;
Index : Node_Id;
Into : Node_Id;
Scalar_Comp : Boolean;
Indexes : List_Id := No_List) return List_Id
is
Loc : constant Source_Ptr := Sloc (N);
Index_Base : constant Entity_Id := Base_Type (Etype (Index));
Index_Base_L : constant Node_Id := Type_Low_Bound (Index_Base);
Index_Base_H : constant Node_Id := Type_High_Bound (Index_Base);
function Add (Val : Int; To : Node_Id) return Node_Id;
-- Returns an expression where Val is added to expression To, unless
-- To+Val is provably out of To's base type range. To must be an
-- already analyzed expression.
function Empty_Range (L, H : Node_Id) return Boolean;
-- Returns True if the range defined by L .. H is certainly empty
function Equal (L, H : Node_Id) return Boolean;
-- Returns True if L = H for sure
function Index_Base_Name return Node_Id;
-- Returns a new reference to the index type name
function Gen_Assign
(Ind : Node_Id;
Expr : Node_Id;
In_Loop : Boolean := False) return List_Id;
-- Ind must be a side-effect-free expression. If the input aggregate N
-- to Build_Loop contains no subaggregates, then this function returns
-- the assignment statement:
--
-- Into (Indexes, Ind) := Expr;
--
-- Otherwise we call Build_Code recursively. Flag In_Loop should be set
-- when the assignment appears within a generated loop.
--
-- Ada 2005 (AI-287): In case of default initialized component, Expr
-- is empty and we generate a call to the corresponding IP subprogram.
function Gen_Loop (L, H : Node_Id; Expr : Node_Id) return List_Id;
-- Nodes L and H must be side-effect-free expressions. If the input
-- aggregate N to Build_Loop contains no subaggregates, this routine
-- returns the for loop statement:
--
-- for J in Index_Base'(L) .. Index_Base'(H) loop
-- Into (Indexes, J) := Expr;
-- end loop;
--
-- Otherwise we call Build_Code recursively. As an optimization if the
-- loop covers 3 or fewer scalar elements we generate a sequence of
-- assignments.
-- If the component association that generates the loop comes from an
-- Iterated_Component_Association, the loop parameter has the name of
-- the corresponding parameter in the original construct.
function Gen_While (L, H : Node_Id; Expr : Node_Id) return List_Id;
-- Nodes L and H must be side-effect-free expressions. If the input
-- aggregate N to Build_Loop contains no subaggregates, this routine
-- returns the while loop statement:
--
-- J : Index_Base := L;
-- while J < H loop
-- J := Index_Base'Succ (J);
-- Into (Indexes, J) := Expr;
-- end loop;
--
-- Otherwise we call Build_Code recursively
function Get_Assoc_Expr (Assoc : Node_Id) return Node_Id;
-- For an association with a box, use value given by aspect
-- Default_Component_Value of array type if specified, else use
-- value given by aspect Default_Value for component type itself
-- if specified, else return Empty.
function Local_Compile_Time_Known_Value (E : Node_Id) return Boolean;
function Local_Expr_Value (E : Node_Id) return Uint;
-- These two Local routines are used to replace the corresponding ones
-- in sem_eval because while processing the bounds of an aggregate with
-- discrete choices whose index type is an enumeration, we build static
-- expressions not recognized by Compile_Time_Known_Value as such since
-- they have not yet been analyzed and resolved. All the expressions in
-- question are things like Index_Base_Name'Val (Const) which we can
-- easily recognize as being constant.
---------
-- Add --
---------
function Add (Val : Int; To : Node_Id) return Node_Id is
Expr_Pos : Node_Id;
Expr : Node_Id;
To_Pos : Node_Id;
U_To : Uint;
U_Val : constant Uint := UI_From_Int (Val);
begin
-- Note: do not try to optimize the case of Val = 0, because
-- we need to build a new node with the proper Sloc value anyway.
-- First test if we can do constant folding
if Local_Compile_Time_Known_Value (To) then
U_To := Local_Expr_Value (To) + Val;
-- Determine if our constant is outside the range of the index.
-- If so return an Empty node. This empty node will be caught
-- by Empty_Range below.
if Compile_Time_Known_Value (Index_Base_L)
and then U_To < Expr_Value (Index_Base_L)
then
return Empty;
elsif Compile_Time_Known_Value (Index_Base_H)
and then U_To > Expr_Value (Index_Base_H)
then
return Empty;
end if;
Expr_Pos := Make_Integer_Literal (Loc, U_To);
Set_Is_Static_Expression (Expr_Pos);
if not Is_Enumeration_Type (Index_Base) then
Expr := Expr_Pos;
-- If we are dealing with enumeration return
-- Index_Base'Val (Expr_Pos)
else
Expr :=
Make_Attribute_Reference
(Loc,
Prefix => Index_Base_Name,
Attribute_Name => Name_Val,
Expressions => New_List (Expr_Pos));
end if;
return Expr;
end if;
-- If we are here no constant folding possible
if not Is_Enumeration_Type (Index_Base) then
Expr :=
Make_Op_Add (Loc,
Left_Opnd => Duplicate_Subexpr (To),
Right_Opnd => Make_Integer_Literal (Loc, U_Val));
-- If we are dealing with enumeration return
-- Index_Base'Val (Index_Base'Pos (To) + Val)
else
To_Pos :=
Make_Attribute_Reference
(Loc,
Prefix => Index_Base_Name,
Attribute_Name => Name_Pos,
Expressions => New_List (Duplicate_Subexpr (To)));
Expr_Pos :=
Make_Op_Add (Loc,
Left_Opnd => To_Pos,
Right_Opnd => Make_Integer_Literal (Loc, U_Val));
Expr :=
Make_Attribute_Reference
(Loc,
Prefix => Index_Base_Name,
Attribute_Name => Name_Val,
Expressions => New_List (Expr_Pos));
end if;
return Expr;
end Add;
-----------------
-- Empty_Range --
-----------------
function Empty_Range (L, H : Node_Id) return Boolean is
Is_Empty : Boolean := False;
Low : Node_Id;
High : Node_Id;
begin
-- First check if L or H were already detected as overflowing the
-- index base range type by function Add above. If this is so Add
-- returns the empty node.
if No (L) or else No (H) then
return True;
end if;
for J in 1 .. 3 loop
case J is
-- L > H range is empty
when 1 =>
Low := L;
High := H;
-- B_L > H range must be empty
when 2 =>
Low := Index_Base_L;
High := H;
-- L > B_H range must be empty
when 3 =>
Low := L;
High := Index_Base_H;
end case;
if Local_Compile_Time_Known_Value (Low)
and then
Local_Compile_Time_Known_Value (High)
then
Is_Empty :=
UI_Gt (Local_Expr_Value (Low), Local_Expr_Value (High));
end if;
exit when Is_Empty;
end loop;
return Is_Empty;
end Empty_Range;
-----------
-- Equal --
-----------
function Equal (L, H : Node_Id) return Boolean is
begin
if L = H then
return True;
elsif Local_Compile_Time_Known_Value (L)
and then
Local_Compile_Time_Known_Value (H)
then
return UI_Eq (Local_Expr_Value (L), Local_Expr_Value (H));
end if;
return False;
end Equal;
----------------
-- Gen_Assign --
----------------
function Gen_Assign
(Ind : Node_Id;
Expr : Node_Id;
In_Loop : Boolean := False) return List_Id
is
function Add_Loop_Actions (Lis : List_Id) return List_Id;
-- Collect insert_actions generated in the construction of a loop,
-- and prepend them to the sequence of assignments to complete the
-- eventual body of the loop.
procedure Initialize_Array_Component
(Arr_Comp : Node_Id;
Comp_Typ : Node_Id;
Init_Expr : Node_Id;
Stmts : List_Id);
-- Perform the initialization of array component Arr_Comp with
-- expected type Comp_Typ. Init_Expr denotes the initialization
-- expression of the array component. All generated code is added
-- to list Stmts.
procedure Initialize_Ctrl_Array_Component
(Arr_Comp : Node_Id;
Comp_Typ : Entity_Id;
Init_Expr : Node_Id;
Stmts : List_Id);
-- Perform the initialization of array component Arr_Comp when its
-- expected type Comp_Typ needs finalization actions. Init_Expr is
-- the initialization expression of the array component. All hook-
-- related declarations are inserted prior to aggregate N. Remaining
-- code is added to list Stmts.
----------------------
-- Add_Loop_Actions --
----------------------
function Add_Loop_Actions (Lis : List_Id) return List_Id is
Res : List_Id;
begin
-- Ada 2005 (AI-287): Do nothing else in case of default
-- initialized component.
if No (Expr) then
return Lis;
elsif Nkind (Parent (Expr)) = N_Component_Association
and then Present (Loop_Actions (Parent (Expr)))
then
Append_List (Lis, Loop_Actions (Parent (Expr)));
Res := Loop_Actions (Parent (Expr));
Set_Loop_Actions (Parent (Expr), No_List);
return Res;
else
return Lis;
end if;
end Add_Loop_Actions;
--------------------------------
-- Initialize_Array_Component --
--------------------------------
procedure Initialize_Array_Component
(Arr_Comp : Node_Id;
Comp_Typ : Node_Id;
Init_Expr : Node_Id;
Stmts : List_Id)
is
Exceptions_OK : constant Boolean :=
not Restriction_Active
(No_Exception_Propagation);
Finalization_OK : constant Boolean :=
Present (Comp_Typ)
and then Needs_Finalization (Comp_Typ);
Full_Typ : constant Entity_Id := Underlying_Type (Comp_Typ);
Adj_Call : Node_Id;
Blk_Stmts : List_Id;
Init_Stmt : Node_Id;
begin
-- Protect the initialization statements from aborts. Generate:
-- Abort_Defer;
if Finalization_OK and Abort_Allowed then
if Exceptions_OK then
Blk_Stmts := New_List;
else
Blk_Stmts := Stmts;
end if;
Append_To (Blk_Stmts, Build_Runtime_Call (Loc, RE_Abort_Defer));
-- Otherwise aborts are not allowed. All generated code is added
-- directly to the input list.
else
Blk_Stmts := Stmts;
end if;
-- Initialize the array element. Generate:
-- Arr_Comp := Init_Expr;
-- Note that the initialization expression is replicated because
-- it has to be reevaluated within a generated loop.
Init_Stmt :=
Make_OK_Assignment_Statement (Loc,
Name => New_Copy_Tree (Arr_Comp),
Expression => New_Copy_Tree (Init_Expr));
Set_No_Ctrl_Actions (Init_Stmt);
-- If this is an aggregate for an array of arrays, each
-- subaggregate will be expanded as well, and even with
-- No_Ctrl_Actions the assignments of inner components will
-- require attachment in their assignments to temporaries. These
-- temporaries must be finalized for each subaggregate. Generate:
-- begin
-- Arr_Comp := Init_Expr;
-- end;
if Finalization_OK and then Is_Array_Type (Comp_Typ) then
Init_Stmt :=
Make_Block_Statement (Loc,
Handled_Statement_Sequence =>
Make_Handled_Sequence_Of_Statements (Loc,
Statements => New_List (Init_Stmt)));
end if;
Append_To (Blk_Stmts, Init_Stmt);
-- Adjust the tag due to a possible view conversion. Generate:
-- Arr_Comp._tag := Full_TypP;
if Tagged_Type_Expansion
and then Present (Comp_Typ)
and then Is_Tagged_Type (Comp_Typ)
then
Append_To (Blk_Stmts,
Make_OK_Assignment_Statement (Loc,
Name =>
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Arr_Comp),
Selector_Name =>
New_Occurrence_Of
(First_Tag_Component (Full_Typ), Loc)),
Expression =>
Unchecked_Convert_To (RTE (RE_Tag),
New_Occurrence_Of
(Node (First_Elmt (Access_Disp_Table (Full_Typ))),
Loc))));
end if;
-- Adjust the array component. Controlled subaggregates are not
-- considered because each of their individual elements will
-- receive an adjustment of its own. Generate:
-- [Deep_]Adjust (Arr_Comp);
if Finalization_OK
and then not Is_Limited_Type (Comp_Typ)
and then not Is_Build_In_Place_Function_Call (Init_Expr)
and then not
(Is_Array_Type (Comp_Typ)
and then Is_Controlled (Component_Type (Comp_Typ))
and then Nkind (Expr) = N_Aggregate)
then
Adj_Call :=
Make_Adjust_Call
(Obj_Ref => New_Copy_Tree (Arr_Comp),
Typ => Comp_Typ);
-- Guard against a missing [Deep_]Adjust when the component
-- type was not frozen properly.
if Present (Adj_Call) then
Append_To (Blk_Stmts, Adj_Call);
end if;
end if;
-- Complete the protection of the initialization statements
if Finalization_OK and Abort_Allowed then
-- Wrap the initialization statements in a block to catch a
-- potential exception. Generate:
-- begin
-- Abort_Defer;
-- Arr_Comp := Init_Expr;
-- Arr_Comp._tag := Full_TypP;
-- [Deep_]Adjust (Arr_Comp);
-- at end
-- Abort_Undefer_Direct;
-- end;
if Exceptions_OK then
Append_To (Stmts,
Build_Abort_Undefer_Block (Loc,
Stmts => Blk_Stmts,
Context => N));
-- Otherwise exceptions are not propagated. Generate:
-- Abort_Defer;
-- Arr_Comp := Init_Expr;
-- Arr_Comp._tag := Full_TypP;
-- [Deep_]Adjust (Arr_Comp);
-- Abort_Undefer;
else
Append_To (Blk_Stmts,
Build_Runtime_Call (Loc, RE_Abort_Undefer));
end if;
end if;
end Initialize_Array_Component;
-------------------------------------
-- Initialize_Ctrl_Array_Component --
-------------------------------------
procedure Initialize_Ctrl_Array_Component
(Arr_Comp : Node_Id;
Comp_Typ : Entity_Id;
Init_Expr : Node_Id;
Stmts : List_Id)
is
Act_Aggr : Node_Id;
Act_Stmts : List_Id;
Expr : Node_Id;
Fin_Call : Node_Id;
Hook_Clear : Node_Id;
In_Place_Expansion : Boolean;
-- Flag set when a nonlimited controlled function call requires
-- in-place expansion.
begin
-- Duplicate the initialization expression in case the context is
-- a multi choice list or an "others" choice which plugs various
-- holes in the aggregate. As a result the expression is no longer
-- shared between the various components and is reevaluated for
-- each such component.
Expr := New_Copy_Tree (Init_Expr);
Set_Parent (Expr, Parent (Init_Expr));
-- Perform a preliminary analysis and resolution to determine what
-- the initialization expression denotes. An unanalyzed function
-- call may appear as an identifier or an indexed component.
if Nkind (Expr) in N_Function_Call
| N_Identifier
| N_Indexed_Component
and then not Analyzed (Expr)
then
Preanalyze_And_Resolve (Expr, Comp_Typ);
end if;
In_Place_Expansion :=
Nkind (Expr) = N_Function_Call
and then not Is_Build_In_Place_Result_Type (Comp_Typ);
-- The initialization expression is a controlled function call.
-- Perform in-place removal of side effects to avoid creating a
-- transient scope, which leads to premature finalization.
-- This in-place expansion is not performed for limited transient
-- objects, because the initialization is already done in place.
if In_Place_Expansion then
-- Suppress the removal of side effects by general analysis,
-- because this behavior is emulated here. This avoids the
-- generation of a transient scope, which leads to out-of-order
-- adjustment and finalization.
Set_No_Side_Effect_Removal (Expr);
-- When the transient component initialization is related to a
-- range or an "others", keep all generated statements within
-- the enclosing loop. This way the controlled function call
-- will be evaluated at each iteration, and its result will be
-- finalized at the end of each iteration.
if In_Loop then
Act_Aggr := Empty;
Act_Stmts := Stmts;
-- Otherwise this is a single component initialization. Hook-
-- related statements are inserted prior to the aggregate.
else
Act_Aggr := N;
Act_Stmts := No_List;
end if;
-- Install all hook-related declarations and prepare the clean
-- up statements.
Process_Transient_Component
(Loc => Loc,
Comp_Typ => Comp_Typ,
Init_Expr => Expr,
Fin_Call => Fin_Call,
Hook_Clear => Hook_Clear,
Aggr => Act_Aggr,
Stmts => Act_Stmts);
end if;
-- Use the noncontrolled component initialization circuitry to
-- assign the result of the function call to the array element.
-- This also performs subaggregate wrapping, tag adjustment, and
-- [deep] adjustment of the array element.
Initialize_Array_Component
(Arr_Comp => Arr_Comp,
Comp_Typ => Comp_Typ,
Init_Expr => Expr,
Stmts => Stmts);
-- At this point the array element is fully initialized. Complete
-- the processing of the controlled array component by finalizing
-- the transient function result.
if In_Place_Expansion then
Process_Transient_Component_Completion
(Loc => Loc,
Aggr => N,
Fin_Call => Fin_Call,
Hook_Clear => Hook_Clear,
Stmts => Stmts);
end if;
end Initialize_Ctrl_Array_Component;
-- Local variables
Stmts : constant List_Id := New_List;
Comp_Typ : Entity_Id := Empty;
Expr_Q : Node_Id;
Indexed_Comp : Node_Id;
Init_Call : Node_Id;
New_Indexes : List_Id;
-- Start of processing for Gen_Assign
begin
if No (Indexes) then
New_Indexes := New_List;
else
New_Indexes := New_Copy_List_Tree (Indexes);
end if;
Append_To (New_Indexes, Ind);
if Present (Next_Index (Index)) then
return
Add_Loop_Actions (
Build_Array_Aggr_Code
(N => Expr,
Ctype => Ctype,
Index => Next_Index (Index),
Into => Into,
Scalar_Comp => Scalar_Comp,
Indexes => New_Indexes));
end if;
-- If we get here then we are at a bottom-level (sub-)aggregate
Indexed_Comp :=
Checks_Off
(Make_Indexed_Component (Loc,
Prefix => New_Copy_Tree (Into),
Expressions => New_Indexes));
Set_Assignment_OK (Indexed_Comp);
-- Ada 2005 (AI-287): In case of default initialized component, Expr
-- is not present (and therefore we also initialize Expr_Q to empty).
if No (Expr) then
Expr_Q := Empty;
elsif Nkind (Expr) = N_Qualified_Expression then
Expr_Q := Expression (Expr);
else
Expr_Q := Expr;
end if;
if Present (Etype (N)) and then Etype (N) /= Any_Composite then
Comp_Typ := Component_Type (Etype (N));
pragma Assert (Comp_Typ = Ctype); -- AI-287
elsif Present (Next (First (New_Indexes))) then
-- Ada 2005 (AI-287): Do nothing in case of default initialized
-- component because we have received the component type in
-- the formal parameter Ctype.
-- ??? Some assert pragmas have been added to check if this new
-- formal can be used to replace this code in all cases.
if Present (Expr) then
-- This is a multidimensional array. Recover the component type
-- from the outermost aggregate, because subaggregates do not
-- have an assigned type.
declare
P : Node_Id;
begin
P := Parent (Expr);
while Present (P) loop
if Nkind (P) = N_Aggregate
and then Present (Etype (P))
then
Comp_Typ := Component_Type (Etype (P));
exit;
else
P := Parent (P);
end if;
end loop;
pragma Assert (Comp_Typ = Ctype); -- AI-287
end;
end if;
end if;
-- Ada 2005 (AI-287): We only analyze the expression in case of non-
-- default initialized components (otherwise Expr_Q is not present).
if Present (Expr_Q)
and then Nkind (Expr_Q) in N_Aggregate | N_Extension_Aggregate
then
-- At this stage the Expression may not have been analyzed yet
-- because the array aggregate code has not been updated to use
-- the Expansion_Delayed flag and avoid analysis altogether to
-- solve the same problem (see Resolve_Aggr_Expr). So let us do
-- the analysis of non-array aggregates now in order to get the
-- value of Expansion_Delayed flag for the inner aggregate ???
-- In the case of an iterated component association, the analysis
-- of the generated loop will analyze the expression in the
-- proper context, in which the loop parameter is visible.
if Present (Comp_Typ) and then not Is_Array_Type (Comp_Typ) then
if Nkind (Parent (Expr_Q)) = N_Iterated_Component_Association
or else Nkind (Parent (Parent ((Expr_Q)))) =
N_Iterated_Component_Association
then
null;
else
Analyze_And_Resolve (Expr_Q, Comp_Typ);
end if;
end if;
if Is_Delayed_Aggregate (Expr_Q) then
-- This is either a subaggregate of a multidimensional array,
-- or a component of an array type whose component type is
-- also an array. In the latter case, the expression may have
-- component associations that provide different bounds from
-- those of the component type, and sliding must occur. Instead
-- of decomposing the current aggregate assignment, force the
-- reanalysis of the assignment, so that a temporary will be
-- generated in the usual fashion, and sliding will take place.
if Nkind (Parent (N)) = N_Assignment_Statement
and then Is_Array_Type (Comp_Typ)
and then Present (Component_Associations (Expr_Q))
and then Must_Slide (Comp_Typ, Etype (Expr_Q))
then
Set_Expansion_Delayed (Expr_Q, False);
Set_Analyzed (Expr_Q, False);
else
return
Add_Loop_Actions (
Late_Expansion (Expr_Q, Etype (Expr_Q), Indexed_Comp));
end if;
end if;
end if;
if Present (Expr) then
-- Handle an initialization expression of a controlled type in
-- case it denotes a function call. In general such a scenario
-- will produce a transient scope, but this will lead to wrong
-- order of initialization, adjustment, and finalization in the
-- context of aggregates.
-- Target (1) := Ctrl_Func_Call;
-- begin -- scope
-- Trans_Obj : ... := Ctrl_Func_Call; -- object
-- Target (1) := Trans_Obj;
-- Finalize (Trans_Obj);
-- end;
-- Target (1)._tag := ...;
-- Adjust (Target (1));
-- In the example above, the call to Finalize occurs too early
-- and as a result it may leave the array component in a bad
-- state. Finalization of the transient object should really
-- happen after adjustment.
-- To avoid this scenario, perform in-place side-effect removal
-- of the function call. This eliminates the transient property
-- of the function result and ensures correct order of actions.
-- Res : ... := Ctrl_Func_Call;
-- Target (1) := Res;
-- Target (1)._tag := ...;
-- Adjust (Target (1));
-- Finalize (Res);
if Present (Comp_Typ)
and then Needs_Finalization (Comp_Typ)
and then Nkind (Expr) /= N_Aggregate
then
Initialize_Ctrl_Array_Component
(Arr_Comp => Indexed_Comp,
Comp_Typ => Comp_Typ,
Init_Expr => Expr,
Stmts => Stmts);
-- Otherwise perform simple component initialization
else
Initialize_Array_Component
(Arr_Comp => Indexed_Comp,
Comp_Typ => Comp_Typ,
Init_Expr => Expr,
Stmts => Stmts);
end if;
-- Ada 2005 (AI-287): In case of default initialized component, call
-- the initialization subprogram associated with the component type.
-- If the component type is an access type, add an explicit null
-- assignment, because for the back-end there is an initialization
-- present for the whole aggregate, and no default initialization
-- will take place.
-- In addition, if the component type is controlled, we must call
-- its Initialize procedure explicitly, because there is no explicit
-- object creation that will invoke it otherwise.
else
if Present (Base_Init_Proc (Base_Type (Ctype)))
or else Has_Task (Base_Type (Ctype))
then
Append_List_To (Stmts,
Build_Initialization_Call (Loc,
Id_Ref => Indexed_Comp,
Typ => Ctype,
With_Default_Init => True));
-- If the component type has invariants, add an invariant
-- check after the component is default-initialized. It will
-- be analyzed and resolved before the code for initialization
-- of other components.
if Has_Invariants (Ctype) then
Set_Etype (Indexed_Comp, Ctype);
Append_To (Stmts, Make_Invariant_Call (Indexed_Comp));
end if;
elsif Is_Access_Type (Ctype) then
Append_To (Stmts,
Make_Assignment_Statement (Loc,
Name => New_Copy_Tree (Indexed_Comp),
Expression => Make_Null (Loc)));
end if;
if Needs_Finalization (Ctype) then
Init_Call :=
Make_Init_Call
(Obj_Ref => New_Copy_Tree (Indexed_Comp),
Typ => Ctype);
-- Guard against a missing [Deep_]Initialize when the component
-- type was not properly frozen.
if Present (Init_Call) then
Append_To (Stmts, Init_Call);
end if;
end if;
-- If Default_Initial_Condition applies to the component type,
-- add a DIC check after the component is default-initialized,
-- as well as after an Initialize procedure is called, in the
-- case of components of a controlled type. It will be analyzed
-- and resolved before the code for initialization of other
-- components.
-- Theoretically this might also be needed for cases where Expr
-- is not empty, but a default init still applies, such as for
-- Default_Value cases, in which case we won't get here. ???
if Has_DIC (Ctype) and then Present (DIC_Procedure (Ctype)) then
Append_To (Stmts,
Build_DIC_Call (Loc, New_Copy_Tree (Indexed_Comp), Ctype));
end if;
end if;
return Add_Loop_Actions (Stmts);
end Gen_Assign;
--------------
-- Gen_Loop --
--------------
function Gen_Loop (L, H : Node_Id; Expr : Node_Id) return List_Id is
Is_Iterated_Component : constant Boolean :=
Nkind (Parent (Expr)) = N_Iterated_Component_Association;
L_J : Node_Id;
L_L : Node_Id;
-- Index_Base'(L)
L_H : Node_Id;
-- Index_Base'(H)
L_Range : Node_Id;
-- Index_Base'(L) .. Index_Base'(H)
L_Iteration_Scheme : Node_Id;
-- L_J in Index_Base'(L) .. Index_Base'(H)
L_Body : List_Id;
-- The statements to execute in the loop
S : constant List_Id := New_List;
-- List of statements
Tcopy : Node_Id;
-- Copy of expression tree, used for checking purposes
begin
-- If loop bounds define an empty range return the null statement
if Empty_Range (L, H) then
Append_To (S, Make_Null_Statement (Loc));
-- Ada 2005 (AI-287): Nothing else need to be done in case of
-- default initialized component.
if No (Expr) then
null;
else
-- The expression must be type-checked even though no component
-- of the aggregate will have this value. This is done only for
-- actual components of the array, not for subaggregates. Do
-- the check on a copy, because the expression may be shared
-- among several choices, some of which might be non-null.
if Present (Etype (N))
and then Is_Array_Type (Etype (N))
and then No (Next_Index (Index))
then
Expander_Mode_Save_And_Set (False);
Tcopy := New_Copy_Tree (Expr);
Set_Parent (Tcopy, N);
-- For iterated_component_association analyze and resolve
-- the expression with name of the index parameter visible.
-- To manipulate scopes, we use entity of the implicit loop.
if Is_Iterated_Component then
declare
Index_Parameter : constant Entity_Id :=
Defining_Identifier (Parent (Expr));
begin
Push_Scope (Scope (Index_Parameter));
Enter_Name (Index_Parameter);
Analyze_And_Resolve
(Tcopy, Component_Type (Etype (N)));
End_Scope;
end;
-- For ordinary component association, just analyze and
-- resolve the expression.
else
Analyze_And_Resolve (Tcopy, Component_Type (Etype (N)));
end if;
Expander_Mode_Restore;
end if;
end if;
return S;
-- If loop bounds are the same then generate an assignment, unless
-- the parent construct is an Iterated_Component_Association.
elsif Equal (L, H) and then not Is_Iterated_Component then
return Gen_Assign (New_Copy_Tree (L), Expr);
-- If H - L <= 2 then generate a sequence of assignments when we are
-- processing the bottom most aggregate and it contains scalar
-- components.
elsif No (Next_Index (Index))
and then Scalar_Comp
and then Local_Compile_Time_Known_Value (L)
and then Local_Compile_Time_Known_Value (H)
and then Local_Expr_Value (H) - Local_Expr_Value (L) <= 2
and then not Is_Iterated_Component
then
Append_List_To (S, Gen_Assign (New_Copy_Tree (L), Expr));
Append_List_To (S, Gen_Assign (Add (1, To => L), Expr));
if Local_Expr_Value (H) - Local_Expr_Value (L) = 2 then
Append_List_To (S, Gen_Assign (Add (2, To => L), Expr));
end if;
return S;
end if;
-- Otherwise construct the loop, starting with the loop index L_J
if Is_Iterated_Component then
L_J :=
Make_Defining_Identifier (Loc,
Chars => (Chars (Defining_Identifier (Parent (Expr)))));
else
L_J := Make_Temporary (Loc, 'J', L);
end if;
-- Construct "L .. H" in Index_Base. We use a qualified expression
-- for the bound to convert to the index base, but we don't need
-- to do that if we already have the base type at hand.
if Etype (L) = Index_Base then
L_L := L;
else
L_L :=
Make_Qualified_Expression (Loc,
Subtype_Mark => Index_Base_Name,
Expression => New_Copy_Tree (L));
end if;
if Etype (H) = Index_Base then
L_H := H;
else
L_H :=
Make_Qualified_Expression (Loc,
Subtype_Mark => Index_Base_Name,
Expression => New_Copy_Tree (H));
end if;
L_Range :=
Make_Range (Loc,
Low_Bound => L_L,
High_Bound => L_H);
-- Construct "for L_J in Index_Base range L .. H"
L_Iteration_Scheme :=
Make_Iteration_Scheme
(Loc,
Loop_Parameter_Specification =>
Make_Loop_Parameter_Specification
(Loc,
Defining_Identifier => L_J,
Discrete_Subtype_Definition => L_Range));
-- Construct the statements to execute in the loop body
L_Body :=
Gen_Assign (New_Occurrence_Of (L_J, Loc), Expr, In_Loop => True);
-- Construct the final loop
Append_To (S,
Make_Implicit_Loop_Statement
(Node => N,
Identifier => Empty,
Iteration_Scheme => L_Iteration_Scheme,
Statements => L_Body));
-- A small optimization: if the aggregate is initialized with a box
-- and the component type has no initialization procedure, remove the
-- useless empty loop.
if Nkind (First (S)) = N_Loop_Statement
and then Is_Empty_List (Statements (First (S)))
then
return New_List (Make_Null_Statement (Loc));
else
return S;
end if;
end Gen_Loop;
---------------
-- Gen_While --
---------------
-- The code built is
-- W_J : Index_Base := L;
-- while W_J < H loop
-- W_J := Index_Base'Succ (W);
-- L_Body;
-- end loop;
function Gen_While (L, H : Node_Id; Expr : Node_Id) return List_Id is
W_J : Node_Id;
W_Decl : Node_Id;
-- W_J : Base_Type := L;
W_Iteration_Scheme : Node_Id;
-- while W_J < H
W_Index_Succ : Node_Id;
-- Index_Base'Succ (J)
W_Increment : Node_Id;
-- W_J := Index_Base'Succ (W)
W_Body : constant List_Id := New_List;
-- The statements to execute in the loop
S : constant List_Id := New_List;
-- list of statement
begin
-- If loop bounds define an empty range or are equal return null
if Empty_Range (L, H) or else Equal (L, H) then
Append_To (S, Make_Null_Statement (Loc));
return S;
end if;
-- Build the decl of W_J
W_J := Make_Temporary (Loc, 'J', L);
W_Decl :=
Make_Object_Declaration
(Loc,
Defining_Identifier => W_J,
Object_Definition => Index_Base_Name,
Expression => L);
-- Theoretically we should do a New_Copy_Tree (L) here, but we know
-- that in this particular case L is a fresh Expr generated by
-- Add which we are the only ones to use.
Append_To (S, W_Decl);
-- Construct " while W_J < H"
W_Iteration_Scheme :=
Make_Iteration_Scheme
(Loc,
Condition => Make_Op_Lt
(Loc,
Left_Opnd => New_Occurrence_Of (W_J, Loc),
Right_Opnd => New_Copy_Tree (H)));
-- Construct the statements to execute in the loop body
W_Index_Succ :=
Make_Attribute_Reference
(Loc,
Prefix => Index_Base_Name,
Attribute_Name => Name_Succ,
Expressions => New_List (New_Occurrence_Of (W_J, Loc)));
W_Increment :=
Make_OK_Assignment_Statement
(Loc,
Name => New_Occurrence_Of (W_J, Loc),
Expression => W_Index_Succ);
Append_To (W_Body, W_Increment);
Append_List_To (W_Body,
Gen_Assign (New_Occurrence_Of (W_J, Loc), Expr, In_Loop => True));
-- Construct the final loop
Append_To (S,
Make_Implicit_Loop_Statement
(Node => N,
Identifier => Empty,
Iteration_Scheme => W_Iteration_Scheme,
Statements => W_Body));
return S;
end Gen_While;
--------------------
-- Get_Assoc_Expr --
--------------------
function Get_Assoc_Expr (Assoc : Node_Id) return Node_Id is
Typ : constant Entity_Id := Base_Type (Etype (N));
begin
if Box_Present (Assoc) then
if 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;
-- Local variables
New_Code : constant List_Id := New_List;
Aggr_L : constant Node_Id := Low_Bound (Aggregate_Bounds (N));
Aggr_H : constant Node_Id := High_Bound (Aggregate_Bounds (N));
-- The aggregate bounds of this specific subaggregate. Note that if the
-- code generated by Build_Array_Aggr_Code is executed then these bounds
-- are OK. Otherwise a Constraint_Error would have been raised.
Aggr_Low : constant Node_Id := Duplicate_Subexpr_No_Checks (Aggr_L);
Aggr_High : constant Node_Id := Duplicate_Subexpr_No_Checks (Aggr_H);
-- After Duplicate_Subexpr these are side-effect free
Assoc : Node_Id;
Choice : Node_Id;
Expr : Node_Id;
High : Node_Id;
Low : Node_Id;
Typ : Entity_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
Others_Assoc : Node_Id := Empty;
-- Start of processing for Build_Array_Aggr_Code
begin
-- First before we start, a special case. if we have a bit packed
-- array represented as a modular type, then clear the value to
-- zero first, to ensure that unused bits are properly cleared.
Typ := Etype (N);
if Present (Typ)
and then Is_Bit_Packed_Array (Typ)
and then Is_Modular_Integer_Type (Packed_Array_Impl_Type (Typ))
then
declare
Zero : constant Node_Id := Make_Integer_Literal (Loc, Uint_0);
begin
Analyze_And_Resolve (Zero, Packed_Array_Impl_Type (Typ));
Append_To (New_Code,
Make_Assignment_Statement (Loc,
Name => New_Copy_Tree (Into),
Expression => Unchecked_Convert_To (Typ, Zero)));
end;
end if;
-- If the component type contains tasks, we need to build a Master
-- entity in the current scope, because it will be needed if build-
-- in-place functions are called in the expanded code.
if Nkind (Parent (N)) = N_Object_Declaration and then Has_Task (Typ) then
Build_Master_Entity (Defining_Identifier (Parent (N)));
end if;
-- STEP 1: Process component associations
-- For those associations that may generate a loop, initialize
-- Loop_Actions to collect inserted actions that may be crated.
-- Skip this if no component associations
if No (Expressions (N)) then
-- STEP 1 (a): Sort the discrete choices
Assoc := First (Component_Associations (N));
while Present (Assoc) loop
Choice := First (Choice_List (Assoc));
while Present (Choice) loop
if Nkind (Choice) = N_Others_Choice then
Others_Assoc := Assoc;
exit;
end if;
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;
Dup_Expr : Node_Id;
begin
for J in 0 .. Nb_Choices loop
if J = 0 then
Low := Aggr_Low;
else
Low := Add (1, To => Table (J).Choice_Hi);
end if;
if J = Nb_Choices then
High := Aggr_High;
else
High := Add (-1, To => Table (J + 1).Choice_Lo);
end if;
-- If this is an expansion within an init proc, make
-- sure that discriminant references are replaced by
-- the corresponding discriminal.
if Inside_Init_Proc then
if Is_Entity_Name (Low)
and then Ekind (Entity (Low)) = E_Discriminant
then
Set_Entity (Low, Discriminal (Entity (Low)));
end if;
if Is_Entity_Name (High)
and then Ekind (Entity (High)) = E_Discriminant
then
Set_Entity (High, Discriminal (Entity (High)));
end if;
end if;
if First
or else not Empty_Range (Low, High)
then
First := False;
-- Duplicate the expression in case we will be generating
-- several loops. As a result the expression is no longer
-- shared between the loops and is reevaluated for each
-- such loop.
Expr := Get_Assoc_Expr (Others_Assoc);
Dup_Expr := New_Copy_Tree (Expr);
Set_Parent (Dup_Expr, Parent (Expr));
Set_Loop_Actions (Others_Assoc, New_List);
Append_List
(Gen_Loop (Low, High, Dup_Expr), To => New_Code);
end if;
end loop;
end;
end if;
-- STEP 2: Process positional components
else
-- STEP 2 (a): Generate the assignments for each positional element
-- Note that here we have to use Aggr_L rather than Aggr_Low because
-- Aggr_L is analyzed and Add wants an analyzed expression.
Expr := First (Expressions (N));
Nb_Elements := -1;
while Present (Expr) loop
Nb_Elements := Nb_Elements + 1;
Append_List (Gen_Assign (Add (Nb_Elements, To => Aggr_L), Expr),
To => New_Code);
Next (Expr);
end loop;
-- STEP 2 (b): Generate final loop if an others choice is present.
-- Here Nb_Elements gives the offset of the last positional element.
if Present (Component_Associations (N)) then
Assoc := Last (Component_Associations (N));
if Nkind (Assoc) = N_Iterated_Component_Association then
-- Ada 2020: generate a loop to have a proper scope for
-- the identifier that typically appears in the expression.
-- The lower bound of the loop is the position after all
-- previous positional components.
Append_List (Gen_Loop (Add (Nb_Elements + 1, To => Aggr_L),
Aggr_High,
Expression (Assoc)),
To => New_Code);
else
-- Ada 2005 (AI-287)
Append_List (Gen_While (Add (Nb_Elements, To => Aggr_L),
Aggr_High,
Get_Assoc_Expr (Assoc)),
To => New_Code);
end if;
end if;
end if;
return New_Code;
end Build_Array_Aggr_Code;
----------------------------
-- Build_Record_Aggr_Code --
----------------------------
function Build_Record_Aggr_Code
(N : Node_Id;
Typ : Entity_Id;
Lhs : Node_Id) return List_Id
is
Loc : constant Source_Ptr := Sloc (N);
L : constant List_Id := New_List;
N_Typ : constant Entity_Id := Etype (N);
Comp : Node_Id;
Instr : Node_Id;
Ref : Node_Id;
Target : Entity_Id;
Comp_Type : Entity_Id;
Selector : Entity_Id;
Comp_Expr : Node_Id;
Expr_Q : Node_Id;
-- If this is an internal aggregate, the External_Final_List is an
-- expression for the controller record of the enclosing type.
-- If the current aggregate has several controlled components, this
-- expression will appear in several calls to attach to the finali-
-- zation list, and it must not be shared.
Ancestor_Is_Expression : Boolean := False;
Ancestor_Is_Subtype_Mark : Boolean := False;
Init_Typ : Entity_Id := Empty;
Finalization_Done : Boolean := False;
-- True if Generate_Finalization_Actions has already been called; calls
-- after the first do nothing.
function Ancestor_Discriminant_Value (Disc : Entity_Id) return Node_Id;
-- Returns the value that the given discriminant of an ancestor type
-- should receive (in the absence of a conflict with the value provided
-- by an ancestor part of an extension aggregate).
procedure Check_Ancestor_Discriminants (Anc_Typ : Entity_Id);
-- Check that each of the discriminant values defined by the ancestor
-- part of an extension aggregate match the corresponding values
-- provided by either an association of the aggregate or by the
-- constraint imposed by a parent type (RM95-4.3.2(8)).
function Compatible_Int_Bounds
(Agg_Bounds : Node_Id;
Typ_Bounds : Node_Id) return Boolean;
-- Return true if Agg_Bounds are equal or within Typ_Bounds. It is
-- assumed that both bounds are integer ranges.
procedure Generate_Finalization_Actions;
-- Deal with the various controlled type data structure initializations
-- (but only if it hasn't been done already).
function Get_Constraint_Association (T : Entity_Id) return Node_Id;
-- Returns the first discriminant association in the constraint
-- associated with T, if any, otherwise returns Empty.
function Get_Explicit_Discriminant_Value (D : Entity_Id) return Node_Id;
-- If the ancestor part is an unconstrained type and further ancestors
-- do not provide discriminants for it, check aggregate components for
-- values of the discriminants.
procedure Init_Hidden_Discriminants (Typ : Entity_Id; List : List_Id);
-- If Typ is derived, and constrains discriminants of the parent type,
-- these discriminants are not components of the aggregate, and must be
-- initialized. The assignments are appended to List. The same is done
-- if Typ derives fron an already constrained subtype of a discriminated
-- parent type.
procedure Init_Stored_Discriminants;
-- If the type is derived and has inherited discriminants, generate
-- explicit assignments for each, using the store constraint of the
-- type. Note that both visible and stored discriminants must be
-- initialized in case the derived type has some renamed and some
-- constrained discriminants.
procedure Init_Visible_Discriminants;
-- If type has discriminants, retrieve their values from aggregate,
-- and generate explicit assignments for each. This does not include
-- discriminants inherited from ancestor, which are handled above.
-- The type of the aggregate is a subtype created ealier using the
-- given values of the discriminant components of the aggregate.
procedure Initialize_Ctrl_Record_Component
(Rec_Comp : Node_Id;
Comp_Typ : Entity_Id;
Init_Expr : Node_Id;
Stmts : List_Id);
-- Perform the initialization of controlled record component Rec_Comp.
-- Comp_Typ is the component type. Init_Expr is the initialization
-- expression for the record component. Hook-related declarations are
-- inserted prior to aggregate N using Insert_Action. All remaining
-- generated code is added to list Stmts.
procedure Initialize_Record_Component
(Rec_Comp : Node_Id;
Comp_Typ : Entity_Id;
Init_Expr : Node_Id;
Stmts : List_Id);
-- Perform the initialization of record component Rec_Comp. Comp_Typ
-- is the component type. Init_Expr is the initialization expression
-- of the record component. All generated code is added to list Stmts.
function Is_Int_Range_Bounds (Bounds : Node_Id) return Boolean;
-- Check whether Bounds is a range node and its lower and higher bounds
-- are integers literals.
function Replace_Type (Expr : Node_Id) return Traverse_Result;
-- If the aggregate contains a self-reference, traverse each expression
-- to replace a possible self-reference with a reference to the proper
-- component of the target of the assignment.
function Rewrite_Discriminant (Expr : Node_Id) return Traverse_Result;
-- If default expression of a component mentions a discriminant of the
-- type, it must be rewritten as the discriminant of the target object.
---------------------------------
-- Ancestor_Discriminant_Value --
---------------------------------
function Ancestor_Discriminant_Value (Disc : Entity_Id) return Node_Id is
Assoc : Node_Id;
Assoc_Elmt : Elmt_Id;
Aggr_Comp : Entity_Id;
Corresp_Disc : Entity_Id;
Current_Typ : Entity_Id := Base_Type (Typ);
Parent_Typ : Entity_Id;
Parent_Disc : Entity_Id;
Save_Assoc : Node_Id := Empty;
begin
-- First check any discriminant associations to see if any of them
-- provide a value for the discriminant.
if Present (Discriminant_Specifications (Parent (Current_Typ))) then
Assoc := First (Component_Associations (N));
while Present (Assoc) loop
Aggr_Comp := Entity (First (Choices (Assoc)));
if Ekind (Aggr_Comp) = E_Discriminant then
Save_Assoc := Expression (Assoc);
Corresp_Disc := Corresponding_Discriminant (Aggr_Comp);
while Present (Corresp_Disc) loop
-- If found a corresponding discriminant then return the
-- value given in the aggregate. (Note: this is not
-- correct in the presence of side effects. ???)
if Disc = Corresp_Disc then
return Duplicate_Subexpr (Expression (Assoc));
end if;
Corresp_Disc := Corresponding_Discriminant (Corresp_Disc);
end loop;
end if;
Next (Assoc);
end loop;
end if;
-- No match found in aggregate, so chain up parent types to find
-- a constraint that defines the value of the discriminant.
Parent_Typ := Etype (Current_Typ);
while Current_Typ /= Parent_Typ loop
if Has_Discriminants (Parent_Typ)
and then not Has_Unknown_Discriminants (Parent_Typ)
then
Parent_Disc := First_Discriminant (Parent_Typ);
-- We either get the association from the subtype indication
-- of the type definition itself, or from the discriminant
-- constraint associated with the type entity (which is
-- preferable, but it's not always present ???)
if Is_Empty_Elmt_List (Discriminant_Constraint (Current_Typ))
then
Assoc := Get_Constraint_Association (Current_Typ);
Assoc_Elmt := No_Elmt;
else
Assoc_Elmt :=
First_Elmt (Discriminant_Constraint (Current_Typ));
Assoc := Node (Assoc_Elmt);
end if;
-- Traverse the discriminants of the parent type looking
-- for one that corresponds.
while Present (Parent_Disc) and then Present (Assoc) loop
Corresp_Disc := Parent_Disc;
while Present (Corresp_Disc)
and then Disc /= Corresp_Disc
loop
Corresp_Disc := Corresponding_Discriminant (Corresp_Disc);
end loop;
if Disc = Corresp_Disc then
if Nkind (Assoc) = N_Discriminant_Association then
Assoc := Expression (Assoc);
end if;
-- If the located association directly denotes
-- a discriminant, then use the value of a saved
-- association of the aggregate. This is an approach
-- used to handle certain cases involving multiple
-- discriminants mapped to a single discriminant of
-- a descendant. It's not clear how to locate the
-- appropriate discriminant value for such cases. ???
if Is_Entity_Name (Assoc)
and then Ekind (Entity (Assoc)) = E_Discriminant
then
Assoc := Save_Assoc;
end if;
return Duplicate_Subexpr (Assoc);
end if;
Next_Discriminant (Parent_Disc);
if No (Assoc_Elmt) then
Next (Assoc);
else
Next_Elmt (Assoc_Elmt);
if Present (Assoc_Elmt) then
Assoc := Node (Assoc_Elmt);
else
Assoc := Empty;
end if;
end if;
end loop;
end if;
Current_Typ := Parent_Typ;
Parent_Typ := Etype (Current_Typ);
end loop;
-- In some cases there's no ancestor value to locate (such as
-- when an ancestor part given by an expression defines the
-- discriminant value).
return Empty;
end Ancestor_Discriminant_Value;
----------------------------------
-- Check_Ancestor_Discriminants --
----------------------------------
procedure Check_Ancestor_Discriminants (Anc_Typ : Entity_Id) is
Discr : Entity_Id;
Disc_Value : Node_Id;
Cond : Node_Id;
begin
Discr := First_Discriminant (Base_Type (Anc_Typ));
while Present (Discr) loop
Disc_Value := Ancestor_Discriminant_Value (Discr);
if Present (Disc_Value) then
Cond := Make_Op_Ne (Loc,
Left_Opnd =>
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Target),
Selector_Name => New_Occurrence_Of (Discr, Loc)),
Right_Opnd => Disc_Value);
Append_To (L,
Make_Raise_Constraint_Error (Loc,
Condition => Cond,
Reason => CE_Discriminant_Check_Failed));
end if;
Next_Discriminant (Discr);
end loop;
end Check_Ancestor_Discriminants;
---------------------------
-- Compatible_Int_Bounds --
---------------------------
function Compatible_Int_Bounds
(Agg_Bounds : Node_Id;
Typ_Bounds : Node_Id) return Boolean
is
Agg_Lo : constant Uint := Intval (Low_Bound (Agg_Bounds));
Agg_Hi : constant Uint := Intval (High_Bound (Agg_Bounds));
Typ_Lo : constant Uint := Intval (Low_Bound (Typ_Bounds));
Typ_Hi : constant Uint := Intval (High_Bound (Typ_Bounds));
begin
return Typ_Lo <= Agg_Lo and then Agg_Hi <= Typ_Hi;
end Compatible_Int_Bounds;
-----------------------------------
-- Generate_Finalization_Actions --
-----------------------------------
procedure Generate_Finalization_Actions is
begin
-- Do the work only the first time this is called
if Finalization_Done then
return;
end if;
Finalization_Done := True;
-- Determine the external finalization list. It is either the
-- finalization list of the outer scope or the one coming from an
-- outer aggregate. When the target is not a temporary, the proper
-- scope is the scope of the target rather than the potentially
-- transient current scope.
if Is_Controlled (Typ) and then Ancestor_Is_Subtype_Mark then
Ref := Convert_To (Init_Typ, New_Copy_Tree (Target));
Set_Assignment_OK (Ref);
Append_To (L,
Make_Procedure_Call_Statement (Loc,
Name =>
New_Occurrence_Of
(Find_Prim_Op (Init_Typ, Name_Initialize), Loc),
Parameter_Associations => New_List (New_Copy_Tree (Ref))));
end if;
end Generate_Finalization_Actions;
--------------------------------
-- Get_Constraint_Association --
--------------------------------
function Get_Constraint_Association (T : Entity_Id) return Node_Id is
Indic : Node_Id;
Typ : Entity_Id;
begin
Typ := T;
-- If type is private, get constraint from full view. This was
-- previously done in an instance context, but is needed whenever
-- the ancestor part has a discriminant, possibly inherited through
-- multiple derivations.
if Is_Private_Type (Typ) and then Present (Full_View (Typ)) then
Typ := Full_View (Typ);
end if;
Indic := Subtype_Indication (Type_Definition (Parent (Typ)));
-- Verify that the subtype indication carries a constraint
if Nkind (Indic) = N_Subtype_Indication
and then Present (Constraint (Indic))
then
return First (Constraints (Constraint (Indic)));
end if;
return Empty;
end Get_Constraint_Association;
-------------------------------------
-- Get_Explicit_Discriminant_Value --
-------------------------------------
function Get_Explicit_Discriminant_Value
(D : Entity_Id) return Node_Id
is
Assoc : Node_Id;
Choice : Node_Id;
Val : Node_Id;
begin
-- The aggregate has been normalized and all associations have a
-- single choice.
Assoc := First (Component_Associations (N));
while Present (Assoc) loop
Choice := First (Choices (Assoc));
if Chars (Choice) = Chars (D) then
Val := Expression (Assoc);
Remove (Assoc);
return Val;
end if;
Next (Assoc);
end loop;
return Empty;
end Get_Explicit_Discriminant_Value;
-------------------------------
-- Init_Hidden_Discriminants --
-------------------------------
procedure Init_Hidden_Discriminants (Typ : Entity_Id; List : List_Id) is
function Is_Completely_Hidden_Discriminant
(Discr : Entity_Id) return Boolean;
-- Determine whether Discr is a completely hidden discriminant of
-- type Typ.
---------------------------------------
-- Is_Completely_Hidden_Discriminant --
---------------------------------------
function Is_Completely_Hidden_Discriminant
(Discr : Entity_Id) return Boolean
is
Item : Entity_Id;
begin
-- Use First/Next_Entity as First/Next_Discriminant do not yield
-- completely hidden discriminants.
Item := First_Entity (Typ);
while Present (Item) loop
if Ekind (Item) = E_Discriminant
and then Is_Completely_Hidden (Item)
and then Chars (Original_Record_Component (Item)) =
Chars (Discr)
then
return True;
end if;
Next_Entity (Item);
end loop;
return False;
end Is_Completely_Hidden_Discriminant;
-- Local variables
Base_Typ : Entity_Id;
Discr : Entity_Id;
Discr_Constr : Elmt_Id;
Discr_Init : Node_Id;
Discr_Val : Node_Id;
In_Aggr_Type : Boolean;
Par_Typ : Entity_Id;
-- Start of processing for Init_Hidden_Discriminants
begin
-- The constraints on the hidden discriminants, if present, are kept
-- in the Stored_Constraint list of the type itself, or in that of
-- the base type. If not in the constraints of the aggregate itself,
-- we examine ancestors to find discriminants that are not renamed
-- by other discriminants but constrained explicitly.
In_Aggr_Type := True;
Base_Typ := Base_Type (Typ);
while Is_Derived_Type (Base_Typ)
and then
(Present (Stored_Constraint (Base_Typ))
or else
(In_Aggr_Type and then Present (Stored_Constraint (Typ))))
loop
Par_Typ := Etype (Base_Typ);
if not Has_Discriminants (Par_Typ) then
return;
end if;
Discr := First_Discriminant (Par_Typ);
-- We know that one of the stored-constraint lists is present
if Present (Stored_Constraint (Base_Typ)) then
Discr_Constr := First_Elmt (Stored_Constraint (Base_Typ));
-- For private extension, stored constraint may be on full view
elsif Is_Private_Type (Base_Typ)
and then Present (Full_View (Base_Typ))
and then Present (Stored_Constraint (Full_View (Base_Typ)))
then
Discr_Constr :=
First_Elmt (Stored_Constraint (Full_View (Base_Typ)));
-- Otherwise, no discriminant to process
else
Discr_Constr := No_Elmt;
end if;
while Present (Discr) and then Present (Discr_Constr) loop
Discr_Val := Node (Discr_Constr);
-- The parent discriminant is renamed in the derived type,
-- nothing to initialize.
-- type Deriv_Typ (Discr : ...)
-- is new Parent_Typ (Discr => Discr);
if Is_Entity_Name (Discr_Val)
and then Ekind (Entity (Discr_Val)) = E_Discriminant
then
null;
-- When the parent discriminant is constrained at the type
-- extension level, it does not appear in the derived type.
-- type Deriv_Typ (Discr : ...)
-- is new Parent_Typ (Discr => Discr,
-- Hidden_Discr => Expression);
elsif Is_Completely_Hidden_Discriminant (Discr) then
null;
-- Otherwise initialize the discriminant
else
Discr_Init :=
Make_OK_Assignment_Statement (Loc,
Name =>
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Target),
Selector_Name => New_Occurrence_Of (Discr, Loc)),
Expression => New_Copy_Tree (Discr_Val));
Append_To (List, Discr_Init);
end if;
Next_Elmt (Discr_Constr);
Next_Discriminant (Discr);
end loop;
In_Aggr_Type := False;
Base_Typ := Base_Type (Par_Typ);
end loop;
end Init_Hidden_Discriminants;
--------------------------------
-- Init_Visible_Discriminants --
--------------------------------
procedure Init_Visible_Discriminants is
Discriminant : Entity_Id;
Discriminant_Value : Node_Id;
begin
Discriminant := First_Discriminant (Typ);
while Present (Discriminant) loop
Comp_Expr :=
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Target),
Selector_Name => New_Occurrence_Of (Discriminant, Loc));
Discriminant_Value :=
Get_Discriminant_Value
(Discriminant, Typ, Discriminant_Constraint (N_Typ));
Instr :=
Make_OK_Assignment_Statement (Loc,
Name => Comp_Expr,
Expression => New_Copy_Tree (Discriminant_Value));
Append_To (L, Instr);
Next_Discriminant (Discriminant);
end loop;
end Init_Visible_Discriminants;
-------------------------------
-- Init_Stored_Discriminants --
-------------------------------
procedure Init_Stored_Discriminants is
Discriminant : Entity_Id;
Discriminant_Value : Node_Id;
begin
Discriminant := First_Stored_Discriminant (Typ);
while Present (Discriminant) loop
Comp_Expr :=
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Target),
Selector_Name => New_Occurrence_Of (Discriminant, Loc));
Discriminant_Value :=
Get_Discriminant_Value
(Discriminant, N_Typ, Discriminant_Constraint (N_Typ));
Instr :=
Make_OK_Assignment_Statement (Loc,
Name => Comp_Expr,
Expression => New_Copy_Tree (Discriminant_Value));
Append_To (L, Instr);
Next_Stored_Discriminant (Discriminant);
end loop;
end Init_Stored_Discriminants;
--------------------------------------
-- Initialize_Ctrl_Record_Component --
--------------------------------------
procedure Initialize_Ctrl_Record_Component
(Rec_Comp : Node_Id;
Comp_Typ : Entity_Id;
Init_Expr : Node_Id;
Stmts : List_Id)
is
Fin_Call : Node_Id;
Hook_Clear : Node_Id;
In_Place_Expansion : Boolean;
-- Flag set when a nonlimited controlled function call requires
-- in-place expansion.
begin
-- Perform a preliminary analysis and resolution to determine what
-- the initialization expression denotes. Unanalyzed function calls
-- may appear as identifiers or indexed components.
if Nkind (Init_Expr) in N_Function_Call
| N_Identifier
| N_Indexed_Component
and then not Analyzed (Init_Expr)
then
Preanalyze_And_Resolve (Init_Expr, Comp_Typ);
end if;
In_Place_Expansion :=
Nkind (Init_Expr) = N_Function_Call
and then not Is_Build_In_Place_Result_Type (Comp_Typ);
-- The initialization expression is a controlled function call.
-- Perform in-place removal of side effects to avoid creating a
-- transient scope.
-- This in-place expansion is not performed for limited transient
-- objects because the initialization is already done in place.
if In_Place_Expansion then
-- Suppress the removal of side effects by general analysis
-- because this behavior is emulated here. This avoids the
-- generation of a transient scope, which leads to out-of-order
-- adjustment and finalization.
Set_No_Side_Effect_Removal (Init_Expr);
-- Install all hook-related declarations and prepare the clean up
-- statements. The generated code follows the initialization order
-- of individual components and discriminants, rather than being
-- inserted prior to the aggregate. This ensures that a transient
-- component which mentions a discriminant has proper visibility
-- of the discriminant.
Process_Transient_Component
(Loc => Loc,
Comp_Typ => Comp_Typ,
Init_Expr => Init_Expr,
Fin_Call => Fin_Call,
Hook_Clear => Hook_Clear,
Stmts => Stmts);
end if;
-- Use the noncontrolled component initialization circuitry to
-- assign the result of the function call to the record component.
-- This also performs tag adjustment and [deep] adjustment of the
-- record component.
Initialize_Record_Component
(Rec_Comp => Rec_Comp,
Comp_Typ => Comp_Typ,
Init_Expr => Init_Expr,
Stmts => Stmts);
-- At this point the record component is fully initialized. Complete
-- the processing of the controlled record component by finalizing
-- the transient function result.
if In_Place_Expansion then
Process_Transient_Component_Completion
(Loc => Loc,
Aggr => N,
Fin_Call => Fin_Call,
Hook_Clear => Hook_Clear,
Stmts => Stmts);
end if;
end Initialize_Ctrl_Record_Component;
---------------------------------
-- Initialize_Record_Component --
---------------------------------
procedure Initialize_Record_Component
(Rec_Comp : Node_Id;
Comp_Typ : Entity_Id;
Init_Expr : Node_Id;
Stmts : List_Id)
is
Exceptions_OK : constant Boolean :=
not Restriction_Active (No_Exception_Propagation);
Finalization_OK : constant Boolean := Needs_Finalization (Comp_Typ);
Full_Typ : constant Entity_Id := Underlying_Type (Comp_Typ);
Adj_Call : Node_Id;
Blk_Stmts : List_Id;
Init_Stmt : Node_Id;
begin
-- Protect the initialization statements from aborts. Generate:
-- Abort_Defer;
if Finalization_OK and Abort_Allowed then
if Exceptions_OK then
Blk_Stmts := New_List;
else
Blk_Stmts := Stmts;
end if;
Append_To (Blk_Stmts, Build_Runtime_Call (Loc, RE_Abort_Defer));
-- Otherwise aborts are not allowed. All generated code is added
-- directly to the input list.
else
Blk_Stmts := Stmts;
end if;
-- Initialize the record component. Generate:
-- Rec_Comp := Init_Expr;
-- Note that the initialization expression is NOT replicated because
-- only a single component may be initialized by it.
Init_Stmt :=
Make_OK_Assignment_Statement (Loc,
Name => New_Copy_Tree (Rec_Comp),
Expression => Init_Expr);
Set_No_Ctrl_Actions (Init_Stmt);
Append_To (Blk_Stmts, Init_Stmt);
-- Adjust the tag due to a possible view conversion. Generate:
-- Rec_Comp._tag := Full_TypeP;
if Tagged_Type_Expansion and then Is_Tagged_Type (Comp_Typ) then
Append_To (Blk_Stmts,
Make_OK_Assignment_Statement (Loc,
Name =>
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Rec_Comp),
Selector_Name =>
New_Occurrence_Of
(First_Tag_Component (Full_Typ), Loc)),
Expression =>
Unchecked_Convert_To (RTE (RE_Tag),
New_Occurrence_Of
(Node (First_Elmt (Access_Disp_Table (Full_Typ))),
Loc))));
end if;
-- Adjust the component. Generate:
-- [Deep_]Adjust (Rec_Comp);
if Finalization_OK
and then not Is_Limited_Type (Comp_Typ)
and then not Is_Build_In_Place_Function_Call (Init_Expr)
then
Adj_Call :=
Make_Adjust_Call
(Obj_Ref => New_Copy_Tree (Rec_Comp),
Typ => Comp_Typ);
-- Guard against a missing [Deep_]Adjust when the component type
-- was not properly frozen.
if Present (Adj_Call) then
Append_To (Blk_Stmts, Adj_Call);
end if;
end if;
-- Complete the protection of the initialization statements
if Finalization_OK and Abort_Allowed then
-- Wrap the initialization statements in a block to catch a
-- potential exception. Generate:
-- begin
-- Abort_Defer;
-- Rec_Comp := Init_Expr;
-- Rec_Comp._tag := Full_TypP;
-- [Deep_]Adjust (Rec_Comp);
-- at end
-- Abort_Undefer_Direct;
-- end;
if Exceptions_OK then
Append_To (Stmts,
Build_Abort_Undefer_Block (Loc,
Stmts => Blk_Stmts,
Context => N));
-- Otherwise exceptions are not propagated. Generate:
-- Abort_Defer;
-- Rec_Comp := Init_Expr;
-- Rec_Comp._tag := Full_TypP;
-- [Deep_]Adjust (Rec_Comp);
-- Abort_Undefer;
else
Append_To (Blk_Stmts,
Build_Runtime_Call (Loc, RE_Abort_Undefer));
end if;
end if;
end Initialize_Record_Component;
-------------------------
-- Is_Int_Range_Bounds --
-------------------------
function Is_Int_Range_Bounds (Bounds : Node_Id) return Boolean is
begin
return Nkind (Bounds) = N_Range
and then Nkind (Low_Bound (Bounds)) = N_Integer_Literal
and then Nkind (High_Bound (Bounds)) = N_Integer_Literal;
end Is_Int_Range_Bounds;
------------------
-- Replace_Type --
------------------
function Replace_Type (Expr : Node_Id) return Traverse_Result is
begin
-- Note regarding the Root_Type test below: Aggregate components for
-- self-referential types include attribute references to the current
-- instance, of the form: Typ'access, etc.. These references are
-- rewritten as references to the target of the aggregate: the
-- left-hand side of an assignment, the entity in a declaration,
-- or a temporary. Without this test, we would improperly extended
-- this rewriting to attribute references whose prefix was not the
-- type of the aggregate.
if Nkind (Expr) = N_Attribute_Reference
and then Is_Entity_Name (Prefix (Expr))
and then Is_Type (Entity (Prefix (Expr)))
and then Root_Type (Etype (N)) = Root_Type (Entity (Prefix (Expr)))
then
if Is_Entity_Name (Lhs) then
Rewrite (Prefix (Expr), New_Occurrence_Of (Entity (Lhs), Loc));
else
Rewrite (Expr,
Make_Attribute_Reference (Loc,
Attribute_Name => Name_Unrestricted_Access,
Prefix => New_Copy_Tree (Lhs)));
Set_Analyzed (Parent (Expr), False);
end if;
end if;
return OK;
end Replace_Type;
--------------------------
-- Rewrite_Discriminant --
--------------------------
function Rewrite_Discriminant (Expr : Node_Id) return Traverse_Result is
begin
if Is_Entity_Name (Expr)
and then Present (Entity (Expr))
and then Ekind (Entity (Expr)) = E_In_Parameter
and then Present (Discriminal_Link (Entity (Expr)))
and then Scope (Discriminal_Link (Entity (Expr))) =
Base_Type (Etype (N))
then
Rewrite (Expr,
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Lhs),
Selector_Name => Make_Identifier (Loc, Chars (Expr))));
-- The generated code will be reanalyzed, but if the reference
-- to the discriminant appears within an already analyzed
-- expression (e.g. a conditional) we must set its proper entity
-- now. Context is an initialization procedure.
Analyze (Expr);
end if;
return OK;
end Rewrite_Discriminant;
procedure Replace_Discriminants is
new Traverse_Proc (Rewrite_Discriminant);
procedure Replace_Self_Reference is
new Traverse_Proc (Replace_Type);
-- Start of processing for Build_Record_Aggr_Code
begin
if Has_Self_Reference (N) then
Replace_Self_Reference (N);
end if;
-- If the target of the aggregate is class-wide, we must convert it
-- to the actual type of the aggregate, so that the proper components
-- are visible. We know already that the types are compatible.
if Present (Etype (Lhs))
and then Is_Class_Wide_Type (Etype (Lhs))
then
Target := Unchecked_Convert_To (Typ, Lhs);
else
Target := Lhs;
end if;
-- Deal with the ancestor part of extension aggregates or with the
-- discriminants of the root type.
if Nkind (N) = N_Extension_Aggregate then
declare
Ancestor : constant Node_Id := Ancestor_Part (N);
Adj_Call : Node_Id;
Assign : List_Id;
begin
-- If the ancestor part is a subtype mark "T", we generate
-- init-proc (T (tmp)); if T is constrained and
-- init-proc (S (tmp)); where S applies an appropriate
-- constraint if T is unconstrained
if Is_Entity_Name (Ancestor)
and then Is_Type (Entity (Ancestor))
then
Ancestor_Is_Subtype_Mark := True;
if Is_Constrained (Entity (Ancestor)) then
Init_Typ := Entity (Ancestor);
-- For an ancestor part given by an unconstrained type mark,
-- create a subtype constrained by appropriate corresponding
-- discriminant values coming from either associations of the
-- aggregate or a constraint on a parent type. The subtype will
-- be used to generate the correct default value for the
-- ancestor part.
elsif Has_Discriminants (Entity (Ancestor)) then
declare
Anc_Typ : constant Entity_Id := Entity (Ancestor);
Anc_Constr : constant List_Id := New_List;
Discrim : Entity_Id;
Disc_Value : Node_Id;
New_Indic : Node_Id;
Subt_Decl : Node_Id;
begin
Discrim := First_Discriminant (Anc_Typ);
while Present (Discrim) loop
Disc_Value := Ancestor_Discriminant_Value (Discrim);
-- If no usable discriminant in ancestors, check
-- whether aggregate has an explicit value for it.
if No (Disc_Value) then
Disc_Value :=
Get_Explicit_Discriminant_Value (Discrim);
end if;
Append_To (Anc_Constr, Disc_Value);
Next_Discriminant (Discrim);
end loop;
New_Indic :=
Make_Subtype_Indication (Loc,
Subtype_Mark => New_Occurrence_Of (Anc_Typ, Loc),
Constraint =>
Make_Index_Or_Discriminant_Constraint (Loc,
Constraints => Anc_Constr));
Init_Typ := Create_Itype (Ekind (Anc_Typ), N);
Subt_Decl :=
Make_Subtype_Declaration (Loc,
Defining_Identifier => Init_Typ,
Subtype_Indication => New_Indic);
-- Itypes must be analyzed with checks off Declaration
-- must have a parent for proper handling of subsidiary
-- actions.
Set_Parent (Subt_Decl, N);
Analyze (Subt_Decl, Suppress => All_Checks);
end;
end if;
Ref := Convert_To (Init_Typ, New_Copy_Tree (Target));
Set_Assignment_OK (Ref);
if not Is_Interface (Init_Typ) then
Append_List_To (L,
Build_Initialization_Call (Loc,
Id_Ref => Ref,
Typ => Init_Typ,
In_Init_Proc => Within_Init_Proc,
With_Default_Init => Has_Default_Init_Comps (N)
or else
Has_Task (Base_Type (Init_Typ))));
if Is_Constrained (Entity (Ancestor))
and then Has_Discriminants (Entity (Ancestor))
then
Check_Ancestor_Discriminants (Entity (Ancestor));
end if;
-- If ancestor type has Default_Initialization_Condition,
-- add a DIC check after the ancestor object is initialized
-- by default.
if Has_DIC (Entity (Ancestor))
and then Present (DIC_Procedure (Entity (Ancestor)))
then
Append_To (L,
Build_DIC_Call
(Loc, New_Copy_Tree (Ref), Entity (Ancestor)));
end if;
end if;
-- Handle calls to C++ constructors
elsif Is_CPP_Constructor_Call (Ancestor) then
Init_Typ := Etype (Ancestor);
Ref := Convert_To (Init_Typ, New_Copy_Tree (Target));
Set_Assignment_OK (Ref);
Append_List_To (L,
Build_Initialization_Call (Loc,
Id_Ref => Ref,
Typ => Init_Typ,
In_Init_Proc => Within_Init_Proc,
With_Default_Init => Has_Default_Init_Comps (N),
Constructor_Ref => Ancestor));
-- Ada 2005 (AI-287): If the ancestor part is an aggregate of
-- limited type, a recursive call expands the ancestor. Note that
-- in the limited case, the ancestor part must be either a
-- function call (possibly qualified) or aggregate (definitely
-- qualified).
elsif Is_Limited_Type (Etype (Ancestor))
and then Nkind (Unqualify (Ancestor)) in
N_Aggregate | N_Extension_Aggregate
then
Ancestor_Is_Expression := True;
-- Set up finalization data for enclosing record, because
-- controlled subcomponents of the ancestor part will be
-- attached to it.
Generate_Finalization_Actions;
Append_List_To (L,
Build_Record_Aggr_Code
(N => Unqualify (Ancestor),
Typ => Etype (Unqualify (Ancestor)),
Lhs => Target));
-- If the ancestor part is an expression "E", we generate
-- T (tmp) := E;
-- In Ada 2005, this includes the case of a (possibly qualified)
-- limited function call. The assignment will turn into a
-- build-in-place function call (for further details, see
-- Make_Build_In_Place_Call_In_Assignment).
else
Ancestor_Is_Expression := True;
Init_Typ := Etype (Ancestor);
-- If the ancestor part is an aggregate, force its full
-- expansion, which was delayed.
if Nkind (Unqualify (Ancestor)) in
N_Aggregate | N_Extension_Aggregate
then
Set_Analyzed (Ancestor, False);
Set_Analyzed (Expression (Ancestor), False);
end if;
Ref := Convert_To (Init_Typ, New_Copy_Tree (Target));
Set_Assignment_OK (Ref);
-- Make the assignment without usual controlled actions, since
-- we only want to Adjust afterwards, but not to Finalize
-- beforehand. Add manual Adjust when necessary.
Assign := New_List (
Make_OK_Assignment_Statement (Loc,
Name => Ref,
Expression => Ancestor));
Set_No_Ctrl_Actions (First (Assign));
-- Assign the tag now to make sure that the dispatching call in
-- the subsequent deep_adjust works properly (unless
-- Tagged_Type_Expansion where tags are implicit).
if Tagged_Type_Expansion then
Instr :=
Make_OK_Assignment_Statement (Loc,
Name =>
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Target),
Selector_Name =>
New_Occurrence_Of
(First_Tag_Component (Base_Type (Typ)), Loc)),
Expression =>
Unchecked_Convert_To (RTE (RE_Tag),
New_Occurrence_Of
(Node (First_Elmt
(Access_Disp_Table (Base_Type (Typ)))),
Loc)));
Set_Assignment_OK (Name (Instr));
Append_To (Assign, Instr);
-- Ada 2005 (AI-251): If tagged type has progenitors we must
-- also initialize tags of the secondary dispatch tables.
if Has_Interfaces (Base_Type (Typ)) then
Init_Secondary_Tags
(Typ => Base_Type (Typ),
Target => Target,
Stmts_List => Assign,
Init_Tags_List => Assign);
end if;
end if;
-- Call Adjust manually
if Needs_Finalization (Etype (Ancestor))
and then not Is_Limited_Type (Etype (Ancestor))
and then not Is_Build_In_Place_Function_Call (Ancestor)
then
Adj_Call :=
Make_Adjust_Call
(Obj_Ref => New_Copy_Tree (Ref),
Typ => Etype (Ancestor));
-- Guard against a missing [Deep_]Adjust when the ancestor
-- type was not properly frozen.
if Present (Adj_Call) then
Append_To (Assign, Adj_Call);
end if;
end if;
Append_To (L,
Make_Unsuppress_Block (Loc, Name_Discriminant_Check, Assign));
if Has_Discriminants (Init_Typ) then
Check_Ancestor_Discriminants (Init_Typ);
end if;
end if;
pragma Assert (Nkind (N) = N_Extension_Aggregate);
pragma Assert
(not (Ancestor_Is_Expression and Ancestor_Is_Subtype_Mark));
end;
-- Generate assignments of hidden discriminants. If the base type is
-- an unchecked union, the discriminants are unknown to the back-end
-- and absent from a value of the type, so assignments for them are
-- not emitted.
if Has_Discriminants (Typ)
and then not Is_Unchecked_Union (Base_Type (Typ))
then
Init_Hidden_Discriminants (Typ, L);
end if;
-- Normal case (not an extension aggregate)
else
-- Generate the discriminant expressions, component by component.
-- If the base type is an unchecked union, the discriminants are
-- unknown to the back-end and absent from a value of the type, so
-- assignments for them are not emitted.
if Has_Discriminants (Typ)
and then not Is_Unchecked_Union (Base_Type (Typ))
then
Init_Hidden_Discriminants (Typ, L);
-- Generate discriminant init values for the visible discriminants
Init_Visible_Discriminants;
if Is_Derived_Type (N_Typ) then
Init_Stored_Discriminants;
end if;
end if;
end if;
-- For CPP types we generate an implicit call to the C++ default
-- constructor to ensure the proper initialization of the _Tag
-- component.
if Is_CPP_Class (Root_Type (Typ)) and then CPP_Num_Prims (Typ) > 0 then
Invoke_Constructor : declare
CPP_Parent : constant Entity_Id := Enclosing_CPP_Parent (Typ);
procedure Invoke_IC_Proc (T : Entity_Id);
-- Recursive routine used to climb to parents. Required because
-- parents must be initialized before descendants to ensure
-- propagation of inherited C++ slots.
--------------------
-- Invoke_IC_Proc --
--------------------
procedure Invoke_IC_Proc (T : Entity_Id) is
begin
-- Avoid generating extra calls. Initialization required
-- only for types defined from the level of derivation of
-- type of the constructor and the type of the aggregate.
if T = CPP_Parent then
return;
end if;
Invoke_IC_Proc (Etype (T));
-- Generate call to the IC routine
if Present (CPP_Init_Proc (T)) then
Append_To (L,
Make_Procedure_Call_Statement (Loc,
Name => New_Occurrence_Of (CPP_Init_Proc (T), Loc)));
end if;
end Invoke_IC_Proc;
-- Start of processing for Invoke_Constructor
begin
-- Implicit invocation of the C++ constructor
if Nkind (N) = N_Aggregate then
Append_To (L,
Make_Procedure_Call_Statement (Loc,
Name =>
New_Occurrence_Of (Base_Init_Proc (CPP_Parent), Loc),
Parameter_Associations => New_List (
Unchecked_Convert_To (CPP_Parent,
New_Copy_Tree (Lhs)))));
end if;
Invoke_IC_Proc (Typ);
end Invoke_Constructor;
end if;
-- Generate the assignments, component by component
-- tmp.comp1 := Expr1_From_Aggr;
-- tmp.comp2 := Expr2_From_Aggr;
-- ....
Comp := First (Component_Associations (N));
while Present (Comp) loop
Selector := Entity (First (Choices (Comp)));
-- C++ constructors
if Is_CPP_Constructor_Call (Expression (Comp)) then
Append_List_To (L,
Build_Initialization_Call (Loc,
Id_Ref =>
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Target),
Selector_Name => New_Occurrence_Of (Selector, Loc)),
Typ => Etype (Selector),
Enclos_Type => Typ,
With_Default_Init => True,
Constructor_Ref => Expression (Comp)));
-- Ada 2005 (AI-287): For each default-initialized component generate
-- a call to the corresponding IP subprogram if available.
elsif Box_Present (Comp)
and then Has_Non_Null_Base_Init_Proc (Etype (Selector))
then
if Ekind (Selector) /= E_Discriminant then
Generate_Finalization_Actions;
end if;
-- Ada 2005 (AI-287): If the component type has tasks then
-- generate the activation chain and master entities (except
-- in case of an allocator because in that case these entities
-- are generated by Build_Task_Allocate_Block_With_Init_Stmts).
declare
Ctype : constant Entity_Id := Etype (Selector);
Inside_Allocator : Boolean := False;
P : Node_Id := Parent (N);
begin
if Is_Task_Type (Ctype) or else Has_Task (Ctype) then
while Present (P) loop
if Nkind (P) = N_Allocator then
Inside_Allocator := True;
exit;
end if;
P := Parent (P);
end loop;
if not Inside_Init_Proc and not Inside_Allocator then
Build_Activation_Chain_Entity (N);
end if;
end if;
end;
Append_List_To (L,
Build_Initialization_Call (Loc,
Id_Ref => Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Target),
Selector_Name =>
New_Occurrence_Of (Selector, Loc)),
Typ => Etype (Selector),
Enclos_Type => Typ,
With_Default_Init => True));
-- Prepare for component assignment
elsif Ekind (Selector) /= E_Discriminant
or else Nkind (N) = N_Extension_Aggregate
then
-- All the discriminants have now been assigned
-- This is now a good moment to initialize and attach all the
-- controllers. Their position may depend on the discriminants.
if Ekind (Selector) /= E_Discriminant then
Generate_Finalization_Actions;
end if;
Comp_Type := Underlying_Type (Etype (Selector));
Comp_Expr :=
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Target),
Selector_Name => New_Occurrence_Of (Selector, Loc));
if Nkind (Expression (Comp)) = N_Qualified_Expression then
Expr_Q := Expression (Expression (Comp));
else
Expr_Q := Expression (Comp);
end if;
-- Now either create the assignment or generate the code for the
-- inner aggregate top-down.
if Is_Delayed_Aggregate (Expr_Q) then
-- We have the following case of aggregate nesting inside
-- an object declaration:
-- type Arr_Typ is array (Integer range <>) of ...;
-- type Rec_Typ (...) is record
-- Obj_Arr_Typ : Arr_Typ (A .. B);
-- end record;
-- Obj_Rec_Typ : Rec_Typ := (...,
-- Obj_Arr_Typ => (X => (...), Y => (...)));
-- The length of the ranges of the aggregate and Obj_Add_Typ
-- are equal (B - A = Y - X), but they do not coincide (X /=
-- A and B /= Y). This case requires array sliding which is
-- performed in the following manner:
-- subtype Arr_Sub is Arr_Typ (X .. Y);
-- Temp : Arr_Sub;
-- Temp (X) := (...);
-- ...
-- Temp (Y) := (...);
-- Obj_Rec_Typ.Obj_Arr_Typ := Temp;
if Ekind (Comp_Type) = E_Array_Subtype
and then Is_Int_Range_Bounds (Aggregate_Bounds (Expr_Q))
and then Is_Int_Range_Bounds (First_Index (Comp_Type))
and then not
Compatible_Int_Bounds
(Agg_Bounds => Aggregate_Bounds (Expr_Q),
Typ_Bounds => First_Index (Comp_Type))
then
-- Create the array subtype with bounds equal to those of
-- the corresponding aggregate.
declare
SubE : constant Entity_Id := Make_Temporary (Loc, 'T');
SubD : constant Node_Id :=
Make_Subtype_Declaration (Loc,
Defining_Identifier => SubE,
Subtype_Indication =>
Make_Subtype_Indication (Loc,
Subtype_Mark =>
New_Occurrence_Of (Etype (Comp_Type), Loc),
Constraint =>
Make_Index_Or_Discriminant_Constraint
(Loc,
Constraints => New_List (
New_Copy_Tree
(Aggregate_Bounds (Expr_Q))))));
-- Create a temporary array of the above subtype which
-- will be used to capture the aggregate assignments.
TmpE : constant Entity_Id := Make_Temporary (Loc, 'A', N);
TmpD : constant Node_Id :=
Make_Object_Declaration (Loc,
Defining_Identifier => TmpE,
Object_Definition => New_Occurrence_Of (SubE, Loc));
begin
Set_No_Initialization (TmpD);
Append_To (L, SubD);
Append_To (L, TmpD);
-- Expand aggregate into assignments to the temp array
Append_List_To (L,
Late_Expansion (Expr_Q, Comp_Type,
New_Occurrence_Of (TmpE, Loc)));
-- Slide
Append_To (L,
Make_Assignment_Statement (Loc,
Name => New_Copy_Tree (Comp_Expr),
Expression => New_Occurrence_Of (TmpE, Loc)));
end;
-- Normal case (sliding not required)
else
Append_List_To (L,
Late_Expansion (Expr_Q, Comp_Type, Comp_Expr));
end if;
-- Expr_Q is not delayed aggregate
else
if Has_Discriminants (Typ) then
Replace_Discriminants (Expr_Q);
-- If the component is an array type that depends on
-- discriminants, and the expression is a single Others
-- clause, create an explicit subtype for it because the
-- backend has troubles recovering the actual bounds.
if Nkind (Expr_Q) = N_Aggregate
and then Is_Array_Type (Comp_Type)
and then Present (Component_Associations (Expr_Q))
then
declare
Assoc : constant Node_Id :=
First (Component_Associations (Expr_Q));
Decl : Node_Id;
begin
if Nkind (First (Choices (Assoc))) = N_Others_Choice
then
Decl :=
Build_Actual_Subtype_Of_Component
(Comp_Type, Comp_Expr);
-- If the component type does not in fact depend on
-- discriminants, the subtype declaration is empty.
if Present (Decl) then
Append_To (L, Decl);
Set_Etype (Comp_Expr, Defining_Entity (Decl));
end if;
end if;
end;
end if;
end if;
if Modify_Tree_For_C
and then Nkind (Expr_Q) = N_Aggregate
and then Is_Array_Type (Etype (Expr_Q))
and then Present (First_Index (Etype (Expr_Q)))
then
declare
Expr_Q_Type : constant Node_Id := Etype (Expr_Q);
begin
Append_List_To (L,
Build_Array_Aggr_Code
(N => Expr_Q,
Ctype => Component_Type (Expr_Q_Type),
Index => First_Index (Expr_Q_Type),
Into => Comp_Expr,
Scalar_Comp =>
Is_Scalar_Type (Component_Type (Expr_Q_Type))));
end;
else
-- Handle an initialization expression of a controlled type
-- in case it denotes a function call. In general such a
-- scenario will produce a transient scope, but this will
-- lead to wrong order of initialization, adjustment, and
-- finalization in the context of aggregates.
-- Target.Comp := Ctrl_Func_Call;
-- begin -- scope
-- Trans_Obj : ... := Ctrl_Func_Call; -- object
-- Target.Comp := Trans_Obj;
-- Finalize (Trans_Obj);
-- end
-- Target.Comp._tag := ...;
-- Adjust (Target.Comp);
-- In the example above, the call to Finalize occurs too
-- early and as a result it may leave the record component
-- in a bad state. Finalization of the transient object
-- should really happen after adjustment.
-- To avoid this scenario, perform in-place side-effect
-- removal of the function call. This eliminates the
-- transient property of the function result and ensures
-- correct order of actions.
-- Res : ... := Ctrl_Func_Call;
-- Target.Comp := Res;
-- Target.Comp._tag := ...;
-- Adjust (Target.Comp);
-- Finalize (Res);
if Needs_Finalization (Comp_Type)
and then Nkind (Expr_Q) /= N_Aggregate
then
Initialize_Ctrl_Record_Component
(Rec_Comp => Comp_Expr,
Comp_Typ => Etype (Selector),
Init_Expr => Expr_Q,
Stmts => L);
-- Otherwise perform single component initialization
else
Initialize_Record_Component
(Rec_Comp => Comp_Expr,
Comp_Typ => Etype (Selector),
Init_Expr => Expr_Q,
Stmts => L);
end if;
end if;
end if;
-- comment would be good here ???
elsif Ekind (Selector) = E_Discriminant
and then Nkind (N) /= N_Extension_Aggregate
and then Nkind (Parent (N)) = N_Component_Association
and then Is_Constrained (Typ)
then
-- We must check that the discriminant value imposed by the
-- context is the same as the value given in the subaggregate,
-- because after the expansion into assignments there is no
-- record on which to perform a regular discriminant check.
declare
D_Val : Elmt_Id;
Disc : Entity_Id;
begin
D_Val := First_Elmt (Discriminant_Constraint (Typ));
Disc := First_Discriminant (Typ);
while Chars (Disc) /= Chars (Selector) loop
Next_Discriminant (Disc);
Next_Elmt (D_Val);
end loop;
pragma Assert (Present (D_Val));
-- This check cannot performed for components that are
-- constrained by a current instance, because this is not a
-- value that can be compared with the actual constraint.
if Nkind (Node (D_Val)) /= N_Attribute_Reference
or else not Is_Entity_Name (Prefix (Node (D_Val)))
or else not Is_Type (Entity (Prefix (Node (D_Val))))
then
Append_To (L,
Make_Raise_Constraint_Error (Loc,
Condition =>
Make_Op_Ne (Loc,
Left_Opnd => New_Copy_Tree (Node (D_Val)),
Right_Opnd => Expression (Comp)),
Reason => CE_Discriminant_Check_Failed));
else
-- Find self-reference in previous discriminant assignment,
-- and replace with proper expression.
declare
Ass : Node_Id;
begin
Ass := First (L);
while Present (Ass) loop
if Nkind (Ass) = N_Assignment_Statement
and then Nkind (Name (Ass)) = N_Selected_Component
and then Chars (Selector_Name (Name (Ass))) =
Chars (Disc)
then
Set_Expression
(Ass, New_Copy_Tree (Expression (Comp)));
exit;
end if;
Next (Ass);
end loop;
end;
end if;
end;
end if;
-- If the component association was specified with a box and the
-- component type has a Default_Initial_Condition, then generate
-- a call to the DIC procedure.
if Has_DIC (Etype (Selector))
and then Was_Default_Init_Box_Association (Comp)
and then Present (DIC_Procedure (Etype (Selector)))
then
Append_To (L,
Build_DIC_Call (Loc,
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Target),
Selector_Name => New_Occurrence_Of (Selector, Loc)),
Etype (Selector)));
end if;
Next (Comp);
end loop;
-- If the type is tagged, the tag needs to be initialized (unless we
-- are in VM-mode where tags are implicit). It is done late in the
-- initialization process because in some cases, we call the init
-- proc of an ancestor which will not leave out the right tag.
if Ancestor_Is_Expression then
null;
-- For CPP types we generated a call to the C++ default constructor
-- before the components have been initialized to ensure the proper
-- initialization of the _Tag component (see above).
elsif Is_CPP_Class (Typ) then
null;
elsif Is_Tagged_Type (Typ) and then Tagged_Type_Expansion then
Instr :=
Make_OK_Assignment_Statement (Loc,
Name =>
Make_Selected_Component (Loc,
Prefix => New_Copy_Tree (Target),
Selector_Name =>
New_Occurrence_Of
(First_Tag_Component (Base_Type (Typ)), Loc)),
Expression =>
Unchecked_Convert_To (RTE (RE_Tag),
New_Occurrence_Of
(Node (First_Elmt (Access_Disp_Table (Base_Type (Typ)))),
Loc)));
Append_To (L, Instr);
-- Ada 2005 (AI-251): If the tagged type has been derived from an
-- abstract interfaces we must also initialize the tags of the
-- secondary dispatch tables.
if Has_Interfaces (Base_Type (Typ)) then
Init_Secondary_Tags
(Typ => Base_Type (Typ),
Target => Target,
Stmts_List => L,
Init_Tags_List => L);
end if;
end if;
-- If the controllers have not been initialized yet (by lack of non-
-- discriminant components), let's do it now.
Generate_Finalization_Actions;
return L;
end Build_Record_Aggr_Code;
---------------------------------------
-- 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 nonstatic, emit a check
if Present (Cond) then
Insert_Action (N,
Make_Raise_Constraint_Error (Loc,
Condition => Cond,
Reason => CE_Discriminant_Check_Failed));
end if;
return True;
end Discriminants_Ok;
-- Start of processing for Convert_Aggr_In_Object_Decl
begin
Set_Assignment_OK (Occ);
if Nkind (Aggr) = N_Qualified_Expression then
Aggr := Expression (Aggr);
end if;
if Has_Discriminants (Typ)
and then Typ /= Etype (Obj)
and then Is_Constrained (Etype (Obj))
and then not Discriminants_Ok
then
return;
end if;
-- If the context is an extended return statement, it has its own
-- finalization machinery (i.e. works like a transient scope) and
-- we do not want to create an additional one, because objects on
-- the finalization list of the return must be moved to the caller's
-- finalization list to complete the return.
-- 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, Manage_Sec_Stack => False);
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
Typ : constant Entity_Id := Etype (Aggr);
Ctyp : constant Entity_Id := Component_Type (Typ);
Aggr_Code : List_Id;
New_Aggr : Node_Id;
begin
-- The target is an explicit dereference of the allocated object
-- If the assignment can be done directly by the back end, then
-- reset Set_Expansion_Delayed and do not expand further.
if not CodePeer_Mode
and then not Modify_Tree_For_C
and then Aggr_Assignment_OK_For_Backend (Aggr)
then
New_Aggr := New_Copy_Tree (Aggr);
Set_Expansion_Delayed (New_Aggr, False);
Aggr_Code :=
New_List (
Make_OK_Assignment_Statement (Sloc (New_Aggr),
Name => Target,
Expression => New_Aggr));
-- Or else, generate component assignments to it, as for an aggregate
-- that appears on the right-hand side of an assignment statement.
else
Aggr_Code :=
Build_Array_Aggr_Code (Aggr,
Ctype => Ctyp,
Index => First_Index (Typ),
Into => Target,
Scalar_Comp => Is_Scalar_Type (Ctyp));
end if;
Insert_Actions_After (Decl, Aggr_Code);
end Convert_Array_Aggr_In_Allocator;
------------------------
-- In_Place_Assign_OK --
------------------------
function In_Place_Assign_OK
(N : Node_Id;
Target_Object : Entity_Id := Empty) return Boolean
is
Is_Array : constant Boolean := Is_Array_Type (Etype (N));
Aggr_In : Node_Id;
Aggr_Lo : Node_Id;
Aggr_Hi : Node_Id;
Obj_In : Node_Id;
Obj_Lo : Node_Id;
Obj_Hi : Node_Id;
Parent_Kind : Node_Kind;
Parent_Node : Node_Id;
function Safe_Aggregate (Aggr : Node_Id) return Boolean;
-- Check recursively that each component of a (sub)aggregate does not
-- depend on the variable being assigned to.
function Safe_Component (Expr : Node_Id) return Boolean;
-- Verify that an expression cannot depend on the target being assigned
-- to. Return true for compile-time known values, stand-alone objects,
-- parameters passed by copy, calls to functions that return by copy,
-- selected components thereof only if the aggregate's type is an array,
-- indexed components and slices thereof only if the aggregate's type is
-- a record, and simple expressions involving only these as operands.
-- This is OK whatever the target because, for a component to overlap
-- with the target, it must be either a direct reference to a component
-- of the target, in which case there must be a matching selection or
-- indexation or slicing, or an indirect reference to such a component,
-- which is excluded by the above condition. Additionally, if the target
-- is statically known, return true for arbitrarily nested selections,
-- indexations or slicings, provided that their ultimate prefix is not
-- the target itself.
--------------------
-- Safe_Aggregate --
--------------------
function Safe_Aggregate (Aggr : Node_Id) return Boolean is
Expr : Node_Id;
begin
if Nkind (Parent (Aggr)) = N_Iterated_Component_Association then
return False;
end if;
if Present (Expressions (Aggr)) then
Expr := First (Expressions (Aggr));
while Present (Expr) loop
if Nkind (Expr) = N_Aggregate then
if not Safe_Aggregate (Expr) then
return False;
end if;
elsif not Safe_Component (Expr) then
return False;
end if;
Next (Expr);
end loop;
end if;
if Present (Component_Associations (Aggr)) then
Expr := First (Component_Associations (Aggr));
while Present (Expr) loop
if Nkind (Expression (Expr)) = N_Aggregate then
if not Safe_Aggregate (Expression (Expr)) then
return False;
end if;
-- If association has a box, no way to determine yet whether
-- default can be assigned in place.
elsif Box_Present (Expr) then
return False;
elsif not Safe_Component (Expression (Expr)) then
return False;
end if;
Next (Expr);
end loop;
end if;
return True;
end Safe_Aggregate;
--------------------
-- Safe_Component --
--------------------
function Safe_Component (Expr : Node_Id) return Boolean is
Comp : Node_Id := Expr;
function Check_Component (C : Node_Id; T_OK : Boolean) return Boolean;
-- Do the recursive traversal, after copy. If T_OK is True, return
-- True for a stand-alone object only if the target is statically
-- known and distinct from the object. At the top level, we start
-- with T_OK set to False and set it to True at a deeper level only
-- if we cannot disambiguate the component here without statically
-- knowing the target. Note that this is not optimal, we should do
-- something along the lines of Denotes_Same_Prefix for that.
---------------------
-- Check_Component --
---------------------
function Check_Component (C : Node_Id; T_OK : Boolean) return Boolean
is
function SDO (E : Entity_Id) return Uint;
-- Return the Scope Depth Of the enclosing dynamic scope of E
---------
-- SDO --
---------
function SDO (E : Entity_Id) return Uint is
begin
return Scope_Depth (Enclosing_Dynamic_Scope (E));
end SDO;
-- Start of processing for Check_Component
begin
if Is_Overloaded (C) then
return False;
elsif Compile_Time_Known_Value (C) then
return True;
end if;
case Nkind (C) is
when N_Attribute_Reference =>
return Check_Component (Prefix (C), T_OK);
when N_Function_Call =>
if Nkind (Name (C)) = N_Explicit_Dereference then
return not Returns_By_Ref (Etype (Name (C)));
else
return not Returns_By_Ref (Entity (Name (C)));
end if;
when N_Indexed_Component | N_Slice =>
-- In a target record, these operations cannot determine
-- alone a component so we can recurse whatever the target.
return Check_Component (Prefix (C), T_OK or else Is_Array);
when N_Selected_Component =>
-- In a target array, this operation cannot determine alone
-- a component so we can recurse whatever the target.
return
Check_Component (Prefix (C), T_OK or else not Is_Array);
when N_Type_Conversion | N_Unchecked_Type_Conversion =>
return Check_Component (Expression (C), T_OK);
when N_Binary_Op =>
return Check_Component (Left_Opnd (C), T_OK)
and then Check_Component (Right_Opnd (C), T_OK);
when N_Unary_Op =>
return Check_Component (Right_Opnd (C), T_OK);
when others =>
if Is_Entity_Name (C) and then Is_Object (Entity (C)) then
-- Case of a formal parameter component. It's either
-- trivial if passed by copy or very annoying if not,
-- because in the latter case it's almost equivalent
-- to a dereference, so the path-based disambiguation
-- logic is totally off and we always need the target.
if Is_Formal (Entity (C)) then
-- If it is passed by copy, then this is safe
if Mechanism (Entity (C)) = By_Copy then
return True;
-- Otherwise, this is safe if the target is present
-- and is at least as deeply nested as the component.
else
return Present (Target_Object)
and then not Is_Formal (Target_Object)
and then SDO (Target_Object) >= SDO (Entity (C));
end if;
-- For a renamed object, recurse
elsif Present (Renamed_Object (Entity (C))) then
return
Check_Component (Renamed_Object (Entity (C)), T_OK);
-- If this is safe whatever the target, we are done
elsif not T_OK then
return True;
-- If there is no target or the component is the target,
-- this is not safe.
elsif No (Target_Object)
or else Entity (C) = Target_Object
then
return False;
-- Case of a formal parameter target. This is safe if it
-- is at most as deeply nested as the component.
elsif Is_Formal (Target_Object) then
return SDO (Target_Object) <= SDO (Entity (C));
-- For distinct stand-alone objects, this is safe
else
return True;
end if;
-- For anything else than an object, this is not safe
else
return False;
end if;
end case;
end Check_Component;
-- Start of processing for Safe_Component
begin
-- If the component appears in an association that may correspond
-- to more than one element, it is not analyzed before expansion
-- into assignments, to avoid side effects. We analyze, but do not
-- resolve the copy, to obtain sufficient entity information for
-- the checks that follow. If component is overloaded we assume
-- an unsafe function call.
if not Analyzed (Comp) then
if Is_Overloaded (Expr) then
return False;
elsif Nkind (Expr) = N_Allocator then
-- For now, too complex to analyze
return False;
elsif Nkind (Parent (Expr)) = N_Iterated_Component_Association then
-- Ditto for iterated component associations, which in general
-- require an enclosing loop and involve nonstatic expressions.
return False;
end if;
Comp := New_Copy_Tree (Expr);
Set_Parent (Comp, Parent (Expr));
Analyze (Comp);
end if;
if Nkind (Comp) = N_Aggregate then
return Safe_Aggregate (Comp);
else
return Check_Component (Comp, False);
end if;
end Safe_Component;
-- Start of processing for In_Place_Assign_OK
begin
-- By-copy semantic cannot be guaranteed for controlled objects
if Needs_Finalization (Etype (N)) then
return False;
end if;
Parent_Node := Parent (N);
Parent_Kind := Nkind (Parent_Node);
if Parent_Kind = N_Qualified_Expression then
Parent_Node := Parent (Parent_Node);
Parent_Kind := Nkind (Parent_Node);
end if;
-- On assignment, sliding can take place, so we cannot do the
-- assignment in place unless the bounds of the aggregate are
-- statically equal to those of the target.
-- If the aggregate is given by an others choice, the bounds are
-- derived from the left-hand side, and the assignment is safe if
-- the expression is.
if Is_Array
and then Present (Component_Associations (N))
and then not Is_Others_Aggregate (N)
then
Aggr_In := First_Index (Etype (N));
-- Context is an assignment
if Parent_Kind = N_Assignment_Statement then
Obj_In := First_Index (Etype (Name (Parent_Node)));
-- Context is an allocator. Check the bounds of the aggregate against
-- those of the designated type, except in the case where the type is
-- unconstrained (and then we can directly return true, see below).
else pragma Assert (Parent_Kind = N_Allocator);
declare
Desig_Typ : constant Entity_Id :=
Designated_Type (Etype (Parent_Node));
begin
if not Is_Constrained (Desig_Typ) then
return True;
end if;
Obj_In := First_Index (Desig_Typ);
end;
end if;
while Present (Aggr_In) loop
Get_Index_Bounds (Aggr_In, Aggr_Lo, Aggr_Hi);
Get_Index_Bounds (Obj_In, Obj_Lo, Obj_Hi);
-- We require static bounds for the target and a static matching
-- of low bound for the aggregate.
if not Compile_Time_Known_Value (Obj_Lo)
or else not Compile_Time_Known_Value (Obj_Hi)
or else not Compile_Time_Known_Value (Aggr_Lo)
or else Expr_Value (Aggr_Lo) /= Expr_Value (Obj_Lo)
then
return False;
-- For an assignment statement we require static matching of
-- bounds. Ditto for an allocator whose qualified expression
-- is a constrained type. If the expression in the allocator
-- is an unconstrained array, we accept an upper bound that
-- is not static, to allow for nonstatic expressions of the
-- base type. Clearly there are further possibilities (with
-- diminishing returns) for safely building arrays in place
-- here.
elsif Parent_Kind = N_Assignment_Statement
or else Is_Constrained (Etype (Parent_Node))
then
if not Compile_Time_Known_Value (Aggr_Hi)
or else Expr_Value (Aggr_Hi) /= Expr_Value (Obj_Hi)
then
return False;
end if;
end if;
Next_Index (Aggr_In);
Next_Index (Obj_In);
end loop;
end if;
-- Now check the component values themselves, except for an allocator
-- for which the target is newly allocated memory.
if Parent_Kind = N_Allocator then
return True;
else
return Safe_Aggregate (N);
end if;
end In_Place_Assign_OK;
----------------------------
-- Convert_To_Assignments --
----------------------------
procedure Convert_To_Assignments (N : Node_Id; Typ : Entity_Id) is
Loc : constant Source_Ptr := Sloc (N);
T : Entity_Id;
Temp : Entity_Id;
Aggr_Code : List_Id;
Instr : Node_Id;
Target_Expr : Node_Id;
Parent_Kind : Node_Kind;
Unc_Decl : Boolean := False;
Parent_Node : Node_Id;
begin
pragma Assert (Nkind (N) in N_Aggregate | N_Extension_Aggregate);
pragma Assert (not Is_Static_Dispatch_Table_Aggregate (N));
pragma Assert (Is_Record_Type (Typ));
Parent_Node := Parent (N);
Parent_Kind := Nkind (Parent_Node);
if Parent_Kind = N_Qualified_Expression then
-- Check if we are in an unconstrained declaration because in this
-- case the current delayed expansion mechanism doesn't work when
-- the declared object size depends on the initializing expr.
Parent_Node := Parent (Parent_Node);
Parent_Kind := Nkind (Parent_Node);
if Parent_Kind = N_Object_Declaration then
Unc_Decl :=
not Is_Entity_Name (Object_Definition (Parent_Node))
or else (Nkind (N) = N_Aggregate
and then
Has_Discriminants
(Entity (Object_Definition (Parent_Node))))
or else Is_Class_Wide_Type
(Entity (Object_Definition (Parent_Node)));
end if;
end if;
-- Just set the Delay flag in the cases where the transformation will be
-- done top down from above.
if 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_Build_In_Place_Aggregate_Return (N)
then
Set_Expansion_Delayed (N);
return;
end if;
-- Otherwise, if a transient scope is required, create it now. If we
-- are within an initialization procedure do not create such, because
-- the target of the assignment must not be declared within a local
-- block, and because cleanup will take place on return from the
-- initialization procedure.
-- Should the condition be more restrictive ???
if Requires_Transient_Scope (Typ) and then not Inside_Init_Proc then
Establish_Transient_Scope (N, Manage_Sec_Stack => False);
end if;
-- If the aggregate is nonlimited, create a temporary, since aggregates
-- have "by copy" semantics. If it is limited and context is an
-- assignment, this is a subaggregate for an enclosing aggregate being
-- expanded. It must be built in place, so use target of the current
-- assignment.
if Is_Limited_Type (Typ)
and then Parent_Kind = N_Assignment_Statement
then
Target_Expr := New_Copy_Tree (Name (Parent_Node));
Insert_Actions (Parent_Node,
Build_Record_Aggr_Code (N, Typ, Target_Expr));
Rewrite (Parent_Node, Make_Null_Statement (Loc));
-- Do not declare a temporary to initialize an aggregate assigned to
-- a target when in-place assignment is possible, i.e. preserving the
-- by-copy semantic of aggregates. This avoids large stack usage and
-- generates more efficient code.
elsif Parent_Kind = N_Assignment_Statement
and then In_Place_Assign_OK (N, Get_Base_Object (Name (Parent_Node)))
then
declare
Lhs : constant Node_Id := Name (Parent_Node);
begin
-- Apply discriminant check if required
if Has_Discriminants (Etype (N)) then
Apply_Discriminant_Check (N, Etype (Lhs), Lhs);
end if;
-- The check just above may have replaced the aggregate with a CE
if Nkind (N) in N_Aggregate | N_Extension_Aggregate then
Target_Expr := New_Copy_Tree (Lhs);
Insert_Actions (Parent_Node,
Build_Record_Aggr_Code (N, Typ, Target_Expr));
Rewrite (Parent_Node, Make_Null_Statement (Loc));
end if;
end;
else
Temp := Make_Temporary (Loc, 'A', N);
-- If the type inherits unknown discriminants, use the view with
-- known discriminants if available.
if Has_Unknown_Discriminants (Typ)
and then Present (Underlying_Record_View (Typ))
then
T := Underlying_Record_View (Typ);
else
T := Typ;
end if;
Instr :=
Make_Object_Declaration (Loc,
Defining_Identifier => Temp,
Object_Definition => New_Occurrence_Of (T, Loc));
Set_No_Initialization (Instr);
Insert_Action (N, Instr);
Initialize_Discriminants (Instr, T);
Target_Expr := New_Occurrence_Of (Temp, Loc);
Aggr_Code := Build_Record_Aggr_Code (N, T, Target_Expr);
-- Save the last assignment statement associated with the aggregate
-- when building a controlled object. This reference is utilized by
-- the finalization machinery when marking an object as successfully
-- initialized.
if Needs_Finalization (T) then
Set_Last_Aggregate_Assignment (Temp, Last (Aggr_Code));
end if;
Insert_Actions (N, Aggr_Code);
Rewrite (N, New_Occurrence_Of (Temp, Loc));
Analyze_And_Resolve (N, T);
end if;
end Convert_To_Assignments;
---------------------------
-- Convert_To_Positional --
---------------------------
procedure Convert_To_Positional
(N : Node_Id;
Handle_Bit_Packed : Boolean := False)
is
Typ : constant Entity_Id := Etype (N);
Dims : constant Nat := Number_Dimensions (Typ);
Max_Others_Replicate : constant Nat := Max_Aggregate_Size (N);
Static_Components : Boolean := True;
procedure Check_Static_Components;
-- Check whether all components of the aggregate are compile-time known
-- values, and can be passed as is to the back-end without further
-- expansion.
function Flatten
(N : Node_Id;
Dims : Nat;
Ix : Node_Id;
Ixb : Node_Id) return Boolean;
-- Convert the aggregate into a purely positional form if possible after
-- checking that the bounds of all dimensions are known to be static.
function Is_Flat (N : Node_Id; Dims : Nat) return Boolean;
-- Return True if the aggregate N is flat (which is not trivial in the
-- case of multidimensional aggregates).
function Is_Static_Element (N : Node_Id; Dims : Nat) return Boolean;
-- Return True if N, an element of a component association list, i.e.
-- N_Component_Association or N_Iterated_Component_Association, has a
-- compile-time known value and can be passed as is to the back-end
-- without further expansion.
-- An Iterated_Component_Association is treated as nonstatic in most
-- cases for now, so there are possibilities for optimization.
-----------------------------
-- Check_Static_Components --
-----------------------------
-- Could use some comments in this body ???
procedure Check_Static_Components is
Assoc : Node_Id;
Expr : Node_Id;
begin
Static_Components := True;
if Nkind (N) = N_String_Literal then
null;
elsif Present (Expressions (N)) then
Expr := First (Expressions (N));
while Present (Expr) loop
if Nkind (Expr) /= N_Aggregate
or else not Compile_Time_Known_Aggregate (Expr)
or else Expansion_Delayed (Expr)
then
Static_Components := False;
exit;
end if;
Next (Expr);
end loop;
end if;
if Nkind (N) = N_Aggregate
and then Present (Component_Associations (N))
then
Assoc := First (Component_Associations (N));
while Present (Assoc) loop
if not Is_Static_Element (Assoc, Dims) then
Static_Components := False;
exit;
end if;
Next (Assoc);
end loop;
end if;
end Check_Static_Components;
-------------
-- Flatten --
-------------
function Flatten
(N : Node_Id;
Dims : Nat;
Ix : Node_Id;
Ixb : Node_Id) return Boolean
is
Loc : constant Source_Ptr := Sloc (N);
Blo : constant Node_Id := Type_Low_Bound (Etype (Ixb));
Lo : constant Node_Id := Type_Low_Bound (Etype (Ix));
Hi : constant Node_Id := Type_High_Bound (Etype (Ix));
function Cannot_Flatten_Next_Aggr (Expr : Node_Id) return Boolean;
-- Return true if Expr is an aggregate for the next dimension that
-- cannot be recursively flattened.
------------------------------
-- Cannot_Flatten_Next_Aggr --
------------------------------
function Cannot_Flatten_Next_Aggr (Expr : Node_Id) return Boolean is
begin
return Nkind (Expr) = N_Aggregate
and then Present (Next_Index (Ix))
and then not
Flatten (Expr, Dims - 1, Next_Index (Ix), Next_Index (Ixb));
end Cannot_Flatten_Next_Aggr;
-- Local variables
Lov : Uint;
Hiv : Uint;
Others_Present : Boolean;
-- Start of processing for Flatten
begin
if Nkind (Original_Node (N)) = N_String_Literal then
return True;
end if;
if not Compile_Time_Known_Value (Lo)
or else not Compile_Time_Known_Value (Hi)
then
return False;
end if;
Lov := Expr_Value (Lo);
Hiv := Expr_Value (Hi);
-- Check if there is an others choice
Others_Present := False;
if Present (Component_Associations (N)) then
declare
Assoc : Node_Id;
Choice : Node_Id;
begin
Assoc := First (Component_Associations (N));
while Present (Assoc) loop
-- If this is a box association, flattening is in general
-- not possible because at this point we cannot tell if the
-- default is static or even exists.
if Box_Present (Assoc) then
return False;
elsif Nkind (Assoc) = N_Iterated_Component_Association then
return False;
end if;
Choice := First (Choice_List (Assoc));
while Present (Choice) loop
if Nkind (Choice) = N_Others_Choice then
Others_Present := True;
end if;
Next (Choice);
end loop;
Next (Assoc);
end loop;
end;
end if;
-- If the low bound is not known at compile time and others is not
-- present we can proceed since the bounds can be obtained from the
-- aggregate.
if Hiv < Lov
or else (not Compile_Time_Known_Value (Blo) and then Others_Present)
then
return False;
end if;
-- Determine if set of alternatives is suitable for conversion and
-- build an array containing the values in sequence.
declare
Vals : array (UI_To_Int (Lov) .. UI_To_Int (Hiv))
of Node_Id := (others => Empty);
-- The values in the aggregate sorted appropriately
Vlist : List_Id;
-- Same data as Vals in list form
Rep_Count : Nat;
-- Used to validate Max_Others_Replicate limit
Elmt : Node_Id;
Expr : Node_Id;
Num : Int := UI_To_Int (Lov);
Choice_Index : Int;
Choice : Node_Id;
Lo, Hi : Node_Id;
begin
if Present (Expressions (N)) then
Elmt := First (Expressions (N));
while Present (Elmt) loop
-- In the case of a multidimensional array, check that the
-- aggregate can be recursively flattened.
if Cannot_Flatten_Next_Aggr (Elmt) then
return False;
end if;
-- Duplicate expression for each index it covers
Vals (Num) := New_Copy_Tree (Elmt);
Num := Num + 1;
Next (Elmt);
end loop;
end if;
if No (Component_Associations (N)) then
return True;
end if;
Elmt := First (Component_Associations (N));
Component_Loop : while Present (Elmt) loop
Expr := Expression (Elmt);
-- In the case of a multidimensional array, check that the
-- aggregate can be recursively flattened.
if Cannot_Flatten_Next_Aggr (Expr) then
return False;
end if;
Choice := First (Choice_List (Elmt));
Choice_Loop : while Present (Choice) loop
-- If we have an others choice, fill in the missing elements
-- subject to the limit established by Max_Others_Replicate.
if Nkind (Choice) = N_Others_Choice then
Rep_Count := 0;
-- If the expression involves a construct that generates
-- a loop, we must generate individual assignments and
-- no flattening is possible.
if Nkind (Expr) = N_Quantified_Expression then
return False;
end if;
for J in Vals'Range loop
if No (Vals (J)) then
Vals (J) := New_Copy_Tree (Expr);
Rep_Count := Rep_Count + 1;
-- Check for maximum others replication. Note that
-- we skip this test if either of the restrictions
-- No_Implicit_Loops or No_Elaboration_Code is
-- active, if this is a preelaborable unit or
-- a predefined unit, or if the unit must be
-- placed in data memory. This also ensures that
-- predefined units get the same level of constant
-- folding in Ada 95 and Ada 2005, where their
-- categorization has changed.
declare
P : constant Entity_Id :=
Cunit_Entity (Current_Sem_Unit);
begin
-- Check if duplication is always OK and, if so,
-- continue processing.
if Restriction_Active (No_Implicit_Loops) then
null;
-- If duplication is not always OK, continue
-- only if either the element is static or is
-- an aggregate (we already know it is OK).
elsif not Is_Static_Element (Elmt, Dims)
and then Nkind (Expr) /= N_Aggregate
then
return False;
-- Check if duplication is OK for elaboration
-- purposes and, if so, continue processing.
elsif Restriction_Active (No_Elaboration_Code)
or else
(Ekind (Current_Scope) = E_Package
and then
Static_Elaboration_Desired (Current_Scope))
or else Is_Preelaborated (P)
or else (Ekind (P) = E_Package_Body
and then
Is_Preelaborated (Spec_Entity (P)))
or else
Is_Predefined_Unit (Get_Source_Unit (P))
then
null;
-- Otherwise, check that the replication count
-- is not too high.
elsif Rep_Count > Max_Others_Replicate then
return False;
end if;
end;
end if;
end loop;
if Rep_Count = 0
and then Warn_On_Redundant_Constructs
then
Error_Msg_N ("there are no others?r?", Elmt);
end if;
exit Component_Loop;
-- Case of a subtype mark, identifier or expanded name
elsif Is_Entity_Name (Choice)
and then Is_Type (Entity (Choice))
then
Lo := Type_Low_Bound (Etype (Choice));
Hi := Type_High_Bound (Etype (Choice));
-- Case of subtype indication
elsif Nkind (Choice) = N_Subtype_Indication then
Lo := Low_Bound (Range_Expression (Constraint (Choice)));
Hi := High_Bound (Range_Expression (Constraint (Choice)));
-- Case of a range
elsif Nkind (Choice) = N_Range then
Lo := Low_Bound (Choice);
Hi := High_Bound (Choice);
-- Normal subexpression case
else pragma Assert (Nkind (Choice) in N_Subexpr);
if not Compile_Time_Known_Value (Choice) then
return False;
else
Choice_Index := UI_To_Int (Expr_Value (Choice));
if Choice_Index in Vals'Range then
Vals (Choice_Index) := New_Copy_Tree (Expr);
goto Continue;
-- Choice is statically out-of-range, will be
-- rewritten to raise Constraint_Error.
else
return False;
end if;
end if;
end if;
-- Range cases merge with Lo,Hi set
if not Compile_Time_Known_Value (Lo)
or else
not Compile_Time_Known_Value (Hi)
then
return False;
else
for J in UI_To_Int (Expr_Value (Lo)) ..
UI_To_Int (Expr_Value (Hi))
loop
Vals (J) := New_Copy_Tree (Expr);
end loop;
end if;
<<Continue>>
Next (Choice);
end loop Choice_Loop;
Next (Elmt);
end loop Component_Loop;
-- If we get here the conversion is possible
Vlist := New_List;
for J in Vals'Range loop
Append (Vals (J), Vlist);
end loop;
Rewrite (N, Make_Aggregate (Loc, Expressions => Vlist));
Set_Aggregate_Bounds (N, Aggregate_Bounds (Original_Node (N)));
return True;
end;
end Flatten;
-------------
-- Is_Flat --
-------------
function Is_Flat (N : Node_Id; Dims : Nat) return Boolean is
Elmt : Node_Id;
begin
if Dims = 0 then
return True;
elsif Nkind (N) = N_Aggregate then
if Present (Component_Associations (N)) then
return False;
else
Elmt := First (Expressions (N));
while Present (Elmt) loop
if not Is_Flat (Elmt, Dims - 1) then
return False;
end if;
Next (Elmt);
end loop;
return True;
end if;
else
return True;
end if;
end Is_Flat;
-------------------------
-- Is_Static_Element --
-------------------------
function Is_Static_Element (N : Node_Id; Dims : Nat) return Boolean is
Expr : constant Node_Id := Expression (N);
begin
-- In most cases the interesting expressions are unambiguously static
if Compile_Time_Known_Value (Expr) then
return True;
elsif Nkind (N) = N_Iterated_Component_Association then
return False;
elsif Nkind (Expr) = N_Aggregate
and then Compile_Time_Known_Aggregate (Expr)
and then not Expansion_Delayed (Expr)
then
return True;
-- However, one may write static expressions that are syntactically
-- ambiguous, so preanalyze the expression before checking it again,
-- but only at the innermost level for a multidimensional array.
elsif Dims = 1 then
Preanalyze_And_Resolve (Expr, Component_Type (Typ));
return Compile_Time_Known_Value (Expr);
else
return False;
end if;
end Is_Static_Element;
-- Start of processing for Convert_To_Positional
begin
-- Only convert to positional when generating C in case of an
-- object declaration, this is the only case where aggregates are
-- supported in C.
if Modify_Tree_For_C and then not Is_CCG_Supported_Aggregate (N) then
return;
end if;
-- Ada 2005 (AI-287): Do not convert in case of default initialized
-- components because in this case will need to call the corresponding
-- IP procedure.
if Has_Default_Init_Comps (N) then
return;
end if;
-- A subaggregate may have been flattened but is not known to be
-- Compile_Time_Known. Set that flag in cases that cannot require
-- elaboration code, so that the aggregate can be used as the
-- initial value of a thread-local variable.
if Is_Flat (N, Dims) then
if Static_Array_Aggregate (N) then
Set_Compile_Time_Known_Aggregate (N);
end if;
return;
end if;
if Is_Bit_Packed_Array (Typ) and then not Handle_Bit_Packed then
return;
end if;
-- Do not convert to positional if controlled components are involved
-- since these require special processing
if Has_Controlled_Component (Typ) then
return;
end if;
Check_Static_Components;
-- If the size is known, or all the components are static, try to
-- build a fully positional aggregate.
-- The size of the type may not be known for an aggregate with
-- discriminated array components, but if the components are static
-- it is still possible to verify statically that the length is
-- compatible with the upper bound of the type, and therefore it is
-- worth flattening such aggregates as well.
if Aggr_Size_OK (N)
and then
Flatten (N, Dims, First_Index (Typ), First_Index (Base_Type (Typ)))
then
if Static_Components then
Set_Compile_Time_Known_Aggregate (N);
Set_Expansion_Delayed (N, False);
end if;
Analyze_And_Resolve (N, Typ);
end if;
-- If Static_Elaboration_Desired has been specified, diagnose aggregates
-- that will still require initialization code.
if (Ekind (Current_Scope) = E_Package
and then Static_Elaboration_Desired (Current_Scope))
and then Nkind (Parent (N)) = N_Object_Declaration
then
declare
Expr : Node_Id;
begin
if Nkind (N) = N_Aggregate and then Present (Expressions (N)) then
Expr := First (Expressions (N));
while Present (Expr) loop
if not Compile_Time_Known_Value (Expr) then
Error_Msg_N
("non-static object requires elaboration code??", N);
exit;
end if;
Next (Expr);
end loop;
if Present (Component_Associations (N)) then
Error_Msg_N ("object requires elaboration code??", N);
end if;
end if;
end;
end if;
end Convert_To_Positional;
----------------------------
-- Expand_Array_Aggregate --
----------------------------
-- Array aggregate expansion proceeds as follows:
-- 1. If requested we generate code to perform all the array aggregate
-- bound checks, specifically
-- (a) Check that the index range defined by aggregate bounds is
-- compatible with corresponding index subtype.
-- (b) If an others choice is present check that no aggregate
-- index is outside the bounds of the index constraint.
-- (c) For multidimensional arrays make sure that all subaggregates
-- corresponding to the same dimension have the same bounds.
-- 2. Check for packed array aggregate which can be converted to a
-- constant so that the aggregate disappears completely.
-- 3. Check case of nested aggregate. Generally nested aggregates are
-- handled during the processing of the parent aggregate.
-- 4. Check if the aggregate can be statically processed. If this is the
-- case pass it as is to Gigi. Note that a necessary condition for
-- static processing is that the aggregate be fully positional.
-- 5. If in-place aggregate expansion is possible (i.e. no need to create
-- a temporary) then mark the aggregate as such and return. Otherwise
-- create a new temporary and generate the appropriate initialization
-- code.
procedure Expand_Array_Aggregate (N : Node_Id) is
Loc : constant Source_Ptr := Sloc (N);
Typ : constant Entity_Id := Etype (N);
Ctyp : constant Entity_Id := Component_Type (Typ);
-- Typ is the correct constrained array subtype of the aggregate
-- Ctyp is the corresponding component type.
Aggr_Dimension : constant Pos := Number_Dimensions (Typ);
-- Number of aggregate index dimensions
Aggr_Low : array (1 .. Aggr_Dimension) of Node_Id;
Aggr_High : array (1 .. Aggr_Dimension) of Node_Id;
-- Low and High bounds of the constraint for each aggregate index
Aggr_Index_Typ : array (1 .. Aggr_Dimension) of Entity_Id;
-- The type of each index
In_Place_Assign_OK_For_Declaration : Boolean := False;
-- True if we are to generate an in-place assignment for a declaration
Maybe_In_Place_OK : Boolean;
-- If the type is neither controlled nor packed and the aggregate
-- is the expression in an assignment, assignment in place may be
-- possible, provided other conditions are met on the LHS.
Others_Present : array (1 .. Aggr_Dimension) of Boolean :=
(others => False);
-- If Others_Present (J) is True, then there is an others choice in one
-- of the subaggregates of N at dimension J.
procedure Build_Constrained_Type (Positional : Boolean);
-- If the subtype is not static or unconstrained, build a constrained
-- type using the computable sizes of the aggregate and its sub-
-- aggregates.
procedure Check_Bounds (Aggr_Bounds : Node_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 multidimensional array aggregate all subaggregates
-- corresponding to the same dimension have the same bounds. Sub_Aggr is
-- an array subaggregate. Dim is the dimension corresponding to the
-- subaggregate.
procedure Compute_Others_Present (Sub_Aggr : Node_Id; Dim : Pos);
-- Computes the values of array Others_Present. Sub_Aggr is the array
-- subaggregate we start the computation from. Dim is the dimension
-- corresponding to the subaggregate.
procedure Others_Check (Sub_Aggr : Node_Id; Dim : Pos);
-- Checks that if an others choice is present in any subaggregate, no
-- aggregate index is outside the bounds of the index constraint.
-- Sub_Aggr is an array subaggregate. Dim is the dimension corresponding
-- to the subaggregate.
function Safe_Left_Hand_Side (N : Node_Id) return Boolean;
-- In addition to Maybe_In_Place_OK, in order for an aggregate to be
-- built directly into the target of the assignment it must be free
-- of side effects.
----------------------------
-- Build_Constrained_Type --
----------------------------
procedure Build_Constrained_Type (Positional : Boolean) is
Loc : constant Source_Ptr := Sloc (N);
Agg_Type : constant Entity_Id := Make_Temporary (Loc, 'A');
Comp : Node_Id;
Decl : Node_Id;
Typ : constant Entity_Id := Etype (N);
Indexes : constant List_Id := New_List;
Num : Nat;
Sub_Agg : Node_Id;
begin
-- If the aggregate is purely positional, all its subaggregates
-- have the same size. We collect the dimensions from the first
-- subaggregate at each level.
if Positional then
Sub_Agg := N;
for D in 1 .. Number_Dimensions (Typ) loop
Sub_Agg := First (Expressions (Sub_Agg));
Comp := Sub_Agg;
Num := 0;
while Present (Comp) loop
Num := Num + 1;
Next (Comp);
end loop;
Append_To (Indexes,
Make_Range (Loc,
Low_Bound => Make_Integer_Literal (Loc, 1),
High_Bound => Make_Integer_Literal (Loc, Num)));
end loop;
else
-- We know the aggregate type is unconstrained and the aggregate
-- is not processable by the back end, therefore not necessarily
-- positional. Retrieve each dimension bounds (computed earlier).
for D in 1 .. Number_Dimensions (Typ) loop
Append_To (Indexes,
Make_Range (Loc,
Low_Bound => Aggr_Low (D),
High_Bound => Aggr_High (D)));
end loop;
end if;
Decl :=
Make_Full_Type_Declaration (Loc,
Defining_Identifier => Agg_Type,
Type_Definition =>
Make_Constrained_Array_Definition (Loc,
Discrete_Subtype_Definitions => Indexes,
Component_Definition =>
Make_Component_Definition (Loc,
Aliased_Present => False,
Subtype_Indication =>
New_Occurrence_Of (Component_Type (Typ), Loc))));
Insert_Action (N, Decl);
Analyze (Decl);
Set_Etype (N, Agg_Type);
Set_Is_Itype (Agg_Type);
Freeze_Itype (Agg_Type, N);
end Build_Constrained_Type;
------------------
-- Check_Bounds --
------------------
procedure Check_Bounds (Aggr_Bounds : Node_Id; Index_Bounds : Node_Id) is
Aggr_Lo : Node_Id;
Aggr_Hi : Node_Id;
Ind_Lo : Node_Id;
Ind_Hi : Node_Id;
Cond : Node_Id := Empty;
begin
Get_Index_Bounds (Aggr_Bounds, Aggr_Lo, Aggr_Hi);
Get_Index_Bounds (Index_Bounds, Ind_Lo, Ind_Hi);
-- Generate the following test:
-- [constraint_error when
-- Aggr_Lo <= Aggr_Hi and then
-- (Aggr_Lo < Ind_Lo or else Aggr_Hi > Ind_Hi)]
-- As an optimization try to see if some tests are trivially vacuous
-- because we are comparing an expression against itself.
if Aggr_Lo = Ind_Lo and then Aggr_Hi = Ind_Hi then
Cond := Empty;
elsif Aggr_Hi = Ind_Hi then
Cond :=
Make_Op_Lt (Loc,
Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo),
Right_Opnd => Duplicate_Subexpr_Move_Checks (Ind_Lo));
elsif Aggr_Lo = Ind_Lo then
Cond :=
Make_Op_Gt (Loc,
Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Hi),
Right_Opnd => Duplicate_Subexpr_Move_Checks (Ind_Hi));
else
Cond :=
Make_Or_Else (Loc,
Left_Opnd =>
Make_Op_Lt (Loc,
Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo),
Right_Opnd => Duplicate_Subexpr_Move_Checks (Ind_Lo)),
Right_Opnd =>
Make_Op_Gt (Loc,
Left_Opnd => Duplicate_Subexpr (Aggr_Hi),
Right_Opnd => Duplicate_Subexpr (Ind_Hi)));
end if;
if Present (Cond) then
Cond :=
Make_And_Then (Loc,
Left_Opnd =>
Make_Op_Le (Loc,
Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo),
Right_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Hi)),
Right_Opnd => Cond);
Set_Analyzed (Left_Opnd (Left_Opnd (Cond)), False);
Set_Analyzed (Right_Opnd (Left_Opnd (Cond)), False);
Insert_Action (N,
Make_Raise_Constraint_Error (Loc,
Condition => Cond,
Reason => CE_Range_Check_Failed));
end if;
end Check_Bounds;
----------------------------
-- Check_Same_Aggr_Bounds --
----------------------------
procedure Check_Same_Aggr_Bounds (Sub_Aggr : Node_Id; Dim : Pos) is
Sub_Lo : constant Node_Id := Low_Bound (Aggregate_Bounds (Sub_Aggr));
Sub_Hi : constant Node_Id := High_Bound (Aggregate_Bounds (Sub_Aggr));
-- The bounds of this specific subaggregate
Aggr_Lo : constant Node_Id := Aggr_Low (Dim);
Aggr_Hi : constant Node_Id := Aggr_High (Dim);
-- The bounds of the aggregate for this dimension
Ind_Typ : constant Entity_Id := Aggr_Index_Typ (Dim);
-- The index type for this dimension.xxx
Cond : Node_Id := Empty;
Assoc : Node_Id;
Expr : Node_Id;
begin
-- If index checks are on generate the test
-- [constraint_error when
-- Aggr_Lo /= Sub_Lo or else Aggr_Hi /= Sub_Hi]
-- As an optimization try to see if some tests are trivially vacuos
-- because we are comparing an expression against itself. Also for
-- the first dimension the test is trivially vacuous because there
-- is just one aggregate for dimension 1.
if Index_Checks_Suppressed (Ind_Typ) then
Cond := Empty;
elsif Dim = 1 or else (Aggr_Lo = Sub_Lo and then Aggr_Hi = Sub_Hi)
then
Cond := Empty;
elsif Aggr_Hi = Sub_Hi then
Cond :=
Make_Op_Ne (Loc,
Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo),
Right_Opnd => Duplicate_Subexpr_Move_Checks (Sub_Lo));
elsif Aggr_Lo = Sub_Lo then
Cond :=
Make_Op_Ne (Loc,
Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Hi),
Right_Opnd => Duplicate_Subexpr_Move_Checks (Sub_Hi));
else
Cond :=
Make_Or_Else (Loc,
Left_Opnd =>
Make_Op_Ne (Loc,
Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo),
Right_Opnd => Duplicate_Subexpr_Move_Checks (Sub_Lo)),
Right_Opnd =>
Make_Op_Ne (Loc,
Left_Opnd => Duplicate_Subexpr (Aggr_Hi),
Right_Opnd => Duplicate_Subexpr (Sub_Hi)));
end if;
if Present (Cond) then
Insert_Action (N,
Make_Raise_Constraint_Error (Loc,
Condition => Cond,
Reason => CE_Length_Check_Failed));
end if;
-- Now look inside the subaggregate to see if there is more work
if Dim < Aggr_Dimension then
-- Process positional components
if Present (Expressions (Sub_Aggr)) then
Expr := First (Expressions (Sub_Aggr));
while Present (Expr) loop
Check_Same_Aggr_Bounds (Expr, Dim + 1);
Next (Expr);
end loop;
end if;
-- Process component associations
if Present (Component_Associations (Sub_Aggr)) then
Assoc := First (Component_Associations (Sub_Aggr));
while Present (Assoc) loop
Expr := Expression (Assoc);
Check_Same_Aggr_Bounds (Expr, Dim + 1);
Next (Assoc);
end loop;
end if;
end if;
end Check_Same_Aggr_Bounds;
----------------------------
-- Compute_Others_Present --
----------------------------
procedure Compute_Others_Present (Sub_Aggr : Node_Id; Dim : Pos) is
Assoc : Node_Id;
Expr : Node_Id;
begin
if Present (Component_Associations (Sub_Aggr)) then
Assoc := Last (Component_Associations (Sub_Aggr));
if Nkind (First (Choice_List (Assoc))) = N_Others_Choice then
Others_Present (Dim) := True;
end if;
end if;
-- Now look inside the subaggregate to see if there is more work
if Dim < Aggr_Dimension then
-- Process positional components
if Present (Expressions (Sub_Aggr)) then
Expr := First (Expressions (Sub_Aggr));
while Present (Expr) loop
Compute_Others_Present (Expr, Dim + 1);
Next (Expr);
end loop;
end if;
-- Process component associations
if Present (Component_Associations (Sub_Aggr)) then
Assoc := First (Component_Associations (Sub_Aggr));
while Present (Assoc) loop
Expr := Expression (Assoc);
Compute_Others_Present (Expr, Dim + 1);
Next (Assoc);
end loop;
end if;
end if;
end Compute_Others_Present;
------------------
-- Others_Check --
------------------
procedure Others_Check (Sub_Aggr : Node_Id; Dim : Pos) is
Aggr_Lo : constant Node_Id := Aggr_Low (Dim);
Aggr_Hi : constant Node_Id := Aggr_High (Dim);
-- The bounds of the aggregate for this dimension
Ind_Typ : constant Entity_Id := Aggr_Index_Typ (Dim);
-- The index type for this dimension
Need_To_Check : Boolean := False;
Choices_Lo : Node_Id := Empty;
Choices_Hi : Node_Id := Empty;
-- The lowest and highest discrete choices for a named subaggregate
Nb_Choices : Int := -1;
-- The number of discrete non-others choices in this subaggregate
Nb_Elements : Uint := Uint_0;
-- The number of elements in a positional aggregate
Cond : Node_Id := Empty;
Assoc : Node_Id;
Choice : Node_Id;
Expr : Node_Id;
begin
-- Check if we have an others choice. If we do make sure that this
-- subaggregate contains at least one element in addition to the
-- others choice.
if Range_Checks_Suppressed (Ind_Typ) then
Need_To_Check := False;
elsif Present (Expressions (Sub_Aggr))
and then Present (Component_Associations (Sub_Aggr))
then
Need_To_Check := True;
elsif Present (Component_Associations (Sub_Aggr)) then
Assoc := Last (Component_Associations (Sub_Aggr));
if Nkind (First (Choice_List (Assoc))) /= N_Others_Choice then
Need_To_Check := False;
else
-- Count the number of discrete choices. Start with -1 because
-- the others choice does not count.
-- Is there some reason we do not use List_Length here ???
Nb_Choices := -1;
Assoc := First (Component_Associations (Sub_Aggr));
while Present (Assoc) loop
Choice := First (Choice_List (Assoc));
while Present (Choice) loop
Nb_Choices := Nb_Choices + 1;
Next (Choice);
end loop;
Next (Assoc);
end loop;
-- If there is only an others choice nothing to do
Need_To_Check := (Nb_Choices > 0);
end if;
else
Need_To_Check := False;
end if;
-- If we are dealing with a positional subaggregate with an others
-- choice then compute the number or positional elements.
if Need_To_Check and then Present (Expressions (Sub_Aggr)) then
Expr := First (Expressions (Sub_Aggr));
Nb_Elements := Uint_0;
while Present (Expr) loop
Nb_Elements := Nb_Elements + 1;
Next (Expr);
end loop;
-- If the aggregate contains discrete choices and an others choice
-- compute the smallest and largest discrete choice values.
elsif Need_To_Check then
Compute_Choices_Lo_And_Choices_Hi : declare
Table : Case_Table_Type (1 .. Nb_Choices);
-- Used to sort all the different choice values
J : Pos := 1;
Low : Node_Id;
High : Node_Id;
begin
Assoc := First (Component_Associations (Sub_Aggr));
while Present (Assoc) loop
Choice := First (Choice_List (Assoc));
while Present (Choice) loop
if Nkind (Choice) = N_Others_Choice then
exit;
end if;
Get_Index_Bounds (Choice, Low, High);
Table (J).Choice_Lo := Low;
Table (J).Choice_Hi := High;
J := J + 1;
Next (Choice);
end loop;
Next (Assoc);
end loop;
-- Sort the discrete choices
Sort_Case_Table (Table);
Choices_Lo := Table (1).Choice_Lo;
Choices_Hi := Table (Nb_Choices).Choice_Hi;
end Compute_Choices_Lo_And_Choices_Hi;
end if;
-- If no others choice in this subaggregate, or the aggregate
-- comprises only an others choice, nothing to do.
if not Need_To_Check then
Cond := Empty;
-- If we are dealing with an aggregate containing an others choice
-- and positional components, we generate the following test:
-- if Ind_Typ'Pos (Aggr_Lo) + (Nb_Elements - 1) >
-- Ind_Typ'Pos (Aggr_Hi)
-- then
-- raise Constraint_Error;
-- end if;
-- in the general case, but the following simpler test:
-- [constraint_error when
-- Aggr_Lo + (Nb_Elements - 1) > Aggr_Hi];
-- instead if the index type is a signed integer.
elsif Nb_Elements > Uint_0 then
if Nb_Elements = Uint_1 then
Cond :=
Make_Op_Gt (Loc,
Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo),
Right_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Hi));
elsif Is_Signed_Integer_Type (Ind_Typ) then
Cond :=
Make_Op_Gt (Loc,
Left_Opnd =>
Make_Op_Add (Loc,
Left_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo),
Right_Opnd =>
Make_Integer_Literal (Loc, Nb_Elements - 1)),
Right_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Hi));
else
Cond :=
Make_Op_Gt (Loc,
Left_Opnd =>
Make_Op_Add (Loc,
Left_Opnd =>
Make_Attribute_Reference (Loc,
Prefix => New_Occurrence_Of (Ind_Typ, Loc),
Attribute_Name => Name_Pos,
Expressions =>
New_List
(Duplicate_Subexpr_Move_Checks (Aggr_Lo))),
Right_Opnd => Make_Integer_Literal (Loc, Nb_Elements - 1)),
Right_Opnd =>
Make_Attribute_Reference (Loc,
Prefix => New_Occurrence_Of (Ind_Typ, Loc),
Attribute_Name => Name_Pos,
Expressions => New_List (
Duplicate_Subexpr_Move_Checks (Aggr_Hi))));
end if;
-- If we are dealing with an aggregate containing an others choice
-- and discrete choices we generate the following test:
-- [constraint_error when
-- Choices_Lo < Aggr_Lo or else Choices_Hi > Aggr_Hi];
else
Cond :=
Make_Or_Else (Loc,
Left_Opnd =>
Make_Op_Lt (Loc,
Left_Opnd => Duplicate_Subexpr_Move_Checks (Choices_Lo),
Right_Opnd => Duplicate_Subexpr_Move_Checks (Aggr_Lo)),
Right_Opnd =>
Make_Op_Gt (Loc,
Left_Opnd => Duplicate_Subexpr (Choices_Hi),
Right_Opnd => Duplicate_Subexpr (Aggr_Hi)));
end if;
if Present (Cond) then
Insert_Action (N,
Make_Raise_Constraint_Error (Loc,
Condition => Cond,
Reason => CE_Length_Check_Failed));
-- Questionable reason code, shouldn't that be a
-- CE_Range_Check_Failed ???
end if;
-- Now look inside the subaggregate to see if there is more work
if Dim < Aggr_Dimension then
-- Process positional components
if Present (Expressions (Sub_Aggr)) then
Expr := First (Expressions (Sub_Aggr));
while Present (Expr) loop
Others_Check (Expr, Dim + 1);
Next (Expr);
end loop;
end if;
-- Process component associations
if Present (Component_Associations (Sub_Aggr)) then
Assoc := First (Component_Associations (Sub_Aggr));
while Present (Assoc) loop
Expr := Expression (Assoc);
Others_Check (Expr, Dim + 1);
Next (Assoc);
end loop;
end if;
end if;
end Others_Check;
-------------------------
-- Safe_Left_Hand_Side --
-------------------------
function Safe_Left_Hand_Side (N : Node_Id) return Boolean is
function Is_Safe_Index (Indx : Node_Id) return Boolean;
-- If the left-hand side includes an indexed component, check that
-- the indexes are free of side effects.
-------------------
-- Is_Safe_Index --
-------------------
function Is_Safe_Index (Indx : Node_Id) return Boolean is
begin
if Is_Entity_Name (Indx) then
return True;
elsif Nkind (Indx) = N_Integer_Literal then
return True;
elsif Nkind (Indx) = N_Function_Call
and then Is_Entity_Name (Name (Indx))
and then Has_Pragma_Pure_Function (Entity (Name (Indx)))
then
return True;
elsif Nkind (Indx) = N_Type_Conversion
and then Is_Safe_Index (Expression (Indx))
then
return True;
else
return False;
end if;
end Is_Safe_Index;
-- Start of processing for Safe_Left_Hand_Side
begin
if Is_Entity_Name (N) then
return True;
elsif Nkind (N) in N_Explicit_Dereference | N_Selected_Component
and then Safe_Left_Hand_Side (Prefix (N))
then
return True;
elsif Nkind (N) = N_Indexed_Component
and then Safe_Left_Hand_Side (Prefix (N))
and then Is_Safe_Index (First (Expressions (N)))
then
return True;
elsif Nkind (N) = N_Unchecked_Type_Conversion then
return Safe_Left_Hand_Side (Expression (N));
else
return False;
end if;
end Safe_Left_Hand_Side;
-- Local variables
Tmp : Entity_Id;
-- Holds the temporary aggregate value
Tmp_Decl : Node_Id;
-- Holds the declaration of Tmp
Aggr_Code : List_Id;
Parent_Node : Node_Id;
Parent_Kind : Node_Kind;
-- Start of processing for Expand_Array_Aggregate
begin
-- Do not touch the special aggregates of attributes used for Asm calls
if Is_RTE (Ctyp, RE_Asm_Input_Operand)
or else Is_RTE (Ctyp, RE_Asm_Output_Operand)
then
return;
-- Do not expand an aggregate for an array type which contains tasks if
-- the aggregate is associated with an unexpanded return statement of a
-- build-in-place function. The aggregate is expanded when the related
-- return statement (rewritten into an extended return) is processed.
-- This delay ensures that any temporaries and initialization code
-- generated for the aggregate appear in the proper return block and
-- use the correct _chain and _master.
elsif Has_Task (Base_Type (Etype (N)))
and then Nkind (Parent (N)) = N_Simple_Return_Statement
and then Is_Build_In_Place_Function
(Return_Applies_To (Return_Statement_Entity (Parent (N))))
then
return;
-- Do not attempt expansion if error already detected. We may reach this
-- point in spite of previous errors when compiling with -gnatq, to
-- force all possible errors (this is the usual ACATS mode).
elsif Error_Posted (N) then
return;
end if;
-- If the semantic analyzer has determined that aggregate N will raise
-- Constraint_Error at run time, then the aggregate node has been
-- replaced with an N_Raise_Constraint_Error node and we should
-- never get here.
pragma Assert (not Raises_Constraint_Error (N));
-- STEP 1a
-- Check that the index range defined by aggregate bounds is
-- compatible with corresponding index subtype.
Index_Compatibility_Check : declare
Aggr_Index_Range : Node_Id := First_Index (Typ);
-- The current aggregate index range
Index_Constraint : Node_Id := First_Index (Etype (Typ));
-- The corresponding index constraint against which we have to
-- check the above aggregate index range.
begin
Compute_Others_Present (N, 1);
for J in 1 .. Aggr_Dimension loop
-- There is no need to emit a check if an others choice is present
-- for this array aggregate dimension since in this case one of
-- N's subaggregates has taken its bounds from the context and
-- these bounds must have been checked already. In addition all
-- subaggregates corresponding to the same dimension must all have
-- the same bounds (checked in (c) below).
if not Range_Checks_Suppressed (Etype (Index_Constraint))
and then not Others_Present (J)
then
-- We don't use Checks.Apply_Range_Check here because it emits
-- a spurious check. Namely it checks that the range defined by
-- the aggregate bounds is nonempty. But we know this already
-- if we get here.
Check_Bounds (Aggr_Index_Range, Index_Constraint);
end if;
-- Save the low and high bounds of the aggregate index as well as
-- the index type for later use in checks (b) and (c) below.
Aggr_Low (J) := Low_Bound (Aggr_Index_Range);
Aggr_High (J) := High_Bound (Aggr_Index_Range);
Aggr_Index_Typ (J) := Etype (Index_Constraint);
Next_Index (Aggr_Index_Range);
Next_Index (Index_Constraint);
end loop;
end Index_Compatibility_Check;
-- STEP 1b
-- If an others choice is present check that no aggregate index is
-- outside the bounds of the index constraint.
Others_Check (N, 1);
-- STEP 1c
-- For multidimensional arrays make sure that all subaggregates
-- corresponding to the same dimension have the same bounds.
if Aggr_Dimension > 1 then
Check_Same_Aggr_Bounds (N, 1);
end if;
-- STEP 1d
-- If we have a default component value, or simple initialization is
-- required for the component type, then we replace <> in component
-- associations by the required default value.
declare
Default_Val : Node_Id;
Assoc : Node_Id;
begin
if (Present (Default_Aspect_Component_Value (Typ))
or else Needs_Simple_Initialization (Ctyp))
and then Present (Component_Associations (N))
then
Assoc := First (Component_Associations (N));
while Present (Assoc) loop
if Nkind (Assoc) = N_Component_Association
and then Box_Present (Assoc)
then
Set_Box_Present (Assoc, False);
if Present (Default_Aspect_Component_Value (Typ)) then
Default_Val := Default_Aspect_Component_Value (Typ);
else
Default_Val := Get_Simple_Init_Val (Ctyp, N);
end if;
Set_Expression (Assoc, New_Copy_Tree (Default_Val));
Analyze_And_Resolve (Expression (Assoc), Ctyp);
end if;
Next (Assoc);
end loop;
end if;
end;
-- STEP 2
-- Here we test for is packed array aggregate that we can handle at
-- compile time. If so, return with transformation done. Note that we do
-- this even if the aggregate is nested, because once we have done this
-- processing, there is no more nested aggregate.
if Packed_Array_Aggregate_Handled (N) then
return;
end if;
-- At this point we try to convert to positional form
Convert_To_Positional (N);
-- If the result is no longer an aggregate (e.g. it may be a string
-- literal, or a temporary which has the needed value), then we are
-- done, since there is no longer a nested aggregate.
if Nkind (N) /= N_Aggregate then
return;
-- We are also done if the result is an analyzed aggregate, indicating
-- that Convert_To_Positional succeeded and reanalyzed the rewritten
-- aggregate.
elsif Analyzed (N) and then Is_Rewrite_Substitution (N) then
return;
end if;
-- If all aggregate components are compile-time known and the aggregate
-- has been flattened, nothing left to do. The same occurs if the
-- aggregate is used to initialize the components of a statically
-- allocated dispatch table.
if Compile_Time_Known_Aggregate (N)
or else Is_Static_Dispatch_Table_Aggregate (N)
then
Set_Expansion_Delayed (N, False);
return;
end if;
-- Now see if back end processing is possible
if Backend_Processing_Possible (N) then
-- If the aggregate is static but the constraints are not, build
-- a static subtype for the aggregate, so that Gigi can place it
-- in static memory. Perform an unchecked_conversion to the non-
-- static type imposed by the context.
declare
Itype : constant Entity_Id := Etype (N);
Index : Node_Id;
Needs_Type : Boolean := False;
begin
Index := First_Index (Itype);
while Present (Index) loop
if not Is_OK_Static_Subtype (Etype (Index)) then
Needs_Type := True;
exit;
else
Next_Index (Index);
end if;
end loop;
if Needs_Type then
Build_Constrained_Type (Positional => True);
Rewrite (N, Unchecked_Convert_To (Itype, N));
Analyze (N);
end if;
end;
return;
end if;
-- STEP 3
-- Delay expansion for nested aggregates: it will be taken care of when
-- the parent aggregate is expanded.
Parent_Node := Parent (N);
Parent_Kind := Nkind (Parent_Node);
if Parent_Kind = N_Qualified_Expression then
Parent_Node := Parent (Parent_Node);
Parent_Kind := Nkind (Parent_Node);
end if;
if Parent_Kind = N_Aggregate
or else Parent_Kind = N_Extension_Aggregate
or else Parent_Kind = N_Component_Association
or else (Parent_Kind = N_Object_Declaration
and then Needs_Finalization (Typ))
or else (Parent_Kind = N_Assignment_Statement
and then Inside_Init_Proc)
then
Set_Expansion_Delayed (N, not Static_Array_Aggregate (N));
return;
end if;
-- STEP 4
-- Check whether in-place aggregate expansion is possible
-- For object declarations we build the aggregate in place, unless
-- the array is bit-packed.
-- For assignments we do the assignment in place if all the component
-- associations have compile-time known values, or are default-
-- initialized limited components, e.g. tasks. For other cases we
-- create a temporary. The analysis for safety of on-line assignment
-- is delicate, i.e. we don't know how to do it fully yet ???
-- For allocators we assign to the designated object in place if the
-- aggregate meets the same conditions as other in-place assignments.
-- In this case the aggregate may not come from source but was created
-- for default initialization, e.g. with Initialize_Scalars.
if Requires_Transient_Scope (Typ) then
Establish_Transient_Scope (N, Manage_Sec_Stack => False);
end if;
-- An array of limited components is built in place
if Is_Limited_Type (Typ) then
Maybe_In_Place_OK := True;
elsif Has_Default_Init_Comps (N) then
Maybe_In_Place_OK := False;
elsif Is_Bit_Packed_Array (Typ)
or else Has_Controlled_Component (Typ)
then
Maybe_In_Place_OK := False;
elsif Parent_Kind = N_Assignment_Statement then
Maybe_In_Place_OK :=
In_Place_Assign_OK (N, Get_Base_Object (Name (Parent_Node)));
elsif Parent_Kind = N_Allocator then
Maybe_In_Place_OK := In_Place_Assign_OK (N);
else
Maybe_In_Place_OK := False;
end if;
-- If this is an array of tasks, it will be expanded into build-in-place
-- assignments. Build an activation chain for the tasks now.
if Has_Task (Etype (N)) then
Build_Activation_Chain_Entity (N);
end if;
-- Perform in-place expansion of aggregate in an object declaration.
-- Note: actions generated for the aggregate will be captured in an
-- expression-with-actions statement so that they can be transferred
-- to freeze actions later if there is an address clause for the
-- object. (Note: we don't use a block statement because this would
-- cause generated freeze nodes to be elaborated in the wrong scope).
-- Arrays of limited components must be built in place. The code
-- previously excluded controlled components but this is an old
-- oversight: the rules in 7.6 (17) are clear.
if Comes_From_Source (Parent_Node)
and then Parent_Kind = N_Object_Declaration
and then Present (Expression (Parent_Node))
and then not
Must_Slide (Etype (Defining_Identifier (Parent_Node)), Typ)
and then not Is_Bit_Packed_Array (Typ)
then
In_Place_Assign_OK_For_Declaration := True;
Tmp := Defining_Identifier (Parent_Node);
Set_No_Initialization (Parent_Node);
Set_Expression (Parent_Node, Empty);
-- Set kind and type of the entity, for use in the analysis
-- of the subsequent assignments. If the nominal type is not
-- constrained, build a subtype from the known bounds of the
-- aggregate. If the declaration has a subtype mark, use it,
-- otherwise use the itype of the aggregate.
Set_Ekind (Tmp, E_Variable);
if not Is_Constrained (Typ) then
Build_Constrained_Type (Positional => False);
elsif Is_Entity_Name (Object_Definition (Parent_Node))
and then Is_Constrained (Entity (Object_Definition (Parent_Node)))
then
Set_Etype (Tmp, Entity (Object_Definition (Parent_Node)));
else
Set_Size_Known_At_Compile_Time (Typ, False);
Set_Etype (Tmp, Typ);
end if;
elsif Maybe_In_Place_OK and then Parent_Kind = N_Allocator then
Set_Expansion_Delayed (N);
return;
-- Limited arrays in return statements are expanded when
-- enclosing construct is expanded.
elsif Maybe_In_Place_OK
and then Parent_Kind = N_Simple_Return_Statement
then
Set_Expansion_Delayed (N);
return;
-- In the remaining cases the aggregate is the RHS of an assignment
elsif Maybe_In_Place_OK
and then Safe_Left_Hand_Side (Name (Parent_Node))
then
Tmp := Name (Parent_Node);
if Etype (Tmp) /= Etype (N) then
Apply_Length_Check (N, Etype (Tmp));
if Nkind (N) = N_Raise_Constraint_Error then
-- Static error, nothing further to expand
return;
end if;
end if;
-- If a slice assignment has an aggregate with a single others_choice,
-- the assignment can be done in place even if bounds are not static,
-- by converting it into a loop over the discrete range of the slice.
elsif Maybe_In_Place_OK
and then Nkind (Name (Parent_Node)) = N_Slice
and then Is_Others_Aggregate (N)
then
Tmp := Name (Parent_Node);
-- Set type of aggregate to be type of lhs in assignment, in order
-- to suppress redundant length checks.
Set_Etype (N, Etype (Tmp));
-- Step 5
-- In-place aggregate expansion is not possible
else
Maybe_In_Place_OK := False;
Tmp := Make_Temporary (Loc, 'A', N);
Tmp_Decl :=
Make_Object_Declaration (Loc,
Defining_Identifier => Tmp,
Object_Definition => New_Occurrence_Of (Typ, Loc));
Set_No_Initialization (Tmp_Decl, True);
Set_Warnings_Off (Tmp);
-- If we are within a loop, the temporary will be pushed on the
-- stack at each iteration. If the aggregate is the expression
-- for an allocator, it will be immediately copied to the heap
-- and can be reclaimed at once. We create a transient scope
-- around the aggregate for this purpose.
if Ekind (Current_Scope) = E_Loop
and then Parent_Kind = N_Allocator
then
Establish_Transient_Scope (N, Manage_Sec_Stack => False);
end if;
Insert_Action (N, Tmp_Decl);
end if;
-- Construct and insert the aggregate code. We can safely suppress index
-- checks because this code is guaranteed not to raise CE on index
-- checks. However we should *not* suppress all checks.
declare
Target : Node_Id;
begin
if Nkind (Tmp) = N_Defining_Identifier then
Target := New_Occurrence_Of (Tmp, Loc);
else
if Has_Default_Init_Comps (N)
and then not Maybe_In_Place_OK
then
-- Ada 2005 (AI-287): This case has not been analyzed???
raise Program_Error;
end if;
-- Name in assignment is explicit dereference
Target := New_Copy (Tmp);
end if;
-- If we are to generate an in-place assignment for a declaration or
-- an assignment statement, and the assignment can be done directly
-- by the back end, then do not expand further.
-- ??? We can also do that if in-place expansion is not possible but
-- then we could go into an infinite recursion.
if (In_Place_Assign_OK_For_Declaration or else Maybe_In_Place_OK)
and then not CodePeer_Mode
and then not Modify_Tree_For_C
and then not Possible_Bit_Aligned_Component (Target)
and then not Is_Possibly_Unaligned_Slice (Target)
and then Aggr_Assignment_OK_For_Backend (N)
then
if Maybe_In_Place_OK then
return;
end if;
Aggr_Code :=
New_List (
Make_Assignment_Statement (Loc,
Name => Target,
Expression => New_Copy_Tree (N)));
else
Aggr_Code :=
Build_Array_Aggr_Code (N,
Ctype => Ctyp,
Index => First_Index (Typ),
Into => Target,
Scalar_Comp => Is_Scalar_Type (Ctyp));
end if;
-- Save the last assignment statement associated with the aggregate
-- when building a controlled object. This reference is utilized by
-- the finalization machinery when marking an object as successfully
-- initialized.
if Needs_Finalization (Typ)
and then Is_Entity_Name (Target)
and then Present (Entity (Target))
and then Ekind (Entity (Target)) in E_Constant | E_Variable
then
Set_Last_Aggregate_Assignment (Entity (Target), Last (Aggr_Code));
end if;
end;
-- If the aggregate is the expression in a declaration, the expanded
-- code must be inserted after it. The defining entity might not come
-- from source if this is part of an inlined body, but the declaration
-- itself will.
if Comes_From_Source (Tmp)
or else
(Nkind (Parent (N)) = N_Object_Declaration
and then Comes_From_Source (Parent (N))
and then Tmp = Defining_Entity (Parent (N)))
then
declare
Node_After : constant Node_Id := Next (Parent_Node);
begin
Insert_Actions_After (Parent_Node, Aggr_Code);
if Parent_Kind = N_Object_Declaration then
Collect_Initialization_Statements
(Obj => Tmp, N => Parent_Node, Node_After => Node_After);
end if;
end;
else
Insert_Actions (N, Aggr_Code);
end if;
-- If the aggregate has been assigned in place, remove the original
-- assignment.
if Parent_Kind = N_Assignment_Statement and then Maybe_In_Place_OK then
Rewrite (Parent_Node, Make_Null_Statement (Loc));
-- Or else, if a temporary was created, replace the aggregate with it
elsif Parent_Kind /= N_Object_Declaration
or else Tmp /= Defining_Identifier (Parent_Node)
then
Rewrite (N, New_Occurrence_Of (Tmp, Loc));
Analyze_And_Resolve (N, Typ);
end if;
end Expand_Array_Aggregate;
------------------------
-- Expand_N_Aggregate --
------------------------
procedure Expand_N_Aggregate (N : Node_Id) is
T : constant Entity_Id := Etype (N);
begin
-- Record aggregate case
if Is_Record_Type (T)
and then not Is_Private_Type (T)
then
Expand_Record_Aggregate (N);
elsif Has_Aspect (T, Aspect_Aggregate) then
Expand_Container_Aggregate (N);
-- Array aggregate case
else
-- A special case, if we have a string subtype with bounds 1 .. N,
-- where N is known at compile time, and the aggregate is of the
-- form (others => 'x'), with a single choice and no expressions,
-- and N is less than 80 (an arbitrary limit for now), then replace
-- the aggregate by the equivalent string literal (but do not mark
-- it as static since it is not).
-- Note: this entire circuit is redundant with respect to code in
-- Expand_Array_Aggregate that collapses others choices to positional
-- form, but there are two problems with that circuit:
-- a) It is limited to very small cases due to ill-understood
-- interactions with bootstrapping. That limit is removed by
-- use of the No_Implicit_Loops restriction.
-- b) It incorrectly ends up with the resulting expressions being
-- considered static when they are not. For example, the
-- following test should fail:
-- pragma Restrictions (No_Implicit_Loops);
-- package NonSOthers4 is
-- B : constant String (1 .. 6) := (others => 'A');
-- DH : constant String (1 .. 8) := B & "BB";
-- X : Integer;
-- pragma Export (C, X, Link_Name => DH);
-- end;
-- But it succeeds (DH looks static to pragma Export)
-- To be sorted out ???
if Present (Component_Associations (N)) then
declare
CA : constant Node_Id := First (Component_Associations (N));
MX : constant := 80;
begin
if Nkind (First (Choice_List (CA))) = N_Others_Choice
and then Nkind (Expression (CA)) = N_Character_Literal
and then No (Expressions (N))
then
declare
X : constant Node_Id := First_Index (T);
EC : constant Node_Id := Expression (CA);
CV : constant Uint := Char_Literal_Value (EC);
CC : constant Int := UI_To_Int (CV);
begin
if Nkind (X) = N_Range
and then Compile_Time_Known_Value (Low_Bound (X))
and then Expr_Value (Low_Bound (X)) = 1
and then Compile_Time_Known_Value (High_Bound (X))
then
declare
Hi : constant Uint := Expr_Value (High_Bound (X));
begin
if Hi <= MX then
Start_String;
for J in 1 .. UI_To_Int (Hi) loop
Store_String_Char (Char_Code (CC));
end loop;
Rewrite (N,
Make_String_Literal (Sloc (N),
Strval => End_String));
if CC >= Int (2 ** 16) then
Set_Has_Wide_Wide_Character (N);
elsif CC >= Int (2 ** 8) then
Set_Has_Wide_Character (N);
end if;
Analyze_And_Resolve (N, T);
Set_Is_Static_Expression (N, False);
return;
end if;
end;
end if;
end;
end if;
end;
end if;
-- Not that special case, so normal expansion of array aggregate
Expand_Array_Aggregate (N);
end if;
exception
when RE_Not_Available =>
return;
end Expand_N_Aggregate;
--------------------------------
-- Expand_Container_Aggregate --
--------------------------------
procedure Expand_Container_Aggregate (N : Node_Id) is
Loc : constant Source_Ptr := Sloc (N);
Typ : constant Entity_Id := Etype (N);
Asp : constant Node_Id := Find_Value_Of_Aspect (Typ, Aspect_Aggregate);
Empty_Subp : Node_Id := Empty;
Add_Named_Subp : Node_Id := Empty;
Add_Unnamed_Subp : Node_Id := Empty;
New_Indexed_Subp : Node_Id := Empty;
Assign_Indexed_Subp : Node_Id := Empty;
Aggr_Code : constant List_Id := New_List;
Temp : constant Entity_Id := Make_Temporary (Loc, 'C', N);
Comp : Node_Id;
Decl : Node_Id;
Default : Node_Id;
Init_Stat : Node_Id;
Siz : Int;
function Aggregate_Size return Int;
-- Compute number of entries in aggregate, including choices
-- that cover a range, as well as iterated constructs.
-- Return -1 if the size is not known statically, in which case
-- we allocate a default size for the aggregate.
procedure Expand_Iterated_Component (Comp : Node_Id);
-- Handle iterated_component_association and iterated_Element
-- association by generating a loop over the specified range,
-- given either by a loop parameter specification or an iterator
-- specification.
--------------------
-- Aggregate_Size --
--------------------
function Aggregate_Size return Int is
Comp : Node_Id;
Choice : Node_Id;
Lo, Hi : Node_Id;
Siz : Int := 0;
procedure Add_Range_Size;
-- Compute size of component association given by
-- range or subtype name.
procedure Add_Range_Size is
begin
if Nkind (Lo) = N_Integer_Literal then
Siz := Siz + UI_To_Int (Intval (Hi))
- UI_To_Int (Intval (Lo)) + 1;
end if;
end Add_Range_Size;
begin
if Present (Expressions (N)) then
Siz := List_Length (Expressions (N));
end if;
if Present (Component_Associations (N)) then
Comp := First (Component_Associations (N));
-- If the component is an Iterated_Element_Association
-- it includes an iterator or a loop parameter, possibly
-- with a filter, so we do not attempt to compute its
-- size. Room for future optimization ???
if Nkind (Comp) = N_Iterated_Element_Association then
return -1;
end if;
while Present (Comp) loop
Choice := First (Choice_List (Comp));
while Present (Choice) loop
Analyze (Choice);
if Nkind (Choice) = N_Range then
Lo := Low_Bound (Choice);
Hi := High_Bound (Choice);
if Nkind (Lo) /= N_Integer_Literal
or else Nkind (Hi) /= N_Integer_Literal
then
return -1;
else
Add_Range_Size;
end if;
elsif Is_Entity_Name (Choice)
and then Is_Type (Entity (Choice))
then
Lo := Type_Low_Bound (Entity (Choice));
Hi := Type_High_Bound (Entity (Choice));
if Nkind (Lo) /= N_Integer_Literal
or else Nkind (Hi) /= N_Integer_Literal
then
return -1;
else
Add_Range_Size;
end if;
Rewrite (Choice,
Make_Range (Loc,
New_Copy_Tree (Lo),
New_Copy_Tree (Hi)));
else
-- Single choice (syntax excludes a subtype
-- indication).
Siz := Siz + 1;
end if;
Next (Choice);
end loop;
Next (Comp);
end loop;
end if;
return Siz;
end Aggregate_Size;
-------------------------------
-- Expand_Iterated_Component --
-------------------------------
procedure Expand_Iterated_Component (Comp : Node_Id) is
Expr : constant Node_Id := Expression (Comp);
Key_Expr : Node_Id := Empty;
Loop_Id : Entity_Id;
L_Range : Node_Id;
L_Iteration_Scheme : Node_Id;
Loop_Stat : Node_Id;
Params : List_Id;
Stats : List_Id;
begin
if Nkind (Comp) = N_Iterated_Element_Association then
Key_Expr := Key_Expression (Comp);
-- We create a new entity as loop identifier in all cases,
-- as is done for generated loops elsewhere, as the loop
-- structure has been previously analyzed.
if Present (Iterator_Specification (Comp)) then
-- Either an Iterator_Specification of a Loop_Parameter_
-- Specification is present.
L_Iteration_Scheme :=
Make_Iteration_Scheme (Loc,
Iterator_Specification => Iterator_Specification (Comp));
Loop_Id :=
Make_Defining_Identifier (Loc,
Chars => Chars (Defining_Identifier
(Iterator_Specification (Comp))));
Set_Defining_Identifier
(Iterator_Specification (L_Iteration_Scheme), Loop_Id);
else
L_Iteration_Scheme :=
Make_Iteration_Scheme (Loc,
Loop_Parameter_Specification =>
Loop_Parameter_Specification (Comp));
Loop_Id :=
Make_Defining_Identifier (Loc,
Chars => Chars (Defining_Identifier
(Loop_Parameter_Specification (Comp))));
Set_Defining_Identifier
(Loop_Parameter_Specification
(L_Iteration_Scheme), Loop_Id);
end if;
else
-- Iterated_Component_Association.
Loop_Id :=
Make_Defining_Identifier (Loc,
Chars => Chars (Defining_Identifier (Comp)));
if Present (Iterator_Specification (Comp)) then
L_Iteration_Scheme :=
Make_Iteration_Scheme (Loc,
Iterator_Specification => Iterator_Specification (Comp));
else
-- Loop_Parameter_Specifcation is parsed with a choice list.
-- where the range is the first (and only) choice.
L_Range := Relocate_Node (First (Discrete_Choices (Comp)));
L_Iteration_Scheme :=
Make_Iteration_Scheme (Loc,
Loop_Parameter_Specification =>
Make_Loop_Parameter_Specification (Loc,
Defining_Identifier => Loop_Id,
Discrete_Subtype_Definition => L_Range));
end if;
end if;
-- Build insertion statement. For a positional aggregate, only the
-- expression is needed. For a named aggregate, the loop variable,
-- whose type is that of the key, is an additional parameter for
-- the insertion operation.
-- If a Key_Expression is present, it serves as the additional
-- parameter. Otherwise the key is given by the loop parameter
-- itself.
if Present (Add_Unnamed_Subp) then
Stats := New_List
(Make_Procedure_Call_Statement (Loc,
Name => New_Occurrence_Of (Entity (Add_Unnamed_Subp), Loc),
Parameter_Associations =>
New_List (New_Occurrence_Of (Temp, Loc),
New_Copy_Tree (Expr))));
else
-- Named or indexed aggregate, for which a key is present,
-- possibly with a specified key_expression.
if Present (Key_Expr) then
Params := New_List (New_Occurrence_Of (Temp, Loc),
New_Copy_Tree (Key_Expr),
New_Copy_Tree (Expr));
else
Params := New_List (New_Occurrence_Of (Temp, Loc),
New_Occurrence_Of (Loop_Id, Loc),
New_Copy_Tree (Expr));
end if;
Stats := New_List
(Make_Procedure_Call_Statement (Loc,
Name => New_Occurrence_Of (Entity (Add_Named_Subp), Loc),
Parameter_Associations => Params));
end if;
Loop_Stat := Make_Implicit_Loop_Statement
(Node => N,
Identifier => Empty,
Iteration_Scheme => L_Iteration_Scheme,
Statements => Stats);
Append (Loop_Stat, Aggr_Code);
end Expand_Iterated_Component;
-- Start of processing for Expand_Container_Aggregate
begin
Parse_Aspect_Aggregate (Asp,
Empty_Subp, Add_Named_Subp, Add_Unnamed_Subp,
New_Indexed_Subp, Assign_Indexed_Subp);
-- The constructor for bounded containers is a function with
-- a parameter that sets the size of the container. If the
-- size cannot be determined statically we use a default value.
Siz := Aggregate_Size;
if Siz < 0 then
Siz := 10;
end if;
if Ekind (Entity (Empty_Subp)) = E_Function
and then Present (First_Formal (Entity (Empty_Subp)))
then
Default := Default_Value (First_Formal (Entity (Empty_Subp)));
-- If aggregate size is not static, use default value of
-- formal parameter for allocation. We assume that this
-- (implementation-dependent) value is static, even though
-- the AI does not require it ???.
if Siz < 0 then
Siz := UI_To_Int (Intval (Default));
end if;
Init_Stat :=
Make_Object_Declaration (Loc,
Defining_Identifier => Temp,
Object_Definition => New_Occurrence_Of (Typ, Loc),
Expression => Make_Function_Call (Loc,
Name => New_Occurrence_Of (Entity (Empty_Subp), Loc),
Parameter_Associations =>
New_List (Make_Integer_Literal (Loc, Siz))));
Append (Init_Stat, Aggr_Code);
-- Use default value when aggregate size is not static.
else
Decl :=
Make_Object_Declaration (Loc,
Defining_Identifier => Temp,
Object_Definition => New_Occurrence_Of (Typ, Loc));
Insert_Action (N, Decl);
if Ekind (Entity (Empty_Subp)) = E_Function then
Init_Stat := Make_Assignment_Statement (Loc,
Name => New_Occurrence_Of (Temp, Loc),
Expression => Make_Function_Call (Loc,
Name => New_Occurrence_Of (Entity (Empty_Subp), Loc)));
else
Init_Stat := Make_Assignment_Statement (Loc,
Name => New_Occurrence_Of (Temp, Loc),
Expression => New_Occurrence_Of (Entity (Empty_Subp), Loc));
end if;
Append (Init_Stat, Aggr_Code);
end if;
---------------------------
-- Positional aggregate --
---------------------------
-- If the aggregate is positional the aspect must include
-- an Add_Unnamed subprogram.
if Present (Add_Unnamed_Subp)
and then No (Component_Associations (N))
then
if Present (Expressions (N)) then
declare
Insert : constant Entity_Id := Entity (Add_Unnamed_Subp);
Comp : Node_Id;
Stat : Node_Id;
begin
Comp := First (Expressions (N));
while Present (Comp) loop
Stat := Make_Procedure_Call_Statement (Loc,
Name => New_Occurrence_Of (Insert, Loc),
Parameter_Associations =>
New_List (New_Occurrence_Of (Temp, Loc),
New_Copy_Tree (Comp)));
Append (Stat, Aggr_Code);
Next (Comp);
end loop;
end;
end if;
-- Iterated component associations may also be present.
Comp := First (Component_Associations (N));
while Present (Comp) loop
Expand_Iterated_Component (Comp);
Next (Comp);
end loop;
---------------------
-- Named_Aggregate --
---------------------
elsif Present (Add_Named_Subp) then
declare
Insert : constant Entity_Id := Entity (Add_Named_Subp);
Stat : Node_Id;
Key : Node_Id;
begin
Comp := First (Component_Associations (N));
-- Each component association may contain several choices;
-- generate an insertion statement for each.
while Present (Comp) loop
if Nkind (Comp) in N_Iterated_Component_Association
| N_Iterated_Element_Association
then
Expand_Iterated_Component (Comp);
else
Key := First (Choices (Comp));
while Present (Key) loop
Stat := Make_Procedure_Call_Statement (Loc,
Name => New_Occurrence_Of (Insert, Loc),
Parameter_Associations =>
New_List (New_Occurrence_Of (Temp, Loc),
New_Copy_Tree (Key),
New_Copy_Tree (Expression (Comp))));
Append (Stat, Aggr_Code);
Next (Key);
end loop;
end if;
Next (Comp);
end loop;
end;
end if;
-----------------------
-- Indexed_Aggregate --
-----------------------
-- For an indexed aggregate there must be an Assigned_Indexeed
-- subprogram. Note that unlike array aggregates, a container
-- aggregate must be fully positional or fully indexed. In the
-- first case the expansion has already taken place.
if Present (Assign_Indexed_Subp)
and then Present (Component_Associations (N))
then
declare
Insert : constant Entity_Id := Entity (Assign_Indexed_Subp);
Index_Type : constant Entity_Id :=
Etype (Next_Formal (First_Formal (Insert)));
function Expand_Range_Component
(Rng : Node_Id;
Expr : Node_Id) return Node_Id;
-- Transform a component assoication with a range into an
-- explicit loop. If the choice is a subtype name, it is
-- rewritten as a range with the corresponding bounds, which
-- are known to be static.
Comp : Node_Id;
Index : Node_Id;
Pos : Int := 0;
Stat : Node_Id;
Key : Node_Id;
-----------------------------
-- Expand_Raange_Component --
-----------------------------
function Expand_Range_Component
(Rng : Node_Id;
Expr : Node_Id) return Node_Id
is
Loop_Id : constant Entity_Id :=
Make_Temporary (Loc, 'T');
L_Iteration_Scheme : Node_Id;
Stats : List_Id;
begin
L_Iteration_Scheme :=
Make_Iteration_Scheme (Loc,
Loop_Parameter_Specification =>
Make_Loop_Parameter_Specification (Loc,
Defining_Identifier => Loop_Id,
Discrete_Subtype_Definition => New_Copy_Tree (Rng)));
Stats := New_List
(Make_Procedure_Call_Statement (Loc,
Name =>
New_Occurrence_Of (Entity (Assign_Indexed_Subp), Loc),
Parameter_Associations =>
New_List (New_Occurrence_Of (Temp, Loc),
New_Occurrence_Of (Loop_Id, Loc),
New_Copy_Tree (Expr))));
return Make_Implicit_Loop_Statement
(Node => N,
Identifier => Empty,
Iteration_Scheme => L_Iteration_Scheme,
Statements => Stats);
end Expand_Range_Component;
begin
if Siz > 0 then
-- Modify the call to the constructor to allocate the
-- required size for the aggregwte : call the provided
-- constructor rather than the Empty aggregate.
Index := Make_Op_Add (Loc,
Left_Opnd => New_Copy_Tree (Type_Low_Bound (Index_Type)),
Right_Opnd => Make_Integer_Literal (Loc, Siz - 1));
Set_Expression (Init_Stat,
Make_Function_Call (Loc,
Name =>
New_Occurrence_Of (Entity (New_Indexed_Subp), Loc),
Parameter_Associations =>
New_List (
New_Copy_Tree (Type_Low_Bound (Index_Type)),
Index)));
end if;
if Present (Expressions (N)) then
Comp := First (Expressions (N));
while Present (Comp) loop
-- Compute index position for successive components
-- in the list of expressions, and use the indexed
-- assignment procedure for each.
Index := Make_Op_Add (Loc,
Left_Opnd => Type_Low_Bound (Index_Type),
Right_Opnd => Make_Integer_Literal (Loc, Pos));
Stat := Make_Procedure_Call_Statement (Loc,
Name => New_Occurrence_Of (Insert, Loc),
Parameter_Associations =>
New_List (New_Occurrence_Of (Temp, Loc),
Index,
New_Copy_Tree (Comp)));
Pos := Pos + 1;
Append (Stat, Aggr_Code);
Next (Comp);
end loop;
end if;
if Present (Component_Associations (N)) then
Comp := First (Component_Associations (N));
-- The choice may be a static value, or a range with
-- static bounds.
while Present (Comp) loop
if Nkind (Comp) = N_Component_Association then
Key := First (Choices (Comp));
while Present (Key) loop
-- If the expression is a box, the corresponding
-- component (s) is left uninitialized.
if Box_Present (Comp) then
goto Next_Key;
elsif Nkind (Key) = N_Range then
-- Create loop for tne specified range,
-- with copies of the expression.
Stat :=
Expand_Range_Component (Key, Expression (Comp));
else
Stat := Make_Procedure_Call_Statement (Loc,
Name => New_Occurrence_Of
(Entity (Assign_Indexed_Subp), Loc),
Parameter_Associations =>
New_List (New_Occurrence_Of (Temp, Loc),
New_Copy_Tree (Key),
New_Copy_Tree (Expression (Comp))));
end if;
Append (Stat, Aggr_Code);
<<Next_Key>>
Next (Key);
end loop;
else
-- Iterated component association. Discard
-- positional insertion procedure.
Add_Named_Subp := Assign_Indexed_Subp;
Add_Unnamed_Subp := Empty;
Expand_Iterated_Component (Comp);
end if;
Next (Comp);
end loop;
end if;
end;
end if;
Insert_Actions (N, Aggr_Code);
Rewrite (N, New_Occurrence_Of (Temp, Loc));
Analyze_And_Resolve (N, Typ);
end Expand_Container_Aggregate;
------------------------------
-- Expand_N_Delta_Aggregate --
------------------------------
procedure Expand_N_Delta_Aggregate (N : Node_Id) is
Loc : constant Source_Ptr := Sloc (N);
Typ : constant Entity_Id := Etype (Expression (N));
Decl : Node_Id;
begin
Decl :=
Make_Object_Declaration (Loc,
Defining_Identifier => Make_Temporary (Loc, 'T'),
Object_Definition => New_Occurrence_Of (Typ, Loc),
Expression => New_Copy_Tree (Expression (N)));
if Is_Array_Type (Etype (N)) then
Expand_Delta_Array_Aggregate (N, New_List (Decl));
else
Expand_Delta_Record_Aggregate (N, New_List (Decl));
end if;
end Expand_N_Delta_Aggregate;
----------------------------------
-- Expand_Delta_Array_Aggregate --
----------------------------------
procedure Expand_Delta_Array_Aggregate (N : Node_Id; Deltas : List_Id) is
Loc : constant Source_Ptr := Sloc (N);
Temp : constant Entity_Id := Defining_Identifier (First (Deltas));
Assoc : Node_Id;
function Generate_Loop (C : Node_Id) return Node_Id;
-- Generate a loop containing individual component assignments for
-- choices that are ranges, subtype indications, subtype names, and
-- iterated component associations.
-------------------
-- Generate_Loop --
-------------------
function Generate_Loop (C : Node_Id) return Node_Id is
Sl : constant Source_Ptr := Sloc (C);
Ix : Entity_Id;
begin
if Nkind (Parent (C)) = N_Iterated_Component_Association then
Ix :=
Make_Defining_Identifier (Loc,
Chars => (Chars (Defining_Identifier (Parent (C)))));
else
Ix := Make_Temporary (Sl, 'I');
end if;
return
Make_Loop_Statement (Loc,
Iteration_Scheme =>
Make_Iteration_Scheme (Sl,
Loop_Parameter_Specification =>
Make_Loop_Parameter_Specification (Sl,
Defining_Identifier => Ix,
Discrete_Subtype_Definition => New_Copy_Tree (C))),
Statements => New_List (
Make_Assignment_Statement (Sl,
Name =>
Make_Indexed_Component (Sl,
Prefix => New_Occurrence_Of (Temp, Sl),
Expressions => New_List (New_Occurrence_Of (Ix, Sl))),
Expression => New_Copy_Tree (Expression (Assoc)))),
End_Label => Empty);
end Generate_Loop;
-- Local variables
Choice : Node_Id;
-- Start of processing for Expand_Delta_Array_Aggregate
begin
Assoc := First (Component_Associations (N));
while Present (Assoc) loop
Choice := First (Choice_List (Assoc));
if Nkind (Assoc) = N_Iterated_Component_Association then
while Present (Choice) loop
Append_To (Deltas, Generate_Loop (Choice));
Next (Choice);
end loop;
else
while Present (Choice) loop
-- Choice can be given by a range, a subtype indication, a
-- subtype name, a scalar value, or an entity.
if Nkind (Choice) = N_Range
or else (Is_Entity_Name (Choice)
and then Is_Type (Entity (Choice)))
then
Append_To (Deltas, Generate_Loop (Choice));
elsif Nkind (Choice) = N_Subtype_Indication then
Append_To (Deltas,
Generate_Loop (Range_Expression (Constraint (Choice))));
else
Append_To (Deltas,
Make_Assignment_Statement (Sloc (Choice),
Name =>
Make_Indexed_Component (Sloc (Choice),
Prefix => New_Occurrence_Of (Temp, Loc),
Expressions => New_List (New_Copy_Tree (Choice))),
Expression => New_Copy_Tree (Expression (Assoc))));
end if;
Next (Choice);
end loop;
end if;
Next (Assoc);
end loop;
Insert_Actions (N, Deltas);
Rewrite (N, New_Occurrence_Of (Temp, Loc));
end Expand_Delta_Array_Aggregate;
-----------------------------------
-- Expand_Delta_Record_Aggregate --
-----------------------------------
procedure Expand_Delta_Record_Aggregate (N : Node_Id; Deltas : List_Id) is
Loc : constant Source_Ptr := Sloc (N);
Temp : constant Entity_Id := Defining_Identifier (First (Deltas));
Assoc : Node_Id;
Choice : Node_Id;
begin
Assoc := First (Component_Associations (N));
while Present (Assoc) loop
Choice := First (Choice_List (Assoc));
while Present (Choice) loop
Append_To (Deltas,
Make_Assignment_Statement (Sloc (Choice),
Name =>
Make_Selected_Component (Sloc (Choice),
Prefix => New_Occurrence_Of (Temp, Loc),
Selector_Name => Make_Identifier (Loc, Chars (Choice))),
Expression => New_Copy_Tree (Expression (Assoc))));
Next (Choice);
end loop;
Next (Assoc);
end loop;
Insert_Actions (N, Deltas);
Rewrite (N, New_Occurrence_Of (Temp, Loc));
end Expand_Delta_Record_Aggregate;
----------------------------------
-- Expand_N_Extension_Aggregate --
----------------------------------
-- If the ancestor part is an expression, add a component association for
-- the parent field. If the type of the ancestor part is not the direct
-- parent of the expected type, build recursively the needed ancestors.
-- If the ancestor part is a subtype_mark, replace aggregate with a
-- declaration for a temporary of the expected type, followed by
-- individual assignments to the given components.
procedure Expand_N_Extension_Aggregate (N : Node_Id) is
A : constant Node_Id := Ancestor_Part (N);
Loc : constant Source_Ptr := Sloc (N);
Typ : constant Entity_Id := Etype (N);
begin
-- If the ancestor is a subtype mark, an init proc must be called
-- on the resulting object which thus has to be materialized in
-- the front-end
if Is_Entity_Name (A) and then Is_Type (Entity (A)) then
Convert_To_Assignments (N, Typ);
-- The extension aggregate is transformed into a record aggregate
-- of the following form (c1 and c2 are inherited components)
-- (Exp with c3 => a, c4 => b)
-- ==> (c1 => Exp.c1, c2 => Exp.c2, c3 => a, c4 => b)
else
Set_Etype (N, Typ);
if Tagged_Type_Expansion then
Expand_Record_Aggregate (N,
Orig_Tag =>
New_Occurrence_Of
(Node (First_Elmt (Access_Disp_Table (Typ))), Loc),
Parent_Expr => A);
-- No tag is needed in the case of a VM
else
Expand_Record_Aggregate (N, Parent_Expr => A);
end if;
end if;
exception
when RE_Not_Available =>
return;
end Expand_N_Extension_Aggregate;
-----------------------------
-- Expand_Record_Aggregate --
-----------------------------
procedure Expand_Record_Aggregate
(N : Node_Id;
Orig_Tag : Node_Id := Empty;
Parent_Expr : Node_Id := Empty)
is
Loc : constant Source_Ptr := Sloc (N);
Comps : constant List_Id := Component_Associations (N);
Typ : constant Entity_Id := Etype (N);
Base_Typ : constant Entity_Id := Base_Type (Typ);
Static_Components : Boolean := True;
-- Flag to indicate whether all components are compile-time known,
-- and the aggregate can be constructed statically and handled by
-- the back-end. Set to False by Component_OK_For_Backend.
procedure Build_Back_End_Aggregate;
-- Build a proper aggregate to be handled by the back-end
function Compile_Time_Known_Composite_Value (N : Node_Id) return Boolean;
-- Returns true if N is an expression of composite type which can be
-- fully evaluated at compile time without raising constraint error.
-- Such expressions can be passed as is to Gigi without any expansion.
--
-- This returns true for N_Aggregate with Compile_Time_Known_Aggregate
-- set and constants whose expression is such an aggregate, recursively.
function Component_OK_For_Backend return Boolean;
-- Check for presence of a component which makes it impossible for the
-- backend to process the aggregate, thus requiring the use of a series
-- of assignment statements. Cases checked for are a nested aggregate
-- needing Late_Expansion, the presence of a tagged component which may
-- need tag adjustment, and a bit unaligned component reference.
--
-- We also force expansion into assignments if a component is of a
-- mutable type (including a private type with discriminants) because
-- in that case the size of the component to be copied may be smaller
-- than the side of the target, and there is no simple way for gigi
-- to compute the size of the object to be copied.
--
-- NOTE: This is part of the ongoing work to define precisely the
-- interface between front-end and back-end handling of aggregates.
-- In general it is desirable to pass aggregates as they are to gigi,
-- in order to minimize elaboration code. This is one case where the
-- semantics of Ada complicate the analysis and lead to anomalies in
-- the gcc back-end if the aggregate is not expanded into assignments.
--
-- NOTE: This sets the global Static_Components to False in most, but
-- not all, cases when it returns False.
function Has_Per_Object_Constraint (L : List_Id) return Boolean;
-- Return True if any element of L has Has_Per_Object_Constraint set.
-- L should be the Choices component of an N_Component_Association.
function Has_Visible_Private_Ancestor (Id : E) return Boolean;
-- If any ancestor of the current type is private, the aggregate
-- cannot be built in place. We cannot rely on Has_Private_Ancestor,
-- because it will not be set when type and its parent are in the
-- same scope, and the parent component needs expansion.
function Top_Level_Aggregate (N : Node_Id) return Node_Id;
-- For nested aggregates return the ultimate enclosing aggregate; for
-- non-nested aggregates return N.
------------------------------
-- Build_Back_End_Aggregate --
------------------------------
procedure Build_Back_End_Aggregate is
Comp : Entity_Id;
New_Comp : Node_Id;
Tag_Value : Node_Id;
begin
if Nkind (N) = N_Aggregate then
-- If the aggregate is static and can be handled by the back-end,
-- nothing left to do.
if Static_Components then
Set_Compile_Time_Known_Aggregate (N);
Set_Expansion_Delayed (N, False);
end if;
end if;
-- If no discriminants, nothing special to do
if not Has_Discriminants (Typ) then
null;
-- Case of discriminants present
elsif Is_Derived_Type (Typ) then
-- For untagged types, non-stored discriminants are replaced with
-- stored discriminants, which are the ones that gigi uses to
-- describe the type and its components.
Generate_Aggregate_For_Derived_Type : declare
procedure Prepend_Stored_Values (T : Entity_Id);
-- Scan the list of stored discriminants of the type, and add
-- their values to the aggregate being built.
---------------------------
-- Prepend_Stored_Values --
---------------------------
procedure Prepend_Stored_Values (T : Entity_Id) is
Discr : Entity_Id;
First_Comp : Node_Id := Empty;
begin
Discr := First_Stored_Discriminant (T);
while Present (Discr) loop
New_Comp :=
Make_Component_Association (Loc,
Choices => New_List (
New_Occurrence_Of (Discr, Loc)),
Expression =>
New_Copy_Tree
(Get_Discriminant_Value
(Discr,
Typ,
Discriminant_Constraint (Typ))));
if No (First_Comp) then
Prepend_To (Component_Associations (N), New_Comp);
else
Insert_After (First_Comp, New_Comp);
end if;
First_Comp := New_Comp;
Next_Stored_Discriminant (Discr);
end loop;
end Prepend_Stored_Values;
-- Local variables
Constraints : constant List_Id := New_List;
Discr : Entity_Id;
Decl : Node_Id;
Num_Disc : Nat := 0;
Num_Gird : Nat := 0;
-- Start of processing for Generate_Aggregate_For_Derived_Type
begin
-- Remove the associations for the discriminant of derived type
declare
First_Comp : Node_Id;
begin
First_Comp := First (Component_Associations (N));
while Present (First_Comp) loop
Comp := First_Comp;
Next (First_Comp);
if Ekind (Entity (First (Choices (Comp)))) =
E_Discriminant
then
Remove (Comp);
Num_Disc := Num_Disc + 1;
end if;
end loop;
end;
-- Insert stored discriminant associations in the correct
-- order. If there are more stored discriminants than new
-- discriminants, there is at least one new discriminant that
-- constrains more than one of the stored discriminants. In
-- this case we need to construct a proper subtype of the
-- parent type, in order to supply values to all the
-- components. Otherwise there is one-one correspondence
-- between the constraints and the stored discriminants.
Discr := First_Stored_Discriminant (Base_Type (Typ));
while Present (Discr) loop
Num_Gird := Num_Gird + 1;
Next_Stored_Discriminant (Discr);
end loop;
-- Case of more stored discriminants than new discriminants
if Num_Gird > Num_Disc then
-- Create a proper subtype of the parent type, which is the
-- proper implementation type for the aggregate, and convert
-- it to the intended target type.
Discr := First_Stored_Discriminant (Base_Type (Typ));
while Present (Discr) loop
New_Comp :=
New_Copy_Tree
(Get_Discriminant_Value
(Discr,
Typ,
Discriminant_Constraint (Typ)));
Append (New_Comp, Constraints);
Next_Stored_Discriminant (Discr);
end loop;
Decl :=
Make_Subtype_Declaration (Loc,
Defining_Identifier => Make_Temporary (Loc, 'T'),
Subtype_Indication =>
Make_Subtype_Indication (Loc,
Subtype_Mark =>
New_Occurrence_Of (Etype (Base_Type (Typ)), Loc),
Constraint =>
Make_Index_Or_Discriminant_Constraint
(Loc, Constraints)));
Insert_Action (N, Decl);
Prepend_Stored_Values (Base_Type (Typ));
Set_Etype (N, Defining_Identifier (Decl));
Set_Analyzed (N);
Rewrite (N, Unchecked_Convert_To (Typ, N));
Analyze (N);
-- Case where we do not have fewer new discriminants than
-- stored discriminants, so in this case we can simply use the
-- stored discriminants of the subtype.
else
Prepend_Stored_Values (Typ);
end if;
end Generate_Aggregate_For_Derived_Type;
end if;
if Is_Tagged_Type (Typ) then
-- In the tagged case, _parent and _tag component must be created
-- Reset Null_Present unconditionally. Tagged records always have
-- at least one field (the tag or the parent).
Set_Null_Record_Present (N, False);
-- When the current aggregate comes from the expansion of an
-- extension aggregate, the parent expr is replaced by an
-- aggregate formed by selected components of this expr.
if Present (Parent_Expr) and then Is_Empty_List (Comps) then
Comp := First_Component_Or_Discriminant (Typ);
while Present (Comp) loop
-- Skip all expander-generated components
if not Comes_From_Source (Original_Record_Component (Comp))
then
null;
else
New_Comp :=
Make_Selected_Component (Loc,
Prefix =>
Unchecked_Convert_To (Typ,
Duplicate_Subexpr (Parent_Expr, True)),
Selector_Name => New_Occurrence_Of (Comp, Loc));
Append_To (Comps,
Make_Component_Association (Loc,
Choices => New_List (
New_Occurrence_Of (Comp, Loc)),
Expression => New_Comp));
Analyze_And_Resolve (New_Comp, Etype (Comp));
end if;
Next_Component_Or_Discriminant (Comp);
end loop;
end if;
-- Compute the value for the Tag now, if the type is a root it
-- will be included in the aggregate right away, otherwise it will
-- be propagated to the parent aggregate.
if Present (Orig_Tag) then
Tag_Value := Orig_Tag;
elsif not Tagged_Type_Expansion then
Tag_Value := Empty;
else
Tag_Value :=
New_Occurrence_Of
(Node (First_Elmt (Access_Disp_Table (Typ))), Loc);
end if;
-- For a derived type, an aggregate for the parent is formed with
-- all the inherited components.
if Is_Derived_Type (Typ) then
declare
First_Comp : Node_Id;
Parent_Comps : List_Id;
Parent_Aggr : Node_Id;
Parent_Name : Node_Id;
begin
-- Remove the inherited component association from the
-- aggregate and store them in the parent aggregate
First_Comp := First (Component_Associations (N));
Parent_Comps := New_List;
while Present (First_Comp)
and then
Scope (Original_Record_Component
(Entity (First (Choices (First_Comp))))) /=
Base_Typ
loop
Comp := First_Comp;
Next (First_Comp);
Remove (Comp);
Append (Comp, Parent_Comps);
end loop;
Parent_Aggr :=
Make_Aggregate (Loc,
Component_Associations => Parent_Comps);
Set_Etype (Parent_Aggr, Etype (Base_Type (Typ)));
-- Find the _parent component
Comp := First_Component (Typ);
while Chars (Comp) /= Name_uParent loop
Next_Component (Comp);
end loop;
Parent_Name := New_Occurrence_Of (Comp, Loc);
-- Insert the parent aggregate
Prepend_To (Component_Associations (N),
Make_Component_Association (Loc,
Choices => New_List (Parent_Name),
Expression => Parent_Aggr));
-- Expand recursively the parent propagating the right Tag
Expand_Record_Aggregate
(Parent_Aggr, Tag_Value, Parent_Expr);
-- The ancestor part may be a nested aggregate that has
-- delayed expansion: recheck now.
if not Component_OK_For_Backend then
Convert_To_Assignments (N, Typ);
end if;
end;
-- For a root type, the tag component is added (unless compiling
-- for the VMs, where tags are implicit).
elsif Tagged_Type_Expansion then
declare
Tag_Name : constant Node_Id :=
New_Occurrence_Of
(First_Tag_Component (Typ), Loc);
Typ_Tag : constant Entity_Id := RTE (RE_Tag);
Conv_Node : constant Node_Id :=
Unchecked_Convert_To (Typ_Tag, Tag_Value);
begin
Set_Etype (Conv_Node, Typ_Tag);
Prepend_To (Component_Associations (N),
Make_Component_Association (Loc,
Choices => New_List (Tag_Name),
Expression => Conv_Node));
end;
end if;
end if;
end Build_Back_End_Aggregate;
----------------------------------------
-- Compile_Time_Known_Composite_Value --
----------------------------------------
function Compile_Time_Known_Composite_Value
(N : Node_Id) return Boolean
is
begin
-- If we have an entity name, then see if it is the name of a
-- constant and if so, test the corresponding constant value.
if Is_Entity_Name (N) then
declare
E : constant Entity_Id := Entity (N);
V : Node_Id;
begin
if Ekind (E) /= E_Constant then
return False;
else
V := Constant_Value (E);
return Present (V)
and then Compile_Time_Known_Composite_Value (V);
end if;
end;
-- We have a value, see if it is compile time known
else
if Nkind (N) = N_Aggregate then
return Compile_Time_Known_Aggregate (N);
end if;
-- All other types of values are not known at compile time
return False;
end if;
end Compile_Time_Known_Composite_Value;
------------------------------
-- Component_OK_For_Backend --
------------------------------
function Component_OK_For_Backend return Boolean is
C : Node_Id;
Expr_Q : Node_Id;
begin
if No (Comps) then
return True;
end if;
C := First (Comps);
while Present (C) loop
-- If the component has box initialization, expansion is needed
-- and component is not ready for backend.
if Box_Present (C) then
return False;
end if;
if Nkind (Expression (C)) = N_Qualified_Expression then
Expr_Q := Expression (Expression (C));
else
Expr_Q := Expression (C);
end if;
-- Return False for array components whose bounds raise
-- constraint error.
declare
Comp : constant Entity_Id := First (Choices (C));
Indx : Node_Id;
begin
if Present (Etype (Comp))
and then Is_Array_Type (Etype (Comp))
then
Indx := First_Index (Etype (Comp));
while Present (Indx) loop
if Nkind (Type_Low_Bound (Etype (Indx))) =
N_Raise_Constraint_Error
or else Nkind (Type_High_Bound (Etype (Indx))) =
N_Raise_Constraint_Error
then
return False;
end if;
Next_Index (Indx);
end loop;
end if;
end;
-- Return False if the aggregate has any associations for tagged
-- components that may require tag adjustment.
-- These are cases where the source expression may have a tag that
-- could differ from the component tag (e.g., can occur for type
-- conversions and formal parameters). (Tag adjustment not needed
-- if Tagged_Type_Expansion because object tags are implicit in
-- the machine.)
if Is_Tagged_Type (Etype (Expr_Q))
and then
(Nkind (Expr_Q) = N_Type_Conversion
or else
(Is_Entity_Name (Expr_Q)
and then Is_Formal (Entity (Expr_Q))))
and then Tagged_Type_Expansion
then
Static_Components := False;
return False;
elsif Is_Delayed_Aggregate (Expr_Q) then
Static_Components := False;
return False;
elsif Nkind (Expr_Q) = N_Quantified_Expression then
Static_Components := False;
return False;
elsif Possible_Bit_Aligned_Component (Expr_Q) then
Static_Components := False;
return False;
elsif Modify_Tree_For_C
and then Nkind (C) = N_Component_Association
and then Has_Per_Object_Constraint (Choices (C))
then
Static_Components := False;
return False;
elsif Modify_Tree_For_C
and then Nkind (Expr_Q) = N_Identifier
and then Is_Array_Type (Etype (Expr_Q))
then
Static_Components := False;
return False;
elsif Modify_Tree_For_C
and then Nkind (Expr_Q) = N_Type_Conversion
and then Is_Array_Type (Etype (Expr_Q))
then
Static_Components := False;
return False;
end if;
if Is_Elementary_Type (Etype (Expr_Q)) then
if not Compile_Time_Known_Value (Expr_Q) then
Static_Components := False;
end if;
elsif not Compile_Time_Known_Composite_Value (Expr_Q) then
Static_Components := False;
if Is_Private_Type (Etype (Expr_Q))
and then Has_Discriminants (Etype (Expr_Q))
then
return False;
end if;
end if;
Next (C);
end loop;
return True;
end Component_OK_For_Backend;
-------------------------------
-- Has_Per_Object_Constraint --
-------------------------------
function Has_Per_Object_Constraint (L : List_Id) return Boolean is
N : Node_Id := First (L);
begin
while Present (N) loop
if Is_Entity_Name (N)
and then Present (Entity (N))
and then Has_Per_Object_Constraint (Entity (N))
then
return True;
end if;
Next (N);
end loop;
return False;
end Has_Per_Object_Constraint;
-----------------------------------
-- Has_Visible_Private_Ancestor --
-----------------------------------
function Has_Visible_Private_Ancestor (Id : E) return Boolean is
R : constant Entity_Id := Root_Type (Id);
T1 : Entity_Id := Id;
begin
loop
if Is_Private_Type (T1) then
return True;
elsif T1 = R then
return False;
else
T1 := Etype (T1);
end if;
end loop;
end Has_Visible_Private_Ancestor;
-------------------------
-- Top_Level_Aggregate --
-------------------------
function Top_Level_Aggregate (N : Node_Id) return Node_Id is
Aggr : Node_Id;
begin
Aggr := N;
while Present (Parent (Aggr))
and then Nkind (Parent (Aggr)) in
N_Aggregate | N_Component_Association
loop
Aggr := Parent (Aggr);
end loop;
return Aggr;
end Top_Level_Aggregate;
-- Local variables
Top_Level_Aggr : constant Node_Id := Top_Level_Aggregate (N);
-- Start of processing for Expand_Record_Aggregate
begin
-- If the aggregate is to be assigned to a full access variable, we have
-- to prevent a piecemeal assignment even if the aggregate is to be
-- expanded. We create a temporary for the aggregate, and assign the
-- temporary instead, so that the back end can generate an atomic move
-- for it.
if Is_Full_Access_Aggregate (N) then
return;
-- No special management required for aggregates used to initialize
-- statically allocated dispatch tables
elsif Is_Static_Dispatch_Table_Aggregate (N) then
return;
end if;
-- If the pragma Aggregate_Individually_Assign is set, always convert to
-- assignments.
if Aggregate_Individually_Assign then
Convert_To_Assignments (N, Typ);
-- Ada 2005 (AI-318-2): We need to convert to assignments if components
-- are build-in-place function calls. The assignments will each turn
-- into a build-in-place function call. If components are all static,
-- we can pass the aggregate to the back end regardless of limitedness.
-- Extension aggregates, aggregates in extended return statements, and
-- aggregates for C++ imported types must be expanded.
elsif Ada_Version >= Ada_2005 and then Is_Limited_View (Typ) then
if Nkind (Parent (N)) not in
N_Component_Association | N_Object_Declaration
then
Convert_To_Assignments (N, Typ);
elsif Nkind (N) = N_Extension_Aggregate
or else Convention (Typ) = Convention_CPP
then
Convert_To_Assignments (N, Typ);
elsif not Size_Known_At_Compile_Time (Typ)
or else not Component_OK_For_Backend
or else not Static_Components
then
Convert_To_Assignments (N, Typ);
-- In all other cases, build a proper aggregate to be handled by
-- the back-end.
else
Build_Back_End_Aggregate;
end if;
-- Gigi doesn't properly handle temporaries of variable size so we
-- generate it in the front-end
elsif not Size_Known_At_Compile_Time (Typ)
and then Tagged_Type_Expansion
then
Convert_To_Assignments (N, Typ);
-- An aggregate used to initialize a controlled object must be turned
-- into component assignments as the components themselves may require
-- finalization actions such as adjustment.
elsif Needs_Finalization (Typ) then
Convert_To_Assignments (N, Typ);
-- Ada 2005 (AI-287): In case of default initialized components we
-- convert the aggregate into assignments.
elsif Has_Default_Init_Comps (N) then
Convert_To_Assignments (N, Typ);
-- Check components
elsif not Component_OK_For_Backend then
Convert_To_Assignments (N, Typ);
-- If an ancestor is private, some components are not inherited and we
-- cannot expand into a record aggregate.
elsif Has_Visible_Private_Ancestor (Typ) then
Convert_To_Assignments (N, Typ);
-- ??? The following was done to compile fxacc00.ads in the ACVCs. Gigi
-- is not able to handle the aggregate for Late_Request.
elsif Is_Tagged_Type (Typ) and then Has_Discriminants (Typ) then
Convert_To_Assignments (N, Typ);
-- If the tagged types covers interface types we need to initialize all
-- hidden components containing pointers to secondary dispatch tables.
elsif Is_Tagged_Type (Typ) and then Has_Interfaces (Typ) then
Convert_To_Assignments (N, Typ);
-- If some components are mutable, the size of the aggregate component
-- may be distinct from the default size of the type component, so
-- we need to expand to insure that the back-end copies the proper
-- size of the data. However, if the aggregate is the initial value of
-- a constant, the target is immutable and might be built statically
-- if components are appropriate.
elsif Has_Mutable_Components (Typ)
and then
(Nkind (Parent (Top_Level_Aggr)) /= N_Object_Declaration
or else not Constant_Present (Parent (Top_Level_Aggr))
or else not Static_Components)
then
Convert_To_Assignments (N, Typ);
-- If the type involved has bit aligned components, then we are not sure
-- that the back end can handle this case correctly.
elsif Type_May_Have_Bit_Aligned_Components (Typ) then
Convert_To_Assignments (N, Typ);
-- When generating C, only generate an aggregate when declaring objects
-- since C does not support aggregates in e.g. assignment statements.
elsif Modify_Tree_For_C and then not Is_CCG_Supported_Aggregate (N) then
Convert_To_Assignments (N, Typ);
-- In all other cases, build a proper aggregate to be handled by gigi
else
Build_Back_End_Aggregate;
end if;
end Expand_Record_Aggregate;
---------------------
-- Get_Base_Object --
---------------------
function Get_Base_Object (N : Node_Id) return Entity_Id is
R : Node_Id;
begin
R := Get_Referenced_Object (N);
while Nkind (R) in N_Indexed_Component | N_Selected_Component | N_Slice
loop
R := Get_Referenced_Object (Prefix (R));
end loop;
if Is_Entity_Name (R) and then Is_Object (Entity (R)) then
return Entity (R);
else
return Empty;
end if;
end Get_Base_Object;
----------------------------
-- Has_Default_Init_Comps --
----------------------------
function Has_Default_Init_Comps (N : Node_Id) return Boolean is
Comps : constant List_Id := Component_Associations (N);
C : Node_Id;
Expr : Node_Id;
begin
pragma Assert (Nkind (N) in N_Aggregate | N_Extension_Aggregate);
if No (Comps) then
return False;
end if;
if Has_Self_Reference (N) then
return True;
end if;
-- Check if any direct component has default initialized components
C := First (Comps);
while Present (C) loop
if Box_Present (C) then
return True;
end if;
Next (C);
end loop;
-- Recursive call in case of aggregate expression
C := First (Comps);
while Present (C) loop
Expr := Expression (C);
if Present (Expr)
and then Nkind (Expr) in N_Aggregate | N_Extension_Aggregate
and then Has_Default_Init_Comps (Expr)
then
return True;
end if;
Next (C);
end loop;
return False;
end Has_Default_Init_Comps;
----------------------------------------
-- Is_Build_In_Place_Aggregate_Return --
----------------------------------------
function Is_Build_In_Place_Aggregate_Return (N : Node_Id) return Boolean is
P : Node_Id := Parent (N);
begin
while Nkind (P) = N_Qualified_Expression loop
P := Parent (P);
end loop;
if Nkind (P) = N_Simple_Return_Statement then
null;
elsif Nkind (Parent (P)) = N_Extended_Return_Statement then
P := Parent (P);
else
return False;
end if;
return
Is_Build_In_Place_Function
(Return_Applies_To (Return_Statement_Entity (P)));
end Is_Build_In_Place_Aggregate_Return;
--------------------------
-- Is_Delayed_Aggregate --
--------------------------
function Is_Delayed_Aggregate (N : Node_Id) return Boolean is
Node : Node_Id := N;
Kind : Node_Kind := Nkind (Node);
begin
if Kind = N_Qualified_Expression then
Node := Expression (Node);
Kind := Nkind (Node);
end if;
if Kind not in N_Aggregate | N_Extension_Aggregate then
return False;
else
return Expansion_Delayed (Node);
end if;
end Is_Delayed_Aggregate;
--------------------------------
-- Is_CCG_Supported_Aggregate --
--------------------------------
function Is_CCG_Supported_Aggregate
(N : Node_Id) return Boolean
is
P : Node_Id := Parent (N);
begin
-- Aggregates are not supported for nonstandard rep clauses, since they
-- may lead to extra padding fields in CCG.
if Ekind (Etype (N)) in Record_Kind
and then Has_Non_Standard_Rep (Etype (N))
then
return False;
end if;
while Present (P) and then Nkind (P) = N_Aggregate loop
P := Parent (P);
end loop;
-- Check cases where aggregates are supported by the CCG backend
if Nkind (P) = N_Object_Declaration then
declare
P_Typ : constant Entity_Id := Etype (Defining_Identifier (P));
begin
if Is_Record_Type (P_Typ) then
return True;
else
return Compile_Time_Known_Bounds (P_Typ);
end if;
end;
elsif Nkind (P) = N_Qualified_Expression then
if Nkind (Parent (P)) = N_Object_Declaration then
declare
P_Typ : constant Entity_Id :=
Etype (Defining_Identifier (Parent (P)));
begin
if Is_Record_Type (P_Typ) then
return True;
else
return Compile_Time_Known_Bounds (P_Typ);
end if;
end;
elsif Nkind (Parent (P)) = N_Allocator then
return True;
end if;
end if;
return False;
end Is_CCG_Supported_Aggregate;
----------------------------------------
-- Is_Static_Dispatch_Table_Aggregate --
----------------------------------------
function Is_Static_Dispatch_Table_Aggregate (N : Node_Id) return Boolean is
Typ : constant Entity_Id := Base_Type (Etype (N));
begin
return Building_Static_Dispatch_Tables
and then Tagged_Type_Expansion
and then RTU_Loaded (Ada_Tags)
-- Avoid circularity when rebuilding the compiler
and then Cunit_Entity (Get_Source_Unit (N)) /= RTU_Entity (Ada_Tags)
and then (Typ = RTE (RE_Dispatch_Table_Wrapper)
or else
Typ = RTE (RE_Address_Array)
or else
Typ = RTE (RE_Type_Specific_Data)
or else
Typ = RTE (RE_Tag_Table)
or else
(RTE_Available (RE_Object_Specific_Data)
and then Typ = RTE (RE_Object_Specific_Data))
or else
(RTE_Available (RE_Interface_Data)
and then Typ = RTE (RE_Interface_Data))
or else
(RTE_Available (RE_Interfaces_Array)
and then Typ = RTE (RE_Interfaces_Array))
or else
(RTE_Available (RE_Interface_Data_Element)
and then Typ = RTE (RE_Interface_Data_Element)));
end Is_Static_Dispatch_Table_Aggregate;
-----------------------------
-- Is_Two_Dim_Packed_Array --
-----------------------------
function Is_Two_Dim_Packed_Array (Typ : Entity_Id) return Boolean is
C : constant Int := UI_To_Int (Component_Size (Typ));
begin
return Number_Dimensions (Typ) = 2
and then Is_Bit_Packed_Array (Typ)
and then (C = 1 or else C = 2 or else C = 4);
end Is_Two_Dim_Packed_Array;
--------------------
-- Late_Expansion --
--------------------
function Late_Expansion
(N : Node_Id;
Typ : Entity_Id;
Target : Node_Id) return List_Id
is
Aggr_Code : List_Id;
New_Aggr : Node_Id;
begin
if Is_Array_Type (Typ) then
-- If the assignment can be done directly by the back end, then
-- reset Set_Expansion_Delayed and do not expand further.
if not CodePeer_Mode
and then not Modify_Tree_For_C
and then not Possible_Bit_Aligned_Component (Target)
and then not Is_Possibly_Unaligned_Slice (Target)
and then Aggr_Assignment_OK_For_Backend (N)
then
New_Aggr := New_Copy_Tree (N);
Set_Expansion_Delayed (New_Aggr, False);
Aggr_Code :=
New_List (
Make_OK_Assignment_Statement (Sloc (New_Aggr),
Name => Target,
Expression => New_Aggr));
-- Or else, generate component assignments to it
else
Aggr_Code :=
Build_Array_Aggr_Code
(N => N,
Ctype => Component_Type (Typ),
Index => First_Index (Typ),
Into => Target,
Scalar_Comp => Is_Scalar_Type (Component_Type (Typ)),
Indexes => No_List);
end if;
-- Directly or indirectly (e.g. access protected procedure) a record
else
Aggr_Code := Build_Record_Aggr_Code (N, Typ, Target);
end if;
-- Save the last assignment statement associated with the aggregate
-- when building a controlled object. This reference is utilized by
-- the finalization machinery when marking an object as successfully
-- initialized.
if Needs_Finalization (Typ)
and then Is_Entity_Name (Target)
and then Present (Entity (Target))
and then Ekind (Entity (Target)) in E_Constant | E_Variable
then
Set_Last_Aggregate_Assignment (Entity (Target), Last (Aggr_Code));
end if;
return Aggr_Code;
end Late_Expansion;
----------------------------------
-- Make_OK_Assignment_Statement --
----------------------------------
function Make_OK_Assignment_Statement
(Sloc : Source_Ptr;
Name : Node_Id;
Expression : Node_Id) return Node_Id
is
begin
Set_Assignment_OK (Name);
return Make_Assignment_Statement (Sloc, Name, Expression);
end Make_OK_Assignment_Statement;
------------------------
-- Max_Aggregate_Size --
------------------------
function Max_Aggregate_Size
(N : Node_Id;
Default_Size : Nat := 5000) return Nat
is
Typ : constant Entity_Id := Etype (N);
function Use_Small_Size (N : Node_Id) return Boolean;
-- True if we should return a very small size, which means large
-- aggregates will be implemented as a loop when possible (potentially
-- transformed to memset calls).
function Aggr_Context (N : Node_Id) return Node_Id;
-- Return the context in which the aggregate appears, not counting
-- qualified expressions and similar.
function Aggr_Context (N : Node_Id) return Node_Id is
Result : Node_Id := Parent (N);
begin
if Nkind (Result) in N_Qualified_Expression
| N_Type_Conversion
| N_Unchecked_Type_Conversion
| N_If_Expression
| N_Case_Expression
| N_Component_Association
| N_Aggregate
then
Result := Aggr_Context (Result);
end if;
return Result;
end Aggr_Context;
function Use_Small_Size (N : Node_Id) return Boolean is
C : constant Node_Id := Aggr_Context (N);
-- The decision depends on the context in which the aggregate occurs,
-- and for variable declarations, whether we are nested inside a
-- subprogram.
begin
case Nkind (C) is
-- True for assignment statements and similar
when N_Assignment_Statement
| N_Simple_Return_Statement
| N_Allocator
| N_Attribute_Reference
=>
return True;
-- True for nested variable declarations. False for library level
-- variables, and for constants (whether or not nested).
when N_Object_Declaration =>
return not Constant_Present (C)
and then Ekind (Current_Scope) in Subprogram_Kind;
-- False for all other contexts
when others =>
return False;
end case;
end Use_Small_Size;
-- Start of processing for Max_Aggregate_Size
begin
-- We use a small limit in CodePeer mode where we favor loops
-- instead of thousands of single assignments (from large aggregates).
-- We also increase the limit to 2**24 (about 16 million) if
-- Restrictions (No_Elaboration_Code) or Restrictions
-- (No_Implicit_Loops) is specified, since in either case we are at risk
-- of declaring the program illegal because of this limit. We also
-- increase the limit when Static_Elaboration_Desired, given that this
-- means that objects are intended to be placed in data memory.
-- Same if the aggregate is for a packed two-dimensional array, because
-- if components are static it is much more efficient to construct a
-- one-dimensional equivalent array with static components.
if CodePeer_Mode then
return 100;
elsif Restriction_Active (No_Elaboration_Code)
or else Restriction_Active (No_Implicit_Loops)
or else Is_Two_Dim_Packed_Array (Typ)
or else (Ekind (Current_Scope) = E_Package
and then Static_Elaboration_Desired (Current_Scope))
then
return 2 ** 24;
elsif Use_Small_Size (N) then
return 64;
end if;
return Default_Size;
end Max_Aggregate_Size;
-----------------------
-- Number_Of_Choices --
-----------------------
function Number_Of_Choices (N : Node_Id) return Nat is
Assoc : Node_Id;
Choice : Node_Id;
Nb_Choices : Nat := 0;
begin
if Present (Expressions (N)) then
return 0;
end if;
Assoc := First (Component_Associations (N));
while Present (Assoc) loop
Choice := First (Choice_List (Assoc));
while Present (Choice) loop
if Nkind (Choice) /= N_Others_Choice then
Nb_Choices := Nb_Choices + 1;
end if;
Next (Choice);
end loop;
Next (Assoc);
end loop;
return Nb_Choices;
end Number_Of_Choices;
------------------------------------
-- Packed_Array_Aggregate_Handled --
------------------------------------
-- The current version of this procedure will handle at compile time
-- any array aggregate that meets these conditions:
-- One and two dimensional, bit packed
-- Underlying packed type is modular type
-- Bounds are within 32-bit Int range
-- All bounds and values are static
-- Note: for now, in the 2-D case, we only handle component sizes of
-- 1, 2, 4 (cases where an integral number of elements occupies a byte).
function Packed_Array_Aggregate_Handled (N : Node_Id) return Boolean is
Loc : constant Source_Ptr := Sloc (N);
Typ : constant Entity_Id := Etype (N);
Ctyp : constant Entity_Id := Component_Type (Typ);
Not_Handled : exception;
-- Exception raised if this aggregate cannot be handled
begin
-- Handle one- or two dimensional bit packed array
if not Is_Bit_Packed_Array (Typ)
or else Number_Dimensions (Typ) > 2
then
return False;
end if;
-- If two-dimensional, check whether it can be folded, and transformed
-- into a one-dimensional aggregate for the Packed_Array_Impl_Type of
-- the original type.
if Number_Dimensions (Typ) = 2 then
return Two_Dim_Packed_Array_Handled (N);
end if;
if not Is_Modular_Integer_Type (Packed_Array_Impl_Type (Typ)) then
return False;
end if;
if not Is_Scalar_Type (Ctyp) then
return False;
end if;
declare
Csiz : constant Nat := UI_To_Int (Component_Size (Typ));
Lo : Node_Id;
Hi : Node_Id;
-- Bounds of index type
Lob : Uint;
Hib : Uint;
-- Values of bounds if compile time known
function Get_Component_Val (N : Node_Id) return Uint;
-- Given a expression value N of the component type Ctyp, returns a
-- value of Csiz (component size) bits representing this value. If
-- the value is nonstatic or any other reason exists why the value
-- cannot be returned, then Not_Handled is raised.
-----------------------
-- Get_Component_Val --
-----------------------
function Get_Component_Val (N : Node_Id) return Uint is
Val : Uint;
begin
-- We have to analyze the expression here before doing any further
-- processing here. The analysis of such expressions is deferred
-- till expansion to prevent some problems of premature analysis.
Analyze_And_Resolve (N, Ctyp);
-- Must have a compile time value. String literals have to be
-- converted into temporaries as well, because they cannot easily
-- be converted into their bit representation.
if not Compile_Time_Known_Value (N)
or else Nkind (N) = N_String_Literal
then
raise Not_Handled;
end if;
Val := Expr_Rep_Value (N);
-- Adjust for bias, and strip proper number of bits
if Has_Biased_Representation (Ctyp) then
Val := Val - Expr_Value (Type_Low_Bound (Ctyp));
end if;
return Val mod Uint_2 ** Csiz;
end Get_Component_Val;
-- Here we know we have a one dimensional bit packed array
begin
Get_Index_Bounds (First_Index (Typ), Lo, Hi);
-- Cannot do anything if bounds are dynamic
if not Compile_Time_Known_Value (Lo)
or else
not Compile_Time_Known_Value (Hi)
then
return False;
end if;
-- Or are silly out of range of int bounds
Lob := Expr_Value (Lo);
Hib := Expr_Value (Hi);
if not UI_Is_In_Int_Range (Lob)
or else
not UI_Is_In_Int_Range (Hib)
then
return False;
end if;
-- At this stage we have a suitable aggregate for handling at compile
-- time. The only remaining checks are that the values of expressions
-- in the aggregate are compile-time known (checks are performed by
-- Get_Component_Val), and that any subtypes or ranges are statically
-- known.
-- If the aggregate is not fully positional at this stage, then
-- convert it to positional form. Either this will fail, in which
-- case we can do nothing, or it will succeed, in which case we have
-- succeeded in handling the aggregate and transforming it into a
-- modular value, or it will stay an aggregate, in which case we
-- have failed to create a packed value for it.
if Present (Component_Associations (N)) then
Convert_To_Positional (N, Handle_Bit_Packed => True);
return Nkind (N) /= N_Aggregate;
end if;
-- Otherwise we are all positional, so convert to proper value
declare
Lov : constant Int := UI_To_Int (Lob);
Hiv : constant Int := UI_To_Int (Hib);
Len : constant Nat := Int'Max (0, Hiv - Lov + 1);
-- The length of the array (number of elements)
Aggregate_Val : Uint;
-- Value of aggregate. The value is set in the low order bits of
-- this value. For the little-endian case, the values are stored
-- from low-order to high-order and for the big-endian case the
-- values are stored from high-order to low-order. Note that gigi
-- will take care of the conversions to left justify the value in
-- the big endian case (because of left justified modular type
-- processing), so we do not have to worry about that here.
Lit : Node_Id;
-- Integer literal for resulting constructed value
Shift : Nat;
-- Shift count from low order for next value
Incr : Int;
-- Shift increment for loop
Expr : Node_Id;
-- Next expression from positional parameters of aggregate
Left_Justified : Boolean;
-- Set True if we are filling the high order bits of the target
-- value (i.e. the value is left justified).
begin
-- For little endian, we fill up the low order bits of the target
-- value. For big endian we fill up the high order bits of the
-- target value (which is a left justified modular value).
Left_Justified := Bytes_Big_Endian;
-- Switch justification if using -gnatd8
if Debug_Flag_8 then
Left_Justified := not Left_Justified;
end if;
-- Switch justfification if reverse storage order
if Reverse_Storage_Order (Base_Type (Typ)) then
Left_Justified := not Left_Justified;
end if;
if Left_Justified then
Shift := Csiz * (Len - 1);
Incr := -Csiz;
else
Shift := 0;
Incr := +Csiz;
end if;
-- Loop to set the values
if Len = 0 then
Aggregate_Val := Uint_0;
else
Expr := First (Expressions (N));
Aggregate_Val := Get_Component_Val (Expr) * Uint_2 ** Shift;
for J in 2 .. Len loop
Shift := Shift + Incr;
Next (Expr);
Aggregate_Val :=
Aggregate_Val + Get_Component_Val (Expr) * Uint_2 ** Shift;
end loop;
end if;
-- Now we can rewrite with the proper value
Lit := Make_Integer_Literal (Loc, Intval => Aggregate_Val);
Set_Print_In_Hex (Lit);
-- Construct the expression using this literal. Note that it is
-- important to qualify the literal with its proper modular type
-- since universal integer does not have the required range and
-- also this is a left justified modular type, which is important
-- in the big-endian case.
Rewrite (N,
Unchecked_Convert_To (Typ,
Make_Qualified_Expression (Loc,
Subtype_Mark =>
New_Occurrence_Of (Packed_Array_Impl_Type (Typ), Loc),
Expression => Lit)));
Analyze_And_Resolve (N, Typ);
return True;
end;
end;
exception
when Not_Handled =>
return False;
end Packed_Array_Aggregate_Handled;
----------------------------
-- Has_Mutable_Components --
----------------------------
function Has_Mutable_Components (Typ : Entity_Id) return Boolean is
Comp : Entity_Id;
Ctyp : Entity_Id;
begin
Comp := First_Component (Typ);
while Present (Comp) loop
Ctyp := Underlying_Type (Etype (Comp));
if Is_Record_Type (Ctyp)
and then Has_Discriminants (Ctyp)
and then not Is_Constrained (Ctyp)
then
return True;
end if;
Next_Component (Comp);
end loop;
return False;
end Has_Mutable_Components;
------------------------------
-- Initialize_Discriminants --
------------------------------
procedure Initialize_Discriminants (N : Node_Id; Typ : Entity_Id) is
Loc : constant Source_Ptr := Sloc (N);
Bas : constant Entity_Id := Base_Type (Typ);
Par : constant Entity_Id := Etype (Bas);
Decl : constant Node_Id := Parent (Par);
Ref : Node_Id;
begin
if Is_Tagged_Type (Bas)
and then Is_Derived_Type (Bas)
and then Has_Discriminants (Par)
and then Has_Discriminants (Bas)
and then Number_Discriminants (Bas) /= Number_Discriminants (Par)
and then Nkind (Decl) = N_Full_Type_Declaration
and then Nkind (Type_Definition (Decl)) = N_Record_Definition
and then
Present (Variant_Part (Component_List (Type_Definition (Decl))))
and then Nkind (N) /= N_Extension_Aggregate
then
-- Call init proc to set discriminants.
-- There should eventually be a special procedure for this ???
Ref := New_Occurrence_Of (Defining_Identifier (N), Loc);
Insert_Actions_After (N,
Build_Initialization_Call (Sloc (N), Ref, Typ));
end if;
end Initialize_Discriminants;
----------------
-- Must_Slide --
----------------
function Must_Slide
(Obj_Type : Entity_Id;
Typ : Entity_Id) return Boolean
is
L1, L2, H1, H2 : Node_Id;
begin
-- No sliding if the type of the object is not established yet, if it is
-- an unconstrained type whose actual subtype comes from the aggregate,
-- or if the two types are identical.
if not Is_Array_Type (Obj_Type) then
return False;
elsif not Is_Constrained (Obj_Type) then
return False;
elsif Typ = Obj_Type then
return False;
else
-- Sliding can only occur along the first dimension
Get_Index_Bounds (First_Index (Typ), L1, H1);
Get_Index_Bounds (First_Index (Obj_Type), L2, H2);
if not Is_OK_Static_Expression (L1) or else
not Is_OK_Static_Expression (L2) or else
not Is_OK_Static_Expression (H1) or else
not Is_OK_Static_Expression (H2)
then
return False;
else
return Expr_Value (L1) /= Expr_Value (L2)
or else
Expr_Value (H1) /= Expr_Value (H2);
end if;
end if;
end Must_Slide;
---------------------------------
-- Process_Transient_Component --
---------------------------------
procedure Process_Transient_Component
(Loc : Source_Ptr;
Comp_Typ : Entity_Id;
Init_Expr : Node_Id;
Fin_Call : out Node_Id;
Hook_Clear : out Node_Id;
Aggr : Node_Id := Empty;
Stmts : List_Id := No_List)
is
procedure Add_Item (Item : Node_Id);
-- Insert arbitrary node Item into the tree depending on the values of
-- Aggr and Stmts.
--------------
-- Add_Item --
--------------
procedure Add_Item (Item : Node_Id) is
begin
if Present (Aggr) then
Insert_Action (Aggr, Item);
else
pragma Assert (Present (Stmts));
Append_To (Stmts, Item);
end if;
end Add_Item;
-- Local variables
Hook_Assign : Node_Id;
Hook_Decl : Node_Id;
Ptr_Decl : Node_Id;
Res_Decl : Node_Id;
Res_Id : Entity_Id;
Res_Typ : Entity_Id;
-- Start of processing for Process_Transient_Component
begin
-- Add the access type, which provides a reference to the function
-- result. Generate:
-- type Res_Typ is access all Comp_Typ;
Res_Typ := Make_Temporary (Loc, 'A');
Set_Ekind (Res_Typ, E_General_Access_Type);
Set_Directly_Designated_Type (Res_Typ, Comp_Typ);
Add_Item
(Make_Full_Type_Declaration (Loc,
Defining_Identifier => Res_Typ,
Type_Definition =>
Make_Access_To_Object_Definition (Loc,
All_Present => True,
Subtype_Indication => New_Occurrence_Of (Comp_Typ, Loc))));
-- Add the temporary which captures the result of the function call.
-- Generate:
-- Res : constant Res_Typ := Init_Expr'Reference;
-- Note that this temporary is effectively a transient object because
-- its lifetime is bounded by the current array or record component.
Res_Id := Make_Temporary (Loc, 'R');
Set_Ekind (Res_Id, E_Constant);
Set_Etype (Res_Id, Res_Typ);
-- Mark the transient object as successfully processed to avoid double
-- finalization.
Set_Is_Finalized_Transient (Res_Id);
-- Signal the general finalization machinery that this transient object
-- should not be considered for finalization actions because its cleanup
-- will be performed by Process_Transient_Component_Completion.
Set_Is_Ignored_Transient (Res_Id);
Res_Decl :=
Make_Object_Declaration (Loc,
Defining_Identifier => Res_Id,
Constant_Present => True,
Object_Definition => New_Occurrence_Of (Res_Typ, Loc),
Expression =>
Make_Reference (Loc, New_Copy_Tree (Init_Expr)));
Add_Item (Res_Decl);
-- Construct all pieces necessary to hook and finalize the transient
-- result.
Build_Transient_Object_Statements
(Obj_Decl => Res_Decl,
Fin_Call => Fin_Call,
Hook_Assign => Hook_Assign,
Hook_Clear => Hook_Clear,
Hook_Decl => Hook_Decl,
Ptr_Decl => Ptr_Decl);
-- Add the access type which provides a reference to the transient
-- result. Generate:
-- type Ptr_Typ is access all Comp_Typ;
Add_Item (Ptr_Decl);
-- Add the temporary which acts as a hook to the transient result.
-- Generate:
-- Hook : Ptr_Typ := null;
Add_Item (Hook_Decl);
-- Attach the transient result to the hook. Generate:
-- Hook := Ptr_Typ (Res);
Add_Item (Hook_Assign);
-- The original initialization expression now references the value of
-- the temporary function result. Generate:
-- Res.all
Rewrite (Init_Expr,
Make_Explicit_Dereference (Loc,
Prefix => New_Occurrence_Of (Res_Id, Loc)));
end Process_Transient_Component;
--------------------------------------------
-- Process_Transient_Component_Completion --
--------------------------------------------
procedure Process_Transient_Component_Completion
(Loc : Source_Ptr;
Aggr : Node_Id;
Fin_Call : Node_Id;
Hook_Clear : Node_Id;
Stmts : List_Id)
is
Exceptions_OK : constant Boolean :=
not Restriction_Active (No_Exception_Propagation);
begin
pragma Assert (Present (Hook_Clear));
-- Generate the following code if exception propagation is allowed:
-- declare
-- Abort : constant Boolean := Triggered_By_Abort;
-- <or>
-- Abort : constant Boolean := False; -- no abort
-- E : Exception_Occurrence;
-- Raised : Boolean := False;
-- begin
-- [Abort_Defer;]
-- begin
-- Hook := null;
-- [Deep_]Finalize (Res.all);
-- exception
-- when others =>
-- if not Raised then
-- Raised := True;
-- Save_Occurrence (E,
-- Get_Curent_Excep.all.all);
-- end if;
-- end;
-- [Abort_Undefer;]
-- if Raised and then not Abort then
-- Raise_From_Controlled_Operation (E);
-- end if;
-- end;
if Exceptions_OK then
Abort_And_Exception : declare
Blk_Decls : constant List_Id := New_List;
Blk_Stmts : constant List_Id := New_List;
Fin_Stmts : constant List_Id := New_List;
Fin_Data : Finalization_Exception_Data;
begin
-- Create the declarations of the two flags and the exception
-- occurrence.
Build_Object_Declarations (Fin_Data, Blk_Decls, Loc);
-- Generate:
-- Abort_Defer;
if Abort_Allowed then
Append_To (Blk_Stmts,
Build_Runtime_Call (Loc, RE_Abort_Defer));
end if;
-- Wrap the hook clear and the finalization call in order to trap
-- a potential exception.
Append_To (Fin_Stmts, Hook_Clear);
if Present (Fin_Call) then
Append_To (Fin_Stmts, Fin_Call);
end if;
Append_To (Blk_Stmts,
Make_Block_Statement (Loc,
Handled_Statement_Sequence =>
Make_Handled_Sequence_Of_Statements (Loc,
Statements => Fin_Stmts,
Exception_Handlers => New_List (
Build_Exception_Handler (Fin_Data)))));
-- Generate:
-- Abort_Undefer;
if Abort_Allowed then
Append_To (Blk_Stmts,
Build_Runtime_Call (Loc, RE_Abort_Undefer));
end if;
-- Reraise the potential exception with a proper "upgrade" to
-- Program_Error if needed.
Append_To (Blk_Stmts, Build_Raise_Statement (Fin_Data));
-- Wrap everything in a block
Append_To (Stmts,
Make_Block_Statement (Loc,
Declarations => Blk_Decls,
Handled_Statement_Sequence =>
Make_Handled_Sequence_Of_Statements (Loc,
Statements => Blk_Stmts)));
end Abort_And_Exception;
-- Generate the following code if exception propagation is not allowed
-- and aborts are allowed:
-- begin
-- Abort_Defer;
-- Hook := null;
-- [Deep_]Finalize (Res.all);
-- at end
-- Abort_Undefer_Direct;
-- end;
elsif Abort_Allowed then
Abort_Only : declare
Blk_Stmts : constant List_Id := New_List;
begin
Append_To (Blk_Stmts, Build_Runtime_Call (Loc, RE_Abort_Defer));
Append_To (Blk_Stmts, Hook_Clear);
if Present (Fin_Call) then
Append_To (Blk_Stmts, Fin_Call);
end if;
Append_To (Stmts,
Build_Abort_Undefer_Block (Loc,
Stmts => Blk_Stmts,
Context => Aggr));
end Abort_Only;
-- Otherwise generate:
-- Hook := null;
-- [Deep_]Finalize (Res.all);
else
Append_To (Stmts, Hook_Clear);
if Present (Fin_Call) then
Append_To (Stmts, Fin_Call);
end if;
end if;
end Process_Transient_Component_Completion;
---------------------
-- Sort_Case_Table --
---------------------
procedure Sort_Case_Table (Case_Table : in out Case_Table_Type) is
L : constant Int := Case_Table'First;
U : constant Int := Case_Table'Last;
K : Int;
J : Int;
T : Case_Bounds;
begin
K := L;
while K /= U loop
T := Case_Table (K + 1);
J := K + 1;
while J /= L
and then Expr_Value (Case_Table (J - 1).Choice_Lo) >
Expr_Value (T.Choice_Lo)
loop
Case_Table (J) := Case_Table (J - 1);
J := J - 1;
end loop;
Case_Table (J) := T;
K := K + 1;
end loop;
end Sort_Case_Table;
----------------------------
-- Static_Array_Aggregate --
----------------------------
function Static_Array_Aggregate (N : Node_Id) return Boolean is
function Is_Static_Component (Nod : Node_Id) return Boolean;
-- Return True if Nod has a compile-time known value and can be passed
-- as is to the back-end without further expansion.
---------------------------
-- Is_Static_Component --
---------------------------
function Is_Static_Component (Nod : Node_Id) return Boolean is
begin
if Nkind (Nod) in N_Integer_Literal | N_Real_Literal then
return True;
elsif Is_Entity_Name (Nod)
and then Present (Entity (Nod))
and then Ekind (Entity (Nod)) = E_Enumeration_Literal
then
return True;
elsif Nkind (Nod) = N_Aggregate
and then Compile_Time_Known_Aggregate (Nod)
then
return True;
else
return False;
end if;
end Is_Static_Component;
-- Local variables
Bounds : constant Node_Id := Aggregate_Bounds (N);
Typ : constant Entity_Id := Etype (N);
Agg : Node_Id;
Expr : Node_Id;
Lo : Node_Id;
Hi : Node_Id;
-- Start of processing for Static_Array_Aggregate
begin
if Is_Packed (Typ) or else Has_Discriminants (Component_Type (Typ)) then
return False;
end if;
if Present (Bounds)
and then Nkind (Bounds) = N_Range
and then Nkind (Low_Bound (Bounds)) = N_Integer_Literal
and then Nkind (High_Bound (Bounds)) = N_Integer_Literal
then
Lo := Low_Bound (Bounds);
Hi := High_Bound (Bounds);
if No (Component_Associations (N)) then
-- Verify that all components are static
Expr := First (Expressions (N));
while Present (Expr) loop
if not Is_Static_Component (Expr) then
return False;
end if;
Next (Expr);
end loop;
return True;
else
-- We allow only a single named association, either a static
-- range or an others_clause, with a static expression.
Expr := First (Component_Associations (N));
if Present (Expressions (N)) then
return False;
elsif Present (Next (Expr)) then
return False;
elsif Present (Next (First (Choice_List (Expr)))) then
return False;
else
-- The aggregate is static if all components are literals,
-- or else all its components are static aggregates for the
-- component type. We also limit the size of a static aggregate
-- to prevent runaway static expressions.
if not Is_Static_Component (Expression (Expr)) then
return False;
end if;
if not Aggr_Size_OK (N) then
return False;
end if;
-- Create a positional aggregate with the right number of
-- copies of the expression.
Agg := Make_Aggregate (Sloc (N), New_List, No_List);
for I in UI_To_Int (Intval (Lo)) .. UI_To_Int (Intval (Hi))
loop
Append_To (Expressions (Agg), New_Copy (Expression (Expr)));
-- The copied expression must be analyzed and resolved.
-- Besides setting the type, this ensures that static
-- expressions are appropriately marked as such.
Analyze_And_Resolve
(Last (Expressions (Agg)), Component_Type (Typ));
end loop;
Set_Aggregate_Bounds (Agg, Bounds);
Set_Etype (Agg, Typ);
Set_Analyzed (Agg);
Rewrite (N, Agg);
Set_Compile_Time_Known_Aggregate (N);
return True;
end if;
end if;
else
return False;
end if;
end Static_Array_Aggregate;
----------------------------------
-- Two_Dim_Packed_Array_Handled --
----------------------------------
function Two_Dim_Packed_Array_Handled (N : Node_Id) return Boolean is
Loc : constant Source_Ptr := Sloc (N);
Typ : constant Entity_Id := Etype (N);
Ctyp : constant Entity_Id := Component_Type (Typ);
Comp_Size : constant Int := UI_To_Int (Component_Size (Typ));
Packed_Array : constant Entity_Id :=
Packed_Array_Impl_Type (Base_Type (Typ));
One_Comp : Node_Id;
-- Expression in original aggregate
One_Dim : Node_Id;
-- One-dimensional subaggregate
begin
-- For now, only deal with cases where an integral number of elements
-- fit in a single byte. This includes the most common boolean case.
if not (Comp_Size = 1 or else
Comp_Size = 2 or else
Comp_Size = 4)
then
return False;
end if;
Convert_To_Positional (N, Handle_Bit_Packed => True);
-- Verify that all components are static
if Nkind (N) = N_Aggregate
and then Compile_Time_Known_Aggregate (N)
then
null;
-- The aggregate may have been reanalyzed and converted already
elsif Nkind (N) /= N_Aggregate then
return True;
-- If component associations remain, the aggregate is not static
elsif Present (Component_Associations (N)) then
return False;
else
One_Dim := First (Expressions (N));
while Present (One_Dim) loop
if Present (Component_Associations (One_Dim)) then
return False;
end if;
One_Comp := First (Expressions (One_Dim));
while Present (One_Comp) loop
if not Is_OK_Static_Expression (One_Comp) then
return False;
end if;
Next (One_Comp);
end loop;
Next (One_Dim);
end loop;
end if;
-- Two-dimensional aggregate is now fully positional so pack one
-- dimension to create a static one-dimensional array, and rewrite
-- as an unchecked conversion to the original type.
declare
Byte_Size : constant Int := UI_To_Int (Component_Size (Packed_Array));
-- The packed array type is a byte array
Packed_Num : Nat;
-- Number of components accumulated in current byte
Comps : List_Id;
-- Assembled list of packed values for equivalent aggregate
Comp_Val : Uint;
-- Integer value of component
Incr : Int;
-- Step size for packing
Init_Shift : Int;
-- Endian-dependent start position for packing
Shift : Int;
-- Current insertion position
Val : Int;
-- Component of packed array being assembled
begin
Comps := New_List;
Val := 0;
Packed_Num := 0;
-- Account for endianness. See corresponding comment in
-- Packed_Array_Aggregate_Handled concerning the following.
if Bytes_Big_Endian
xor Debug_Flag_8
xor Reverse_Storage_Order (Base_Type (Typ))
then
Init_Shift := Byte_Size - Comp_Size;
Incr := -Comp_Size;
else
Init_Shift := 0;
Incr := +Comp_Size;
end if;
-- Iterate over each subaggregate
Shift := Init_Shift;
One_Dim := First (Expressions (N));
while Present (One_Dim) loop
One_Comp := First (Expressions (One_Dim));
while Present (One_Comp) loop
if Packed_Num = Byte_Size / Comp_Size then
-- Byte is complete, add to list of expressions
Append (Make_Integer_Literal (Sloc (One_Dim), Val), Comps);
Val := 0;
Shift := Init_Shift;
Packed_Num := 0;
else
Comp_Val := Expr_Rep_Value (One_Comp);
-- Adjust for bias, and strip proper number of bits
if Has_Biased_Representation (Ctyp) then
Comp_Val := Comp_Val - Expr_Value (Type_Low_Bound (Ctyp));
end if;
Comp_Val := Comp_Val mod Uint_2 ** Comp_Size;
Val := UI_To_Int (Val + Comp_Val * Uint_2 ** Shift);
Shift := Shift + Incr;
Next (One_Comp);
Packed_Num := Packed_Num + 1;
end if;
end loop;
Next (One_Dim);
end loop;
if Packed_Num > 0 then
-- Add final incomplete byte if present
Append (Make_Integer_Literal (Sloc (One_Dim), Val), Comps);
end if;
Rewrite (N,
Unchecked_Convert_To (Typ,
Make_Qualified_Expression (Loc,
Subtype_Mark => New_Occurrence_Of (Packed_Array, Loc),
Expression => Make_Aggregate (Loc, Expressions => Comps))));
Analyze_And_Resolve (N);
return True;
end;
end Two_Dim_Packed_Array_Handled;
end Exp_Aggr;