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