------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- E X P _ C H 4 -- -- -- -- B o d y -- -- -- -- Copyright (C) 1992-2020, Free Software Foundation, Inc. -- -- -- -- GNAT is free software; you can redistribute it and/or modify it under -- -- terms of the GNU General Public License as published by the Free Soft- -- -- ware Foundation; either version 3, or (at your option) any later ver- -- -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- -- for more details. You should have received a copy of the GNU General -- -- Public License distributed with GNAT; see file COPYING3. If not, go to -- -- http://www.gnu.org/licenses for a complete copy of the license. -- -- -- -- GNAT was originally developed by the GNAT team at New York University. -- -- Extensive contributions were provided by Ada Core Technologies Inc. -- -- -- ------------------------------------------------------------------------------ with Atree; use Atree; with Checks; use Checks; with Debug; use Debug; with Einfo; use Einfo; with Elists; use Elists; with Errout; use Errout; with Exp_Aggr; use Exp_Aggr; with Exp_Atag; use Exp_Atag; with Exp_Ch2; use Exp_Ch2; 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_Fixd; use Exp_Fixd; with Exp_Intr; use Exp_Intr; with Exp_Pakd; use Exp_Pakd; with Exp_Tss; use Exp_Tss; with Exp_Util; use Exp_Util; with Freeze; use Freeze; with Inline; use Inline; with Namet; use Namet; with Nlists; use Nlists; with Nmake; use Nmake; with Opt; use Opt; with Par_SCO; use Par_SCO; with Restrict; use Restrict; with Rident; use Rident; with Rtsfind; use Rtsfind; with Sem; use Sem; with Sem_Aux; use Sem_Aux; with Sem_Cat; use Sem_Cat; with Sem_Ch3; use Sem_Ch3; with Sem_Ch13; use Sem_Ch13; with Sem_Eval; use Sem_Eval; with Sem_Res; use Sem_Res; with Sem_Type; use Sem_Type; with Sem_Util; use Sem_Util; with Sem_Warn; use Sem_Warn; with Sinfo; use Sinfo; with Snames; use Snames; with Stand; use Stand; with SCIL_LL; use SCIL_LL; with Targparm; use Targparm; with Tbuild; use Tbuild; with Ttypes; use Ttypes; with Uintp; use Uintp; with Urealp; use Urealp; with Validsw; use Validsw; with Warnsw; use Warnsw; package body Exp_Ch4 is ----------------------- -- Local Subprograms -- ----------------------- procedure Binary_Op_Validity_Checks (N : Node_Id); pragma Inline (Binary_Op_Validity_Checks); -- Performs validity checks for a binary operator procedure Build_Boolean_Array_Proc_Call (N : Node_Id; Op1 : Node_Id; Op2 : Node_Id); -- If a boolean array assignment can be done in place, build call to -- corresponding library procedure. procedure Displace_Allocator_Pointer (N : Node_Id); -- Ada 2005 (AI-251): Subsidiary procedure to Expand_N_Allocator and -- Expand_Allocator_Expression. Allocating class-wide interface objects -- this routine displaces the pointer to the allocated object to reference -- the component referencing the corresponding secondary dispatch table. procedure Expand_Allocator_Expression (N : Node_Id); -- Subsidiary to Expand_N_Allocator, for the case when the expression -- is a qualified expression. procedure Expand_Array_Comparison (N : Node_Id); -- This routine handles expansion of the comparison operators (N_Op_Lt, -- N_Op_Le, N_Op_Gt, N_Op_Ge) when operating on an array type. The basic -- code for these operators is similar, differing only in the details of -- the actual comparison call that is made. Special processing (call a -- run-time routine) function Expand_Array_Equality (Nod : Node_Id; Lhs : Node_Id; Rhs : Node_Id; Bodies : List_Id; Typ : Entity_Id) return Node_Id; -- Expand an array equality into a call to a function implementing this -- equality, and a call to it. Loc is the location for the generated nodes. -- Lhs and Rhs are the array expressions to be compared. Bodies is a list -- on which to attach bodies of local functions that are created in the -- process. It is the responsibility of the caller to insert those bodies -- at the right place. Nod provides the Sloc value for the generated code. -- Normally the types used for the generated equality routine are taken -- from Lhs and Rhs. However, in some situations of generated code, the -- Etype fields of Lhs and Rhs are not set yet. In such cases, Typ supplies -- the type to be used for the formal parameters. procedure Expand_Boolean_Operator (N : Node_Id); -- Common expansion processing for Boolean operators (And, Or, Xor) for the -- case of array type arguments. procedure Expand_Nonbinary_Modular_Op (N : Node_Id); -- When generating C code, convert nonbinary modular arithmetic operations -- into code that relies on the front-end expansion of operator Mod. No -- expansion is performed if N is not a nonbinary modular operand. procedure Expand_Short_Circuit_Operator (N : Node_Id); -- Common expansion processing for short-circuit boolean operators procedure Expand_Compare_Minimize_Eliminate_Overflow (N : Node_Id); -- Deal with comparison in MINIMIZED/ELIMINATED overflow mode. This is -- where we allow comparison of "out of range" values. function Expand_Composite_Equality (Nod : Node_Id; Typ : Entity_Id; Lhs : Node_Id; Rhs : Node_Id; Bodies : List_Id) return Node_Id; -- Local recursive function used to expand equality for nested composite -- types. Used by Expand_Record/Array_Equality, Bodies is a list on which -- to attach bodies of local functions that are created in the process. It -- is the responsibility of the caller to insert those bodies at the right -- place. Nod provides the Sloc value for generated code. Lhs and Rhs are -- the left and right sides for the comparison, and Typ is the type of the -- objects to compare. procedure Expand_Concatenate (Cnode : Node_Id; Opnds : List_Id); -- Routine to expand concatenation of a sequence of two or more operands -- (in the list Operands) and replace node Cnode with the result of the -- concatenation. The operands can be of any appropriate type, and can -- include both arrays and singleton elements. procedure Expand_Membership_Minimize_Eliminate_Overflow (N : Node_Id); -- N is an N_In membership test mode, with the overflow check mode set to -- MINIMIZED or ELIMINATED, and the type of the left operand is a signed -- integer type. This is a case where top level processing is required to -- handle overflow checks in subtrees. procedure Fixup_Universal_Fixed_Operation (N : Node_Id); -- N is a N_Op_Divide or N_Op_Multiply node whose result is universal -- fixed. We do not have such a type at runtime, so the purpose of this -- routine is to find the real type by looking up the tree. We also -- determine if the operation must be rounded. function Has_Inferable_Discriminants (N : Node_Id) return Boolean; -- Ada 2005 (AI-216): A view of an Unchecked_Union object has inferable -- discriminants if it has a constrained nominal type, unless the object -- is a component of an enclosing Unchecked_Union object that is subject -- to a per-object constraint and the enclosing object lacks inferable -- discriminants. -- -- An expression of an Unchecked_Union type has inferable discriminants -- if it is either a name of an object with inferable discriminants or a -- qualified expression whose subtype mark denotes a constrained subtype. procedure Insert_Dereference_Action (N : Node_Id); -- N is an expression whose type is an access. When the type of the -- associated storage pool is derived from Checked_Pool, generate a -- call to the 'Dereference' primitive operation. function Make_Array_Comparison_Op (Typ : Entity_Id; Nod : Node_Id) return Node_Id; -- Comparisons between arrays are expanded in line. This function produces -- the body of the implementation of (a > b), where a and b are one- -- dimensional arrays of some discrete type. The original node is then -- expanded into the appropriate call to this function. Nod provides the -- Sloc value for the generated code. function Make_Boolean_Array_Op (Typ : Entity_Id; N : Node_Id) return Node_Id; -- Boolean operations on boolean arrays are expanded in line. This function -- produce the body for the node N, which is (a and b), (a or b), or (a xor -- b). It is used only the normal case and not the packed case. The type -- involved, Typ, is the Boolean array type, and the logical operations in -- the body are simple boolean operations. Note that Typ is always a -- constrained type (the caller has ensured this by using -- Convert_To_Actual_Subtype if necessary). function Minimized_Eliminated_Overflow_Check (N : Node_Id) return Boolean; -- For signed arithmetic operations when the current overflow mode is -- MINIMIZED or ELIMINATED, we must call Apply_Arithmetic_Overflow_Checks -- as the first thing we do. We then return. We count on the recursive -- apparatus for overflow checks to call us back with an equivalent -- operation that is in CHECKED mode, avoiding a recursive entry into this -- routine, and that is when we will proceed with the expansion of the -- operator (e.g. converting X+0 to X, or X**2 to X*X). We cannot do -- these optimizations without first making this check, since there may be -- operands further down the tree that are relying on the recursive calls -- triggered by the top level nodes to properly process overflow checking -- and remaining expansion on these nodes. Note that this call back may be -- skipped if the operation is done in Bignum mode but that's fine, since -- the Bignum call takes care of everything. procedure Narrow_Large_Operation (N : Node_Id); -- Try to compute the result of a large operation in a narrower type than -- its nominal type. This is mainly aimed to get rid of operations done in -- Universal_Integer that can be generated for attributes. procedure Optimize_Length_Comparison (N : Node_Id); -- Given an expression, if it is of the form X'Length op N (or the other -- way round), where N is known at compile time to be 0 or 1, or something -- else where the value is known to be nonnegative and in the 32-bit range, -- and X is a simple entity, and op is a comparison operator, optimizes it -- into a comparison of X'First and X'Last. procedure Process_If_Case_Statements (N : Node_Id; Stmts : List_Id); -- Inspect and process statement list Stmt of if or case expression N for -- transient objects. If such objects are found, the routine generates code -- to clean them up when the context of the expression is evaluated. procedure Process_Transient_In_Expression (Obj_Decl : Node_Id; Expr : Node_Id; Stmts : List_Id); -- Subsidiary routine to the expansion of expression_with_actions, if and -- case expressions. Generate all necessary code to finalize a transient -- object when the enclosing context is elaborated or evaluated. Obj_Decl -- denotes the declaration of the transient object, which is usually the -- result of a controlled function call. Expr denotes the expression with -- actions, if expression, or case expression node. Stmts denotes the -- statement list which contains Decl, either at the top level or within a -- nested construct. procedure Rewrite_Comparison (N : Node_Id); -- If N is the node for a comparison whose outcome can be determined at -- compile time, then the node N can be rewritten with True or False. If -- the outcome cannot be determined at compile time, the call has no -- effect. If N is a type conversion, then this processing is applied to -- its expression. If N is neither comparison nor a type conversion, the -- call has no effect. procedure Tagged_Membership (N : Node_Id; SCIL_Node : out Node_Id; Result : out Node_Id); -- Construct the expression corresponding to the tagged membership test. -- Deals with a second operand being (or not) a class-wide type. function Safe_In_Place_Array_Op (Lhs : Node_Id; Op1 : Node_Id; Op2 : Node_Id) return Boolean; -- In the context of an assignment, where the right-hand side is a boolean -- operation on arrays, check whether operation can be performed in place. procedure Unary_Op_Validity_Checks (N : Node_Id); pragma Inline (Unary_Op_Validity_Checks); -- Performs validity checks for a unary operator ------------------------------- -- Binary_Op_Validity_Checks -- ------------------------------- procedure Binary_Op_Validity_Checks (N : Node_Id) is begin if Validity_Checks_On and Validity_Check_Operands then Ensure_Valid (Left_Opnd (N)); Ensure_Valid (Right_Opnd (N)); end if; end Binary_Op_Validity_Checks; ------------------------------------ -- Build_Boolean_Array_Proc_Call -- ------------------------------------ procedure Build_Boolean_Array_Proc_Call (N : Node_Id; Op1 : Node_Id; Op2 : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Kind : constant Node_Kind := Nkind (Expression (N)); Target : constant Node_Id := Make_Attribute_Reference (Loc, Prefix => Name (N), Attribute_Name => Name_Address); Arg1 : Node_Id := Op1; Arg2 : Node_Id := Op2; Call_Node : Node_Id; Proc_Name : Entity_Id; begin if Kind = N_Op_Not then if Nkind (Op1) in N_Binary_Op then -- Use negated version of the binary operators if Nkind (Op1) = N_Op_And then Proc_Name := RTE (RE_Vector_Nand); elsif Nkind (Op1) = N_Op_Or then Proc_Name := RTE (RE_Vector_Nor); else pragma Assert (Nkind (Op1) = N_Op_Xor); Proc_Name := RTE (RE_Vector_Xor); end if; Call_Node := Make_Procedure_Call_Statement (Loc, Name => New_Occurrence_Of (Proc_Name, Loc), Parameter_Associations => New_List ( Target, Make_Attribute_Reference (Loc, Prefix => Left_Opnd (Op1), Attribute_Name => Name_Address), Make_Attribute_Reference (Loc, Prefix => Right_Opnd (Op1), Attribute_Name => Name_Address), Make_Attribute_Reference (Loc, Prefix => Left_Opnd (Op1), Attribute_Name => Name_Length))); else Proc_Name := RTE (RE_Vector_Not); Call_Node := Make_Procedure_Call_Statement (Loc, Name => New_Occurrence_Of (Proc_Name, Loc), Parameter_Associations => New_List ( Target, Make_Attribute_Reference (Loc, Prefix => Op1, Attribute_Name => Name_Address), Make_Attribute_Reference (Loc, Prefix => Op1, Attribute_Name => Name_Length))); end if; else -- We use the following equivalences: -- (not X) or (not Y) = not (X and Y) = Nand (X, Y) -- (not X) and (not Y) = not (X or Y) = Nor (X, Y) -- (not X) xor (not Y) = X xor Y -- X xor (not Y) = not (X xor Y) = Nxor (X, Y) if Nkind (Op1) = N_Op_Not then Arg1 := Right_Opnd (Op1); Arg2 := Right_Opnd (Op2); if Kind = N_Op_And then Proc_Name := RTE (RE_Vector_Nor); elsif Kind = N_Op_Or then Proc_Name := RTE (RE_Vector_Nand); else Proc_Name := RTE (RE_Vector_Xor); end if; else if Kind = N_Op_And then Proc_Name := RTE (RE_Vector_And); elsif Kind = N_Op_Or then Proc_Name := RTE (RE_Vector_Or); elsif Nkind (Op2) = N_Op_Not then Proc_Name := RTE (RE_Vector_Nxor); Arg2 := Right_Opnd (Op2); else Proc_Name := RTE (RE_Vector_Xor); end if; end if; Call_Node := Make_Procedure_Call_Statement (Loc, Name => New_Occurrence_Of (Proc_Name, Loc), Parameter_Associations => New_List ( Target, Make_Attribute_Reference (Loc, Prefix => Arg1, Attribute_Name => Name_Address), Make_Attribute_Reference (Loc, Prefix => Arg2, Attribute_Name => Name_Address), Make_Attribute_Reference (Loc, Prefix => Arg1, Attribute_Name => Name_Length))); end if; Rewrite (N, Call_Node); Analyze (N); exception when RE_Not_Available => return; end Build_Boolean_Array_Proc_Call; ----------------------- -- Build_Eq_Call -- ----------------------- function Build_Eq_Call (Typ : Entity_Id; Loc : Source_Ptr; Lhs : Node_Id; Rhs : Node_Id) return Node_Id is Prim : Node_Id; Prim_E : Elmt_Id; begin Prim_E := First_Elmt (Collect_Primitive_Operations (Typ)); while Present (Prim_E) loop Prim := Node (Prim_E); -- Locate primitive equality with the right signature if Chars (Prim) = Name_Op_Eq and then Etype (First_Formal (Prim)) = Etype (Next_Formal (First_Formal (Prim))) and then Etype (Prim) = Standard_Boolean then if Is_Abstract_Subprogram (Prim) then return Make_Raise_Program_Error (Loc, Reason => PE_Explicit_Raise); else return Make_Function_Call (Loc, Name => New_Occurrence_Of (Prim, Loc), Parameter_Associations => New_List (Lhs, Rhs)); end if; end if; Next_Elmt (Prim_E); end loop; -- If not found, predefined operation will be used return Empty; end Build_Eq_Call; -------------------------------- -- Displace_Allocator_Pointer -- -------------------------------- procedure Displace_Allocator_Pointer (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Orig_Node : constant Node_Id := Original_Node (N); Dtyp : Entity_Id; Etyp : Entity_Id; PtrT : Entity_Id; begin -- Do nothing in case of VM targets: the virtual machine will handle -- interfaces directly. if not Tagged_Type_Expansion then return; end if; pragma Assert (Nkind (N) = N_Identifier and then Nkind (Orig_Node) = N_Allocator); PtrT := Etype (Orig_Node); Dtyp := Available_View (Designated_Type (PtrT)); Etyp := Etype (Expression (Orig_Node)); if Is_Class_Wide_Type (Dtyp) and then Is_Interface (Dtyp) then -- If the type of the allocator expression is not an interface type -- we can generate code to reference the record component containing -- the pointer to the secondary dispatch table. if not Is_Interface (Etyp) then declare Saved_Typ : constant Entity_Id := Etype (Orig_Node); begin -- 1) Get access to the allocated object Rewrite (N, Make_Explicit_Dereference (Loc, Relocate_Node (N))); Set_Etype (N, Etyp); Set_Analyzed (N); -- 2) Add the conversion to displace the pointer to reference -- the secondary dispatch table. Rewrite (N, Convert_To (Dtyp, Relocate_Node (N))); Analyze_And_Resolve (N, Dtyp); -- 3) The 'access to the secondary dispatch table will be used -- as the value returned by the allocator. Rewrite (N, Make_Attribute_Reference (Loc, Prefix => Relocate_Node (N), Attribute_Name => Name_Access)); Set_Etype (N, Saved_Typ); Set_Analyzed (N); end; -- If the type of the allocator expression is an interface type we -- generate a run-time call to displace "this" to reference the -- component containing the pointer to the secondary dispatch table -- or else raise Constraint_Error if the actual object does not -- implement the target interface. This case corresponds to the -- following example: -- function Op (Obj : Iface_1'Class) return access Iface_2'Class is -- begin -- return new Iface_2'Class'(Obj); -- end Op; else Rewrite (N, Unchecked_Convert_To (PtrT, Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (RE_Displace), Loc), Parameter_Associations => New_List ( Unchecked_Convert_To (RTE (RE_Address), Relocate_Node (N)), New_Occurrence_Of (Elists.Node (First_Elmt (Access_Disp_Table (Etype (Base_Type (Dtyp))))), Loc))))); Analyze_And_Resolve (N, PtrT); end if; end if; end Displace_Allocator_Pointer; --------------------------------- -- Expand_Allocator_Expression -- --------------------------------- procedure Expand_Allocator_Expression (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Exp : constant Node_Id := Expression (Expression (N)); PtrT : constant Entity_Id := Etype (N); DesigT : constant Entity_Id := Designated_Type (PtrT); procedure Apply_Accessibility_Check (Ref : Node_Id; Built_In_Place : Boolean := False); -- Ada 2005 (AI-344): For an allocator with a class-wide designated -- type, generate an accessibility check to verify that the level of the -- type of the created object is not deeper than the level of the access -- type. If the type of the qualified expression is class-wide, then -- always generate the check (except in the case where it is known to be -- unnecessary, see comment below). Otherwise, only generate the check -- if the level of the qualified expression type is statically deeper -- than the access type. -- -- Although the static accessibility will generally have been performed -- as a legality check, it won't have been done in cases where the -- allocator appears in generic body, so a run-time check is needed in -- general. One special case is when the access type is declared in the -- same scope as the class-wide allocator, in which case the check can -- never fail, so it need not be generated. -- -- As an open issue, there seem to be cases where the static level -- associated with the class-wide object's underlying type is not -- sufficient to perform the proper accessibility check, such as for -- allocators in nested subprograms or accept statements initialized by -- class-wide formals when the actual originates outside at a deeper -- static level. The nested subprogram case might require passing -- accessibility levels along with class-wide parameters, and the task -- case seems to be an actual gap in the language rules that needs to -- be fixed by the ARG. ??? ------------------------------- -- Apply_Accessibility_Check -- ------------------------------- procedure Apply_Accessibility_Check (Ref : Node_Id; Built_In_Place : Boolean := False) is Pool_Id : constant Entity_Id := Associated_Storage_Pool (PtrT); Cond : Node_Id; Fin_Call : Node_Id; Free_Stmt : Node_Id; Obj_Ref : Node_Id; Stmts : List_Id; begin if Ada_Version >= Ada_2005 and then Is_Class_Wide_Type (DesigT) and then Tagged_Type_Expansion and then not Scope_Suppress.Suppress (Accessibility_Check) and then (Type_Access_Level (Etype (Exp)) > Type_Access_Level (PtrT) or else (Is_Class_Wide_Type (Etype (Exp)) and then Scope (PtrT) /= Current_Scope)) then -- If the allocator was built in place, Ref is already a reference -- to the access object initialized to the result of the allocator -- (see Exp_Ch6.Make_Build_In_Place_Call_In_Allocator). We call -- Remove_Side_Effects for cases where the build-in-place call may -- still be the prefix of the reference (to avoid generating -- duplicate calls). Otherwise, it is the entity associated with -- the object containing the address of the allocated object. if Built_In_Place then Remove_Side_Effects (Ref); Obj_Ref := New_Copy_Tree (Ref); else Obj_Ref := New_Occurrence_Of (Ref, Loc); end if; -- For access to interface types we must generate code to displace -- the pointer to the base of the object since the subsequent code -- references components located in the TSD of the object (which -- is associated with the primary dispatch table --see a-tags.ads) -- and also generates code invoking Free, which requires also a -- reference to the base of the unallocated object. if Is_Interface (DesigT) and then Tagged_Type_Expansion then Obj_Ref := Unchecked_Convert_To (Etype (Obj_Ref), Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (RE_Base_Address), Loc), Parameter_Associations => New_List ( Unchecked_Convert_To (RTE (RE_Address), New_Copy_Tree (Obj_Ref))))); end if; -- Step 1: Create the object clean up code Stmts := New_List; -- Deallocate the object if the accessibility check fails. This -- is done only on targets or profiles that support deallocation. -- Free (Obj_Ref); if RTE_Available (RE_Free) then Free_Stmt := Make_Free_Statement (Loc, New_Copy_Tree (Obj_Ref)); Set_Storage_Pool (Free_Stmt, Pool_Id); Append_To (Stmts, Free_Stmt); -- The target or profile cannot deallocate objects else Free_Stmt := Empty; end if; -- Finalize the object if applicable. Generate: -- [Deep_]Finalize (Obj_Ref.all); if Needs_Finalization (DesigT) and then not No_Heap_Finalization (PtrT) then Fin_Call := Make_Final_Call (Obj_Ref => Make_Explicit_Dereference (Loc, New_Copy (Obj_Ref)), Typ => DesigT); -- Guard against a missing [Deep_]Finalize when the designated -- type was not properly frozen. if No (Fin_Call) then Fin_Call := Make_Null_Statement (Loc); end if; -- When the target or profile supports deallocation, wrap the -- finalization call in a block to ensure proper deallocation -- even if finalization fails. Generate: -- begin -- -- exception -- when others => -- -- raise; -- end; if Present (Free_Stmt) then Fin_Call := Make_Block_Statement (Loc, Handled_Statement_Sequence => Make_Handled_Sequence_Of_Statements (Loc, Statements => New_List (Fin_Call), Exception_Handlers => New_List ( Make_Exception_Handler (Loc, Exception_Choices => New_List ( Make_Others_Choice (Loc)), Statements => New_List ( New_Copy_Tree (Free_Stmt), Make_Raise_Statement (Loc)))))); end if; Prepend_To (Stmts, Fin_Call); end if; -- Signal the accessibility failure through a Program_Error Append_To (Stmts, Make_Raise_Program_Error (Loc, Condition => New_Occurrence_Of (Standard_True, Loc), Reason => PE_Accessibility_Check_Failed)); -- Step 2: Create the accessibility comparison -- Generate: -- Ref'Tag Obj_Ref := Make_Attribute_Reference (Loc, Prefix => Obj_Ref, Attribute_Name => Name_Tag); -- For tagged types, determine the accessibility level by looking -- at the type specific data of the dispatch table. Generate: -- Type_Specific_Data (Address (Ref'Tag)).Access_Level if Tagged_Type_Expansion then Cond := Build_Get_Access_Level (Loc, Obj_Ref); -- Use a runtime call to determine the accessibility level when -- compiling on virtual machine targets. Generate: -- Get_Access_Level (Ref'Tag) else Cond := Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (RE_Get_Access_Level), Loc), Parameter_Associations => New_List (Obj_Ref)); end if; Cond := Make_Op_Gt (Loc, Left_Opnd => Cond, Right_Opnd => Make_Integer_Literal (Loc, Type_Access_Level (PtrT))); -- Due to the complexity and side effects of the check, utilize an -- if statement instead of the regular Program_Error circuitry. Insert_Action (N, Make_Implicit_If_Statement (N, Condition => Cond, Then_Statements => Stmts)); end if; end Apply_Accessibility_Check; -- Local variables Indic : constant Node_Id := Subtype_Mark (Expression (N)); T : constant Entity_Id := Entity (Indic); Adj_Call : Node_Id; Aggr_In_Place : Boolean; Node : Node_Id; Tag_Assign : Node_Id; Temp : Entity_Id; Temp_Decl : Node_Id; TagT : Entity_Id := Empty; -- Type used as source for tag assignment TagR : Node_Id := Empty; -- Target reference for tag assignment -- Start of processing for Expand_Allocator_Expression begin -- Handle call to C++ constructor if Is_CPP_Constructor_Call (Exp) then Make_CPP_Constructor_Call_In_Allocator (Allocator => N, Function_Call => Exp); return; end if; -- If we have: -- type A is access T1; -- X : A := new T2'(...); -- T1 and T2 can be different subtypes, and we might need to check -- both constraints. First check against the type of the qualified -- expression. Apply_Constraint_Check (Exp, T, No_Sliding => True); Apply_Predicate_Check (Exp, T); if Do_Range_Check (Exp) then Generate_Range_Check (Exp, T, CE_Range_Check_Failed); end if; -- A check is also needed in cases where the designated subtype is -- constrained and differs from the subtype given in the qualified -- expression. Note that the check on the qualified expression does -- not allow sliding, but this check does (a relaxation from Ada 83). if Is_Constrained (DesigT) and then not Subtypes_Statically_Match (T, DesigT) then Apply_Constraint_Check (Exp, DesigT, No_Sliding => False); Apply_Predicate_Check (Exp, DesigT); if Do_Range_Check (Exp) then Generate_Range_Check (Exp, DesigT, CE_Range_Check_Failed); end if; end if; if Nkind (Exp) = N_Raise_Constraint_Error then Rewrite (N, New_Copy (Exp)); Set_Etype (N, PtrT); return; end if; -- In the case of an Ada 2012 allocator whose initial value comes from a -- function call, pass "the accessibility level determined by the point -- of call" (AI05-0234) to the function. Conceptually, this belongs in -- Expand_Call but it couldn't be done there (because the Etype of the -- allocator wasn't set then) so we generate the parameter here. See -- the Boolean variable Defer in (a block within) Expand_Call. if Ada_Version >= Ada_2012 and then Nkind (Exp) = N_Function_Call then declare Subp : Entity_Id; begin if Nkind (Name (Exp)) = N_Explicit_Dereference then Subp := Designated_Type (Etype (Prefix (Name (Exp)))); else Subp := Entity (Name (Exp)); end if; Subp := Ultimate_Alias (Subp); if Present (Extra_Accessibility_Of_Result (Subp)) then Add_Extra_Actual_To_Call (Subprogram_Call => Exp, Extra_Formal => Extra_Accessibility_Of_Result (Subp), Extra_Actual => Dynamic_Accessibility_Level (PtrT)); end if; end; end if; Aggr_In_Place := Is_Delayed_Aggregate (Exp); -- Case of tagged type or type requiring finalization if Is_Tagged_Type (T) or else Needs_Finalization (T) then -- Ada 2005 (AI-318-02): If the initialization expression is a call -- to a build-in-place function, then access to the allocated object -- must be passed to the function. if Is_Build_In_Place_Function_Call (Exp) then Make_Build_In_Place_Call_In_Allocator (N, Exp); Apply_Accessibility_Check (N, Built_In_Place => True); return; -- Ada 2005 (AI-318-02): Specialization of the previous case for -- expressions containing a build-in-place function call whose -- returned object covers interface types, and Expr has calls to -- Ada.Tags.Displace to displace the pointer to the returned build- -- in-place object to reference the secondary dispatch table of a -- covered interface type. elsif Present (Unqual_BIP_Iface_Function_Call (Exp)) then Make_Build_In_Place_Iface_Call_In_Allocator (N, Exp); Apply_Accessibility_Check (N, Built_In_Place => True); return; end if; -- Actions inserted before: -- Temp : constant ptr_T := new T'(Expression); -- Temp._tag = T'tag; -- when not class-wide -- [Deep_]Adjust (Temp.all); -- We analyze by hand the new internal allocator to avoid any -- recursion and inappropriate call to Initialize. -- We don't want to remove side effects when the expression must be -- built in place. In the case of a build-in-place function call, -- that could lead to a duplication of the call, which was already -- substituted for the allocator. if not Aggr_In_Place then Remove_Side_Effects (Exp); end if; Temp := Make_Temporary (Loc, 'P', N); -- For a class wide allocation generate the following code: -- type Equiv_Record is record ... end record; -- implicit subtype CW is ; -- temp : PtrT := new CW'(CW!(expr)); if Is_Class_Wide_Type (T) then Expand_Subtype_From_Expr (Empty, T, Indic, Exp); -- Ada 2005 (AI-251): If the expression is a class-wide interface -- object we generate code to move up "this" to reference the -- base of the object before allocating the new object. -- Note that Exp'Address is recursively expanded into a call -- to Base_Address (Exp.Tag) if Is_Class_Wide_Type (Etype (Exp)) and then Is_Interface (Etype (Exp)) and then Tagged_Type_Expansion then Set_Expression (Expression (N), Unchecked_Convert_To (Entity (Indic), Make_Explicit_Dereference (Loc, Unchecked_Convert_To (RTE (RE_Tag_Ptr), Make_Attribute_Reference (Loc, Prefix => Exp, Attribute_Name => Name_Address))))); else Set_Expression (Expression (N), Unchecked_Convert_To (Entity (Indic), Exp)); end if; Analyze_And_Resolve (Expression (N), Entity (Indic)); end if; -- Processing for allocators returning non-interface types if not Is_Interface (Directly_Designated_Type (PtrT)) then if Aggr_In_Place then Temp_Decl := Make_Object_Declaration (Loc, Defining_Identifier => Temp, Object_Definition => New_Occurrence_Of (PtrT, Loc), Expression => Make_Allocator (Loc, Expression => New_Occurrence_Of (Etype (Exp), Loc))); -- Copy the Comes_From_Source flag for the allocator we just -- built, since logically this allocator is a replacement of -- the original allocator node. This is for proper handling of -- restriction No_Implicit_Heap_Allocations. Preserve_Comes_From_Source (Expression (Temp_Decl), N); Set_No_Initialization (Expression (Temp_Decl)); Insert_Action (N, Temp_Decl); Build_Allocate_Deallocate_Proc (Temp_Decl, True); Convert_Aggr_In_Allocator (N, Temp_Decl, Exp); else Node := Relocate_Node (N); Set_Analyzed (Node); Temp_Decl := Make_Object_Declaration (Loc, Defining_Identifier => Temp, Constant_Present => True, Object_Definition => New_Occurrence_Of (PtrT, Loc), Expression => Node); Insert_Action (N, Temp_Decl); Build_Allocate_Deallocate_Proc (Temp_Decl, True); end if; -- Ada 2005 (AI-251): Handle allocators whose designated type is an -- interface type. In this case we use the type of the qualified -- expression to allocate the object. else declare Def_Id : constant Entity_Id := Make_Temporary (Loc, 'T'); New_Decl : Node_Id; begin New_Decl := Make_Full_Type_Declaration (Loc, Defining_Identifier => Def_Id, Type_Definition => Make_Access_To_Object_Definition (Loc, All_Present => True, Null_Exclusion_Present => False, Constant_Present => Is_Access_Constant (Etype (N)), Subtype_Indication => New_Occurrence_Of (Etype (Exp), Loc))); Insert_Action (N, New_Decl); -- Inherit the allocation-related attributes from the original -- access type. Set_Finalization_Master (Def_Id, Finalization_Master (PtrT)); Set_Associated_Storage_Pool (Def_Id, Associated_Storage_Pool (PtrT)); -- Declare the object using the previous type declaration if Aggr_In_Place then Temp_Decl := Make_Object_Declaration (Loc, Defining_Identifier => Temp, Object_Definition => New_Occurrence_Of (Def_Id, Loc), Expression => Make_Allocator (Loc, New_Occurrence_Of (Etype (Exp), Loc))); -- Copy the Comes_From_Source flag for the allocator we just -- built, since logically this allocator is a replacement of -- the original allocator node. This is for proper handling -- of restriction No_Implicit_Heap_Allocations. Set_Comes_From_Source (Expression (Temp_Decl), Comes_From_Source (N)); Set_No_Initialization (Expression (Temp_Decl)); Insert_Action (N, Temp_Decl); Build_Allocate_Deallocate_Proc (Temp_Decl, True); Convert_Aggr_In_Allocator (N, Temp_Decl, Exp); else Node := Relocate_Node (N); Set_Analyzed (Node); Temp_Decl := Make_Object_Declaration (Loc, Defining_Identifier => Temp, Constant_Present => True, Object_Definition => New_Occurrence_Of (Def_Id, Loc), Expression => Node); Insert_Action (N, Temp_Decl); Build_Allocate_Deallocate_Proc (Temp_Decl, True); end if; -- Generate an additional object containing the address of the -- returned object. The type of this second object declaration -- is the correct type required for the common processing that -- is still performed by this subprogram. The displacement of -- this pointer to reference the component associated with the -- interface type will be done at the end of common processing. New_Decl := Make_Object_Declaration (Loc, Defining_Identifier => Make_Temporary (Loc, 'P'), Object_Definition => New_Occurrence_Of (PtrT, Loc), Expression => Unchecked_Convert_To (PtrT, New_Occurrence_Of (Temp, Loc))); Insert_Action (N, New_Decl); Temp_Decl := New_Decl; Temp := Defining_Identifier (New_Decl); end; end if; -- Generate the tag assignment -- Suppress the tag assignment for VM targets because VM tags are -- represented implicitly in objects. if not Tagged_Type_Expansion then null; -- Ada 2005 (AI-251): Suppress the tag assignment with class-wide -- interface objects because in this case the tag does not change. elsif Is_Interface (Directly_Designated_Type (Etype (N))) then pragma Assert (Is_Class_Wide_Type (Directly_Designated_Type (Etype (N)))); null; elsif Is_Tagged_Type (T) and then not Is_Class_Wide_Type (T) then TagT := T; TagR := Make_Explicit_Dereference (Loc, Prefix => New_Occurrence_Of (Temp, Loc)); elsif Is_Private_Type (T) and then Is_Tagged_Type (Underlying_Type (T)) then TagT := Underlying_Type (T); TagR := Unchecked_Convert_To (Underlying_Type (T), Make_Explicit_Dereference (Loc, Prefix => New_Occurrence_Of (Temp, Loc))); end if; if Present (TagT) then declare Full_T : constant Entity_Id := Underlying_Type (TagT); begin Tag_Assign := Make_Assignment_Statement (Loc, Name => Make_Selected_Component (Loc, Prefix => TagR, Selector_Name => New_Occurrence_Of (First_Tag_Component (Full_T), Loc)), Expression => Unchecked_Convert_To (RTE (RE_Tag), New_Occurrence_Of (Elists.Node (First_Elmt (Access_Disp_Table (Full_T))), Loc))); end; -- The previous assignment has to be done in any case Set_Assignment_OK (Name (Tag_Assign)); Insert_Action (N, Tag_Assign); end if; -- Generate an Adjust call if the object will be moved. In Ada 2005, -- the object may be inherently limited, in which case there is no -- Adjust procedure, and the object is built in place. In Ada 95, the -- object can be limited but not inherently limited if this allocator -- came from a return statement (we're allocating the result on the -- secondary stack). In that case, the object will be moved, so we do -- want to Adjust. However, if it's a nonlimited build-in-place -- function call, Adjust is not wanted. if Needs_Finalization (DesigT) and then Needs_Finalization (T) and then not Aggr_In_Place and then not Is_Limited_View (T) and then not Alloc_For_BIP_Return (N) and then not Is_Build_In_Place_Function_Call (Expression (N)) then -- An unchecked conversion is needed in the classwide case because -- the designated type can be an ancestor of the subtype mark of -- the allocator. Adj_Call := Make_Adjust_Call (Obj_Ref => Unchecked_Convert_To (T, Make_Explicit_Dereference (Loc, Prefix => New_Occurrence_Of (Temp, Loc))), Typ => T); if Present (Adj_Call) then Insert_Action (N, Adj_Call); end if; end if; -- Note: the accessibility check must be inserted after the call to -- [Deep_]Adjust to ensure proper completion of the assignment. Apply_Accessibility_Check (Temp); Rewrite (N, New_Occurrence_Of (Temp, Loc)); Analyze_And_Resolve (N, PtrT); -- Ada 2005 (AI-251): Displace the pointer to reference the record -- component containing the secondary dispatch table of the interface -- type. if Is_Interface (Directly_Designated_Type (PtrT)) then Displace_Allocator_Pointer (N); end if; -- Always force the generation of a temporary for aggregates when -- generating C code, to simplify the work in the code generator. elsif Aggr_In_Place or else (Modify_Tree_For_C and then Nkind (Exp) = N_Aggregate) then Temp := Make_Temporary (Loc, 'P', N); Temp_Decl := Make_Object_Declaration (Loc, Defining_Identifier => Temp, Object_Definition => New_Occurrence_Of (PtrT, Loc), Expression => Make_Allocator (Loc, Expression => New_Occurrence_Of (Etype (Exp), Loc))); -- Copy the Comes_From_Source flag for the allocator we just built, -- since logically this allocator is a replacement of the original -- allocator node. This is for proper handling of restriction -- No_Implicit_Heap_Allocations. Set_Comes_From_Source (Expression (Temp_Decl), Comes_From_Source (N)); Set_No_Initialization (Expression (Temp_Decl)); Insert_Action (N, Temp_Decl); Build_Allocate_Deallocate_Proc (Temp_Decl, True); Convert_Aggr_In_Allocator (N, Temp_Decl, Exp); Rewrite (N, New_Occurrence_Of (Temp, Loc)); Analyze_And_Resolve (N, PtrT); elsif Is_Access_Type (T) and then Can_Never_Be_Null (T) then Install_Null_Excluding_Check (Exp); elsif Is_Access_Type (DesigT) and then Nkind (Exp) = N_Allocator and then Nkind (Expression (Exp)) /= N_Qualified_Expression then -- Apply constraint to designated subtype indication Apply_Constraint_Check (Expression (Exp), Designated_Type (DesigT), No_Sliding => True); if Nkind (Expression (Exp)) = N_Raise_Constraint_Error then -- Propagate constraint_error to enclosing allocator Rewrite (Exp, New_Copy (Expression (Exp))); end if; else Build_Allocate_Deallocate_Proc (N, True); -- For an access to unconstrained packed array, GIGI needs to see an -- expression with a constrained subtype in order to compute the -- proper size for the allocator. if Is_Array_Type (T) and then not Is_Constrained (T) and then Is_Packed (T) then declare ConstrT : constant Entity_Id := Make_Temporary (Loc, 'A'); Internal_Exp : constant Node_Id := Relocate_Node (Exp); begin Insert_Action (Exp, Make_Subtype_Declaration (Loc, Defining_Identifier => ConstrT, Subtype_Indication => Make_Subtype_From_Expr (Internal_Exp, T))); Freeze_Itype (ConstrT, Exp); Rewrite (Exp, OK_Convert_To (ConstrT, Internal_Exp)); end; end if; -- Ada 2005 (AI-318-02): If the initialization expression is a call -- to a build-in-place function, then access to the allocated object -- must be passed to the function. if Is_Build_In_Place_Function_Call (Exp) then Make_Build_In_Place_Call_In_Allocator (N, Exp); end if; end if; exception when RE_Not_Available => return; end Expand_Allocator_Expression; ----------------------------- -- Expand_Array_Comparison -- ----------------------------- -- Expansion is only required in the case of array types. For the unpacked -- case, an appropriate runtime routine is called. For packed cases, and -- also in some other cases where a runtime routine cannot be called, the -- form of the expansion is: -- [body for greater_nn; boolean_expression] -- The body is built by Make_Array_Comparison_Op, and the form of the -- Boolean expression depends on the operator involved. procedure Expand_Array_Comparison (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Op1 : Node_Id := Left_Opnd (N); Op2 : Node_Id := Right_Opnd (N); Typ1 : constant Entity_Id := Base_Type (Etype (Op1)); Ctyp : constant Entity_Id := Component_Type (Typ1); Expr : Node_Id; Func_Body : Node_Id; Func_Name : Entity_Id; Comp : RE_Id; Byte_Addressable : constant Boolean := System_Storage_Unit = Byte'Size; -- True for byte addressable target function Length_Less_Than_4 (Opnd : Node_Id) return Boolean; -- Returns True if the length of the given operand is known to be less -- than 4. Returns False if this length is known to be four or greater -- or is not known at compile time. ------------------------ -- Length_Less_Than_4 -- ------------------------ function Length_Less_Than_4 (Opnd : Node_Id) return Boolean is Otyp : constant Entity_Id := Etype (Opnd); begin if Ekind (Otyp) = E_String_Literal_Subtype then return String_Literal_Length (Otyp) < 4; else declare Ityp : constant Entity_Id := Etype (First_Index (Otyp)); Lo : constant Node_Id := Type_Low_Bound (Ityp); Hi : constant Node_Id := Type_High_Bound (Ityp); Lov : Uint; Hiv : Uint; begin if Compile_Time_Known_Value (Lo) then Lov := Expr_Value (Lo); else return False; end if; if Compile_Time_Known_Value (Hi) then Hiv := Expr_Value (Hi); else return False; end if; return Hiv < Lov + 3; end; end if; end Length_Less_Than_4; -- Start of processing for Expand_Array_Comparison begin -- Deal first with unpacked case, where we can call a runtime routine -- except that we avoid this for targets for which are not addressable -- by bytes. if not Is_Bit_Packed_Array (Typ1) and then Byte_Addressable then -- The call we generate is: -- Compare_Array_xn[_Unaligned] -- (left'address, right'address, left'length, right'length) 0 -- x = U for unsigned, S for signed -- n = 8,16,32,64 for component size -- Add _Unaligned if length < 4 and component size is 8. -- is the standard comparison operator if Component_Size (Typ1) = 8 then if Length_Less_Than_4 (Op1) or else Length_Less_Than_4 (Op2) then if Is_Unsigned_Type (Ctyp) then Comp := RE_Compare_Array_U8_Unaligned; else Comp := RE_Compare_Array_S8_Unaligned; end if; else if Is_Unsigned_Type (Ctyp) then Comp := RE_Compare_Array_U8; else Comp := RE_Compare_Array_S8; end if; end if; elsif Component_Size (Typ1) = 16 then if Is_Unsigned_Type (Ctyp) then Comp := RE_Compare_Array_U16; else Comp := RE_Compare_Array_S16; end if; elsif Component_Size (Typ1) = 32 then if Is_Unsigned_Type (Ctyp) then Comp := RE_Compare_Array_U32; else Comp := RE_Compare_Array_S32; end if; else pragma Assert (Component_Size (Typ1) = 64); if Is_Unsigned_Type (Ctyp) then Comp := RE_Compare_Array_U64; else Comp := RE_Compare_Array_S64; end if; end if; if RTE_Available (Comp) then -- Expand to a call only if the runtime function is available, -- otherwise fall back to inline code. Remove_Side_Effects (Op1, Name_Req => True); Remove_Side_Effects (Op2, Name_Req => True); Rewrite (Op1, Make_Function_Call (Sloc (Op1), Name => New_Occurrence_Of (RTE (Comp), Loc), Parameter_Associations => New_List ( Make_Attribute_Reference (Loc, Prefix => Relocate_Node (Op1), Attribute_Name => Name_Address), Make_Attribute_Reference (Loc, Prefix => Relocate_Node (Op2), Attribute_Name => Name_Address), Make_Attribute_Reference (Loc, Prefix => Relocate_Node (Op1), Attribute_Name => Name_Length), Make_Attribute_Reference (Loc, Prefix => Relocate_Node (Op2), Attribute_Name => Name_Length)))); Rewrite (Op2, Make_Integer_Literal (Sloc (Op2), Intval => Uint_0)); Analyze_And_Resolve (Op1, Standard_Integer); Analyze_And_Resolve (Op2, Standard_Integer); return; end if; end if; -- Cases where we cannot make runtime call -- For (a <= b) we convert to not (a > b) if Chars (N) = Name_Op_Le then Rewrite (N, Make_Op_Not (Loc, Right_Opnd => Make_Op_Gt (Loc, Left_Opnd => Op1, Right_Opnd => Op2))); Analyze_And_Resolve (N, Standard_Boolean); return; -- For < the Boolean expression is -- greater__nn (op2, op1) elsif Chars (N) = Name_Op_Lt then Func_Body := Make_Array_Comparison_Op (Typ1, N); -- Switch operands Op1 := Right_Opnd (N); Op2 := Left_Opnd (N); -- For (a >= b) we convert to not (a < b) elsif Chars (N) = Name_Op_Ge then Rewrite (N, Make_Op_Not (Loc, Right_Opnd => Make_Op_Lt (Loc, Left_Opnd => Op1, Right_Opnd => Op2))); Analyze_And_Resolve (N, Standard_Boolean); return; -- For > the Boolean expression is -- greater__nn (op1, op2) else pragma Assert (Chars (N) = Name_Op_Gt); Func_Body := Make_Array_Comparison_Op (Typ1, N); end if; Func_Name := Defining_Unit_Name (Specification (Func_Body)); Expr := Make_Function_Call (Loc, Name => New_Occurrence_Of (Func_Name, Loc), Parameter_Associations => New_List (Op1, Op2)); Insert_Action (N, Func_Body); Rewrite (N, Expr); Analyze_And_Resolve (N, Standard_Boolean); end Expand_Array_Comparison; --------------------------- -- Expand_Array_Equality -- --------------------------- -- Expand an equality function for multi-dimensional arrays. Here is an -- example of such a function for Nb_Dimension = 2 -- function Enn (A : atyp; B : btyp) return boolean is -- begin -- if (A'length (1) = 0 or else A'length (2) = 0) -- and then -- (B'length (1) = 0 or else B'length (2) = 0) -- then -- return True; -- RM 4.5.2(22) -- end if; -- if A'length (1) /= B'length (1) -- or else -- A'length (2) /= B'length (2) -- then -- return False; -- RM 4.5.2(23) -- end if; -- declare -- A1 : Index_T1 := A'first (1); -- B1 : Index_T1 := B'first (1); -- begin -- loop -- declare -- A2 : Index_T2 := A'first (2); -- B2 : Index_T2 := B'first (2); -- begin -- loop -- if A (A1, A2) /= B (B1, B2) then -- return False; -- end if; -- exit when A2 = A'last (2); -- A2 := Index_T2'succ (A2); -- B2 := Index_T2'succ (B2); -- end loop; -- end; -- exit when A1 = A'last (1); -- A1 := Index_T1'succ (A1); -- B1 := Index_T1'succ (B1); -- end loop; -- end; -- return true; -- end Enn; -- Note on the formal types used (atyp and btyp). If either of the arrays -- is of a private type, we use the underlying type, and do an unchecked -- conversion of the actual. If either of the arrays has a bound depending -- on a discriminant, then we use the base type since otherwise we have an -- escaped discriminant in the function. -- If both arrays are constrained and have the same bounds, we can generate -- a loop with an explicit iteration scheme using a 'Range attribute over -- the first array. function Expand_Array_Equality (Nod : Node_Id; Lhs : Node_Id; Rhs : Node_Id; Bodies : List_Id; Typ : Entity_Id) return Node_Id is Loc : constant Source_Ptr := Sloc (Nod); Decls : constant List_Id := New_List; Index_List1 : constant List_Id := New_List; Index_List2 : constant List_Id := New_List; First_Idx : Node_Id; Formals : List_Id; Func_Name : Entity_Id; Func_Body : Node_Id; A : constant Entity_Id := Make_Defining_Identifier (Loc, Name_uA); B : constant Entity_Id := Make_Defining_Identifier (Loc, Name_uB); Ltyp : Entity_Id; Rtyp : Entity_Id; -- The parameter types to be used for the formals New_Lhs : Node_Id; New_Rhs : Node_Id; -- The LHS and RHS converted to the parameter types function Arr_Attr (Arr : Entity_Id; Nam : Name_Id; Num : Int) return Node_Id; -- This builds the attribute reference Arr'Nam (Expr) function Component_Equality (Typ : Entity_Id) return Node_Id; -- Create one statement to compare corresponding components, designated -- by a full set of indexes. function Get_Arg_Type (N : Node_Id) return Entity_Id; -- Given one of the arguments, computes the appropriate type to be used -- for that argument in the corresponding function formal function Handle_One_Dimension (N : Int; Index : Node_Id) return Node_Id; -- This procedure returns the following code -- -- declare -- Bn : Index_T := B'First (N); -- begin -- loop -- xxx -- exit when An = A'Last (N); -- An := Index_T'Succ (An) -- Bn := Index_T'Succ (Bn) -- end loop; -- end; -- -- If both indexes are constrained and identical, the procedure -- returns a simpler loop: -- -- for An in A'Range (N) loop -- xxx -- end loop -- -- N is the dimension for which we are generating a loop. Index is the -- N'th index node, whose Etype is Index_Type_n in the above code. The -- xxx statement is either the loop or declare for the next dimension -- or if this is the last dimension the comparison of corresponding -- components of the arrays. -- -- The actual way the code works is to return the comparison of -- corresponding components for the N+1 call. That's neater. function Test_Empty_Arrays return Node_Id; -- This function constructs the test for both arrays being empty -- (A'length (1) = 0 or else A'length (2) = 0 or else ...) -- and then -- (B'length (1) = 0 or else B'length (2) = 0 or else ...) function Test_Lengths_Correspond return Node_Id; -- This function constructs the test for arrays having different lengths -- in at least one index position, in which case the resulting code is: -- A'length (1) /= B'length (1) -- or else -- A'length (2) /= B'length (2) -- or else -- ... -------------- -- Arr_Attr -- -------------- function Arr_Attr (Arr : Entity_Id; Nam : Name_Id; Num : Int) return Node_Id is begin return Make_Attribute_Reference (Loc, Attribute_Name => Nam, Prefix => New_Occurrence_Of (Arr, Loc), Expressions => New_List (Make_Integer_Literal (Loc, Num))); end Arr_Attr; ------------------------ -- Component_Equality -- ------------------------ function Component_Equality (Typ : Entity_Id) return Node_Id is Test : Node_Id; L, R : Node_Id; begin -- if a(i1...) /= b(j1...) then return false; end if; L := Make_Indexed_Component (Loc, Prefix => Make_Identifier (Loc, Chars (A)), Expressions => Index_List1); R := Make_Indexed_Component (Loc, Prefix => Make_Identifier (Loc, Chars (B)), Expressions => Index_List2); Test := Expand_Composite_Equality (Nod, Component_Type (Typ), L, R, Decls); -- If some (sub)component is an unchecked_union, the whole operation -- will raise program error. if Nkind (Test) = N_Raise_Program_Error then -- This node is going to be inserted at a location where a -- statement is expected: clear its Etype so analysis will set -- it to the expected Standard_Void_Type. Set_Etype (Test, Empty); return Test; else return Make_Implicit_If_Statement (Nod, Condition => Make_Op_Not (Loc, Right_Opnd => Test), Then_Statements => New_List ( Make_Simple_Return_Statement (Loc, Expression => New_Occurrence_Of (Standard_False, Loc)))); end if; end Component_Equality; ------------------ -- Get_Arg_Type -- ------------------ function Get_Arg_Type (N : Node_Id) return Entity_Id is T : Entity_Id; X : Node_Id; begin T := Etype (N); if No (T) then return Typ; else T := Underlying_Type (T); X := First_Index (T); while Present (X) loop if Denotes_Discriminant (Type_Low_Bound (Etype (X))) or else Denotes_Discriminant (Type_High_Bound (Etype (X))) then T := Base_Type (T); exit; end if; Next_Index (X); end loop; return T; end if; end Get_Arg_Type; -------------------------- -- Handle_One_Dimension -- --------------------------- function Handle_One_Dimension (N : Int; Index : Node_Id) return Node_Id is Need_Separate_Indexes : constant Boolean := Ltyp /= Rtyp or else not Is_Constrained (Ltyp); -- If the index types are identical, and we are working with -- constrained types, then we can use the same index for both -- of the arrays. An : constant Entity_Id := Make_Temporary (Loc, 'A'); Bn : Entity_Id; Index_T : Entity_Id; Stm_List : List_Id; Loop_Stm : Node_Id; begin if N > Number_Dimensions (Ltyp) then return Component_Equality (Ltyp); end if; -- Case where we generate a loop Index_T := Base_Type (Etype (Index)); if Need_Separate_Indexes then Bn := Make_Temporary (Loc, 'B'); else Bn := An; end if; Append (New_Occurrence_Of (An, Loc), Index_List1); Append (New_Occurrence_Of (Bn, Loc), Index_List2); Stm_List := New_List ( Handle_One_Dimension (N + 1, Next_Index (Index))); if Need_Separate_Indexes then -- Generate guard for loop, followed by increments of indexes Append_To (Stm_List, Make_Exit_Statement (Loc, Condition => Make_Op_Eq (Loc, Left_Opnd => New_Occurrence_Of (An, Loc), Right_Opnd => Arr_Attr (A, Name_Last, N)))); Append_To (Stm_List, Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (An, Loc), Expression => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Index_T, Loc), Attribute_Name => Name_Succ, Expressions => New_List ( New_Occurrence_Of (An, Loc))))); Append_To (Stm_List, Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (Bn, Loc), Expression => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Index_T, Loc), Attribute_Name => Name_Succ, Expressions => New_List ( New_Occurrence_Of (Bn, Loc))))); end if; -- If separate indexes, we need a declare block for An and Bn, and a -- loop without an iteration scheme. if Need_Separate_Indexes then Loop_Stm := Make_Implicit_Loop_Statement (Nod, Statements => Stm_List); return Make_Block_Statement (Loc, Declarations => New_List ( Make_Object_Declaration (Loc, Defining_Identifier => An, Object_Definition => New_Occurrence_Of (Index_T, Loc), Expression => Arr_Attr (A, Name_First, N)), Make_Object_Declaration (Loc, Defining_Identifier => Bn, Object_Definition => New_Occurrence_Of (Index_T, Loc), Expression => Arr_Attr (B, Name_First, N))), Handled_Statement_Sequence => Make_Handled_Sequence_Of_Statements (Loc, Statements => New_List (Loop_Stm))); -- If no separate indexes, return loop statement with explicit -- iteration scheme on its own. else Loop_Stm := Make_Implicit_Loop_Statement (Nod, Statements => Stm_List, Iteration_Scheme => Make_Iteration_Scheme (Loc, Loop_Parameter_Specification => Make_Loop_Parameter_Specification (Loc, Defining_Identifier => An, Discrete_Subtype_Definition => Arr_Attr (A, Name_Range, N)))); return Loop_Stm; end if; end Handle_One_Dimension; ----------------------- -- Test_Empty_Arrays -- ----------------------- function Test_Empty_Arrays return Node_Id is Alist : Node_Id; Blist : Node_Id; Atest : Node_Id; Btest : Node_Id; begin Alist := Empty; Blist := Empty; for J in 1 .. Number_Dimensions (Ltyp) loop Atest := Make_Op_Eq (Loc, Left_Opnd => Arr_Attr (A, Name_Length, J), Right_Opnd => Make_Integer_Literal (Loc, 0)); Btest := Make_Op_Eq (Loc, Left_Opnd => Arr_Attr (B, Name_Length, J), Right_Opnd => Make_Integer_Literal (Loc, 0)); if No (Alist) then Alist := Atest; Blist := Btest; else Alist := Make_Or_Else (Loc, Left_Opnd => Relocate_Node (Alist), Right_Opnd => Atest); Blist := Make_Or_Else (Loc, Left_Opnd => Relocate_Node (Blist), Right_Opnd => Btest); end if; end loop; return Make_And_Then (Loc, Left_Opnd => Alist, Right_Opnd => Blist); end Test_Empty_Arrays; ----------------------------- -- Test_Lengths_Correspond -- ----------------------------- function Test_Lengths_Correspond return Node_Id is Result : Node_Id; Rtest : Node_Id; begin Result := Empty; for J in 1 .. Number_Dimensions (Ltyp) loop Rtest := Make_Op_Ne (Loc, Left_Opnd => Arr_Attr (A, Name_Length, J), Right_Opnd => Arr_Attr (B, Name_Length, J)); if No (Result) then Result := Rtest; else Result := Make_Or_Else (Loc, Left_Opnd => Relocate_Node (Result), Right_Opnd => Rtest); end if; end loop; return Result; end Test_Lengths_Correspond; -- Start of processing for Expand_Array_Equality begin Ltyp := Get_Arg_Type (Lhs); Rtyp := Get_Arg_Type (Rhs); -- For now, if the argument types are not the same, go to the base type, -- since the code assumes that the formals have the same type. This is -- fixable in future ??? if Ltyp /= Rtyp then Ltyp := Base_Type (Ltyp); Rtyp := Base_Type (Rtyp); pragma Assert (Ltyp = Rtyp); end if; -- If the array type is distinct from the type of the arguments, it -- is the full view of a private type. Apply an unchecked conversion -- to ensure that analysis of the code below succeeds. if No (Etype (Lhs)) or else Base_Type (Etype (Lhs)) /= Base_Type (Ltyp) then New_Lhs := OK_Convert_To (Ltyp, Lhs); else New_Lhs := Lhs; end if; if No (Etype (Rhs)) or else Base_Type (Etype (Rhs)) /= Base_Type (Rtyp) then New_Rhs := OK_Convert_To (Rtyp, Rhs); else New_Rhs := Rhs; end if; First_Idx := First_Index (Ltyp); -- If optimization is enabled and the array boils down to a couple of -- consecutive elements, generate a simple conjunction of comparisons -- which should be easier to optimize by the code generator. if Optimization_Level > 0 and then Ltyp = Rtyp and then Is_Constrained (Ltyp) and then Number_Dimensions (Ltyp) = 1 and then Nkind (First_Idx) = N_Range and then Compile_Time_Known_Value (Low_Bound (First_Idx)) and then Compile_Time_Known_Value (High_Bound (First_Idx)) and then Expr_Value (High_Bound (First_Idx)) = Expr_Value (Low_Bound (First_Idx)) + 1 then declare Ctyp : constant Entity_Id := Component_Type (Ltyp); L, R : Node_Id; TestL, TestH : Node_Id; begin L := Make_Indexed_Component (Loc, Prefix => New_Copy_Tree (New_Lhs), Expressions => New_List (New_Copy_Tree (Low_Bound (First_Idx)))); R := Make_Indexed_Component (Loc, Prefix => New_Copy_Tree (New_Rhs), Expressions => New_List (New_Copy_Tree (Low_Bound (First_Idx)))); TestL := Expand_Composite_Equality (Nod, Ctyp, L, R, Bodies); L := Make_Indexed_Component (Loc, Prefix => New_Lhs, Expressions => New_List (New_Copy_Tree (High_Bound (First_Idx)))); R := Make_Indexed_Component (Loc, Prefix => New_Rhs, Expressions => New_List (New_Copy_Tree (High_Bound (First_Idx)))); TestH := Expand_Composite_Equality (Nod, Ctyp, L, R, Bodies); return Make_And_Then (Loc, Left_Opnd => TestL, Right_Opnd => TestH); end; end if; -- Build list of formals for function Formals := New_List ( Make_Parameter_Specification (Loc, Defining_Identifier => A, Parameter_Type => New_Occurrence_Of (Ltyp, Loc)), Make_Parameter_Specification (Loc, Defining_Identifier => B, Parameter_Type => New_Occurrence_Of (Rtyp, Loc))); Func_Name := Make_Temporary (Loc, 'E'); -- Build statement sequence for function Func_Body := Make_Subprogram_Body (Loc, Specification => Make_Function_Specification (Loc, Defining_Unit_Name => Func_Name, Parameter_Specifications => Formals, Result_Definition => New_Occurrence_Of (Standard_Boolean, Loc)), Declarations => Decls, Handled_Statement_Sequence => Make_Handled_Sequence_Of_Statements (Loc, Statements => New_List ( Make_Implicit_If_Statement (Nod, Condition => Test_Empty_Arrays, Then_Statements => New_List ( Make_Simple_Return_Statement (Loc, Expression => New_Occurrence_Of (Standard_True, Loc)))), Make_Implicit_If_Statement (Nod, Condition => Test_Lengths_Correspond, Then_Statements => New_List ( Make_Simple_Return_Statement (Loc, Expression => New_Occurrence_Of (Standard_False, Loc)))), Handle_One_Dimension (1, First_Idx), Make_Simple_Return_Statement (Loc, Expression => New_Occurrence_Of (Standard_True, Loc))))); Set_Has_Completion (Func_Name, True); Set_Is_Inlined (Func_Name); Append_To (Bodies, Func_Body); return Make_Function_Call (Loc, Name => New_Occurrence_Of (Func_Name, Loc), Parameter_Associations => New_List (New_Lhs, New_Rhs)); end Expand_Array_Equality; ----------------------------- -- Expand_Boolean_Operator -- ----------------------------- -- Note that we first get the actual subtypes of the operands, since we -- always want to deal with types that have bounds. procedure Expand_Boolean_Operator (N : Node_Id) is Typ : constant Entity_Id := Etype (N); begin -- Special case of bit packed array where both operands are known to be -- properly aligned. In this case we use an efficient run time routine -- to carry out the operation (see System.Bit_Ops). if Is_Bit_Packed_Array (Typ) and then not Is_Possibly_Unaligned_Object (Left_Opnd (N)) and then not Is_Possibly_Unaligned_Object (Right_Opnd (N)) then Expand_Packed_Boolean_Operator (N); return; end if; -- For the normal non-packed case, the general expansion is to build -- function for carrying out the comparison (use Make_Boolean_Array_Op) -- and then inserting it into the tree. The original operator node is -- then rewritten as a call to this function. We also use this in the -- packed case if either operand is a possibly unaligned object. declare Loc : constant Source_Ptr := Sloc (N); L : constant Node_Id := Relocate_Node (Left_Opnd (N)); R : Node_Id := Relocate_Node (Right_Opnd (N)); Func_Body : Node_Id; Func_Name : Entity_Id; begin Convert_To_Actual_Subtype (L); Convert_To_Actual_Subtype (R); Ensure_Defined (Etype (L), N); Ensure_Defined (Etype (R), N); Apply_Length_Check (R, Etype (L)); if Nkind (N) = N_Op_Xor then R := Duplicate_Subexpr (R); Silly_Boolean_Array_Xor_Test (N, R, Etype (L)); end if; if Nkind (Parent (N)) = N_Assignment_Statement and then Safe_In_Place_Array_Op (Name (Parent (N)), L, R) then Build_Boolean_Array_Proc_Call (Parent (N), L, R); elsif Nkind (Parent (N)) = N_Op_Not and then Nkind (N) = N_Op_And and then Nkind (Parent (Parent (N))) = N_Assignment_Statement and then Safe_In_Place_Array_Op (Name (Parent (Parent (N))), L, R) then return; else Func_Body := Make_Boolean_Array_Op (Etype (L), N); Func_Name := Defining_Unit_Name (Specification (Func_Body)); Insert_Action (N, Func_Body); -- Now rewrite the expression with a call Rewrite (N, Make_Function_Call (Loc, Name => New_Occurrence_Of (Func_Name, Loc), Parameter_Associations => New_List ( L, Make_Type_Conversion (Loc, New_Occurrence_Of (Etype (L), Loc), R)))); Analyze_And_Resolve (N, Typ); end if; end; end Expand_Boolean_Operator; ------------------------------------------------ -- Expand_Compare_Minimize_Eliminate_Overflow -- ------------------------------------------------ procedure Expand_Compare_Minimize_Eliminate_Overflow (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Result_Type : constant Entity_Id := Etype (N); -- Capture result type (could be a derived boolean type) Llo, Lhi : Uint; Rlo, Rhi : Uint; LLIB : constant Entity_Id := Base_Type (Standard_Long_Long_Integer); -- Entity for Long_Long_Integer'Base Check : constant Overflow_Mode_Type := Overflow_Check_Mode; -- Current overflow checking mode procedure Set_True; procedure Set_False; -- These procedures rewrite N with an occurrence of Standard_True or -- Standard_False, and then makes a call to Warn_On_Known_Condition. --------------- -- Set_False -- --------------- procedure Set_False is begin Rewrite (N, New_Occurrence_Of (Standard_False, Loc)); Warn_On_Known_Condition (N); end Set_False; -------------- -- Set_True -- -------------- procedure Set_True is begin Rewrite (N, New_Occurrence_Of (Standard_True, Loc)); Warn_On_Known_Condition (N); end Set_True; -- Start of processing for Expand_Compare_Minimize_Eliminate_Overflow begin -- Nothing to do unless we have a comparison operator with operands -- that are signed integer types, and we are operating in either -- MINIMIZED or ELIMINATED overflow checking mode. if Nkind (N) not in N_Op_Compare or else Check not in Minimized_Or_Eliminated or else not Is_Signed_Integer_Type (Etype (Left_Opnd (N))) then return; end if; -- OK, this is the case we are interested in. First step is to process -- our operands using the Minimize_Eliminate circuitry which applies -- this processing to the two operand subtrees. Minimize_Eliminate_Overflows (Left_Opnd (N), Llo, Lhi, Top_Level => False); Minimize_Eliminate_Overflows (Right_Opnd (N), Rlo, Rhi, Top_Level => False); -- See if the range information decides the result of the comparison. -- We can only do this if we in fact have full range information (which -- won't be the case if either operand is bignum at this stage). if Llo /= No_Uint and then Rlo /= No_Uint then case N_Op_Compare (Nkind (N)) is when N_Op_Eq => if Llo = Lhi and then Rlo = Rhi and then Llo = Rlo then Set_True; elsif Llo > Rhi or else Lhi < Rlo then Set_False; end if; when N_Op_Ge => if Llo >= Rhi then Set_True; elsif Lhi < Rlo then Set_False; end if; when N_Op_Gt => if Llo > Rhi then Set_True; elsif Lhi <= Rlo then Set_False; end if; when N_Op_Le => if Llo > Rhi then Set_False; elsif Lhi <= Rlo then Set_True; end if; when N_Op_Lt => if Llo >= Rhi then Set_False; elsif Lhi < Rlo then Set_True; end if; when N_Op_Ne => if Llo = Lhi and then Rlo = Rhi and then Llo = Rlo then Set_False; elsif Llo > Rhi or else Lhi < Rlo then Set_True; end if; end case; -- All done if we did the rewrite if Nkind (N) not in N_Op_Compare then return; end if; end if; -- Otherwise, time to do the comparison declare Ltype : constant Entity_Id := Etype (Left_Opnd (N)); Rtype : constant Entity_Id := Etype (Right_Opnd (N)); begin -- If the two operands have the same signed integer type we are -- all set, nothing more to do. This is the case where either -- both operands were unchanged, or we rewrote both of them to -- be Long_Long_Integer. -- Note: Entity for the comparison may be wrong, but it's not worth -- the effort to change it, since the back end does not use it. if Is_Signed_Integer_Type (Ltype) and then Base_Type (Ltype) = Base_Type (Rtype) then return; -- Here if bignums are involved (can only happen in ELIMINATED mode) elsif Is_RTE (Ltype, RE_Bignum) or else Is_RTE (Rtype, RE_Bignum) then declare Left : Node_Id := Left_Opnd (N); Right : Node_Id := Right_Opnd (N); -- Bignum references for left and right operands begin if not Is_RTE (Ltype, RE_Bignum) then Left := Convert_To_Bignum (Left); elsif not Is_RTE (Rtype, RE_Bignum) then Right := Convert_To_Bignum (Right); end if; -- We rewrite our node with: -- do -- Bnn : Result_Type; -- declare -- M : Mark_Id := SS_Mark; -- begin -- Bnn := Big_xx (Left, Right); (xx = EQ, NT etc) -- SS_Release (M); -- end; -- in -- Bnn -- end declare Blk : constant Node_Id := Make_Bignum_Block (Loc); Bnn : constant Entity_Id := Make_Temporary (Loc, 'B', N); Ent : RE_Id; begin case N_Op_Compare (Nkind (N)) is when N_Op_Eq => Ent := RE_Big_EQ; when N_Op_Ge => Ent := RE_Big_GE; when N_Op_Gt => Ent := RE_Big_GT; when N_Op_Le => Ent := RE_Big_LE; when N_Op_Lt => Ent := RE_Big_LT; when N_Op_Ne => Ent := RE_Big_NE; end case; -- Insert assignment to Bnn into the bignum block Insert_Before (First (Statements (Handled_Statement_Sequence (Blk))), Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (Bnn, Loc), Expression => Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (Ent), Loc), Parameter_Associations => New_List (Left, Right)))); -- Now do the rewrite with expression actions Rewrite (N, Make_Expression_With_Actions (Loc, Actions => New_List ( Make_Object_Declaration (Loc, Defining_Identifier => Bnn, Object_Definition => New_Occurrence_Of (Result_Type, Loc)), Blk), Expression => New_Occurrence_Of (Bnn, Loc))); Analyze_And_Resolve (N, Result_Type); end; end; -- No bignums involved, but types are different, so we must have -- rewritten one of the operands as a Long_Long_Integer but not -- the other one. -- If left operand is Long_Long_Integer, convert right operand -- and we are done (with a comparison of two Long_Long_Integers). elsif Ltype = LLIB then Convert_To_And_Rewrite (LLIB, Right_Opnd (N)); Analyze_And_Resolve (Right_Opnd (N), LLIB, Suppress => All_Checks); return; -- If right operand is Long_Long_Integer, convert left operand -- and we are done (with a comparison of two Long_Long_Integers). -- This is the only remaining possibility else pragma Assert (Rtype = LLIB); Convert_To_And_Rewrite (LLIB, Left_Opnd (N)); Analyze_And_Resolve (Left_Opnd (N), LLIB, Suppress => All_Checks); return; end if; end; end Expand_Compare_Minimize_Eliminate_Overflow; ------------------------------- -- Expand_Composite_Equality -- ------------------------------- -- This function is only called for comparing internal fields of composite -- types when these fields are themselves composites. This is a special -- case because it is not possible to respect normal Ada visibility rules. function Expand_Composite_Equality (Nod : Node_Id; Typ : Entity_Id; Lhs : Node_Id; Rhs : Node_Id; Bodies : List_Id) return Node_Id is Loc : constant Source_Ptr := Sloc (Nod); Full_Type : Entity_Id; Eq_Op : Entity_Id; -- Start of processing for Expand_Composite_Equality begin if Is_Private_Type (Typ) then Full_Type := Underlying_Type (Typ); else Full_Type := Typ; end if; -- If the private type has no completion the context may be the -- expansion of a composite equality for a composite type with some -- still incomplete components. The expression will not be analyzed -- until the enclosing type is completed, at which point this will be -- properly expanded, unless there is a bona fide completion error. if No (Full_Type) then return Make_Op_Eq (Loc, Left_Opnd => Lhs, Right_Opnd => Rhs); end if; Full_Type := Base_Type (Full_Type); -- When the base type itself is private, use the full view to expand -- the composite equality. if Is_Private_Type (Full_Type) then Full_Type := Underlying_Type (Full_Type); end if; -- Case of array types if Is_Array_Type (Full_Type) then -- If the operand is an elementary type other than a floating-point -- type, then we can simply use the built-in block bitwise equality, -- since the predefined equality operators always apply and bitwise -- equality is fine for all these cases. if Is_Elementary_Type (Component_Type (Full_Type)) and then not Is_Floating_Point_Type (Component_Type (Full_Type)) then return Make_Op_Eq (Loc, Left_Opnd => Lhs, Right_Opnd => Rhs); -- For composite component types, and floating-point types, use the -- expansion. This deals with tagged component types (where we use -- the applicable equality routine) and floating-point (where we -- need to worry about negative zeroes), and also the case of any -- composite type recursively containing such fields. else declare Comp_Typ : Entity_Id; Hi : Node_Id; Indx : Node_Id; Ityp : Entity_Id; Lo : Node_Id; begin -- Do the comparison in the type (or its full view) and not in -- its unconstrained base type, because the latter operation is -- more complex and would also require an unchecked conversion. if Is_Private_Type (Typ) then Comp_Typ := Underlying_Type (Typ); else Comp_Typ := Typ; end if; -- Except for the case where the bounds of the type depend on a -- discriminant, or else we would run into scoping issues. Indx := First_Index (Comp_Typ); while Present (Indx) loop Ityp := Etype (Indx); Lo := Type_Low_Bound (Ityp); Hi := Type_High_Bound (Ityp); if (Nkind (Lo) = N_Identifier and then Ekind (Entity (Lo)) = E_Discriminant) or else (Nkind (Hi) = N_Identifier and then Ekind (Entity (Hi)) = E_Discriminant) then Comp_Typ := Full_Type; exit; end if; Next_Index (Indx); end loop; return Expand_Array_Equality (Nod, Lhs, Rhs, Bodies, Comp_Typ); end; end if; -- Case of tagged record types elsif Is_Tagged_Type (Full_Type) then Eq_Op := Find_Primitive_Eq (Typ); pragma Assert (Present (Eq_Op)); return Make_Function_Call (Loc, Name => New_Occurrence_Of (Eq_Op, Loc), Parameter_Associations => New_List (Unchecked_Convert_To (Etype (First_Formal (Eq_Op)), Lhs), Unchecked_Convert_To (Etype (First_Formal (Eq_Op)), Rhs))); -- Case of untagged record types elsif Is_Record_Type (Full_Type) then Eq_Op := TSS (Full_Type, TSS_Composite_Equality); if Present (Eq_Op) then if Etype (First_Formal (Eq_Op)) /= Full_Type then -- Inherited equality from parent type. Convert the actuals to -- match signature of operation. declare T : constant Entity_Id := Etype (First_Formal (Eq_Op)); begin return Make_Function_Call (Loc, Name => New_Occurrence_Of (Eq_Op, Loc), Parameter_Associations => New_List ( OK_Convert_To (T, Lhs), OK_Convert_To (T, Rhs))); end; else -- Comparison between Unchecked_Union components if Is_Unchecked_Union (Full_Type) then declare Lhs_Type : Node_Id := Full_Type; Rhs_Type : Node_Id := Full_Type; Lhs_Discr_Val : Node_Id; Rhs_Discr_Val : Node_Id; begin -- Lhs subtype if Nkind (Lhs) = N_Selected_Component then Lhs_Type := Etype (Entity (Selector_Name (Lhs))); end if; -- Rhs subtype if Nkind (Rhs) = N_Selected_Component then Rhs_Type := Etype (Entity (Selector_Name (Rhs))); end if; -- Lhs of the composite equality if Is_Constrained (Lhs_Type) then -- Since the enclosing record type can never be an -- Unchecked_Union (this code is executed for records -- that do not have variants), we may reference its -- discriminant(s). if Nkind (Lhs) = N_Selected_Component and then Has_Per_Object_Constraint (Entity (Selector_Name (Lhs))) then Lhs_Discr_Val := Make_Selected_Component (Loc, Prefix => Prefix (Lhs), Selector_Name => New_Copy (Get_Discriminant_Value (First_Discriminant (Lhs_Type), Lhs_Type, Stored_Constraint (Lhs_Type)))); else Lhs_Discr_Val := New_Copy (Get_Discriminant_Value (First_Discriminant (Lhs_Type), Lhs_Type, Stored_Constraint (Lhs_Type))); end if; else -- It is not possible to infer the discriminant since -- the subtype is not constrained. return Make_Raise_Program_Error (Loc, Reason => PE_Unchecked_Union_Restriction); end if; -- Rhs of the composite equality if Is_Constrained (Rhs_Type) then if Nkind (Rhs) = N_Selected_Component and then Has_Per_Object_Constraint (Entity (Selector_Name (Rhs))) then Rhs_Discr_Val := Make_Selected_Component (Loc, Prefix => Prefix (Rhs), Selector_Name => New_Copy (Get_Discriminant_Value (First_Discriminant (Rhs_Type), Rhs_Type, Stored_Constraint (Rhs_Type)))); else Rhs_Discr_Val := New_Copy (Get_Discriminant_Value (First_Discriminant (Rhs_Type), Rhs_Type, Stored_Constraint (Rhs_Type))); end if; else return Make_Raise_Program_Error (Loc, Reason => PE_Unchecked_Union_Restriction); end if; -- Call the TSS equality function with the inferred -- discriminant values. return Make_Function_Call (Loc, Name => New_Occurrence_Of (Eq_Op, Loc), Parameter_Associations => New_List ( Lhs, Rhs, Lhs_Discr_Val, Rhs_Discr_Val)); end; -- All cases other than comparing Unchecked_Union types else declare T : constant Entity_Id := Etype (First_Formal (Eq_Op)); begin return Make_Function_Call (Loc, Name => New_Occurrence_Of (Eq_Op, Loc), Parameter_Associations => New_List ( OK_Convert_To (T, Lhs), OK_Convert_To (T, Rhs))); end; end if; end if; -- Equality composes in Ada 2012 for untagged record types. It also -- composes for bounded strings, because they are part of the -- predefined environment. We could make it compose for bounded -- strings by making them tagged, or by making sure all subcomponents -- are set to the same value, even when not used. Instead, we have -- this special case in the compiler, because it's more efficient. elsif Ada_Version >= Ada_2012 or else Is_Bounded_String (Typ) then -- If no TSS has been created for the type, check whether there is -- a primitive equality declared for it. declare Op : constant Node_Id := Build_Eq_Call (Typ, Loc, Lhs, Rhs); begin -- Use user-defined primitive if it exists, otherwise use -- predefined equality. if Present (Op) then return Op; else return Make_Op_Eq (Loc, Lhs, Rhs); end if; end; else return Expand_Record_Equality (Nod, Full_Type, Lhs, Rhs, Bodies); end if; -- Non-composite types (always use predefined equality) else return Make_Op_Eq (Loc, Left_Opnd => Lhs, Right_Opnd => Rhs); end if; end Expand_Composite_Equality; ------------------------ -- Expand_Concatenate -- ------------------------ procedure Expand_Concatenate (Cnode : Node_Id; Opnds : List_Id) is Loc : constant Source_Ptr := Sloc (Cnode); Atyp : constant Entity_Id := Base_Type (Etype (Cnode)); -- Result type of concatenation Ctyp : constant Entity_Id := Base_Type (Component_Type (Etype (Cnode))); -- Component type. Elements of this component type can appear as one -- of the operands of concatenation as well as arrays. Istyp : constant Entity_Id := Etype (First_Index (Atyp)); -- Index subtype Ityp : constant Entity_Id := Base_Type (Istyp); -- Index type. This is the base type of the index subtype, and is used -- for all computed bounds (which may be out of range of Istyp in the -- case of null ranges). Artyp : Entity_Id; -- This is the type we use to do arithmetic to compute the bounds and -- lengths of operands. The choice of this type is a little subtle and -- is discussed in a separate section at the start of the body code. Concatenation_Error : exception; -- Raised if concatenation is sure to raise a CE Result_May_Be_Null : Boolean := True; -- Reset to False if at least one operand is encountered which is known -- at compile time to be non-null. Used for handling the special case -- of setting the high bound to the last operand high bound for a null -- result, thus ensuring a proper high bound in the super-flat case. N : constant Nat := List_Length (Opnds); -- Number of concatenation operands including possibly null operands NN : Nat := 0; -- Number of operands excluding any known to be null, except that the -- last operand is always retained, in case it provides the bounds for -- a null result. Opnd : Node_Id := Empty; -- Current operand being processed in the loop through operands. After -- this loop is complete, always contains the last operand (which is not -- the same as Operands (NN), since null operands are skipped). -- Arrays describing the operands, only the first NN entries of each -- array are set (NN < N when we exclude known null operands). Is_Fixed_Length : array (1 .. N) of Boolean; -- True if length of corresponding operand known at compile time Operands : array (1 .. N) of Node_Id; -- Set to the corresponding entry in the Opnds list (but note that null -- operands are excluded, so not all entries in the list are stored). Fixed_Length : array (1 .. N) of Uint; -- Set to length of operand. Entries in this array are set only if the -- corresponding entry in Is_Fixed_Length is True. Opnd_Low_Bound : array (1 .. N) of Node_Id; -- Set to lower bound of operand. Either an integer literal in the case -- where the bound is known at compile time, else actual lower bound. -- The operand low bound is of type Ityp. Var_Length : array (1 .. N) of Entity_Id; -- Set to an entity of type Natural that contains the length of an -- operand whose length is not known at compile time. Entries in this -- array are set only if the corresponding entry in Is_Fixed_Length -- is False. The entity is of type Artyp. Aggr_Length : array (0 .. N) of Node_Id; -- The J'th entry in an expression node that represents the total length -- of operands 1 through J. It is either an integer literal node, or a -- reference to a constant entity with the right value, so it is fine -- to just do a Copy_Node to get an appropriate copy. The extra zeroth -- entry always is set to zero. The length is of type Artyp. Low_Bound : Node_Id := Empty; -- A tree node representing the low bound of the result (of type Ityp). -- This is either an integer literal node, or an identifier reference to -- a constant entity initialized to the appropriate value. Last_Opnd_Low_Bound : Node_Id := Empty; -- A tree node representing the low bound of the last operand. This -- need only be set if the result could be null. It is used for the -- special case of setting the right low bound for a null result. -- This is of type Ityp. Last_Opnd_High_Bound : Node_Id := Empty; -- A tree node representing the high bound of the last operand. This -- need only be set if the result could be null. It is used for the -- special case of setting the right high bound for a null result. -- This is of type Ityp. High_Bound : Node_Id := Empty; -- A tree node representing the high bound of the result (of type Ityp) Result : Node_Id := Empty; -- Result of the concatenation (of type Ityp) Actions : constant List_Id := New_List; -- Collect actions to be inserted Known_Non_Null_Operand_Seen : Boolean; -- Set True during generation of the assignments of operands into -- result once an operand known to be non-null has been seen. function Library_Level_Target return Boolean; -- Return True if the concatenation is within the expression of the -- declaration of a library-level object. function Make_Artyp_Literal (Val : Nat) return Node_Id; -- This function makes an N_Integer_Literal node that is returned in -- analyzed form with the type set to Artyp. Importantly this literal -- is not flagged as static, so that if we do computations with it that -- result in statically detected out of range conditions, we will not -- generate error messages but instead warning messages. function To_Artyp (X : Node_Id) return Node_Id; -- Given a node of type Ityp, returns the corresponding value of type -- Artyp. For non-enumeration types, this is a plain integer conversion. -- For enum types, the Pos of the value is returned. function To_Ityp (X : Node_Id) return Node_Id; -- The inverse function (uses Val in the case of enumeration types) -------------------------- -- Library_Level_Target -- -------------------------- function Library_Level_Target return Boolean is P : Node_Id := Parent (Cnode); begin while Present (P) loop if Nkind (P) = N_Object_Declaration then return Is_Library_Level_Entity (Defining_Identifier (P)); -- Prevent the search from going too far elsif Is_Body_Or_Package_Declaration (P) then return False; end if; P := Parent (P); end loop; return False; end Library_Level_Target; ------------------------ -- Make_Artyp_Literal -- ------------------------ function Make_Artyp_Literal (Val : Nat) return Node_Id is Result : constant Node_Id := Make_Integer_Literal (Loc, Val); begin Set_Etype (Result, Artyp); Set_Analyzed (Result, True); Set_Is_Static_Expression (Result, False); return Result; end Make_Artyp_Literal; -------------- -- To_Artyp -- -------------- function To_Artyp (X : Node_Id) return Node_Id is begin if Ityp = Base_Type (Artyp) then return X; elsif Is_Enumeration_Type (Ityp) then return Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Ityp, Loc), Attribute_Name => Name_Pos, Expressions => New_List (X)); else return Convert_To (Artyp, X); end if; end To_Artyp; ------------- -- To_Ityp -- ------------- function To_Ityp (X : Node_Id) return Node_Id is begin if Is_Enumeration_Type (Ityp) then return Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Ityp, Loc), Attribute_Name => Name_Val, Expressions => New_List (X)); -- Case where we will do a type conversion else if Ityp = Base_Type (Artyp) then return X; else return Convert_To (Ityp, X); end if; end if; end To_Ityp; -- Local Declarations Opnd_Typ : Entity_Id; Ent : Entity_Id; Len : Uint; J : Nat; Clen : Node_Id; Set : Boolean; -- Start of processing for Expand_Concatenate begin -- Choose an appropriate computational type -- We will be doing calculations of lengths and bounds in this routine -- and computing one from the other in some cases, e.g. getting the high -- bound by adding the length-1 to the low bound. -- We can't just use the index type, or even its base type for this -- purpose for two reasons. First it might be an enumeration type which -- is not suitable for computations of any kind, and second it may -- simply not have enough range. For example if the index type is -- -128..+127 then lengths can be up to 256, which is out of range of -- the type. -- For enumeration types, we can simply use Standard_Integer, this is -- sufficient since the actual number of enumeration literals cannot -- possibly exceed the range of integer (remember we will be doing the -- arithmetic with POS values, not representation values). if Is_Enumeration_Type (Ityp) then Artyp := Standard_Integer; -- If index type is Positive, we use the standard unsigned type, to give -- more room on the top of the range, obviating the need for an overflow -- check when creating the upper bound. This is needed to avoid junk -- overflow checks in the common case of String types. -- ??? Disabled for now -- elsif Istyp = Standard_Positive then -- Artyp := Standard_Unsigned; -- For modular types, we use a 32-bit modular type for types whose size -- is in the range 1-31 bits. For 32-bit unsigned types, we use the -- identity type, and for larger unsigned types we use 64-bits. elsif Is_Modular_Integer_Type (Ityp) then if RM_Size (Ityp) < RM_Size (Standard_Unsigned) then Artyp := Standard_Unsigned; elsif RM_Size (Ityp) = RM_Size (Standard_Unsigned) then Artyp := Ityp; else Artyp := RTE (RE_Long_Long_Unsigned); end if; -- Similar treatment for signed types else if RM_Size (Ityp) < RM_Size (Standard_Integer) then Artyp := Standard_Integer; elsif RM_Size (Ityp) = RM_Size (Standard_Integer) then Artyp := Ityp; else Artyp := Standard_Long_Long_Integer; end if; end if; -- Supply dummy entry at start of length array Aggr_Length (0) := Make_Artyp_Literal (0); -- Go through operands setting up the above arrays J := 1; while J <= N loop Opnd := Remove_Head (Opnds); Opnd_Typ := Etype (Opnd); -- The parent got messed up when we put the operands in a list, -- so now put back the proper parent for the saved operand, that -- is to say the concatenation node, to make sure that each operand -- is seen as a subexpression, e.g. if actions must be inserted. Set_Parent (Opnd, Cnode); -- Set will be True when we have setup one entry in the array Set := False; -- Singleton element (or character literal) case if Base_Type (Opnd_Typ) = Ctyp then NN := NN + 1; Operands (NN) := Opnd; Is_Fixed_Length (NN) := True; Fixed_Length (NN) := Uint_1; Result_May_Be_Null := False; -- Set low bound of operand (no need to set Last_Opnd_High_Bound -- since we know that the result cannot be null). Opnd_Low_Bound (NN) := Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Istyp, Loc), Attribute_Name => Name_First); Set := True; -- String literal case (can only occur for strings of course) elsif Nkind (Opnd) = N_String_Literal then Len := String_Literal_Length (Opnd_Typ); if Len /= 0 then Result_May_Be_Null := False; end if; -- Capture last operand low and high bound if result could be null if J = N and then Result_May_Be_Null then Last_Opnd_Low_Bound := New_Copy_Tree (String_Literal_Low_Bound (Opnd_Typ)); Last_Opnd_High_Bound := Make_Op_Subtract (Loc, Left_Opnd => New_Copy_Tree (String_Literal_Low_Bound (Opnd_Typ)), Right_Opnd => Make_Integer_Literal (Loc, 1)); end if; -- Skip null string literal if J < N and then Len = 0 then goto Continue; end if; NN := NN + 1; Operands (NN) := Opnd; Is_Fixed_Length (NN) := True; -- Set length and bounds Fixed_Length (NN) := Len; Opnd_Low_Bound (NN) := New_Copy_Tree (String_Literal_Low_Bound (Opnd_Typ)); Set := True; -- All other cases else -- Check constrained case with known bounds if Is_Constrained (Opnd_Typ) then declare Index : constant Node_Id := First_Index (Opnd_Typ); Indx_Typ : constant Entity_Id := Etype (Index); Lo : constant Node_Id := Type_Low_Bound (Indx_Typ); Hi : constant Node_Id := Type_High_Bound (Indx_Typ); begin -- Fixed length constrained array type with known at compile -- time bounds is last case of fixed length operand. if Compile_Time_Known_Value (Lo) and then Compile_Time_Known_Value (Hi) then declare Loval : constant Uint := Expr_Value (Lo); Hival : constant Uint := Expr_Value (Hi); Len : constant Uint := UI_Max (Hival - Loval + 1, Uint_0); begin if Len > 0 then Result_May_Be_Null := False; end if; -- Capture last operand bounds if result could be null if J = N and then Result_May_Be_Null then Last_Opnd_Low_Bound := Convert_To (Ityp, Make_Integer_Literal (Loc, Expr_Value (Lo))); Last_Opnd_High_Bound := Convert_To (Ityp, Make_Integer_Literal (Loc, Expr_Value (Hi))); end if; -- Exclude null length case unless last operand if J < N and then Len = 0 then goto Continue; end if; NN := NN + 1; Operands (NN) := Opnd; Is_Fixed_Length (NN) := True; Fixed_Length (NN) := Len; Opnd_Low_Bound (NN) := To_Ityp (Make_Integer_Literal (Loc, Expr_Value (Lo))); Set := True; end; end if; end; end if; -- All cases where the length is not known at compile time, or the -- special case of an operand which is known to be null but has a -- lower bound other than 1 or is other than a string type. if not Set then NN := NN + 1; -- Capture operand bounds Opnd_Low_Bound (NN) := Make_Attribute_Reference (Loc, Prefix => Duplicate_Subexpr (Opnd, Name_Req => True), Attribute_Name => Name_First); -- Capture last operand bounds if result could be null if J = N and Result_May_Be_Null then Last_Opnd_Low_Bound := Convert_To (Ityp, Make_Attribute_Reference (Loc, Prefix => Duplicate_Subexpr (Opnd, Name_Req => True), Attribute_Name => Name_First)); Last_Opnd_High_Bound := Convert_To (Ityp, Make_Attribute_Reference (Loc, Prefix => Duplicate_Subexpr (Opnd, Name_Req => True), Attribute_Name => Name_Last)); end if; -- Capture length of operand in entity Operands (NN) := Opnd; Is_Fixed_Length (NN) := False; Var_Length (NN) := Make_Temporary (Loc, 'L'); Append_To (Actions, Make_Object_Declaration (Loc, Defining_Identifier => Var_Length (NN), Constant_Present => True, Object_Definition => New_Occurrence_Of (Artyp, Loc), Expression => Make_Attribute_Reference (Loc, Prefix => Duplicate_Subexpr (Opnd, Name_Req => True), Attribute_Name => Name_Length))); end if; end if; -- Set next entry in aggregate length array -- For first entry, make either integer literal for fixed length -- or a reference to the saved length for variable length. if NN = 1 then if Is_Fixed_Length (1) then Aggr_Length (1) := Make_Integer_Literal (Loc, Fixed_Length (1)); else Aggr_Length (1) := New_Occurrence_Of (Var_Length (1), Loc); end if; -- If entry is fixed length and only fixed lengths so far, make -- appropriate new integer literal adding new length. elsif Is_Fixed_Length (NN) and then Nkind (Aggr_Length (NN - 1)) = N_Integer_Literal then Aggr_Length (NN) := Make_Integer_Literal (Loc, Intval => Fixed_Length (NN) + Intval (Aggr_Length (NN - 1))); -- All other cases, construct an addition node for the length and -- create an entity initialized to this length. else Ent := Make_Temporary (Loc, 'L'); if Is_Fixed_Length (NN) then Clen := Make_Integer_Literal (Loc, Fixed_Length (NN)); else Clen := New_Occurrence_Of (Var_Length (NN), Loc); end if; Append_To (Actions, Make_Object_Declaration (Loc, Defining_Identifier => Ent, Constant_Present => True, Object_Definition => New_Occurrence_Of (Artyp, Loc), Expression => Make_Op_Add (Loc, Left_Opnd => New_Copy_Tree (Aggr_Length (NN - 1)), Right_Opnd => Clen))); Aggr_Length (NN) := Make_Identifier (Loc, Chars => Chars (Ent)); end if; <> J := J + 1; end loop; -- If we have only skipped null operands, return the last operand if NN = 0 then Result := Opnd; goto Done; end if; -- If we have only one non-null operand, return it and we are done. -- There is one case in which this cannot be done, and that is when -- the sole operand is of the element type, in which case it must be -- converted to an array, and the easiest way of doing that is to go -- through the normal general circuit. if NN = 1 and then Base_Type (Etype (Operands (1))) /= Ctyp then Result := Operands (1); goto Done; end if; -- Cases where we have a real concatenation -- Next step is to find the low bound for the result array that we -- will allocate. The rules for this are in (RM 4.5.6(5-7)). -- If the ultimate ancestor of the index subtype is a constrained array -- definition, then the lower bound is that of the index subtype as -- specified by (RM 4.5.3(6)). -- The right test here is to go to the root type, and then the ultimate -- ancestor is the first subtype of this root type. if Is_Constrained (First_Subtype (Root_Type (Atyp))) then Low_Bound := Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (First_Subtype (Root_Type (Atyp)), Loc), Attribute_Name => Name_First); -- If the first operand in the list has known length we know that -- the lower bound of the result is the lower bound of this operand. elsif Is_Fixed_Length (1) then Low_Bound := Opnd_Low_Bound (1); -- OK, we don't know the lower bound, we have to build a horrible -- if expression node of the form -- if Cond1'Length /= 0 then -- Opnd1 low bound -- else -- if Opnd2'Length /= 0 then -- Opnd2 low bound -- else -- ... -- The nesting ends either when we hit an operand whose length is known -- at compile time, or on reaching the last operand, whose low bound we -- take unconditionally whether or not it is null. It's easiest to do -- this with a recursive procedure: else declare function Get_Known_Bound (J : Nat) return Node_Id; -- Returns the lower bound determined by operands J .. NN --------------------- -- Get_Known_Bound -- --------------------- function Get_Known_Bound (J : Nat) return Node_Id is begin if Is_Fixed_Length (J) or else J = NN then return New_Copy_Tree (Opnd_Low_Bound (J)); else return Make_If_Expression (Loc, Expressions => New_List ( Make_Op_Ne (Loc, Left_Opnd => New_Occurrence_Of (Var_Length (J), Loc), Right_Opnd => Make_Integer_Literal (Loc, 0)), New_Copy_Tree (Opnd_Low_Bound (J)), Get_Known_Bound (J + 1))); end if; end Get_Known_Bound; begin Ent := Make_Temporary (Loc, 'L'); Append_To (Actions, Make_Object_Declaration (Loc, Defining_Identifier => Ent, Constant_Present => True, Object_Definition => New_Occurrence_Of (Ityp, Loc), Expression => Get_Known_Bound (1))); Low_Bound := New_Occurrence_Of (Ent, Loc); end; end if; pragma Assert (Present (Low_Bound)); -- Now we can safely compute the upper bound, normally -- Low_Bound + Length - 1. High_Bound := To_Ityp (Make_Op_Add (Loc, Left_Opnd => To_Artyp (New_Copy_Tree (Low_Bound)), Right_Opnd => Make_Op_Subtract (Loc, Left_Opnd => New_Copy_Tree (Aggr_Length (NN)), Right_Opnd => Make_Artyp_Literal (1)))); -- Note that calculation of the high bound may cause overflow in some -- very weird cases, so in the general case we need an overflow check on -- the high bound. We can avoid this for the common case of string types -- and other types whose index is Positive, since we chose a wider range -- for the arithmetic type. If checks are suppressed we do not set the -- flag, and possibly superfluous warnings will be omitted. if Istyp /= Standard_Positive and then not Overflow_Checks_Suppressed (Istyp) then Activate_Overflow_Check (High_Bound); end if; -- Handle the exceptional case where the result is null, in which case -- case the bounds come from the last operand (so that we get the proper -- bounds if the last operand is super-flat). if Result_May_Be_Null then Low_Bound := Make_If_Expression (Loc, Expressions => New_List ( Make_Op_Eq (Loc, Left_Opnd => New_Copy_Tree (Aggr_Length (NN)), Right_Opnd => Make_Artyp_Literal (0)), Last_Opnd_Low_Bound, Low_Bound)); High_Bound := Make_If_Expression (Loc, Expressions => New_List ( Make_Op_Eq (Loc, Left_Opnd => New_Copy_Tree (Aggr_Length (NN)), Right_Opnd => Make_Artyp_Literal (0)), Last_Opnd_High_Bound, High_Bound)); end if; -- Here is where we insert the saved up actions Insert_Actions (Cnode, Actions, Suppress => All_Checks); -- Now we construct an array object with appropriate bounds. We mark -- the target as internal to prevent useless initialization when -- Initialize_Scalars is enabled. Also since this is the actual result -- entity, we make sure we have debug information for the result. Ent := Make_Temporary (Loc, 'S'); Set_Is_Internal (Ent); Set_Debug_Info_Needed (Ent); -- If the bound is statically known to be out of range, we do not want -- to abort, we want a warning and a runtime constraint error. Note that -- we have arranged that the result will not be treated as a static -- constant, so we won't get an illegality during this insertion. Insert_Action (Cnode, Make_Object_Declaration (Loc, Defining_Identifier => Ent, Object_Definition => Make_Subtype_Indication (Loc, Subtype_Mark => New_Occurrence_Of (Atyp, Loc), Constraint => Make_Index_Or_Discriminant_Constraint (Loc, Constraints => New_List ( Make_Range (Loc, Low_Bound => Low_Bound, High_Bound => High_Bound))))), Suppress => All_Checks); -- If the result of the concatenation appears as the initializing -- expression of an object declaration, we can just rename the -- result, rather than copying it. Set_OK_To_Rename (Ent); -- Catch the static out of range case now if Raises_Constraint_Error (High_Bound) then raise Concatenation_Error; end if; -- Now we will generate the assignments to do the actual concatenation -- There is one case in which we will not do this, namely when all the -- following conditions are met: -- The result type is Standard.String -- There are nine or fewer retained (non-null) operands -- The optimization level is -O0 or the debug flag gnatd.C is set, -- and the debug flag gnatd.c is not set. -- The corresponding System.Concat_n.Str_Concat_n routine is -- available in the run time. -- If all these conditions are met then we generate a call to the -- relevant concatenation routine. The purpose of this is to avoid -- undesirable code bloat at -O0. -- If the concatenation is within the declaration of a library-level -- object, we call the built-in concatenation routines to prevent code -- bloat, regardless of the optimization level. This is space efficient -- and prevents linking problems when units are compiled with different -- optimization levels. if Atyp = Standard_String and then NN in 2 .. 9 and then (((Optimization_Level = 0 or else Debug_Flag_Dot_CC) and then not Debug_Flag_Dot_C) or else Library_Level_Target) then declare RR : constant array (Nat range 2 .. 9) of RE_Id := (RE_Str_Concat_2, RE_Str_Concat_3, RE_Str_Concat_4, RE_Str_Concat_5, RE_Str_Concat_6, RE_Str_Concat_7, RE_Str_Concat_8, RE_Str_Concat_9); begin if RTE_Available (RR (NN)) then declare Opnds : constant List_Id := New_List (New_Occurrence_Of (Ent, Loc)); begin for J in 1 .. NN loop if Is_List_Member (Operands (J)) then Remove (Operands (J)); end if; if Base_Type (Etype (Operands (J))) = Ctyp then Append_To (Opnds, Make_Aggregate (Loc, Component_Associations => New_List ( Make_Component_Association (Loc, Choices => New_List ( Make_Integer_Literal (Loc, 1)), Expression => Operands (J))))); else Append_To (Opnds, Operands (J)); end if; end loop; Insert_Action (Cnode, Make_Procedure_Call_Statement (Loc, Name => New_Occurrence_Of (RTE (RR (NN)), Loc), Parameter_Associations => Opnds)); Result := New_Occurrence_Of (Ent, Loc); goto Done; end; end if; end; end if; -- Not special case so generate the assignments Known_Non_Null_Operand_Seen := False; for J in 1 .. NN loop declare Lo : constant Node_Id := Make_Op_Add (Loc, Left_Opnd => To_Artyp (New_Copy_Tree (Low_Bound)), Right_Opnd => Aggr_Length (J - 1)); Hi : constant Node_Id := Make_Op_Add (Loc, Left_Opnd => To_Artyp (New_Copy_Tree (Low_Bound)), Right_Opnd => Make_Op_Subtract (Loc, Left_Opnd => Aggr_Length (J), Right_Opnd => Make_Artyp_Literal (1))); begin -- Singleton case, simple assignment if Base_Type (Etype (Operands (J))) = Ctyp then Known_Non_Null_Operand_Seen := True; Insert_Action (Cnode, Make_Assignment_Statement (Loc, Name => Make_Indexed_Component (Loc, Prefix => New_Occurrence_Of (Ent, Loc), Expressions => New_List (To_Ityp (Lo))), Expression => Operands (J)), Suppress => All_Checks); -- Array case, slice assignment, skipped when argument is fixed -- length and known to be null. elsif (not Is_Fixed_Length (J)) or else (Fixed_Length (J) > 0) then declare Assign : Node_Id := Make_Assignment_Statement (Loc, Name => Make_Slice (Loc, Prefix => New_Occurrence_Of (Ent, Loc), Discrete_Range => Make_Range (Loc, Low_Bound => To_Ityp (Lo), High_Bound => To_Ityp (Hi))), Expression => Operands (J)); begin if Is_Fixed_Length (J) then Known_Non_Null_Operand_Seen := True; elsif not Known_Non_Null_Operand_Seen then -- Here if operand length is not statically known and no -- operand known to be non-null has been processed yet. -- If operand length is 0, we do not need to perform the -- assignment, and we must avoid the evaluation of the -- high bound of the slice, since it may underflow if the -- low bound is Ityp'First. Assign := Make_Implicit_If_Statement (Cnode, Condition => Make_Op_Ne (Loc, Left_Opnd => New_Occurrence_Of (Var_Length (J), Loc), Right_Opnd => Make_Integer_Literal (Loc, 0)), Then_Statements => New_List (Assign)); end if; Insert_Action (Cnode, Assign, Suppress => All_Checks); end; end if; end; end loop; -- Finally we build the result, which is a reference to the array object Result := New_Occurrence_Of (Ent, Loc); <> pragma Assert (Present (Result)); Rewrite (Cnode, Result); Analyze_And_Resolve (Cnode, Atyp); exception when Concatenation_Error => -- Kill warning generated for the declaration of the static out of -- range high bound, and instead generate a Constraint_Error with -- an appropriate specific message. Kill_Dead_Code (Declaration_Node (Entity (High_Bound))); Apply_Compile_Time_Constraint_Error (N => Cnode, Msg => "concatenation result upper bound out of range??", Reason => CE_Range_Check_Failed); end Expand_Concatenate; --------------------------------------------------- -- Expand_Membership_Minimize_Eliminate_Overflow -- --------------------------------------------------- procedure Expand_Membership_Minimize_Eliminate_Overflow (N : Node_Id) is pragma Assert (Nkind (N) = N_In); -- Despite the name, this routine applies only to N_In, not to -- N_Not_In. The latter is always rewritten as not (X in Y). Result_Type : constant Entity_Id := Etype (N); -- Capture result type, may be a derived boolean type Loc : constant Source_Ptr := Sloc (N); Lop : constant Node_Id := Left_Opnd (N); Rop : constant Node_Id := Right_Opnd (N); -- Note: there are many referencs to Etype (Lop) and Etype (Rop). It -- is thus tempting to capture these values, but due to the rewrites -- that occur as a result of overflow checking, these values change -- as we go along, and it is safe just to always use Etype explicitly. Restype : constant Entity_Id := Etype (N); -- Save result type Lo, Hi : Uint; -- Bounds in Minimize calls, not used currently LLIB : constant Entity_Id := Base_Type (Standard_Long_Long_Integer); -- Entity for Long_Long_Integer'Base (Standard should export this???) begin Minimize_Eliminate_Overflows (Lop, Lo, Hi, Top_Level => False); -- If right operand is a subtype name, and the subtype name has no -- predicate, then we can just replace the right operand with an -- explicit range T'First .. T'Last, and use the explicit range code. if Nkind (Rop) /= N_Range and then No (Predicate_Function (Etype (Rop))) then declare Rtyp : constant Entity_Id := Etype (Rop); begin Rewrite (Rop, Make_Range (Loc, Low_Bound => Make_Attribute_Reference (Loc, Attribute_Name => Name_First, Prefix => New_Occurrence_Of (Rtyp, Loc)), High_Bound => Make_Attribute_Reference (Loc, Attribute_Name => Name_Last, Prefix => New_Occurrence_Of (Rtyp, Loc)))); Analyze_And_Resolve (Rop, Rtyp, Suppress => All_Checks); end; end if; -- Here for the explicit range case. Note that the bounds of the range -- have not been processed for minimized or eliminated checks. if Nkind (Rop) = N_Range then Minimize_Eliminate_Overflows (Low_Bound (Rop), Lo, Hi, Top_Level => False); Minimize_Eliminate_Overflows (High_Bound (Rop), Lo, Hi, Top_Level => False); -- We have A in B .. C, treated as A >= B and then A <= C -- Bignum case if Is_RTE (Etype (Lop), RE_Bignum) or else Is_RTE (Etype (Low_Bound (Rop)), RE_Bignum) or else Is_RTE (Etype (High_Bound (Rop)), RE_Bignum) then declare Blk : constant Node_Id := Make_Bignum_Block (Loc); Bnn : constant Entity_Id := Make_Temporary (Loc, 'B', N); L : constant Entity_Id := Make_Defining_Identifier (Loc, Name_uL); Lopnd : constant Node_Id := Convert_To_Bignum (Lop); Lbound : constant Node_Id := Convert_To_Bignum (Low_Bound (Rop)); Hbound : constant Node_Id := Convert_To_Bignum (High_Bound (Rop)); -- Now we rewrite the membership test node to look like -- do -- Bnn : Result_Type; -- declare -- M : Mark_Id := SS_Mark; -- L : Bignum := Lopnd; -- begin -- Bnn := Big_GE (L, Lbound) and then Big_LE (L, Hbound) -- SS_Release (M); -- end; -- in -- Bnn -- end begin -- Insert declaration of L into declarations of bignum block Insert_After (Last (Declarations (Blk)), Make_Object_Declaration (Loc, Defining_Identifier => L, Object_Definition => New_Occurrence_Of (RTE (RE_Bignum), Loc), Expression => Lopnd)); -- Insert assignment to Bnn into expressions of bignum block Insert_Before (First (Statements (Handled_Statement_Sequence (Blk))), Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (Bnn, Loc), Expression => Make_And_Then (Loc, Left_Opnd => Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (RE_Big_GE), Loc), Parameter_Associations => New_List ( New_Occurrence_Of (L, Loc), Lbound)), Right_Opnd => Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (RE_Big_LE), Loc), Parameter_Associations => New_List ( New_Occurrence_Of (L, Loc), Hbound))))); -- Now rewrite the node Rewrite (N, Make_Expression_With_Actions (Loc, Actions => New_List ( Make_Object_Declaration (Loc, Defining_Identifier => Bnn, Object_Definition => New_Occurrence_Of (Result_Type, Loc)), Blk), Expression => New_Occurrence_Of (Bnn, Loc))); Analyze_And_Resolve (N, Result_Type); return; end; -- Here if no bignums around else -- Case where types are all the same if Base_Type (Etype (Lop)) = Base_Type (Etype (Low_Bound (Rop))) and then Base_Type (Etype (Lop)) = Base_Type (Etype (High_Bound (Rop))) then null; -- If types are not all the same, it means that we have rewritten -- at least one of them to be of type Long_Long_Integer, and we -- will convert the other operands to Long_Long_Integer. else Convert_To_And_Rewrite (LLIB, Lop); Set_Analyzed (Lop, False); Analyze_And_Resolve (Lop, LLIB); -- For the right operand, avoid unnecessary recursion into -- this routine, we know that overflow is not possible. Convert_To_And_Rewrite (LLIB, Low_Bound (Rop)); Convert_To_And_Rewrite (LLIB, High_Bound (Rop)); Set_Analyzed (Rop, False); Analyze_And_Resolve (Rop, LLIB, Suppress => Overflow_Check); end if; -- Now the three operands are of the same signed integer type, -- so we can use the normal expansion routine for membership, -- setting the flag to prevent recursion into this procedure. Set_No_Minimize_Eliminate (N); Expand_N_In (N); end if; -- Right operand is a subtype name and the subtype has a predicate. We -- have to make sure the predicate is checked, and for that we need to -- use the standard N_In circuitry with appropriate types. else pragma Assert (Present (Predicate_Function (Etype (Rop)))); -- If types are "right", just call Expand_N_In preventing recursion if Base_Type (Etype (Lop)) = Base_Type (Etype (Rop)) then Set_No_Minimize_Eliminate (N); Expand_N_In (N); -- Bignum case elsif Is_RTE (Etype (Lop), RE_Bignum) then -- For X in T, we want to rewrite our node as -- do -- Bnn : Result_Type; -- declare -- M : Mark_Id := SS_Mark; -- Lnn : Long_Long_Integer'Base -- Nnn : Bignum; -- begin -- Nnn := X; -- if not Bignum_In_LLI_Range (Nnn) then -- Bnn := False; -- else -- Lnn := From_Bignum (Nnn); -- Bnn := -- Lnn in LLIB (T'Base'First) .. LLIB (T'Base'Last) -- and then T'Base (Lnn) in T; -- end if; -- SS_Release (M); -- end -- in -- Bnn -- end -- A bit gruesome, but there doesn't seem to be a simpler way declare Blk : constant Node_Id := Make_Bignum_Block (Loc); Bnn : constant Entity_Id := Make_Temporary (Loc, 'B', N); Lnn : constant Entity_Id := Make_Temporary (Loc, 'L', N); Nnn : constant Entity_Id := Make_Temporary (Loc, 'N', N); T : constant Entity_Id := Etype (Rop); TB : constant Entity_Id := Base_Type (T); Nin : Node_Id; begin -- Mark the last membership operation to prevent recursion Nin := Make_In (Loc, Left_Opnd => Convert_To (TB, New_Occurrence_Of (Lnn, Loc)), Right_Opnd => New_Occurrence_Of (T, Loc)); Set_No_Minimize_Eliminate (Nin); -- Now decorate the block Insert_After (Last (Declarations (Blk)), Make_Object_Declaration (Loc, Defining_Identifier => Lnn, Object_Definition => New_Occurrence_Of (LLIB, Loc))); Insert_After (Last (Declarations (Blk)), Make_Object_Declaration (Loc, Defining_Identifier => Nnn, Object_Definition => New_Occurrence_Of (RTE (RE_Bignum), Loc))); Insert_List_Before (First (Statements (Handled_Statement_Sequence (Blk))), New_List ( Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (Nnn, Loc), Expression => Relocate_Node (Lop)), Make_Implicit_If_Statement (N, Condition => Make_Op_Not (Loc, Right_Opnd => Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (RE_Bignum_In_LLI_Range), Loc), Parameter_Associations => New_List ( New_Occurrence_Of (Nnn, Loc)))), Then_Statements => New_List ( Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (Bnn, Loc), Expression => New_Occurrence_Of (Standard_False, Loc))), Else_Statements => New_List ( Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (Lnn, Loc), Expression => Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (RE_From_Bignum), Loc), Parameter_Associations => New_List ( New_Occurrence_Of (Nnn, Loc)))), Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (Bnn, Loc), Expression => Make_And_Then (Loc, Left_Opnd => Make_In (Loc, Left_Opnd => New_Occurrence_Of (Lnn, Loc), Right_Opnd => Make_Range (Loc, Low_Bound => Convert_To (LLIB, Make_Attribute_Reference (Loc, Attribute_Name => Name_First, Prefix => New_Occurrence_Of (TB, Loc))), High_Bound => Convert_To (LLIB, Make_Attribute_Reference (Loc, Attribute_Name => Name_Last, Prefix => New_Occurrence_Of (TB, Loc))))), Right_Opnd => Nin)))))); -- Now we can do the rewrite Rewrite (N, Make_Expression_With_Actions (Loc, Actions => New_List ( Make_Object_Declaration (Loc, Defining_Identifier => Bnn, Object_Definition => New_Occurrence_Of (Result_Type, Loc)), Blk), Expression => New_Occurrence_Of (Bnn, Loc))); Analyze_And_Resolve (N, Result_Type); return; end; -- Not bignum case, but types don't match (this means we rewrote the -- left operand to be Long_Long_Integer). else pragma Assert (Base_Type (Etype (Lop)) = LLIB); -- We rewrite the membership test as (where T is the type with -- the predicate, i.e. the type of the right operand) -- Lop in LLIB (T'Base'First) .. LLIB (T'Base'Last) -- and then T'Base (Lop) in T declare T : constant Entity_Id := Etype (Rop); TB : constant Entity_Id := Base_Type (T); Nin : Node_Id; begin -- The last membership test is marked to prevent recursion Nin := Make_In (Loc, Left_Opnd => Convert_To (TB, Duplicate_Subexpr (Lop)), Right_Opnd => New_Occurrence_Of (T, Loc)); Set_No_Minimize_Eliminate (Nin); -- Now do the rewrite Rewrite (N, Make_And_Then (Loc, Left_Opnd => Make_In (Loc, Left_Opnd => Lop, Right_Opnd => Make_Range (Loc, Low_Bound => Convert_To (LLIB, Make_Attribute_Reference (Loc, Attribute_Name => Name_First, Prefix => New_Occurrence_Of (TB, Loc))), High_Bound => Convert_To (LLIB, Make_Attribute_Reference (Loc, Attribute_Name => Name_Last, Prefix => New_Occurrence_Of (TB, Loc))))), Right_Opnd => Nin)); Set_Analyzed (N, False); Analyze_And_Resolve (N, Restype); end; end if; end if; end Expand_Membership_Minimize_Eliminate_Overflow; --------------------------------- -- Expand_Nonbinary_Modular_Op -- --------------------------------- procedure Expand_Nonbinary_Modular_Op (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); procedure Expand_Modular_Addition; -- Expand the modular addition, handling the special case of adding a -- constant. procedure Expand_Modular_Op; -- Compute the general rule: (lhs OP rhs) mod Modulus procedure Expand_Modular_Subtraction; -- Expand the modular addition, handling the special case of subtracting -- a constant. ----------------------------- -- Expand_Modular_Addition -- ----------------------------- procedure Expand_Modular_Addition is begin -- If this is not the addition of a constant then compute it using -- the general rule: (lhs + rhs) mod Modulus if Nkind (Right_Opnd (N)) /= N_Integer_Literal then Expand_Modular_Op; -- If this is an addition of a constant, convert it to a subtraction -- plus a conditional expression since we can compute it faster than -- computing the modulus. -- modMinusRhs = Modulus - rhs -- if lhs < modMinusRhs then lhs + rhs -- else lhs - modMinusRhs else declare Mod_Minus_Right : constant Uint := Modulus (Typ) - Intval (Right_Opnd (N)); Exprs : constant List_Id := New_List; Cond_Expr : constant Node_Id := New_Op_Node (N_Op_Lt, Loc); Then_Expr : constant Node_Id := New_Op_Node (N_Op_Add, Loc); Else_Expr : constant Node_Id := New_Op_Node (N_Op_Subtract, Loc); begin -- To prevent spurious visibility issues, convert all -- operands to Standard.Unsigned. Set_Left_Opnd (Cond_Expr, Unchecked_Convert_To (Standard_Unsigned, New_Copy_Tree (Left_Opnd (N)))); Set_Right_Opnd (Cond_Expr, Make_Integer_Literal (Loc, Mod_Minus_Right)); Append_To (Exprs, Cond_Expr); Set_Left_Opnd (Then_Expr, Unchecked_Convert_To (Standard_Unsigned, New_Copy_Tree (Left_Opnd (N)))); Set_Right_Opnd (Then_Expr, Make_Integer_Literal (Loc, Intval (Right_Opnd (N)))); Append_To (Exprs, Then_Expr); Set_Left_Opnd (Else_Expr, Unchecked_Convert_To (Standard_Unsigned, New_Copy_Tree (Left_Opnd (N)))); Set_Right_Opnd (Else_Expr, Make_Integer_Literal (Loc, Mod_Minus_Right)); Append_To (Exprs, Else_Expr); Rewrite (N, Unchecked_Convert_To (Typ, Make_If_Expression (Loc, Expressions => Exprs))); end; end if; end Expand_Modular_Addition; ----------------------- -- Expand_Modular_Op -- ----------------------- procedure Expand_Modular_Op is Op_Expr : constant Node_Id := New_Op_Node (Nkind (N), Loc); Mod_Expr : constant Node_Id := New_Op_Node (N_Op_Mod, Loc); Target_Type : Entity_Id; begin -- Convert nonbinary modular type operands into integer values. Thus -- we avoid never-ending loops expanding them, and we also ensure -- the back end never receives nonbinary modular type expressions. if Nkind_In (Nkind (N), N_Op_And, N_Op_Or, N_Op_Xor) then Set_Left_Opnd (Op_Expr, Unchecked_Convert_To (Standard_Unsigned, New_Copy_Tree (Left_Opnd (N)))); Set_Right_Opnd (Op_Expr, Unchecked_Convert_To (Standard_Unsigned, New_Copy_Tree (Right_Opnd (N)))); Set_Left_Opnd (Mod_Expr, Unchecked_Convert_To (Standard_Integer, Op_Expr)); else -- If the modulus of the type is larger than Integer'Last use a -- larger type for the operands, to prevent spurious constraint -- errors on large legal literals of the type. if Modulus (Etype (N)) > UI_From_Int (Int (Integer'Last)) then Target_Type := Standard_Long_Integer; else Target_Type := Standard_Integer; end if; Set_Left_Opnd (Op_Expr, Unchecked_Convert_To (Target_Type, New_Copy_Tree (Left_Opnd (N)))); Set_Right_Opnd (Op_Expr, Unchecked_Convert_To (Target_Type, New_Copy_Tree (Right_Opnd (N)))); -- Link this node to the tree to analyze it -- If the parent node is an expression with actions we link it to -- N since otherwise Force_Evaluation cannot identify if this node -- comes from the Expression and rejects generating the temporary. if Nkind (Parent (N)) = N_Expression_With_Actions then Set_Parent (Op_Expr, N); -- Common case else Set_Parent (Op_Expr, Parent (N)); end if; Analyze (Op_Expr); -- Force generating a temporary because in the expansion of this -- expression we may generate code that performs this computation -- several times. Force_Evaluation (Op_Expr, Mode => Strict); Set_Left_Opnd (Mod_Expr, Op_Expr); end if; Set_Right_Opnd (Mod_Expr, Make_Integer_Literal (Loc, Modulus (Typ))); Rewrite (N, Unchecked_Convert_To (Typ, Mod_Expr)); end Expand_Modular_Op; -------------------------------- -- Expand_Modular_Subtraction -- -------------------------------- procedure Expand_Modular_Subtraction is begin -- If this is not the addition of a constant then compute it using -- the general rule: (lhs + rhs) mod Modulus if Nkind (Right_Opnd (N)) /= N_Integer_Literal then Expand_Modular_Op; -- If this is an addition of a constant, convert it to a subtraction -- plus a conditional expression since we can compute it faster than -- computing the modulus. -- modMinusRhs = Modulus - rhs -- if lhs < rhs then lhs + modMinusRhs -- else lhs - rhs else declare Mod_Minus_Right : constant Uint := Modulus (Typ) - Intval (Right_Opnd (N)); Exprs : constant List_Id := New_List; Cond_Expr : constant Node_Id := New_Op_Node (N_Op_Lt, Loc); Then_Expr : constant Node_Id := New_Op_Node (N_Op_Add, Loc); Else_Expr : constant Node_Id := New_Op_Node (N_Op_Subtract, Loc); begin Set_Left_Opnd (Cond_Expr, Unchecked_Convert_To (Standard_Unsigned, New_Copy_Tree (Left_Opnd (N)))); Set_Right_Opnd (Cond_Expr, Make_Integer_Literal (Loc, Intval (Right_Opnd (N)))); Append_To (Exprs, Cond_Expr); Set_Left_Opnd (Then_Expr, Unchecked_Convert_To (Standard_Unsigned, New_Copy_Tree (Left_Opnd (N)))); Set_Right_Opnd (Then_Expr, Make_Integer_Literal (Loc, Mod_Minus_Right)); Append_To (Exprs, Then_Expr); Set_Left_Opnd (Else_Expr, Unchecked_Convert_To (Standard_Unsigned, New_Copy_Tree (Left_Opnd (N)))); Set_Right_Opnd (Else_Expr, Unchecked_Convert_To (Standard_Unsigned, New_Copy_Tree (Right_Opnd (N)))); Append_To (Exprs, Else_Expr); Rewrite (N, Unchecked_Convert_To (Typ, Make_If_Expression (Loc, Expressions => Exprs))); end; end if; end Expand_Modular_Subtraction; -- Start of processing for Expand_Nonbinary_Modular_Op begin -- No action needed if front-end expansion is not required or if we -- have a binary modular operand. if not Expand_Nonbinary_Modular_Ops or else not Non_Binary_Modulus (Typ) then return; end if; case Nkind (N) is when N_Op_Add => Expand_Modular_Addition; when N_Op_Subtract => Expand_Modular_Subtraction; when N_Op_Minus => -- Expand -expr into (0 - expr) Rewrite (N, Make_Op_Subtract (Loc, Left_Opnd => Make_Integer_Literal (Loc, 0), Right_Opnd => Right_Opnd (N))); Analyze_And_Resolve (N, Typ); when others => Expand_Modular_Op; end case; Analyze_And_Resolve (N, Typ); end Expand_Nonbinary_Modular_Op; ------------------------ -- Expand_N_Allocator -- ------------------------ procedure Expand_N_Allocator (N : Node_Id) is Etyp : constant Entity_Id := Etype (Expression (N)); Loc : constant Source_Ptr := Sloc (N); PtrT : constant Entity_Id := Etype (N); procedure Rewrite_Coextension (N : Node_Id); -- Static coextensions have the same lifetime as the entity they -- constrain. Such occurrences can be rewritten as aliased objects -- and their unrestricted access used instead of the coextension. function Size_In_Storage_Elements (E : Entity_Id) return Node_Id; -- Given a constrained array type E, returns a node representing the -- code to compute a close approximation of the size in storage elements -- for the given type; for indexes that are modular types we compute -- 'Last - First (instead of 'Length) because for large arrays computing -- 'Last -'First + 1 causes overflow. This is done without using the -- attribute 'Size_In_Storage_Elements (which malfunctions for large -- sizes ???). ------------------------- -- Rewrite_Coextension -- ------------------------- procedure Rewrite_Coextension (N : Node_Id) is Temp_Id : constant Node_Id := Make_Temporary (Loc, 'C'); Temp_Decl : Node_Id; begin -- Generate: -- Cnn : aliased Etyp; Temp_Decl := Make_Object_Declaration (Loc, Defining_Identifier => Temp_Id, Aliased_Present => True, Object_Definition => New_Occurrence_Of (Etyp, Loc)); if Nkind (Expression (N)) = N_Qualified_Expression then Set_Expression (Temp_Decl, Expression (Expression (N))); end if; Insert_Action (N, Temp_Decl); Rewrite (N, Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Temp_Id, Loc), Attribute_Name => Name_Unrestricted_Access)); Analyze_And_Resolve (N, PtrT); end Rewrite_Coextension; ------------------------------ -- Size_In_Storage_Elements -- ------------------------------ function Size_In_Storage_Elements (E : Entity_Id) return Node_Id is begin -- Logically this just returns E'Max_Size_In_Storage_Elements. -- However, the reason for the existence of this function is -- to construct a test for sizes too large, which means near the -- 32-bit limit on a 32-bit machine, and precisely the trouble -- is that we get overflows when sizes are greater than 2**31. -- So what we end up doing for array types is to use the expression: -- number-of-elements * component_type'Max_Size_In_Storage_Elements -- which avoids this problem. All this is a bit bogus, but it does -- mean we catch common cases of trying to allocate arrays that -- are too large, and which in the absence of a check results in -- undetected chaos ??? -- Note in particular that this is a pessimistic estimate in the -- case of packed array types, where an array element might occupy -- just a fraction of a storage element??? declare Idx : Node_Id := First_Index (E); Len : Node_Id; Res : Node_Id := Empty; begin for J in 1 .. Number_Dimensions (E) loop if not Is_Modular_Integer_Type (Etype (Idx)) then Len := Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (E, Loc), Attribute_Name => Name_Length, Expressions => New_List (Make_Integer_Literal (Loc, J))); -- For indexes that are modular types we cannot generate code -- to compute 'Length since for large arrays 'Last -'First + 1 -- causes overflow; therefore we compute 'Last - 'First (which -- is not the exact number of components but it is valid for -- the purpose of this runtime check on 32-bit targets). else declare Len_Minus_1_Expr : Node_Id; Test_Gt : Node_Id; begin Test_Gt := Make_Op_Gt (Loc, Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (E, Loc), Attribute_Name => Name_Last, Expressions => New_List (Make_Integer_Literal (Loc, J))), Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (E, Loc), Attribute_Name => Name_First, Expressions => New_List (Make_Integer_Literal (Loc, J)))); Len_Minus_1_Expr := Convert_To (Standard_Unsigned, Make_Op_Subtract (Loc, Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (E, Loc), Attribute_Name => Name_Last, Expressions => New_List (Make_Integer_Literal (Loc, J))), Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (E, Loc), Attribute_Name => Name_First, Expressions => New_List (Make_Integer_Literal (Loc, J))))); -- Handle superflat arrays, i.e. arrays with such bounds -- as 4 .. 2, to ensure that the result is correct. -- Generate: -- (if X'Last > X'First then X'Last - X'First else 0) Len := Make_If_Expression (Loc, Expressions => New_List ( Test_Gt, Len_Minus_1_Expr, Make_Integer_Literal (Loc, Uint_0))); end; end if; if J = 1 then Res := Len; else pragma Assert (Present (Res)); Res := Make_Op_Multiply (Loc, Left_Opnd => Res, Right_Opnd => Len); end if; Next_Index (Idx); end loop; return Make_Op_Multiply (Loc, Left_Opnd => Len, Right_Opnd => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Component_Type (E), Loc), Attribute_Name => Name_Max_Size_In_Storage_Elements)); end; end Size_In_Storage_Elements; -- Local variables Dtyp : constant Entity_Id := Available_View (Designated_Type (PtrT)); Desig : Entity_Id; Nod : Node_Id; Pool : Entity_Id; Rel_Typ : Entity_Id; Temp : Entity_Id; -- Start of processing for Expand_N_Allocator begin -- Warn on the presence of an allocator of an anonymous access type when -- enabled, except when it's an object declaration at library level. if Warn_On_Anonymous_Allocators and then Ekind (PtrT) = E_Anonymous_Access_Type and then not (Is_Library_Level_Entity (PtrT) and then Nkind (Associated_Node_For_Itype (PtrT)) = N_Object_Declaration) then Error_Msg_N ("??use of an anonymous access type allocator", N); end if; -- RM E.2.2(17). We enforce that the expected type of an allocator -- shall not be a remote access-to-class-wide-limited-private type -- Why is this being done at expansion time, seems clearly wrong ??? Validate_Remote_Access_To_Class_Wide_Type (N); -- Processing for anonymous access-to-controlled types. These access -- types receive a special finalization master which appears in the -- declarations of the enclosing semantic unit. This expansion is done -- now to ensure that any additional types generated by this routine or -- Expand_Allocator_Expression inherit the proper type attributes. if (Ekind (PtrT) = E_Anonymous_Access_Type or else (Is_Itype (PtrT) and then No (Finalization_Master (PtrT)))) and then Needs_Finalization (Dtyp) then -- Detect the allocation of an anonymous controlled object where the -- type of the context is named. For example: -- procedure Proc (Ptr : Named_Access_Typ); -- Proc (new Designated_Typ); -- Regardless of the anonymous-to-named access type conversion, the -- lifetime of the object must be associated with the named access -- type. Use the finalization-related attributes of this type. if Nkind_In (Parent (N), N_Type_Conversion, N_Unchecked_Type_Conversion) and then Ekind_In (Etype (Parent (N)), E_Access_Subtype, E_Access_Type, E_General_Access_Type) then Rel_Typ := Etype (Parent (N)); else Rel_Typ := Empty; end if; -- Anonymous access-to-controlled types allocate on the global pool. -- Note that this is a "root type only" attribute. if No (Associated_Storage_Pool (PtrT)) then if Present (Rel_Typ) then Set_Associated_Storage_Pool (Root_Type (PtrT), Associated_Storage_Pool (Rel_Typ)); else Set_Associated_Storage_Pool (Root_Type (PtrT), RTE (RE_Global_Pool_Object)); end if; end if; -- The finalization master must be inserted and analyzed as part of -- the current semantic unit. Note that the master is updated when -- analysis changes current units. Note that this is a "root type -- only" attribute. if Present (Rel_Typ) then Set_Finalization_Master (Root_Type (PtrT), Finalization_Master (Rel_Typ)); else Build_Anonymous_Master (Root_Type (PtrT)); end if; end if; -- Set the storage pool and find the appropriate version of Allocate to -- call. Do not overwrite the storage pool if it is already set, which -- can happen for build-in-place function returns (see -- Exp_Ch4.Expand_N_Extended_Return_Statement). if No (Storage_Pool (N)) then Pool := Associated_Storage_Pool (Root_Type (PtrT)); if Present (Pool) then Set_Storage_Pool (N, Pool); if Is_RTE (Pool, RE_SS_Pool) then Check_Restriction (No_Secondary_Stack, N); Set_Procedure_To_Call (N, RTE (RE_SS_Allocate)); -- In the case of an allocator for a simple storage pool, locate -- and save a reference to the pool type's Allocate routine. elsif Present (Get_Rep_Pragma (Etype (Pool), Name_Simple_Storage_Pool_Type)) then declare Pool_Type : constant Entity_Id := Base_Type (Etype (Pool)); Alloc_Op : Entity_Id; begin Alloc_Op := Get_Name_Entity_Id (Name_Allocate); while Present (Alloc_Op) loop if Scope (Alloc_Op) = Scope (Pool_Type) and then Present (First_Formal (Alloc_Op)) and then Etype (First_Formal (Alloc_Op)) = Pool_Type then Set_Procedure_To_Call (N, Alloc_Op); exit; else Alloc_Op := Homonym (Alloc_Op); end if; end loop; end; elsif Is_Class_Wide_Type (Etype (Pool)) then Set_Procedure_To_Call (N, RTE (RE_Allocate_Any)); else Set_Procedure_To_Call (N, Find_Prim_Op (Etype (Pool), Name_Allocate)); end if; end if; end if; -- Under certain circumstances we can replace an allocator by an access -- to statically allocated storage. The conditions, as noted in AARM -- 3.10 (10c) are as follows: -- Size and initial value is known at compile time -- Access type is access-to-constant -- The allocator is not part of a constraint on a record component, -- because in that case the inserted actions are delayed until the -- record declaration is fully analyzed, which is too late for the -- analysis of the rewritten allocator. if Is_Access_Constant (PtrT) and then Nkind (Expression (N)) = N_Qualified_Expression and then Compile_Time_Known_Value (Expression (Expression (N))) and then Size_Known_At_Compile_Time (Etype (Expression (Expression (N)))) and then not Is_Record_Type (Current_Scope) then -- Here we can do the optimization. For the allocator -- new x'(y) -- We insert an object declaration -- Tnn : aliased x := y; -- and replace the allocator by Tnn'Unrestricted_Access. Tnn is -- marked as requiring static allocation. Temp := Make_Temporary (Loc, 'T', Expression (Expression (N))); Desig := Subtype_Mark (Expression (N)); -- If context is constrained, use constrained subtype directly, -- so that the constant is not labelled as having a nominally -- unconstrained subtype. if Entity (Desig) = Base_Type (Dtyp) then Desig := New_Occurrence_Of (Dtyp, Loc); end if; Insert_Action (N, Make_Object_Declaration (Loc, Defining_Identifier => Temp, Aliased_Present => True, Constant_Present => Is_Access_Constant (PtrT), Object_Definition => Desig, Expression => Expression (Expression (N)))); Rewrite (N, Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Temp, Loc), Attribute_Name => Name_Unrestricted_Access)); Analyze_And_Resolve (N, PtrT); -- We set the variable as statically allocated, since we don't want -- it going on the stack of the current procedure. Set_Is_Statically_Allocated (Temp); return; end if; -- Same if the allocator is an access discriminant for a local object: -- instead of an allocator we create a local value and constrain the -- enclosing object with the corresponding access attribute. if Is_Static_Coextension (N) then Rewrite_Coextension (N); return; end if; -- Check for size too large, we do this because the back end misses -- proper checks here and can generate rubbish allocation calls when -- we are near the limit. We only do this for the 32-bit address case -- since that is from a practical point of view where we see a problem. if System_Address_Size = 32 and then not Storage_Checks_Suppressed (PtrT) and then not Storage_Checks_Suppressed (Dtyp) and then not Storage_Checks_Suppressed (Etyp) then -- The check we want to generate should look like -- if Etyp'Max_Size_In_Storage_Elements > 3.5 gigabytes then -- raise Storage_Error; -- end if; -- where 3.5 gigabytes is a constant large enough to accommodate any -- reasonable request for. But we can't do it this way because at -- least at the moment we don't compute this attribute right, and -- can silently give wrong results when the result gets large. Since -- this is all about large results, that's bad, so instead we only -- apply the check for constrained arrays, and manually compute the -- value of the attribute ??? -- The check on No_Initialization is used here to prevent generating -- this runtime check twice when the allocator is locally replaced by -- the expander with another one. if Is_Array_Type (Etyp) and then not No_Initialization (N) then declare Cond : Node_Id; Ins_Nod : Node_Id := N; Siz_Typ : Entity_Id := Etyp; Expr : Node_Id; begin -- For unconstrained array types initialized with a qualified -- expression we use its type to perform this check if not Is_Constrained (Etyp) and then not No_Initialization (N) and then Nkind (Expression (N)) = N_Qualified_Expression then Expr := Expression (Expression (N)); Siz_Typ := Etype (Expression (Expression (N))); -- If the qualified expression has been moved to an internal -- temporary (to remove side effects) then we must insert -- the runtime check before its declaration to ensure that -- the check is performed before the execution of the code -- computing the qualified expression. if Nkind (Expr) = N_Identifier and then Is_Internal_Name (Chars (Expr)) and then Nkind (Parent (Entity (Expr))) = N_Object_Declaration then Ins_Nod := Parent (Entity (Expr)); else Ins_Nod := Expr; end if; end if; if Is_Constrained (Siz_Typ) and then Ekind (Siz_Typ) /= E_String_Literal_Subtype then -- For CCG targets, the largest array may have up to 2**31-1 -- components (i.e. 2 gigabytes if each array component is -- one byte). This ensures that fat pointer fields do not -- overflow, since they are 32-bit integer types, and also -- ensures that 'Length can be computed at run time. if Modify_Tree_For_C then Cond := Make_Op_Gt (Loc, Left_Opnd => Size_In_Storage_Elements (Siz_Typ), Right_Opnd => Make_Integer_Literal (Loc, Uint_2 ** 31 - Uint_1)); -- For native targets the largest object is 3.5 gigabytes else Cond := Make_Op_Gt (Loc, Left_Opnd => Size_In_Storage_Elements (Siz_Typ), Right_Opnd => Make_Integer_Literal (Loc, Uint_7 * (Uint_2 ** 29))); end if; Insert_Action (Ins_Nod, Make_Raise_Storage_Error (Loc, Condition => Cond, Reason => SE_Object_Too_Large)); if Entity (Cond) = Standard_True then Error_Msg_N ("object too large: Storage_Error will be raised at " & "run time??", N); end if; end if; end; end if; end if; -- If no storage pool has been specified, or the storage pool -- is System.Pool_Global.Global_Pool_Object, and the restriction -- No_Standard_Allocators_After_Elaboration is present, then generate -- a call to Elaboration_Allocators.Check_Standard_Allocator. if Nkind (N) = N_Allocator and then (No (Storage_Pool (N)) or else Is_RTE (Storage_Pool (N), RE_Global_Pool_Object)) and then Restriction_Active (No_Standard_Allocators_After_Elaboration) then Insert_Action (N, Make_Procedure_Call_Statement (Loc, Name => New_Occurrence_Of (RTE (RE_Check_Standard_Allocator), Loc))); end if; -- Handle case of qualified expression (other than optimization above) if Nkind (Expression (N)) = N_Qualified_Expression then Expand_Allocator_Expression (N); return; end if; -- If the allocator is for a type which requires initialization, and -- there is no initial value (i.e. operand is a subtype indication -- rather than a qualified expression), then we must generate a call to -- the initialization routine using an expressions action node: -- [Pnnn : constant ptr_T := new (T); Init (Pnnn.all,...); Pnnn] -- Here ptr_T is the pointer type for the allocator, and T is the -- subtype of the allocator. A special case arises if the designated -- type of the access type is a task or contains tasks. In this case -- the call to Init (Temp.all ...) is replaced by code that ensures -- that tasks get activated (see Exp_Ch9.Build_Task_Allocate_Block -- for details). In addition, if the type T is a task type, then the -- first argument to Init must be converted to the task record type. declare T : constant Entity_Id := Etype (Expression (N)); Args : List_Id; Decls : List_Id; Decl : Node_Id; Discr : Elmt_Id; Init : Entity_Id; Init_Arg1 : Node_Id; Init_Call : Node_Id; Temp_Decl : Node_Id; Temp_Type : Entity_Id; begin -- Apply constraint checks against designated subtype (RM 4.8(10/2)). -- Discriminant checks will be generated by the expansion below. if Is_Array_Type (Dtyp) then Apply_Constraint_Check (Expression (N), Dtyp, No_Sliding => True); Apply_Predicate_Check (Expression (N), Dtyp); if Nkind (Expression (N)) = N_Raise_Constraint_Error then Rewrite (N, New_Copy (Expression (N))); Set_Etype (N, PtrT); return; end if; end if; if No_Initialization (N) then -- Even though this might be a simple allocation, create a custom -- Allocate if the context requires it. if Present (Finalization_Master (PtrT)) then Build_Allocate_Deallocate_Proc (N => N, Is_Allocate => True); end if; -- Optimize the default allocation of an array object when pragma -- Initialize_Scalars or Normalize_Scalars is in effect. Construct an -- in-place initialization aggregate which may be convert into a fast -- memset by the backend. elsif Init_Or_Norm_Scalars and then Is_Array_Type (T) -- The array must lack atomic components because they are treated -- as non-static, and as a result the backend will not initialize -- the memory in one go. and then not Has_Atomic_Components (T) -- The array must not be packed because the invalid values in -- System.Scalar_Values are multiples of Storage_Unit. and then not Is_Packed (T) -- The array must have static non-empty ranges, otherwise the -- backend cannot initialize the memory in one go. and then Has_Static_Non_Empty_Array_Bounds (T) -- The optimization is only relevant for arrays of scalar types and then Is_Scalar_Type (Component_Type (T)) -- Similar to regular array initialization using a type init proc, -- predicate checks are not performed because the initialization -- values are intentionally invalid, and may violate the predicate. and then not Has_Predicates (Component_Type (T)) -- The component type must have a single initialization value and then Needs_Simple_Initialization (Typ => Component_Type (T), Consider_IS => True) then Set_Analyzed (N); Temp := Make_Temporary (Loc, 'P'); -- Generate: -- Temp : Ptr_Typ := new ...; Insert_Action (Assoc_Node => N, Ins_Action => Make_Object_Declaration (Loc, Defining_Identifier => Temp, Object_Definition => New_Occurrence_Of (PtrT, Loc), Expression => Relocate_Node (N)), Suppress => All_Checks); -- Generate: -- Temp.all := (others => ...); Insert_Action (Assoc_Node => N, Ins_Action => Make_Assignment_Statement (Loc, Name => Make_Explicit_Dereference (Loc, Prefix => New_Occurrence_Of (Temp, Loc)), Expression => Get_Simple_Init_Val (Typ => T, N => N, Size => Esize (Component_Type (T)))), Suppress => All_Checks); Rewrite (N, New_Occurrence_Of (Temp, Loc)); Analyze_And_Resolve (N, PtrT); -- Case of no initialization procedure present elsif not Has_Non_Null_Base_Init_Proc (T) then -- Case of simple initialization required if Needs_Simple_Initialization (T) then Check_Restriction (No_Default_Initialization, N); Rewrite (Expression (N), Make_Qualified_Expression (Loc, Subtype_Mark => New_Occurrence_Of (T, Loc), Expression => Get_Simple_Init_Val (T, N))); Analyze_And_Resolve (Expression (Expression (N)), T); Analyze_And_Resolve (Expression (N), T); Set_Paren_Count (Expression (Expression (N)), 1); Expand_N_Allocator (N); -- No initialization required else Build_Allocate_Deallocate_Proc (N => N, Is_Allocate => True); end if; -- Case of initialization procedure present, must be called -- NOTE: There is a *huge* amount of code duplication here from -- Build_Initialization_Call. We should probably refactor??? else Check_Restriction (No_Default_Initialization, N); if not Restriction_Active (No_Default_Initialization) then Init := Base_Init_Proc (T); Nod := N; Temp := Make_Temporary (Loc, 'P'); -- Construct argument list for the initialization routine call Init_Arg1 := Make_Explicit_Dereference (Loc, Prefix => New_Occurrence_Of (Temp, Loc)); Set_Assignment_OK (Init_Arg1); Temp_Type := PtrT; -- The initialization procedure expects a specific type. if the -- context is access to class wide, indicate that the object -- being allocated has the right specific type. if Is_Class_Wide_Type (Dtyp) then Init_Arg1 := Unchecked_Convert_To (T, Init_Arg1); end if; -- If designated type is a concurrent type or if it is private -- type whose definition is a concurrent type, the first -- argument in the Init routine has to be unchecked conversion -- to the corresponding record type. If the designated type is -- a derived type, also convert the argument to its root type. if Is_Concurrent_Type (T) then Init_Arg1 := Unchecked_Convert_To ( Corresponding_Record_Type (T), Init_Arg1); elsif Is_Private_Type (T) and then Present (Full_View (T)) and then Is_Concurrent_Type (Full_View (T)) then Init_Arg1 := Unchecked_Convert_To (Corresponding_Record_Type (Full_View (T)), Init_Arg1); elsif Etype (First_Formal (Init)) /= Base_Type (T) then declare Ftyp : constant Entity_Id := Etype (First_Formal (Init)); begin Init_Arg1 := OK_Convert_To (Etype (Ftyp), Init_Arg1); Set_Etype (Init_Arg1, Ftyp); end; end if; Args := New_List (Init_Arg1); -- For the task case, pass the Master_Id of the access type as -- the value of the _Master parameter, and _Chain as the value -- of the _Chain parameter (_Chain will be defined as part of -- the generated code for the allocator). -- In Ada 2005, the context may be a function that returns an -- anonymous access type. In that case the Master_Id has been -- created when expanding the function declaration. if Has_Task (T) then if No (Master_Id (Base_Type (PtrT))) then -- The designated type was an incomplete type, and the -- access type did not get expanded. Salvage it now. if Present (Parent (Base_Type (PtrT))) then Expand_N_Full_Type_Declaration (Parent (Base_Type (PtrT))); -- The only other possibility is an itype. For this -- case, the master must exist in the context. This is -- the case when the allocator initializes an access -- component in an init-proc. else pragma Assert (Is_Itype (PtrT)); Build_Master_Renaming (PtrT, N); end if; end if; -- If the context of the allocator is a declaration or an -- assignment, we can generate a meaningful image for it, -- even though subsequent assignments might remove the -- connection between task and entity. We build this image -- when the left-hand side is a simple variable, a simple -- indexed assignment or a simple selected component. if Nkind (Parent (N)) = N_Assignment_Statement then declare Nam : constant Node_Id := Name (Parent (N)); begin if Is_Entity_Name (Nam) then Decls := Build_Task_Image_Decls (Loc, New_Occurrence_Of (Entity (Nam), Sloc (Nam)), T); elsif Nkind_In (Nam, N_Indexed_Component, N_Selected_Component) and then Is_Entity_Name (Prefix (Nam)) then Decls := Build_Task_Image_Decls (Loc, Nam, Etype (Prefix (Nam))); else Decls := Build_Task_Image_Decls (Loc, T, T); end if; end; elsif Nkind (Parent (N)) = N_Object_Declaration then Decls := Build_Task_Image_Decls (Loc, Defining_Identifier (Parent (N)), T); else Decls := Build_Task_Image_Decls (Loc, T, T); end if; if Restriction_Active (No_Task_Hierarchy) then Append_To (Args, New_Occurrence_Of (RTE (RE_Library_Task_Level), Loc)); else Append_To (Args, New_Occurrence_Of (Master_Id (Base_Type (Root_Type (PtrT))), Loc)); end if; Append_To (Args, Make_Identifier (Loc, Name_uChain)); Decl := Last (Decls); Append_To (Args, New_Occurrence_Of (Defining_Identifier (Decl), Loc)); -- Has_Task is false, Decls not used else Decls := No_List; end if; -- Add discriminants if discriminated type declare Dis : Boolean := False; Typ : Entity_Id := Empty; begin if Has_Discriminants (T) then Dis := True; Typ := T; -- Type may be a private type with no visible discriminants -- in which case check full view if in scope, or the -- underlying_full_view if dealing with a type whose full -- view may be derived from a private type whose own full -- view has discriminants. elsif Is_Private_Type (T) then if Present (Full_View (T)) and then Has_Discriminants (Full_View (T)) then Dis := True; Typ := Full_View (T); elsif Present (Underlying_Full_View (T)) and then Has_Discriminants (Underlying_Full_View (T)) then Dis := True; Typ := Underlying_Full_View (T); end if; end if; if Dis then -- If the allocated object will be constrained by the -- default values for discriminants, then build a subtype -- with those defaults, and change the allocated subtype -- to that. Note that this happens in fewer cases in Ada -- 2005 (AI-363). if not Is_Constrained (Typ) and then Present (Discriminant_Default_Value (First_Discriminant (Typ))) and then (Ada_Version < Ada_2005 or else not Object_Type_Has_Constrained_Partial_View (Typ, Current_Scope)) then Typ := Build_Default_Subtype (Typ, N); Set_Expression (N, New_Occurrence_Of (Typ, Loc)); end if; Discr := First_Elmt (Discriminant_Constraint (Typ)); while Present (Discr) loop Nod := Node (Discr); Append (New_Copy_Tree (Node (Discr)), Args); -- AI-416: when the discriminant constraint is an -- anonymous access type make sure an accessibility -- check is inserted if necessary (3.10.2(22.q/2)) if Ada_Version >= Ada_2005 and then Ekind (Etype (Nod)) = E_Anonymous_Access_Type then Apply_Accessibility_Check (Nod, Typ, Insert_Node => Nod); end if; Next_Elmt (Discr); end loop; end if; end; -- We set the allocator as analyzed so that when we analyze -- the if expression node, we do not get an unwanted recursive -- expansion of the allocator expression. Set_Analyzed (N, True); Nod := Relocate_Node (N); -- Here is the transformation: -- input: new Ctrl_Typ -- output: Temp : constant Ctrl_Typ_Ptr := new Ctrl_Typ; -- Ctrl_TypIP (Temp.all, ...); -- [Deep_]Initialize (Temp.all); -- Here Ctrl_Typ_Ptr is the pointer type for the allocator, and -- is the subtype of the allocator. Temp_Decl := Make_Object_Declaration (Loc, Defining_Identifier => Temp, Constant_Present => True, Object_Definition => New_Occurrence_Of (Temp_Type, Loc), Expression => Nod); Set_Assignment_OK (Temp_Decl); Insert_Action (N, Temp_Decl, Suppress => All_Checks); Build_Allocate_Deallocate_Proc (Temp_Decl, True); -- If the designated type is a task type or contains tasks, -- create block to activate created tasks, and insert -- declaration for Task_Image variable ahead of call. if Has_Task (T) then declare L : constant List_Id := New_List; Blk : Node_Id; begin Build_Task_Allocate_Block (L, Nod, Args); Blk := Last (L); Insert_List_Before (First (Declarations (Blk)), Decls); Insert_Actions (N, L); end; else Insert_Action (N, Make_Procedure_Call_Statement (Loc, Name => New_Occurrence_Of (Init, Loc), Parameter_Associations => Args)); end if; if Needs_Finalization (T) then -- Generate: -- [Deep_]Initialize (Init_Arg1); Init_Call := Make_Init_Call (Obj_Ref => New_Copy_Tree (Init_Arg1), Typ => T); -- Guard against a missing [Deep_]Initialize when the -- designated type was not properly frozen. if Present (Init_Call) then Insert_Action (N, Init_Call); end if; end if; Rewrite (N, New_Occurrence_Of (Temp, Loc)); Analyze_And_Resolve (N, PtrT); end if; end if; end; -- Ada 2005 (AI-251): If the allocator is for a class-wide interface -- object that has been rewritten as a reference, we displace "this" -- to reference properly its secondary dispatch table. if Nkind (N) = N_Identifier and then Is_Interface (Dtyp) then Displace_Allocator_Pointer (N); end if; exception when RE_Not_Available => return; end Expand_N_Allocator; ----------------------- -- Expand_N_And_Then -- ----------------------- procedure Expand_N_And_Then (N : Node_Id) renames Expand_Short_Circuit_Operator; ------------------------------ -- Expand_N_Case_Expression -- ------------------------------ procedure Expand_N_Case_Expression (N : Node_Id) is function Is_Copy_Type (Typ : Entity_Id) return Boolean; -- Return True if we can copy objects of this type when expanding a case -- expression. ------------------ -- Is_Copy_Type -- ------------------ function Is_Copy_Type (Typ : Entity_Id) return Boolean is begin -- If Minimize_Expression_With_Actions is True, we can afford to copy -- large objects, as long as they are constrained and not limited. return Is_Elementary_Type (Underlying_Type (Typ)) or else (Minimize_Expression_With_Actions and then Is_Constrained (Underlying_Type (Typ)) and then not Is_Limited_Type (Underlying_Type (Typ))); end Is_Copy_Type; -- Local variables Loc : constant Source_Ptr := Sloc (N); Par : constant Node_Id := Parent (N); Typ : constant Entity_Id := Etype (N); Acts : List_Id; Alt : Node_Id; Case_Stmt : Node_Id; Decl : Node_Id; Expr : Node_Id; Target : Entity_Id := Empty; Target_Typ : Entity_Id; In_Predicate : Boolean := False; -- Flag set when the case expression appears within a predicate Optimize_Return_Stmt : Boolean := False; -- Flag set when the case expression can be optimized in the context of -- a simple return statement. -- Start of processing for Expand_N_Case_Expression begin -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- If the case expression is a predicate specification, and the type -- to which it applies has a static predicate aspect, do not expand, -- because it will be converted to the proper predicate form later. if Ekind_In (Current_Scope, E_Function, E_Procedure) and then Is_Predicate_Function (Current_Scope) then In_Predicate := True; if Has_Static_Predicate_Aspect (Etype (First_Entity (Current_Scope))) then return; end if; end if; -- When the type of the case expression is elementary, expand -- (case X is when A => AX, when B => BX ...) -- into -- do -- Target : Typ; -- case X is -- when A => -- Target := AX; -- when B => -- Target := BX; -- ... -- end case; -- in Target end; -- In all other cases expand into -- do -- type Ptr_Typ is access all Typ; -- Target : Ptr_Typ; -- case X is -- when A => -- Target := AX'Unrestricted_Access; -- when B => -- Target := BX'Unrestricted_Access; -- ... -- end case; -- in Target.all end; -- This approach avoids extra copies of potentially large objects. It -- also allows handling of values of limited or unconstrained types. -- Note that we do the copy also for constrained, nonlimited types -- when minimizing expressions with actions (e.g. when generating C -- code) since it allows us to do the optimization below in more cases. -- Small optimization: when the case expression appears in the context -- of a simple return statement, expand into -- case X is -- when A => -- return AX; -- when B => -- return BX; -- ... -- end case; Case_Stmt := Make_Case_Statement (Loc, Expression => Expression (N), Alternatives => New_List); -- Preserve the original context for which the case statement is being -- generated. This is needed by the finalization machinery to prevent -- the premature finalization of controlled objects found within the -- case statement. Set_From_Conditional_Expression (Case_Stmt); Acts := New_List; -- Scalar/Copy case if Is_Copy_Type (Typ) then Target_Typ := Typ; -- ??? Do not perform the optimization when the return statement is -- within a predicate function, as this causes spurious errors. Could -- this be a possible mismatch in handling this case somewhere else -- in semantic analysis? Optimize_Return_Stmt := Nkind (Par) = N_Simple_Return_Statement and then not In_Predicate; -- Otherwise create an access type to handle the general case using -- 'Unrestricted_Access. -- Generate: -- type Ptr_Typ is access all Typ; else if Generate_C_Code then -- We cannot ensure that correct C code will be generated if any -- temporary is created down the line (to e.g. handle checks or -- capture values) since we might end up with dangling references -- to local variables, so better be safe and reject the construct. Error_Msg_N ("case expression too complex, use case statement instead", N); end if; Target_Typ := Make_Temporary (Loc, 'P'); Append_To (Acts, Make_Full_Type_Declaration (Loc, Defining_Identifier => Target_Typ, Type_Definition => Make_Access_To_Object_Definition (Loc, All_Present => True, Subtype_Indication => New_Occurrence_Of (Typ, Loc)))); end if; -- Create the declaration of the target which captures the value of the -- expression. -- Generate: -- Target : [Ptr_]Typ; if not Optimize_Return_Stmt then Target := Make_Temporary (Loc, 'T'); Decl := Make_Object_Declaration (Loc, Defining_Identifier => Target, Object_Definition => New_Occurrence_Of (Target_Typ, Loc)); Set_No_Initialization (Decl); Append_To (Acts, Decl); end if; -- Process the alternatives Alt := First (Alternatives (N)); while Present (Alt) loop declare Alt_Expr : Node_Id := Expression (Alt); Alt_Loc : constant Source_Ptr := Sloc (Alt_Expr); LHS : Node_Id; Stmts : List_Id; begin -- Take the unrestricted access of the expression value for non- -- scalar types. This approach avoids big copies and covers the -- limited and unconstrained cases. -- Generate: -- AX'Unrestricted_Access if not Is_Copy_Type (Typ) then Alt_Expr := Make_Attribute_Reference (Alt_Loc, Prefix => Relocate_Node (Alt_Expr), Attribute_Name => Name_Unrestricted_Access); end if; -- Generate: -- return AX['Unrestricted_Access]; if Optimize_Return_Stmt then Stmts := New_List ( Make_Simple_Return_Statement (Alt_Loc, Expression => Alt_Expr)); -- Generate: -- Target := AX['Unrestricted_Access]; else LHS := New_Occurrence_Of (Target, Loc); Set_Assignment_OK (LHS); Stmts := New_List ( Make_Assignment_Statement (Alt_Loc, Name => LHS, Expression => Alt_Expr)); end if; -- Propagate declarations inserted in the node by Insert_Actions -- (for example, temporaries generated to remove side effects). -- These actions must remain attached to the alternative, given -- that they are generated by the corresponding expression. if Present (Actions (Alt)) then Prepend_List (Actions (Alt), Stmts); end if; -- Finalize any transient objects on exit from the alternative. -- This is done only in the return optimization case because -- otherwise the case expression is converted into an expression -- with actions which already contains this form of processing. if Optimize_Return_Stmt then Process_If_Case_Statements (N, Stmts); end if; Append_To (Alternatives (Case_Stmt), Make_Case_Statement_Alternative (Sloc (Alt), Discrete_Choices => Discrete_Choices (Alt), Statements => Stmts)); end; Next (Alt); end loop; -- Rewrite the parent return statement as a case statement if Optimize_Return_Stmt then Rewrite (Par, Case_Stmt); Analyze (Par); -- Otherwise convert the case expression into an expression with actions else Append_To (Acts, Case_Stmt); if Is_Copy_Type (Typ) then Expr := New_Occurrence_Of (Target, Loc); else Expr := Make_Explicit_Dereference (Loc, Prefix => New_Occurrence_Of (Target, Loc)); end if; -- Generate: -- do -- ... -- in Target[.all] end; Rewrite (N, Make_Expression_With_Actions (Loc, Expression => Expr, Actions => Acts)); Analyze_And_Resolve (N, Typ); end if; end Expand_N_Case_Expression; ----------------------------------- -- Expand_N_Explicit_Dereference -- ----------------------------------- procedure Expand_N_Explicit_Dereference (N : Node_Id) is begin -- Insert explicit dereference call for the checked storage pool case Insert_Dereference_Action (Prefix (N)); -- If the type is an Atomic type for which Atomic_Sync is enabled, then -- we set the atomic sync flag. if Is_Atomic (Etype (N)) and then not Atomic_Synchronization_Disabled (Etype (N)) then Activate_Atomic_Synchronization (N); end if; end Expand_N_Explicit_Dereference; -------------------------------------- -- Expand_N_Expression_With_Actions -- -------------------------------------- procedure Expand_N_Expression_With_Actions (N : Node_Id) is Acts : constant List_Id := Actions (N); procedure Force_Boolean_Evaluation (Expr : Node_Id); -- Force the evaluation of Boolean expression Expr function Process_Action (Act : Node_Id) return Traverse_Result; -- Inspect and process a single action of an expression_with_actions for -- transient objects. If such objects are found, the routine generates -- code to clean them up when the context of the expression is evaluated -- or elaborated. ------------------------------ -- Force_Boolean_Evaluation -- ------------------------------ procedure Force_Boolean_Evaluation (Expr : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Flag_Decl : Node_Id; Flag_Id : Entity_Id; begin -- Relocate the expression to the actions list by capturing its value -- in a Boolean flag. Generate: -- Flag : constant Boolean := Expr; Flag_Id := Make_Temporary (Loc, 'F'); Flag_Decl := Make_Object_Declaration (Loc, Defining_Identifier => Flag_Id, Constant_Present => True, Object_Definition => New_Occurrence_Of (Standard_Boolean, Loc), Expression => Relocate_Node (Expr)); Append (Flag_Decl, Acts); Analyze (Flag_Decl); -- Replace the expression with a reference to the flag Rewrite (Expression (N), New_Occurrence_Of (Flag_Id, Loc)); Analyze (Expression (N)); end Force_Boolean_Evaluation; -------------------- -- Process_Action -- -------------------- function Process_Action (Act : Node_Id) return Traverse_Result is begin if Nkind (Act) = N_Object_Declaration and then Is_Finalizable_Transient (Act, N) then Process_Transient_In_Expression (Act, N, Acts); return Skip; -- Avoid processing temporary function results multiple times when -- dealing with nested expression_with_actions. elsif Nkind (Act) = N_Expression_With_Actions then return Abandon; -- Do not process temporary function results in loops. This is done -- by Expand_N_Loop_Statement and Build_Finalizer. elsif Nkind (Act) = N_Loop_Statement then return Abandon; end if; return OK; end Process_Action; procedure Process_Single_Action is new Traverse_Proc (Process_Action); -- Local variables Act : Node_Id; -- Start of processing for Expand_N_Expression_With_Actions begin -- Do not evaluate the expression when it denotes an entity because the -- expression_with_actions node will be replaced by the reference. if Is_Entity_Name (Expression (N)) then null; -- Do not evaluate the expression when there are no actions because the -- expression_with_actions node will be replaced by the expression. elsif No (Acts) or else Is_Empty_List (Acts) then null; -- Force the evaluation of the expression by capturing its value in a -- temporary. This ensures that aliases of transient objects do not leak -- to the expression of the expression_with_actions node: -- do -- Trans_Id : Ctrl_Typ := ...; -- Alias : ... := Trans_Id; -- in ... Alias ... end; -- In the example above, Trans_Id cannot be finalized at the end of the -- actions list because this may affect the alias and the final value of -- the expression_with_actions. Forcing the evaluation encapsulates the -- reference to the Alias within the actions list: -- do -- Trans_Id : Ctrl_Typ := ...; -- Alias : ... := Trans_Id; -- Val : constant Boolean := ... Alias ...; -- -- in Val end; -- Once this transformation is performed, it is safe to finalize the -- transient object at the end of the actions list. -- Note that Force_Evaluation does not remove side effects in operators -- because it assumes that all operands are evaluated and side effect -- free. This is not the case when an operand depends implicitly on the -- transient object through the use of access types. elsif Is_Boolean_Type (Etype (Expression (N))) then Force_Boolean_Evaluation (Expression (N)); -- The expression of an expression_with_actions node may not necessarily -- be Boolean when the node appears in an if expression. In this case do -- the usual forced evaluation to encapsulate potential aliasing. else Force_Evaluation (Expression (N)); end if; -- Process all transient objects found within the actions of the EWA -- node. Act := First (Acts); while Present (Act) loop Process_Single_Action (Act); Next (Act); end loop; -- Deal with case where there are no actions. In this case we simply -- rewrite the node with its expression since we don't need the actions -- and the specification of this node does not allow a null action list. -- Note: we use Rewrite instead of Replace, because Codepeer is using -- the expanded tree and relying on being able to retrieve the original -- tree in cases like this. This raises a whole lot of issues of whether -- we have problems elsewhere, which will be addressed in the future??? if Is_Empty_List (Acts) then Rewrite (N, Relocate_Node (Expression (N))); end if; end Expand_N_Expression_With_Actions; ---------------------------- -- Expand_N_If_Expression -- ---------------------------- -- Deal with limited types and condition actions procedure Expand_N_If_Expression (N : Node_Id) is Cond : constant Node_Id := First (Expressions (N)); Loc : constant Source_Ptr := Sloc (N); Thenx : constant Node_Id := Next (Cond); Elsex : constant Node_Id := Next (Thenx); Typ : constant Entity_Id := Etype (N); Actions : List_Id; Decl : Node_Id; Expr : Node_Id; New_If : Node_Id; New_N : Node_Id; -- Determine if we are dealing with a special case of a conditional -- expression used as an actual for an anonymous access type which -- forces us to transform the if expression into an expression with -- actions in order to create a temporary to capture the level of the -- expression in each branch. Force_Expand : constant Boolean := Is_Anonymous_Access_Actual (N); -- Start of processing for Expand_N_If_Expression begin -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- Fold at compile time if condition known. We have already folded -- static if expressions, but it is possible to fold any case in which -- the condition is known at compile time, even though the result is -- non-static. -- Note that we don't do the fold of such cases in Sem_Elab because -- it can cause infinite loops with the expander adding a conditional -- expression, and Sem_Elab circuitry removing it repeatedly. if Compile_Time_Known_Value (Cond) then declare function Fold_Known_Value (Cond : Node_Id) return Boolean; -- Fold at compile time. Assumes condition known. Return True if -- folding occurred, meaning we're done. ---------------------- -- Fold_Known_Value -- ---------------------- function Fold_Known_Value (Cond : Node_Id) return Boolean is begin if Is_True (Expr_Value (Cond)) then Expr := Thenx; Actions := Then_Actions (N); else Expr := Elsex; Actions := Else_Actions (N); end if; Remove (Expr); if Present (Actions) then -- To minimize the use of Expression_With_Actions, just skip -- the optimization as it is not critical for correctness. if Minimize_Expression_With_Actions then return False; end if; Rewrite (N, Make_Expression_With_Actions (Loc, Expression => Relocate_Node (Expr), Actions => Actions)); Analyze_And_Resolve (N, Typ); else Rewrite (N, Relocate_Node (Expr)); end if; -- Note that the result is never static (legitimate cases of -- static if expressions were folded in Sem_Eval). Set_Is_Static_Expression (N, False); return True; end Fold_Known_Value; begin if Fold_Known_Value (Cond) then return; end if; end; end if; -- If the type is limited, and the back end does not handle limited -- types, then we expand as follows to avoid the possibility of -- improper copying. -- type Ptr is access all Typ; -- Cnn : Ptr; -- if cond then -- <> -- Cnn := then-expr'Unrestricted_Access; -- else -- <> -- Cnn := else-expr'Unrestricted_Access; -- end if; -- and replace the if expression by a reference to Cnn.all. -- This special case can be skipped if the back end handles limited -- types properly and ensures that no incorrect copies are made. if Is_By_Reference_Type (Typ) and then not Back_End_Handles_Limited_Types then -- When the "then" or "else" expressions involve controlled function -- calls, generated temporaries are chained on the corresponding list -- of actions. These temporaries need to be finalized after the if -- expression is evaluated. Process_If_Case_Statements (N, Then_Actions (N)); Process_If_Case_Statements (N, Else_Actions (N)); declare Cnn : constant Entity_Id := Make_Temporary (Loc, 'C', N); Ptr_Typ : constant Entity_Id := Make_Temporary (Loc, 'A'); begin -- Generate: -- type Ann is access all Typ; Insert_Action (N, Make_Full_Type_Declaration (Loc, Defining_Identifier => Ptr_Typ, Type_Definition => Make_Access_To_Object_Definition (Loc, All_Present => True, Subtype_Indication => New_Occurrence_Of (Typ, Loc)))); -- Generate: -- Cnn : Ann; Decl := Make_Object_Declaration (Loc, Defining_Identifier => Cnn, Object_Definition => New_Occurrence_Of (Ptr_Typ, Loc)); -- Generate: -- if Cond then -- Cnn := 'Unrestricted_Access; -- else -- Cnn := 'Unrestricted_Access; -- end if; New_If := Make_Implicit_If_Statement (N, Condition => Relocate_Node (Cond), Then_Statements => New_List ( Make_Assignment_Statement (Sloc (Thenx), Name => New_Occurrence_Of (Cnn, Sloc (Thenx)), Expression => Make_Attribute_Reference (Loc, Prefix => Relocate_Node (Thenx), Attribute_Name => Name_Unrestricted_Access))), Else_Statements => New_List ( Make_Assignment_Statement (Sloc (Elsex), Name => New_Occurrence_Of (Cnn, Sloc (Elsex)), Expression => Make_Attribute_Reference (Loc, Prefix => Relocate_Node (Elsex), Attribute_Name => Name_Unrestricted_Access)))); -- Preserve the original context for which the if statement is -- being generated. This is needed by the finalization machinery -- to prevent the premature finalization of controlled objects -- found within the if statement. Set_From_Conditional_Expression (New_If); New_N := Make_Explicit_Dereference (Loc, Prefix => New_Occurrence_Of (Cnn, Loc)); end; -- If the result is an unconstrained array and the if expression is in a -- context other than the initializing expression of the declaration of -- an object, then we pull out the if expression as follows: -- Cnn : constant typ := if-expression -- and then replace the if expression with an occurrence of Cnn. This -- avoids the need in the back end to create on-the-fly variable length -- temporaries (which it cannot do!) -- Note that the test for being in an object declaration avoids doing an -- unnecessary expansion, and also avoids infinite recursion. elsif Is_Array_Type (Typ) and then not Is_Constrained (Typ) and then (Nkind (Parent (N)) /= N_Object_Declaration or else Expression (Parent (N)) /= N) then declare Cnn : constant Node_Id := Make_Temporary (Loc, 'C', N); begin Insert_Action (N, Make_Object_Declaration (Loc, Defining_Identifier => Cnn, Constant_Present => True, Object_Definition => New_Occurrence_Of (Typ, Loc), Expression => Relocate_Node (N), Has_Init_Expression => True)); Rewrite (N, New_Occurrence_Of (Cnn, Loc)); return; end; -- For other types, we only need to expand if there are other actions -- associated with either branch or we need to force expansion to deal -- with if expressions used as an actual of an anonymous access type. elsif Present (Then_Actions (N)) or else Present (Else_Actions (N)) or else Force_Expand then -- We now wrap the actions into the appropriate expression if Minimize_Expression_With_Actions and then (Is_Elementary_Type (Underlying_Type (Typ)) or else Is_Constrained (Underlying_Type (Typ))) then -- If we can't use N_Expression_With_Actions nodes, then we insert -- the following sequence of actions (using Insert_Actions): -- Cnn : typ; -- if cond then -- <> -- Cnn := then-expr; -- else -- <> -- Cnn := else-expr -- end if; -- and replace the if expression by a reference to Cnn declare Cnn : constant Node_Id := Make_Temporary (Loc, 'C', N); begin Decl := Make_Object_Declaration (Loc, Defining_Identifier => Cnn, Object_Definition => New_Occurrence_Of (Typ, Loc)); New_If := Make_Implicit_If_Statement (N, Condition => Relocate_Node (Cond), Then_Statements => New_List ( Make_Assignment_Statement (Sloc (Thenx), Name => New_Occurrence_Of (Cnn, Sloc (Thenx)), Expression => Relocate_Node (Thenx))), Else_Statements => New_List ( Make_Assignment_Statement (Sloc (Elsex), Name => New_Occurrence_Of (Cnn, Sloc (Elsex)), Expression => Relocate_Node (Elsex)))); Set_Assignment_OK (Name (First (Then_Statements (New_If)))); Set_Assignment_OK (Name (First (Else_Statements (New_If)))); New_N := New_Occurrence_Of (Cnn, Loc); end; -- Regular path using Expression_With_Actions else if Present (Then_Actions (N)) then Rewrite (Thenx, Make_Expression_With_Actions (Sloc (Thenx), Actions => Then_Actions (N), Expression => Relocate_Node (Thenx))); Set_Then_Actions (N, No_List); Analyze_And_Resolve (Thenx, Typ); end if; if Present (Else_Actions (N)) then Rewrite (Elsex, Make_Expression_With_Actions (Sloc (Elsex), Actions => Else_Actions (N), Expression => Relocate_Node (Elsex))); Set_Else_Actions (N, No_List); Analyze_And_Resolve (Elsex, Typ); end if; -- We must force expansion into an expression with actions when -- an if expression gets used directly as an actual for an -- anonymous access type. if Force_Expand then declare Cnn : constant Entity_Id := Make_Temporary (Loc, 'C'); Acts : List_Id; begin Acts := New_List; -- Generate: -- Cnn : Ann; Decl := Make_Object_Declaration (Loc, Defining_Identifier => Cnn, Object_Definition => New_Occurrence_Of (Typ, Loc)); Append_To (Acts, Decl); Set_No_Initialization (Decl); -- Generate: -- if Cond then -- Cnn := ; -- else -- Cnn := ; -- end if; New_If := Make_Implicit_If_Statement (N, Condition => Relocate_Node (Cond), Then_Statements => New_List ( Make_Assignment_Statement (Sloc (Thenx), Name => New_Occurrence_Of (Cnn, Sloc (Thenx)), Expression => Relocate_Node (Thenx))), Else_Statements => New_List ( Make_Assignment_Statement (Sloc (Elsex), Name => New_Occurrence_Of (Cnn, Sloc (Elsex)), Expression => Relocate_Node (Elsex)))); Append_To (Acts, New_If); -- Generate: -- do -- ... -- in Cnn end; Rewrite (N, Make_Expression_With_Actions (Loc, Expression => New_Occurrence_Of (Cnn, Loc), Actions => Acts)); Analyze_And_Resolve (N, Typ); end; end if; return; end if; -- If no actions then no expansion needed, gigi will handle it using the -- same approach as a C conditional expression. else return; end if; -- Fall through here for either the limited expansion, or the case of -- inserting actions for nonlimited types. In both these cases, we must -- move the SLOC of the parent If statement to the newly created one and -- change it to the SLOC of the expression which, after expansion, will -- correspond to what is being evaluated. if Present (Parent (N)) and then Nkind (Parent (N)) = N_If_Statement then Set_Sloc (New_If, Sloc (Parent (N))); Set_Sloc (Parent (N), Loc); end if; -- Make sure Then_Actions and Else_Actions are appropriately moved -- to the new if statement. if Present (Then_Actions (N)) then Insert_List_Before (First (Then_Statements (New_If)), Then_Actions (N)); end if; if Present (Else_Actions (N)) then Insert_List_Before (First (Else_Statements (New_If)), Else_Actions (N)); end if; Insert_Action (N, Decl); Insert_Action (N, New_If); Rewrite (N, New_N); Analyze_And_Resolve (N, Typ); end Expand_N_If_Expression; ----------------- -- Expand_N_In -- ----------------- procedure Expand_N_In (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Restyp : constant Entity_Id := Etype (N); Lop : constant Node_Id := Left_Opnd (N); Rop : constant Node_Id := Right_Opnd (N); Static : constant Boolean := Is_OK_Static_Expression (N); procedure Substitute_Valid_Check; -- Replaces node N by Lop'Valid. This is done when we have an explicit -- test for the left operand being in range of its subtype. ---------------------------- -- Substitute_Valid_Check -- ---------------------------- procedure Substitute_Valid_Check is function Is_OK_Object_Reference (Nod : Node_Id) return Boolean; -- Determine whether arbitrary node Nod denotes a source object that -- may safely act as prefix of attribute 'Valid. ---------------------------- -- Is_OK_Object_Reference -- ---------------------------- function Is_OK_Object_Reference (Nod : Node_Id) return Boolean is Obj_Ref : Node_Id; begin -- Inspect the original operand Obj_Ref := Original_Node (Nod); -- The object reference must be a source construct, otherwise the -- codefix suggestion may refer to nonexistent code from a user -- perspective. if Comes_From_Source (Obj_Ref) then -- Recover the actual object reference. There may be more cases -- to consider??? loop if Nkind_In (Obj_Ref, N_Type_Conversion, N_Unchecked_Type_Conversion) then Obj_Ref := Expression (Obj_Ref); else exit; end if; end loop; return Is_Object_Reference (Obj_Ref); end if; return False; end Is_OK_Object_Reference; -- Start of processing for Substitute_Valid_Check begin Rewrite (N, Make_Attribute_Reference (Loc, Prefix => Relocate_Node (Lop), Attribute_Name => Name_Valid)); Analyze_And_Resolve (N, Restyp); -- Emit a warning when the left-hand operand of the membership test -- is a source object, otherwise the use of attribute 'Valid would be -- illegal. The warning is not given when overflow checking is either -- MINIMIZED or ELIMINATED, as the danger of optimization has been -- eliminated above. if Is_OK_Object_Reference (Lop) and then Overflow_Check_Mode not in Minimized_Or_Eliminated then Error_Msg_N ("??explicit membership test may be optimized away", N); Error_Msg_N -- CODEFIX ("\??use ''Valid attribute instead", N); end if; end Substitute_Valid_Check; -- Local variables Ltyp : Entity_Id; Rtyp : Entity_Id; -- Start of processing for Expand_N_In begin -- If set membership case, expand with separate procedure if Present (Alternatives (N)) then Expand_Set_Membership (N); return; end if; -- Not set membership, proceed with expansion Ltyp := Etype (Left_Opnd (N)); Rtyp := Etype (Right_Opnd (N)); -- If MINIMIZED/ELIMINATED overflow mode and type is a signed integer -- type, then expand with a separate procedure. Note the use of the -- flag No_Minimize_Eliminate to prevent infinite recursion. if Overflow_Check_Mode in Minimized_Or_Eliminated and then Is_Signed_Integer_Type (Ltyp) and then not No_Minimize_Eliminate (N) then Expand_Membership_Minimize_Eliminate_Overflow (N); return; end if; -- Check case of explicit test for an expression in range of its -- subtype. This is suspicious usage and we replace it with a 'Valid -- test and give a warning for scalar types. if Is_Scalar_Type (Ltyp) -- Only relevant for source comparisons and then Comes_From_Source (N) -- In floating-point this is a standard way to check for finite values -- and using 'Valid would typically be a pessimization. and then not Is_Floating_Point_Type (Ltyp) -- Don't give the message unless right operand is a type entity and -- the type of the left operand matches this type. Note that this -- eliminates the cases where MINIMIZED/ELIMINATED mode overflow -- checks have changed the type of the left operand. and then Nkind (Rop) in N_Has_Entity and then Ltyp = Entity (Rop) -- Skip this for predicated types, where such expressions are a -- reasonable way of testing if something meets the predicate. and then not Present (Predicate_Function (Ltyp)) then Substitute_Valid_Check; return; end if; -- Do validity check on operands if Validity_Checks_On and Validity_Check_Operands then Ensure_Valid (Left_Opnd (N)); Validity_Check_Range (Right_Opnd (N)); end if; -- Case of explicit range if Nkind (Rop) = N_Range then declare Lo : constant Node_Id := Low_Bound (Rop); Hi : constant Node_Id := High_Bound (Rop); Lo_Orig : constant Node_Id := Original_Node (Lo); Hi_Orig : constant Node_Id := Original_Node (Hi); Lcheck : Compare_Result; Ucheck : Compare_Result; Warn1 : constant Boolean := Constant_Condition_Warnings and then Comes_From_Source (N) and then not In_Instance; -- This must be true for any of the optimization warnings, we -- clearly want to give them only for source with the flag on. We -- also skip these warnings in an instance since it may be the -- case that different instantiations have different ranges. Warn2 : constant Boolean := Warn1 and then Nkind (Original_Node (Rop)) = N_Range and then Is_Integer_Type (Etype (Lo)); -- For the case where only one bound warning is elided, we also -- insist on an explicit range and an integer type. The reason is -- that the use of enumeration ranges including an end point is -- common, as is the use of a subtype name, one of whose bounds is -- the same as the type of the expression. begin -- If test is explicit x'First .. x'Last, replace by valid check -- Could use some individual comments for this complex test ??? if Is_Scalar_Type (Ltyp) -- And left operand is X'First where X matches left operand -- type (this eliminates cases of type mismatch, including -- the cases where ELIMINATED/MINIMIZED mode has changed the -- type of the left operand. and then Nkind (Lo_Orig) = N_Attribute_Reference and then Attribute_Name (Lo_Orig) = Name_First and then Nkind (Prefix (Lo_Orig)) in N_Has_Entity and then Entity (Prefix (Lo_Orig)) = Ltyp -- Same tests for right operand and then Nkind (Hi_Orig) = N_Attribute_Reference and then Attribute_Name (Hi_Orig) = Name_Last and then Nkind (Prefix (Hi_Orig)) in N_Has_Entity and then Entity (Prefix (Hi_Orig)) = Ltyp -- Relevant only for source cases and then Comes_From_Source (N) then Substitute_Valid_Check; goto Leave; end if; -- If bounds of type are known at compile time, and the end points -- are known at compile time and identical, this is another case -- for substituting a valid test. We only do this for discrete -- types, since it won't arise in practice for float types. if Comes_From_Source (N) and then Is_Discrete_Type (Ltyp) and then Compile_Time_Known_Value (Type_High_Bound (Ltyp)) and then Compile_Time_Known_Value (Type_Low_Bound (Ltyp)) and then Compile_Time_Known_Value (Lo) and then Compile_Time_Known_Value (Hi) and then Expr_Value (Type_High_Bound (Ltyp)) = Expr_Value (Hi) and then Expr_Value (Type_Low_Bound (Ltyp)) = Expr_Value (Lo) -- Kill warnings in instances, since they may be cases where we -- have a test in the generic that makes sense with some types -- and not with other types. -- Similarly, do not rewrite membership as a validity check if -- within the predicate function for the type. -- Finally, if the original bounds are type conversions, even -- if they have been folded into constants, there are different -- types involved and 'Valid is not appropriate. then if In_Instance or else (Ekind (Current_Scope) = E_Function and then Is_Predicate_Function (Current_Scope)) then null; elsif Nkind (Lo_Orig) = N_Type_Conversion or else Nkind (Hi_Orig) = N_Type_Conversion then null; else Substitute_Valid_Check; goto Leave; end if; end if; -- If we have an explicit range, do a bit of optimization based on -- range analysis (we may be able to kill one or both checks). Lcheck := Compile_Time_Compare (Lop, Lo, Assume_Valid => False); Ucheck := Compile_Time_Compare (Lop, Hi, Assume_Valid => False); -- If either check is known to fail, replace result by False since -- the other check does not matter. Preserve the static flag for -- legality checks, because we are constant-folding beyond RM 4.9. if Lcheck = LT or else Ucheck = GT then if Warn1 then Error_Msg_N ("?c?range test optimized away", N); Error_Msg_N ("\?c?value is known to be out of range", N); end if; Rewrite (N, New_Occurrence_Of (Standard_False, Loc)); Analyze_And_Resolve (N, Restyp); Set_Is_Static_Expression (N, Static); goto Leave; -- If both checks are known to succeed, replace result by True, -- since we know we are in range. elsif Lcheck in Compare_GE and then Ucheck in Compare_LE then if Warn1 then Error_Msg_N ("?c?range test optimized away", N); Error_Msg_N ("\?c?value is known to be in range", N); end if; Rewrite (N, New_Occurrence_Of (Standard_True, Loc)); Analyze_And_Resolve (N, Restyp); Set_Is_Static_Expression (N, Static); goto Leave; -- If lower bound check succeeds and upper bound check is not -- known to succeed or fail, then replace the range check with -- a comparison against the upper bound. elsif Lcheck in Compare_GE then if Warn2 and then not In_Instance then Error_Msg_N ("??lower bound test optimized away", Lo); Error_Msg_N ("\??value is known to be in range", Lo); end if; Rewrite (N, Make_Op_Le (Loc, Left_Opnd => Lop, Right_Opnd => High_Bound (Rop))); Analyze_And_Resolve (N, Restyp); goto Leave; -- If upper bound check succeeds and lower bound check is not -- known to succeed or fail, then replace the range check with -- a comparison against the lower bound. elsif Ucheck in Compare_LE then if Warn2 and then not In_Instance then Error_Msg_N ("??upper bound test optimized away", Hi); Error_Msg_N ("\??value is known to be in range", Hi); end if; Rewrite (N, Make_Op_Ge (Loc, Left_Opnd => Lop, Right_Opnd => Low_Bound (Rop))); Analyze_And_Resolve (N, Restyp); goto Leave; end if; -- We couldn't optimize away the range check, but there is one -- more issue. If we are checking constant conditionals, then we -- see if we can determine the outcome assuming everything is -- valid, and if so give an appropriate warning. if Warn1 and then not Assume_No_Invalid_Values then Lcheck := Compile_Time_Compare (Lop, Lo, Assume_Valid => True); Ucheck := Compile_Time_Compare (Lop, Hi, Assume_Valid => True); -- Result is out of range for valid value if Lcheck = LT or else Ucheck = GT then Error_Msg_N ("?c?value can only be in range if it is invalid", N); -- Result is in range for valid value elsif Lcheck in Compare_GE and then Ucheck in Compare_LE then Error_Msg_N ("?c?value can only be out of range if it is invalid", N); -- Lower bound check succeeds if value is valid elsif Warn2 and then Lcheck in Compare_GE then Error_Msg_N ("?c?lower bound check only fails if it is invalid", Lo); -- Upper bound check succeeds if value is valid elsif Warn2 and then Ucheck in Compare_LE then Error_Msg_N ("?c?upper bound check only fails for invalid values", Hi); end if; end if; end; -- Try to narrow the operation if Ltyp = Universal_Integer and then Nkind (N) = N_In then Narrow_Large_Operation (N); end if; -- For all other cases of an explicit range, nothing to be done goto Leave; -- Here right operand is a subtype mark else declare Typ : Entity_Id := Etype (Rop); Is_Acc : constant Boolean := Is_Access_Type (Typ); Check_Null_Exclusion : Boolean; Cond : Node_Id := Empty; New_N : Node_Id; Obj : Node_Id := Lop; SCIL_Node : Node_Id; begin Remove_Side_Effects (Obj); -- For tagged type, do tagged membership operation if Is_Tagged_Type (Typ) then -- No expansion will be performed for VM targets, as the VM -- back ends will handle the membership tests directly. if Tagged_Type_Expansion then Tagged_Membership (N, SCIL_Node, New_N); Rewrite (N, New_N); Analyze_And_Resolve (N, Restyp, Suppress => All_Checks); -- Update decoration of relocated node referenced by the -- SCIL node. if Generate_SCIL and then Present (SCIL_Node) then Set_SCIL_Node (N, SCIL_Node); end if; end if; goto Leave; -- If type is scalar type, rewrite as x in t'First .. t'Last. -- This reason we do this is that the bounds may have the wrong -- type if they come from the original type definition. Also this -- way we get all the processing above for an explicit range. -- Don't do this for predicated types, since in this case we -- want to check the predicate. elsif Is_Scalar_Type (Typ) then if No (Predicate_Function (Typ)) then Rewrite (Rop, Make_Range (Loc, Low_Bound => Make_Attribute_Reference (Loc, Attribute_Name => Name_First, Prefix => New_Occurrence_Of (Typ, Loc)), High_Bound => Make_Attribute_Reference (Loc, Attribute_Name => Name_Last, Prefix => New_Occurrence_Of (Typ, Loc)))); Analyze_And_Resolve (N, Restyp); end if; goto Leave; -- Ada 2005 (AI95-0216 amended by AI12-0162): Program_Error is -- raised when evaluating an individual membership test if the -- subtype mark denotes a constrained Unchecked_Union subtype -- and the expression lacks inferable discriminants. elsif Is_Unchecked_Union (Base_Type (Typ)) and then Is_Constrained (Typ) and then not Has_Inferable_Discriminants (Lop) then Rewrite (N, Make_Expression_With_Actions (Loc, Actions => New_List (Make_Raise_Program_Error (Loc, Reason => PE_Unchecked_Union_Restriction)), Expression => New_Occurrence_Of (Standard_False, Loc))); Analyze_And_Resolve (N, Restyp); goto Leave; end if; -- Here we have a non-scalar type if Is_Acc then -- If the null exclusion checks are not compatible, need to -- perform further checks. In other words, we cannot have -- Ltyp including null and Typ excluding null. All other cases -- are OK. Check_Null_Exclusion := Can_Never_Be_Null (Typ) and then not Can_Never_Be_Null (Ltyp); Typ := Designated_Type (Typ); end if; if not Is_Constrained (Typ) then Cond := New_Occurrence_Of (Standard_True, Loc); -- For the constrained array case, we have to check the subscripts -- for an exact match if the lengths are non-zero (the lengths -- must match in any case). elsif Is_Array_Type (Typ) then Check_Subscripts : declare function Build_Attribute_Reference (E : Node_Id; Nam : Name_Id; Dim : Nat) return Node_Id; -- Build attribute reference E'Nam (Dim) ------------------------------- -- Build_Attribute_Reference -- ------------------------------- function Build_Attribute_Reference (E : Node_Id; Nam : Name_Id; Dim : Nat) return Node_Id is begin return Make_Attribute_Reference (Loc, Prefix => E, Attribute_Name => Nam, Expressions => New_List ( Make_Integer_Literal (Loc, Dim))); end Build_Attribute_Reference; -- Start of processing for Check_Subscripts begin for J in 1 .. Number_Dimensions (Typ) loop Evolve_And_Then (Cond, Make_Op_Eq (Loc, Left_Opnd => Build_Attribute_Reference (Duplicate_Subexpr_No_Checks (Obj), Name_First, J), Right_Opnd => Build_Attribute_Reference (New_Occurrence_Of (Typ, Loc), Name_First, J))); Evolve_And_Then (Cond, Make_Op_Eq (Loc, Left_Opnd => Build_Attribute_Reference (Duplicate_Subexpr_No_Checks (Obj), Name_Last, J), Right_Opnd => Build_Attribute_Reference (New_Occurrence_Of (Typ, Loc), Name_Last, J))); end loop; end Check_Subscripts; -- These are the cases where constraint checks may be required, -- e.g. records with possible discriminants else -- Expand the test into a series of discriminant comparisons. -- The expression that is built is the negation of the one that -- is used for checking discriminant constraints. Obj := Relocate_Node (Left_Opnd (N)); if Has_Discriminants (Typ) then Cond := Make_Op_Not (Loc, Right_Opnd => Build_Discriminant_Checks (Obj, Typ)); else Cond := New_Occurrence_Of (Standard_True, Loc); end if; end if; if Is_Acc then if Check_Null_Exclusion then Cond := Make_And_Then (Loc, Left_Opnd => Make_Op_Ne (Loc, Left_Opnd => Obj, Right_Opnd => Make_Null (Loc)), Right_Opnd => Cond); else Cond := Make_Or_Else (Loc, Left_Opnd => Make_Op_Eq (Loc, Left_Opnd => Obj, Right_Opnd => Make_Null (Loc)), Right_Opnd => Cond); end if; end if; Rewrite (N, Cond); Analyze_And_Resolve (N, Restyp); -- Ada 2012 (AI05-0149): Handle membership tests applied to an -- expression of an anonymous access type. This can involve an -- accessibility test and a tagged type membership test in the -- case of tagged designated types. if Ada_Version >= Ada_2012 and then Is_Acc and then Ekind (Ltyp) = E_Anonymous_Access_Type then declare Expr_Entity : Entity_Id := Empty; New_N : Node_Id; Param_Level : Node_Id; Type_Level : Node_Id; begin if Is_Entity_Name (Lop) then Expr_Entity := Param_Entity (Lop); if not Present (Expr_Entity) then Expr_Entity := Entity (Lop); end if; end if; -- If a conversion of the anonymous access value to the -- tested type would be illegal, then the result is False. if not Valid_Conversion (Lop, Rtyp, Lop, Report_Errs => False) then Rewrite (N, New_Occurrence_Of (Standard_False, Loc)); Analyze_And_Resolve (N, Restyp); -- Apply an accessibility check if the access object has an -- associated access level and when the level of the type is -- less deep than the level of the access parameter. This -- only occur for access parameters and stand-alone objects -- of an anonymous access type. else if Present (Expr_Entity) and then Present (Effective_Extra_Accessibility (Expr_Entity)) and then UI_Gt (Object_Access_Level (Lop), Type_Access_Level (Rtyp)) then Param_Level := New_Occurrence_Of (Effective_Extra_Accessibility (Expr_Entity), Loc); Type_Level := Make_Integer_Literal (Loc, Type_Access_Level (Rtyp)); -- Return True only if the accessibility level of the -- expression entity is not deeper than the level of -- the tested access type. Rewrite (N, Make_And_Then (Loc, Left_Opnd => Relocate_Node (N), Right_Opnd => Make_Op_Le (Loc, Left_Opnd => Param_Level, Right_Opnd => Type_Level))); Analyze_And_Resolve (N); end if; -- If the designated type is tagged, do tagged membership -- operation. -- *** NOTE: we have to check not null before doing the -- tagged membership test (but maybe that can be done -- inside Tagged_Membership?). if Is_Tagged_Type (Typ) then Rewrite (N, Make_And_Then (Loc, Left_Opnd => Relocate_Node (N), Right_Opnd => Make_Op_Ne (Loc, Left_Opnd => Obj, Right_Opnd => Make_Null (Loc)))); -- No expansion will be performed for VM targets, as -- the VM back ends will handle the membership tests -- directly. if Tagged_Type_Expansion then -- Note that we have to pass Original_Node, because -- the membership test might already have been -- rewritten by earlier parts of membership test. Tagged_Membership (Original_Node (N), SCIL_Node, New_N); -- Update decoration of relocated node referenced -- by the SCIL node. if Generate_SCIL and then Present (SCIL_Node) then Set_SCIL_Node (New_N, SCIL_Node); end if; Rewrite (N, Make_And_Then (Loc, Left_Opnd => Relocate_Node (N), Right_Opnd => New_N)); Analyze_And_Resolve (N, Restyp); end if; end if; end if; end; end if; end; end if; -- At this point, we have done the processing required for the basic -- membership test, but not yet dealt with the predicate. <> -- If a predicate is present, then we do the predicate test, but we -- most certainly want to omit this if we are within the predicate -- function itself, since otherwise we have an infinite recursion. -- The check should also not be emitted when testing against a range -- (the check is only done when the right operand is a subtype; see -- RM12-4.5.2 (28.1/3-30/3)). Predicate_Check : declare function In_Range_Check return Boolean; -- Within an expanded range check that may raise Constraint_Error do -- not generate a predicate check as well. It is redundant because -- the context will add an explicit predicate check, and it will -- raise the wrong exception if it fails. -------------------- -- In_Range_Check -- -------------------- function In_Range_Check return Boolean is P : Node_Id; begin P := Parent (N); while Present (P) loop if Nkind (P) = N_Raise_Constraint_Error then return True; elsif Nkind (P) in N_Statement_Other_Than_Procedure_Call or else Nkind (P) = N_Procedure_Call_Statement or else Nkind (P) in N_Declaration then return False; end if; P := Parent (P); end loop; return False; end In_Range_Check; -- Local variables PFunc : constant Entity_Id := Predicate_Function (Rtyp); R_Op : Node_Id; -- Start of processing for Predicate_Check begin if Present (PFunc) and then Current_Scope /= PFunc and then Nkind (Rop) /= N_Range then if not In_Range_Check then R_Op := Make_Predicate_Call (Rtyp, Lop, Mem => True); else R_Op := New_Occurrence_Of (Standard_True, Loc); end if; Rewrite (N, Make_And_Then (Loc, Left_Opnd => Relocate_Node (N), Right_Opnd => R_Op)); -- Analyze new expression, mark left operand as analyzed to -- avoid infinite recursion adding predicate calls. Similarly, -- suppress further range checks on the call. Set_Analyzed (Left_Opnd (N)); Analyze_And_Resolve (N, Standard_Boolean, Suppress => All_Checks); -- All done, skip attempt at compile time determination of result return; end if; end Predicate_Check; end Expand_N_In; -------------------------------- -- Expand_N_Indexed_Component -- -------------------------------- procedure Expand_N_Indexed_Component (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); P : constant Node_Id := Prefix (N); T : constant Entity_Id := Etype (P); begin -- A special optimization, if we have an indexed component that is -- selecting from a slice, then we can eliminate the slice, since, for -- example, x (i .. j)(k) is identical to x(k). The only difference is -- the range check required by the slice. The range check for the slice -- itself has already been generated. The range check for the -- subscripting operation is ensured by converting the subject to -- the subtype of the slice. -- This optimization not only generates better code, avoiding slice -- messing especially in the packed case, but more importantly bypasses -- some problems in handling this peculiar case, for example, the issue -- of dealing specially with object renamings. if Nkind (P) = N_Slice -- This optimization is disabled for CodePeer because it can transform -- an index-check constraint_error into a range-check constraint_error -- and CodePeer cares about that distinction. and then not CodePeer_Mode then Rewrite (N, Make_Indexed_Component (Loc, Prefix => Prefix (P), Expressions => New_List ( Convert_To (Etype (First_Index (Etype (P))), First (Expressions (N)))))); Analyze_And_Resolve (N, Typ); return; end if; -- Ada 2005 (AI-318-02): If the prefix is a call to a build-in-place -- function, then additional actuals must be passed. if Is_Build_In_Place_Function_Call (P) then Make_Build_In_Place_Call_In_Anonymous_Context (P); -- Ada 2005 (AI-318-02): Specialization of the previous case for prefix -- containing build-in-place function calls whose returned object covers -- interface types. elsif Present (Unqual_BIP_Iface_Function_Call (P)) then Make_Build_In_Place_Iface_Call_In_Anonymous_Context (P); end if; -- Generate index and validity checks Generate_Index_Checks (N); if Validity_Checks_On and then Validity_Check_Subscripts then Apply_Subscript_Validity_Checks (N); end if; -- If selecting from an array with atomic components, and atomic sync -- is not suppressed for this array type, set atomic sync flag. if (Has_Atomic_Components (T) and then not Atomic_Synchronization_Disabled (T)) or else (Is_Atomic (Typ) and then not Atomic_Synchronization_Disabled (Typ)) or else (Is_Entity_Name (P) and then Has_Atomic_Components (Entity (P)) and then not Atomic_Synchronization_Disabled (Entity (P))) then Activate_Atomic_Synchronization (N); end if; -- All done if the prefix is not a packed array implemented specially if not (Is_Packed (Etype (Prefix (N))) and then Present (Packed_Array_Impl_Type (Etype (Prefix (N))))) then return; end if; -- For packed arrays that are not bit-packed (i.e. the case of an array -- with one or more index types with a non-contiguous enumeration type), -- we can always use the normal packed element get circuit. if not Is_Bit_Packed_Array (Etype (Prefix (N))) then Expand_Packed_Element_Reference (N); return; end if; -- For a reference to a component of a bit packed array, we convert it -- to a reference to the corresponding Packed_Array_Impl_Type. We only -- want to do this for simple references, and not for: -- Left side of assignment, or prefix of left side of assignment, or -- prefix of the prefix, to handle packed arrays of packed arrays, -- This case is handled in Exp_Ch5.Expand_N_Assignment_Statement -- Renaming objects in renaming associations -- This case is handled when a use of the renamed variable occurs -- Actual parameters for a subprogram call -- This case is handled in Exp_Ch6.Expand_Actuals -- The second expression in a 'Read attribute reference -- The prefix of an address or bit or size attribute reference -- The following circuit detects these exceptions. Note that we need to -- deal with implicit dereferences when climbing up the parent chain, -- with the additional difficulty that the type of parents may have yet -- to be resolved since prefixes are usually resolved first. declare Child : Node_Id := N; Parnt : Node_Id := Parent (N); begin loop if Nkind (Parnt) = N_Unchecked_Expression then null; elsif Nkind (Parnt) = N_Object_Renaming_Declaration then return; elsif Nkind (Parnt) in N_Subprogram_Call or else (Nkind (Parnt) = N_Parameter_Association and then Nkind (Parent (Parnt)) in N_Subprogram_Call) then return; elsif Nkind (Parnt) = N_Attribute_Reference and then Nam_In (Attribute_Name (Parnt), Name_Address, Name_Bit, Name_Size) and then Prefix (Parnt) = Child then return; elsif Nkind (Parnt) = N_Assignment_Statement and then Name (Parnt) = Child then return; -- If the expression is an index of an indexed component, it must -- be expanded regardless of context. elsif Nkind (Parnt) = N_Indexed_Component and then Child /= Prefix (Parnt) then Expand_Packed_Element_Reference (N); return; elsif Nkind (Parent (Parnt)) = N_Assignment_Statement and then Name (Parent (Parnt)) = Parnt then return; elsif Nkind (Parnt) = N_Attribute_Reference and then Attribute_Name (Parnt) = Name_Read and then Next (First (Expressions (Parnt))) = Child then return; elsif Nkind (Parnt) = N_Indexed_Component and then Prefix (Parnt) = Child then null; elsif Nkind (Parnt) = N_Selected_Component and then Prefix (Parnt) = Child and then not (Present (Etype (Selector_Name (Parnt))) and then Is_Access_Type (Etype (Selector_Name (Parnt)))) then null; -- If the parent is a dereference, either implicit or explicit, -- then the packed reference needs to be expanded. else Expand_Packed_Element_Reference (N); return; end if; -- Keep looking up tree for unchecked expression, or if we are the -- prefix of a possible assignment left side. Child := Parnt; Parnt := Parent (Child); end loop; end; end Expand_N_Indexed_Component; --------------------- -- Expand_N_Not_In -- --------------------- -- Replace a not in b by not (a in b) so that the expansions for (a in b) -- can be done. This avoids needing to duplicate this expansion code. procedure Expand_N_Not_In (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); Cfs : constant Boolean := Comes_From_Source (N); begin Rewrite (N, Make_Op_Not (Loc, Right_Opnd => Make_In (Loc, Left_Opnd => Left_Opnd (N), Right_Opnd => Right_Opnd (N)))); -- If this is a set membership, preserve list of alternatives Set_Alternatives (Right_Opnd (N), Alternatives (Original_Node (N))); -- We want this to appear as coming from source if original does (see -- transformations in Expand_N_In). Set_Comes_From_Source (N, Cfs); Set_Comes_From_Source (Right_Opnd (N), Cfs); -- Now analyze transformed node Analyze_And_Resolve (N, Typ); end Expand_N_Not_In; ------------------- -- Expand_N_Null -- ------------------- -- The only replacement required is for the case of a null of a type that -- is an access to protected subprogram, or a subtype thereof. We represent -- such access values as a record, and so we must replace the occurrence of -- null by the equivalent record (with a null address and a null pointer in -- it), so that the back end creates the proper value. procedure Expand_N_Null (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Base_Type (Etype (N)); Agg : Node_Id; begin if Is_Access_Protected_Subprogram_Type (Typ) then Agg := Make_Aggregate (Loc, Expressions => New_List ( New_Occurrence_Of (RTE (RE_Null_Address), Loc), Make_Null (Loc))); Rewrite (N, Agg); Analyze_And_Resolve (N, Equivalent_Type (Typ)); -- For subsequent semantic analysis, the node must retain its type. -- Gigi in any case replaces this type by the corresponding record -- type before processing the node. Set_Etype (N, Typ); end if; exception when RE_Not_Available => return; end Expand_N_Null; --------------------- -- Expand_N_Op_Abs -- --------------------- procedure Expand_N_Op_Abs (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Expr : constant Node_Id := Right_Opnd (N); Typ : constant Entity_Id := Etype (N); begin Unary_Op_Validity_Checks (N); -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- Try to narrow the operation if Typ = Universal_Integer then Narrow_Large_Operation (N); if Nkind (N) /= N_Op_Abs then return; end if; end if; -- Deal with software overflow checking if Is_Signed_Integer_Type (Typ) and then Do_Overflow_Check (N) then -- The only case to worry about is when the argument is equal to the -- largest negative number, so what we do is to insert the check: -- [constraint_error when Expr = typ'Base'First] -- with the usual Duplicate_Subexpr use coding for expr Insert_Action (N, Make_Raise_Constraint_Error (Loc, Condition => Make_Op_Eq (Loc, Left_Opnd => Duplicate_Subexpr (Expr), Right_Opnd => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Base_Type (Etype (Expr)), Loc), Attribute_Name => Name_First)), Reason => CE_Overflow_Check_Failed)); Set_Do_Overflow_Check (N, False); end if; end Expand_N_Op_Abs; --------------------- -- Expand_N_Op_Add -- --------------------- procedure Expand_N_Op_Add (N : Node_Id) is Typ : constant Entity_Id := Etype (N); begin Binary_Op_Validity_Checks (N); -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- N + 0 = 0 + N = N for integer types if Is_Integer_Type (Typ) then if Compile_Time_Known_Value (Right_Opnd (N)) and then Expr_Value (Right_Opnd (N)) = Uint_0 then Rewrite (N, Left_Opnd (N)); return; elsif Compile_Time_Known_Value (Left_Opnd (N)) and then Expr_Value (Left_Opnd (N)) = Uint_0 then Rewrite (N, Right_Opnd (N)); return; end if; end if; -- Try to narrow the operation if Typ = Universal_Integer then Narrow_Large_Operation (N); if Nkind (N) /= N_Op_Add then return; end if; end if; -- Arithmetic overflow checks for signed integer/fixed point types if Is_Signed_Integer_Type (Typ) or else Is_Fixed_Point_Type (Typ) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- Overflow checks for floating-point if -gnateF mode active Check_Float_Op_Overflow (N); Expand_Nonbinary_Modular_Op (N); end Expand_N_Op_Add; --------------------- -- Expand_N_Op_And -- --------------------- procedure Expand_N_Op_And (N : Node_Id) is Typ : constant Entity_Id := Etype (N); begin Binary_Op_Validity_Checks (N); if Is_Array_Type (Etype (N)) then Expand_Boolean_Operator (N); elsif Is_Boolean_Type (Etype (N)) then Adjust_Condition (Left_Opnd (N)); Adjust_Condition (Right_Opnd (N)); Set_Etype (N, Standard_Boolean); Adjust_Result_Type (N, Typ); elsif Is_Intrinsic_Subprogram (Entity (N)) then Expand_Intrinsic_Call (N, Entity (N)); end if; Expand_Nonbinary_Modular_Op (N); end Expand_N_Op_And; ------------------------ -- Expand_N_Op_Concat -- ------------------------ procedure Expand_N_Op_Concat (N : Node_Id) is Opnds : List_Id; -- List of operands to be concatenated Cnode : Node_Id; -- Node which is to be replaced by the result of concatenating the nodes -- in the list Opnds. begin -- Ensure validity of both operands Binary_Op_Validity_Checks (N); -- If we are the left operand of a concatenation higher up the tree, -- then do nothing for now, since we want to deal with a series of -- concatenations as a unit. if Nkind (Parent (N)) = N_Op_Concat and then N = Left_Opnd (Parent (N)) then return; end if; -- We get here with a concatenation whose left operand may be a -- concatenation itself with a consistent type. We need to process -- these concatenation operands from left to right, which means -- from the deepest node in the tree to the highest node. Cnode := N; while Nkind (Left_Opnd (Cnode)) = N_Op_Concat loop Cnode := Left_Opnd (Cnode); end loop; -- Now Cnode is the deepest concatenation, and its parents are the -- concatenation nodes above, so now we process bottom up, doing the -- operands. -- The outer loop runs more than once if more than one concatenation -- type is involved. Outer : loop Opnds := New_List (Left_Opnd (Cnode), Right_Opnd (Cnode)); Set_Parent (Opnds, N); -- The inner loop gathers concatenation operands Inner : while Cnode /= N and then Base_Type (Etype (Cnode)) = Base_Type (Etype (Parent (Cnode))) loop Cnode := Parent (Cnode); Append (Right_Opnd (Cnode), Opnds); end loop Inner; -- Note: The following code is a temporary workaround for N731-034 -- and N829-028 and will be kept until the general issue of internal -- symbol serialization is addressed. The workaround is kept under a -- debug switch to avoid permiating into the general case. -- Wrap the node to concatenate into an expression actions node to -- keep it nicely packaged. This is useful in the case of an assert -- pragma with a concatenation where we want to be able to delete -- the concatenation and all its expansion stuff. if Debug_Flag_Dot_H then declare Cnod : constant Node_Id := New_Copy_Tree (Cnode); Typ : constant Entity_Id := Base_Type (Etype (Cnode)); begin -- Note: use Rewrite rather than Replace here, so that for -- example Why_Not_Static can find the original concatenation -- node OK! Rewrite (Cnode, Make_Expression_With_Actions (Sloc (Cnode), Actions => New_List (Make_Null_Statement (Sloc (Cnode))), Expression => Cnod)); Expand_Concatenate (Cnod, Opnds); Analyze_And_Resolve (Cnode, Typ); end; -- Default case else Expand_Concatenate (Cnode, Opnds); end if; exit Outer when Cnode = N; Cnode := Parent (Cnode); end loop Outer; end Expand_N_Op_Concat; ------------------------ -- Expand_N_Op_Divide -- ------------------------ procedure Expand_N_Op_Divide (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Lopnd : constant Node_Id := Left_Opnd (N); Ropnd : constant Node_Id := Right_Opnd (N); Ltyp : constant Entity_Id := Etype (Lopnd); Rtyp : constant Entity_Id := Etype (Ropnd); Typ : Entity_Id := Etype (N); Rknow : constant Boolean := Is_Integer_Type (Typ) and then Compile_Time_Known_Value (Ropnd); Rval : Uint; begin Binary_Op_Validity_Checks (N); -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- Otherwise proceed with expansion of division if Rknow then Rval := Expr_Value (Ropnd); end if; -- N / 1 = N for integer types if Rknow and then Rval = Uint_1 then Rewrite (N, Lopnd); return; end if; -- Try to narrow the operation if Typ = Universal_Integer then Narrow_Large_Operation (N); if Nkind (N) /= N_Op_Divide then return; end if; end if; -- Convert x / 2 ** y to Shift_Right (x, y). Note that the fact that -- Is_Power_Of_2_For_Shift is set means that we know that our left -- operand is an unsigned integer, as required for this to work. if Nkind (Ropnd) = N_Op_Expon and then Is_Power_Of_2_For_Shift (Ropnd) -- We cannot do this transformation in configurable run time mode if we -- have 64-bit integers and long shifts are not available. and then (Esize (Ltyp) <= 32 or else Support_Long_Shifts_On_Target) then Rewrite (N, Make_Op_Shift_Right (Loc, Left_Opnd => Lopnd, Right_Opnd => Convert_To (Standard_Natural, Right_Opnd (Ropnd)))); Analyze_And_Resolve (N, Typ); return; end if; -- Do required fixup of universal fixed operation if Typ = Universal_Fixed then Fixup_Universal_Fixed_Operation (N); Typ := Etype (N); end if; -- Divisions with fixed-point results if Is_Fixed_Point_Type (Typ) then if Is_Integer_Type (Rtyp) then Expand_Divide_Fixed_By_Integer_Giving_Fixed (N); else Expand_Divide_Fixed_By_Fixed_Giving_Fixed (N); end if; -- Deal with divide-by-zero check if back end cannot handle them -- and the flag is set indicating that we need such a check. Note -- that we don't need to bother here with the case of mixed-mode -- (Right operand an integer type), since these will be rewritten -- with conversions to a divide with a fixed-point right operand. if Nkind (N) = N_Op_Divide and then Do_Division_Check (N) and then not Backend_Divide_Checks_On_Target and then not Is_Integer_Type (Rtyp) then Set_Do_Division_Check (N, False); Insert_Action (N, Make_Raise_Constraint_Error (Loc, Condition => Make_Op_Eq (Loc, Left_Opnd => Duplicate_Subexpr_Move_Checks (Ropnd), Right_Opnd => Make_Real_Literal (Loc, Ureal_0)), Reason => CE_Divide_By_Zero)); end if; -- Other cases of division of fixed-point operands elsif Is_Fixed_Point_Type (Ltyp) or else Is_Fixed_Point_Type (Rtyp) then if Is_Integer_Type (Typ) then Expand_Divide_Fixed_By_Fixed_Giving_Integer (N); else pragma Assert (Is_Floating_Point_Type (Typ)); Expand_Divide_Fixed_By_Fixed_Giving_Float (N); end if; -- Mixed-mode operations can appear in a non-static universal context, -- in which case the integer argument must be converted explicitly. elsif Typ = Universal_Real and then Is_Integer_Type (Rtyp) then Rewrite (Ropnd, Convert_To (Universal_Real, Relocate_Node (Ropnd))); Analyze_And_Resolve (Ropnd, Universal_Real); elsif Typ = Universal_Real and then Is_Integer_Type (Ltyp) then Rewrite (Lopnd, Convert_To (Universal_Real, Relocate_Node (Lopnd))); Analyze_And_Resolve (Lopnd, Universal_Real); -- Non-fixed point cases, do integer zero divide and overflow checks elsif Is_Integer_Type (Typ) then Apply_Divide_Checks (N); end if; -- Overflow checks for floating-point if -gnateF mode active Check_Float_Op_Overflow (N); Expand_Nonbinary_Modular_Op (N); end Expand_N_Op_Divide; -------------------- -- Expand_N_Op_Eq -- -------------------- procedure Expand_N_Op_Eq (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); Lhs : constant Node_Id := Left_Opnd (N); Rhs : constant Node_Id := Right_Opnd (N); Bodies : constant List_Id := New_List; A_Typ : constant Entity_Id := Etype (Lhs); procedure Build_Equality_Call (Eq : Entity_Id); -- If a constructed equality exists for the type or for its parent, -- build and analyze call, adding conversions if the operation is -- inherited. function Is_Equality (Subp : Entity_Id; Typ : Entity_Id := Empty) return Boolean; -- Determine whether arbitrary Entity_Id denotes a function with the -- right name and profile for an equality op, specifically for the -- base type Typ if Typ is nonempty. function Find_Equality (Prims : Elist_Id) return Entity_Id; -- Find a primitive equality function within primitive operation list -- Prims. function User_Defined_Primitive_Equality_Op (Typ : Entity_Id) return Entity_Id; -- Find a user-defined primitive equality function for a given untagged -- record type, ignoring visibility. Return Empty if no such op found. function Has_Unconstrained_UU_Component (Typ : Entity_Id) return Boolean; -- Determines whether a type has a subcomponent of an unconstrained -- Unchecked_Union subtype. Typ is a record type. ------------------------- -- Build_Equality_Call -- ------------------------- procedure Build_Equality_Call (Eq : Entity_Id) is Op_Type : constant Entity_Id := Etype (First_Formal (Eq)); L_Exp : Node_Id := Relocate_Node (Lhs); R_Exp : Node_Id := Relocate_Node (Rhs); begin -- Adjust operands if necessary to comparison type if Base_Type (Op_Type) /= Base_Type (A_Typ) and then not Is_Class_Wide_Type (A_Typ) then L_Exp := OK_Convert_To (Op_Type, L_Exp); R_Exp := OK_Convert_To (Op_Type, R_Exp); end if; -- If we have an Unchecked_Union, we need to add the inferred -- discriminant values as actuals in the function call. At this -- point, the expansion has determined that both operands have -- inferable discriminants. if Is_Unchecked_Union (Op_Type) then declare Lhs_Type : constant Node_Id := Etype (L_Exp); Rhs_Type : constant Node_Id := Etype (R_Exp); Lhs_Discr_Vals : Elist_Id; -- List of inferred discriminant values for left operand. Rhs_Discr_Vals : Elist_Id; -- List of inferred discriminant values for right operand. Discr : Entity_Id; begin Lhs_Discr_Vals := New_Elmt_List; Rhs_Discr_Vals := New_Elmt_List; -- Per-object constrained selected components require special -- attention. If the enclosing scope of the component is an -- Unchecked_Union, we cannot reference its discriminants -- directly. This is why we use the extra parameters of the -- equality function of the enclosing Unchecked_Union. -- type UU_Type (Discr : Integer := 0) is -- . . . -- end record; -- pragma Unchecked_Union (UU_Type); -- 1. Unchecked_Union enclosing record: -- type Enclosing_UU_Type (Discr : Integer := 0) is record -- . . . -- Comp : UU_Type (Discr); -- . . . -- end Enclosing_UU_Type; -- pragma Unchecked_Union (Enclosing_UU_Type); -- Obj1 : Enclosing_UU_Type; -- Obj2 : Enclosing_UU_Type (1); -- [. . .] Obj1 = Obj2 [. . .] -- Generated code: -- if not (uu_typeEQ (obj1.comp, obj2.comp, a, b)) then -- A and B are the formal parameters of the equality function -- of Enclosing_UU_Type. The function always has two extra -- formals to capture the inferred discriminant values for -- each discriminant of the type. -- 2. Non-Unchecked_Union enclosing record: -- type -- Enclosing_Non_UU_Type (Discr : Integer := 0) -- is record -- . . . -- Comp : UU_Type (Discr); -- . . . -- end Enclosing_Non_UU_Type; -- Obj1 : Enclosing_Non_UU_Type; -- Obj2 : Enclosing_Non_UU_Type (1); -- ... Obj1 = Obj2 ... -- Generated code: -- if not (uu_typeEQ (obj1.comp, obj2.comp, -- obj1.discr, obj2.discr)) then -- In this case we can directly reference the discriminants of -- the enclosing record. -- Process left operand of equality if Nkind (Lhs) = N_Selected_Component and then Has_Per_Object_Constraint (Entity (Selector_Name (Lhs))) then -- If enclosing record is an Unchecked_Union, use formals -- corresponding to each discriminant. The name of the -- formal is that of the discriminant, with added suffix, -- see Exp_Ch3.Build_Record_Equality for details. if Is_Unchecked_Union (Scope (Entity (Selector_Name (Lhs)))) then Discr := First_Discriminant (Scope (Entity (Selector_Name (Lhs)))); while Present (Discr) loop Append_Elmt (Make_Identifier (Loc, Chars => New_External_Name (Chars (Discr), 'A')), To => Lhs_Discr_Vals); Next_Discriminant (Discr); end loop; -- If enclosing record is of a non-Unchecked_Union type, it -- is possible to reference its discriminants directly. else Discr := First_Discriminant (Lhs_Type); while Present (Discr) loop Append_Elmt (Make_Selected_Component (Loc, Prefix => Prefix (Lhs), Selector_Name => New_Copy (Get_Discriminant_Value (Discr, Lhs_Type, Stored_Constraint (Lhs_Type)))), To => Lhs_Discr_Vals); Next_Discriminant (Discr); end loop; end if; -- Otherwise operand is on object with a constrained type. -- Infer the discriminant values from the constraint. else Discr := First_Discriminant (Lhs_Type); while Present (Discr) loop Append_Elmt (New_Copy (Get_Discriminant_Value (Discr, Lhs_Type, Stored_Constraint (Lhs_Type))), To => Lhs_Discr_Vals); Next_Discriminant (Discr); end loop; end if; -- Similar processing for right operand of equality if Nkind (Rhs) = N_Selected_Component and then Has_Per_Object_Constraint (Entity (Selector_Name (Rhs))) then if Is_Unchecked_Union (Scope (Entity (Selector_Name (Rhs)))) then Discr := First_Discriminant (Scope (Entity (Selector_Name (Rhs)))); while Present (Discr) loop Append_Elmt (Make_Identifier (Loc, Chars => New_External_Name (Chars (Discr), 'B')), To => Rhs_Discr_Vals); Next_Discriminant (Discr); end loop; else Discr := First_Discriminant (Rhs_Type); while Present (Discr) loop Append_Elmt (Make_Selected_Component (Loc, Prefix => Prefix (Rhs), Selector_Name => New_Copy (Get_Discriminant_Value (Discr, Rhs_Type, Stored_Constraint (Rhs_Type)))), To => Rhs_Discr_Vals); Next_Discriminant (Discr); end loop; end if; else Discr := First_Discriminant (Rhs_Type); while Present (Discr) loop Append_Elmt (New_Copy (Get_Discriminant_Value (Discr, Rhs_Type, Stored_Constraint (Rhs_Type))), To => Rhs_Discr_Vals); Next_Discriminant (Discr); end loop; end if; -- Now merge the list of discriminant values so that values -- of corresponding discriminants are adjacent. declare Params : List_Id; L_Elmt : Elmt_Id; R_Elmt : Elmt_Id; begin Params := New_List (L_Exp, R_Exp); L_Elmt := First_Elmt (Lhs_Discr_Vals); R_Elmt := First_Elmt (Rhs_Discr_Vals); while Present (L_Elmt) loop Append_To (Params, Node (L_Elmt)); Append_To (Params, Node (R_Elmt)); Next_Elmt (L_Elmt); Next_Elmt (R_Elmt); end loop; Rewrite (N, Make_Function_Call (Loc, Name => New_Occurrence_Of (Eq, Loc), Parameter_Associations => Params)); end; end; -- Normal case, not an unchecked union else Rewrite (N, Make_Function_Call (Loc, Name => New_Occurrence_Of (Eq, Loc), Parameter_Associations => New_List (L_Exp, R_Exp))); end if; Analyze_And_Resolve (N, Standard_Boolean, Suppress => All_Checks); end Build_Equality_Call; ----------------- -- Is_Equality -- ----------------- function Is_Equality (Subp : Entity_Id; Typ : Entity_Id := Empty) return Boolean is Formal_1 : Entity_Id; Formal_2 : Entity_Id; begin -- The equality function carries name "=", returns Boolean, and has -- exactly two formal parameters of an identical type. if Ekind (Subp) = E_Function and then Chars (Subp) = Name_Op_Eq and then Base_Type (Etype (Subp)) = Standard_Boolean then Formal_1 := First_Formal (Subp); Formal_2 := Empty; if Present (Formal_1) then Formal_2 := Next_Formal (Formal_1); end if; return Present (Formal_1) and then Present (Formal_2) and then No (Next_Formal (Formal_2)) and then Base_Type (Etype (Formal_1)) = Base_Type (Etype (Formal_2)) and then (not Present (Typ) or else Implementation_Base_Type (Etype (Formal_1)) = Typ); end if; return False; end Is_Equality; ------------------- -- Find_Equality -- ------------------- function Find_Equality (Prims : Elist_Id) return Entity_Id is function Find_Aliased_Equality (Prim : Entity_Id) return Entity_Id; -- Find an equality in a possible alias chain starting from primitive -- operation Prim. --------------------------- -- Find_Aliased_Equality -- --------------------------- function Find_Aliased_Equality (Prim : Entity_Id) return Entity_Id is Candid : Entity_Id; begin -- Inspect each candidate in the alias chain, checking whether it -- denotes an equality. Candid := Prim; while Present (Candid) loop if Is_Equality (Candid) then return Candid; end if; Candid := Alias (Candid); end loop; return Empty; end Find_Aliased_Equality; -- Local variables Eq_Prim : Entity_Id; Prim_Elmt : Elmt_Id; -- Start of processing for Find_Equality begin -- Assume that the tagged type lacks an equality Eq_Prim := Empty; -- Inspect the list of primitives looking for a suitable equality -- within a possible chain of aliases. Prim_Elmt := First_Elmt (Prims); while Present (Prim_Elmt) and then No (Eq_Prim) loop Eq_Prim := Find_Aliased_Equality (Node (Prim_Elmt)); Next_Elmt (Prim_Elmt); end loop; -- A tagged type should always have an equality pragma Assert (Present (Eq_Prim)); return Eq_Prim; end Find_Equality; ---------------------------------------- -- User_Defined_Primitive_Equality_Op -- ---------------------------------------- function User_Defined_Primitive_Equality_Op (Typ : Entity_Id) return Entity_Id is Enclosing_Scope : constant Node_Id := Scope (Typ); E : Entity_Id; begin -- Prune this search by somehow not looking at decls that precede -- the declaration of the first view of Typ (which might be a partial -- view)??? for Private_Entities in Boolean loop if Private_Entities then if Ekind (Enclosing_Scope) /= E_Package then exit; end if; E := First_Private_Entity (Enclosing_Scope); else E := First_Entity (Enclosing_Scope); end if; while Present (E) loop if Is_Equality (E, Typ) then return E; end if; Next_Entity (E); end loop; end loop; if Is_Derived_Type (Typ) then return User_Defined_Primitive_Equality_Op (Implementation_Base_Type (Etype (Typ))); end if; return Empty; end User_Defined_Primitive_Equality_Op; ------------------------------------ -- Has_Unconstrained_UU_Component -- ------------------------------------ function Has_Unconstrained_UU_Component (Typ : Entity_Id) return Boolean is Tdef : constant Node_Id := Type_Definition (Declaration_Node (Base_Type (Typ))); Clist : Node_Id; Vpart : Node_Id; function Component_Is_Unconstrained_UU (Comp : Node_Id) return Boolean; -- Determines whether the subtype of the component is an -- unconstrained Unchecked_Union. function Variant_Is_Unconstrained_UU (Variant : Node_Id) return Boolean; -- Determines whether a component of the variant has an unconstrained -- Unchecked_Union subtype. ----------------------------------- -- Component_Is_Unconstrained_UU -- ----------------------------------- function Component_Is_Unconstrained_UU (Comp : Node_Id) return Boolean is begin if Nkind (Comp) /= N_Component_Declaration then return False; end if; declare Sindic : constant Node_Id := Subtype_Indication (Component_Definition (Comp)); begin -- Unconstrained nominal type. In the case of a constraint -- present, the node kind would have been N_Subtype_Indication. if Nkind (Sindic) = N_Identifier then return Is_Unchecked_Union (Base_Type (Etype (Sindic))); end if; return False; end; end Component_Is_Unconstrained_UU; --------------------------------- -- Variant_Is_Unconstrained_UU -- --------------------------------- function Variant_Is_Unconstrained_UU (Variant : Node_Id) return Boolean is Clist : constant Node_Id := Component_List (Variant); begin if Is_Empty_List (Component_Items (Clist)) then return False; end if; -- We only need to test one component declare Comp : Node_Id := First (Component_Items (Clist)); begin while Present (Comp) loop if Component_Is_Unconstrained_UU (Comp) then return True; end if; Next (Comp); end loop; end; -- None of the components withing the variant were of -- unconstrained Unchecked_Union type. return False; end Variant_Is_Unconstrained_UU; -- Start of processing for Has_Unconstrained_UU_Component begin if Null_Present (Tdef) then return False; end if; Clist := Component_List (Tdef); Vpart := Variant_Part (Clist); -- Inspect available components if Present (Component_Items (Clist)) then declare Comp : Node_Id := First (Component_Items (Clist)); begin while Present (Comp) loop -- One component is sufficient if Component_Is_Unconstrained_UU (Comp) then return True; end if; Next (Comp); end loop; end; end if; -- Inspect available components withing variants if Present (Vpart) then declare Variant : Node_Id := First (Variants (Vpart)); begin while Present (Variant) loop -- One component within a variant is sufficient if Variant_Is_Unconstrained_UU (Variant) then return True; end if; Next (Variant); end loop; end; end if; -- Neither the available components, nor the components inside the -- variant parts were of an unconstrained Unchecked_Union subtype. return False; end Has_Unconstrained_UU_Component; -- Local variables Typl : Entity_Id; -- Start of processing for Expand_N_Op_Eq begin Binary_Op_Validity_Checks (N); -- Deal with private types Typl := A_Typ; if Ekind (Typl) = E_Private_Type then Typl := Underlying_Type (Typl); elsif Ekind (Typl) = E_Private_Subtype then Typl := Underlying_Type (Base_Type (Typl)); end if; -- It may happen in error situations that the underlying type is not -- set. The error will be detected later, here we just defend the -- expander code. if No (Typl) then return; end if; -- Now get the implementation base type (note that plain Base_Type here -- might lead us back to the private type, which is not what we want!) Typl := Implementation_Base_Type (Typl); -- Equality between variant records results in a call to a routine -- that has conditional tests of the discriminant value(s), and hence -- violates the No_Implicit_Conditionals restriction. if Has_Variant_Part (Typl) then declare Msg : Boolean; begin Check_Restriction (Msg, No_Implicit_Conditionals, N); if Msg then Error_Msg_N ("\comparison of variant records tests discriminants", N); return; end if; end; end if; -- Deal with overflow checks in MINIMIZED/ELIMINATED mode and if that -- means we no longer have a comparison operation, we are all done. Expand_Compare_Minimize_Eliminate_Overflow (N); if Nkind (N) /= N_Op_Eq then return; end if; -- Boolean types (requiring handling of non-standard case) if Is_Boolean_Type (Typl) then Adjust_Condition (Left_Opnd (N)); Adjust_Condition (Right_Opnd (N)); Set_Etype (N, Standard_Boolean); Adjust_Result_Type (N, Typ); -- Array types elsif Is_Array_Type (Typl) then -- If we are doing full validity checking, and it is possible for the -- array elements to be invalid then expand out array comparisons to -- make sure that we check the array elements. if Validity_Check_Operands and then not Is_Known_Valid (Component_Type (Typl)) then declare Save_Force_Validity_Checks : constant Boolean := Force_Validity_Checks; begin Force_Validity_Checks := True; Rewrite (N, Expand_Array_Equality (N, Relocate_Node (Lhs), Relocate_Node (Rhs), Bodies, Typl)); Insert_Actions (N, Bodies); Analyze_And_Resolve (N, Standard_Boolean); Force_Validity_Checks := Save_Force_Validity_Checks; end; -- Packed case where both operands are known aligned elsif Is_Bit_Packed_Array (Typl) and then not Is_Possibly_Unaligned_Object (Lhs) and then not Is_Possibly_Unaligned_Object (Rhs) then Expand_Packed_Eq (N); -- Where the component type is elementary we can use a block bit -- comparison (if supported on the target) exception in the case -- of floating-point (negative zero issues require element by -- element comparison), and atomic/VFA types (where we must be sure -- to load elements independently) and possibly unaligned arrays. elsif Is_Elementary_Type (Component_Type (Typl)) and then not Is_Floating_Point_Type (Component_Type (Typl)) and then not Is_Atomic_Or_VFA (Component_Type (Typl)) and then not Is_Possibly_Unaligned_Object (Lhs) and then not Is_Possibly_Unaligned_Slice (Lhs) and then not Is_Possibly_Unaligned_Object (Rhs) and then not Is_Possibly_Unaligned_Slice (Rhs) and then Support_Composite_Compare_On_Target then null; -- For composite and floating-point cases, expand equality loop to -- make sure of using proper comparisons for tagged types, and -- correctly handling the floating-point case. else Rewrite (N, Expand_Array_Equality (N, Relocate_Node (Lhs), Relocate_Node (Rhs), Bodies, Typl)); Insert_Actions (N, Bodies, Suppress => All_Checks); Analyze_And_Resolve (N, Standard_Boolean, Suppress => All_Checks); end if; -- Record Types elsif Is_Record_Type (Typl) then -- For tagged types, use the primitive "=" if Is_Tagged_Type (Typl) then -- No need to do anything else compiling under restriction -- No_Dispatching_Calls. During the semantic analysis we -- already notified such violation. if Restriction_Active (No_Dispatching_Calls) then return; end if; -- If this is an untagged private type completed with a derivation -- of an untagged private type whose full view is a tagged type, -- we use the primitive operations of the private type (since it -- does not have a full view, and also because its equality -- primitive may have been overridden in its untagged full view). if Inherits_From_Tagged_Full_View (A_Typ) then Build_Equality_Call (Find_Equality (Collect_Primitive_Operations (A_Typ))); -- Find the type's predefined equality or an overriding -- user-defined equality. The reason for not simply calling -- Find_Prim_Op here is that there may be a user-defined -- overloaded equality op that precedes the equality that we -- want, so we have to explicitly search (e.g., there could be -- an equality with two different parameter types). else if Is_Class_Wide_Type (Typl) then Typl := Find_Specific_Type (Typl); end if; Build_Equality_Call (Find_Equality (Primitive_Operations (Typl))); end if; -- See AI12-0101 (which only removes a legality rule) and then -- AI05-0123 (which then applies in the previously illegal case). -- AI12-0101 is a binding interpretation. elsif Ada_Version >= Ada_2012 and then Present (User_Defined_Primitive_Equality_Op (Typl)) then Build_Equality_Call (User_Defined_Primitive_Equality_Op (Typl)); -- Ada 2005 (AI-216): Program_Error is raised when evaluating the -- predefined equality operator for a type which has a subcomponent -- of an Unchecked_Union type whose nominal subtype is unconstrained. elsif Has_Unconstrained_UU_Component (Typl) then Insert_Action (N, Make_Raise_Program_Error (Loc, Reason => PE_Unchecked_Union_Restriction)); -- Prevent Gigi from generating incorrect code by rewriting the -- equality as a standard False. (is this documented somewhere???) Rewrite (N, New_Occurrence_Of (Standard_False, Loc)); elsif Is_Unchecked_Union (Typl) then -- If we can infer the discriminants of the operands, we make a -- call to the TSS equality function. if Has_Inferable_Discriminants (Lhs) and then Has_Inferable_Discriminants (Rhs) then Build_Equality_Call (TSS (Root_Type (Typl), TSS_Composite_Equality)); else -- Ada 2005 (AI-216): Program_Error is raised when evaluating -- the predefined equality operator for an Unchecked_Union type -- if either of the operands lack inferable discriminants. Insert_Action (N, Make_Raise_Program_Error (Loc, Reason => PE_Unchecked_Union_Restriction)); -- Emit a warning on source equalities only, otherwise the -- message may appear out of place due to internal use. The -- warning is unconditional because it is required by the -- language. if Comes_From_Source (N) then Error_Msg_N ("Unchecked_Union discriminants cannot be determined??", N); Error_Msg_N ("\Program_Error will be raised for equality operation??", N); end if; -- Prevent Gigi from generating incorrect code by rewriting -- the equality as a standard False (documented where???). Rewrite (N, New_Occurrence_Of (Standard_False, Loc)); end if; -- If a type support function is present (for complex cases), use it elsif Present (TSS (Root_Type (Typl), TSS_Composite_Equality)) then Build_Equality_Call (TSS (Root_Type (Typl), TSS_Composite_Equality)); -- When comparing two Bounded_Strings, use the primitive equality of -- the root Super_String type. elsif Is_Bounded_String (Typl) then Build_Equality_Call (Find_Equality (Collect_Primitive_Operations (Root_Type (Typl)))); -- Otherwise expand the component by component equality. Note that -- we never use block-bit comparisons for records, because of the -- problems with gaps. The back end will often be able to recombine -- the separate comparisons that we generate here. else Remove_Side_Effects (Lhs); Remove_Side_Effects (Rhs); Rewrite (N, Expand_Record_Equality (N, Typl, Lhs, Rhs, Bodies)); Insert_Actions (N, Bodies, Suppress => All_Checks); Analyze_And_Resolve (N, Standard_Boolean, Suppress => All_Checks); end if; -- If unnesting, handle elementary types whose Equivalent_Types are -- records because there may be padding or undefined fields. elsif Unnest_Subprogram_Mode and then Ekind_In (Typl, E_Class_Wide_Type, E_Class_Wide_Subtype, E_Access_Subprogram_Type, E_Access_Protected_Subprogram_Type, E_Anonymous_Access_Protected_Subprogram_Type, E_Access_Subprogram_Type, E_Exception_Type) and then Present (Equivalent_Type (Typl)) and then Is_Record_Type (Equivalent_Type (Typl)) then Typl := Equivalent_Type (Typl); Remove_Side_Effects (Lhs); Remove_Side_Effects (Rhs); Rewrite (N, Expand_Record_Equality (N, Typl, Unchecked_Convert_To (Typl, Lhs), Unchecked_Convert_To (Typl, Rhs), Bodies)); Insert_Actions (N, Bodies, Suppress => All_Checks); Analyze_And_Resolve (N, Standard_Boolean, Suppress => All_Checks); end if; -- Test if result is known at compile time Rewrite_Comparison (N); -- Try to narrow the operation if Typl = Universal_Integer and then Nkind (N) = N_Op_Eq then Narrow_Large_Operation (N); end if; -- Special optimization of length comparison Optimize_Length_Comparison (N); -- One more special case: if we have a comparison of X'Result = expr -- in floating-point, then if not already there, change expr to be -- f'Machine (expr) to eliminate surprise from extra precision. if Is_Floating_Point_Type (Typl) and then Nkind (Original_Node (Lhs)) = N_Attribute_Reference and then Attribute_Name (Original_Node (Lhs)) = Name_Result then -- Stick in the Typ'Machine call if not already there if Nkind (Rhs) /= N_Attribute_Reference or else Attribute_Name (Rhs) /= Name_Machine then Rewrite (Rhs, Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Typl, Loc), Attribute_Name => Name_Machine, Expressions => New_List (Relocate_Node (Rhs)))); Analyze_And_Resolve (Rhs, Typl); end if; end if; end Expand_N_Op_Eq; ----------------------- -- Expand_N_Op_Expon -- ----------------------- procedure Expand_N_Op_Expon (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Ovflo : constant Boolean := Do_Overflow_Check (N); Typ : constant Entity_Id := Etype (N); Rtyp : constant Entity_Id := Root_Type (Typ); Bastyp : Entity_Id; function Wrap_MA (Exp : Node_Id) return Node_Id; -- Given an expression Exp, if the root type is Float or Long_Float, -- then wrap the expression in a call of Bastyp'Machine, to stop any -- extra precision. This is done to ensure that X**A = X**B when A is -- a static constant and B is a variable with the same value. For any -- other type, the node Exp is returned unchanged. ------------- -- Wrap_MA -- ------------- function Wrap_MA (Exp : Node_Id) return Node_Id is Loc : constant Source_Ptr := Sloc (Exp); begin if Rtyp = Standard_Float or else Rtyp = Standard_Long_Float then return Make_Attribute_Reference (Loc, Attribute_Name => Name_Machine, Prefix => New_Occurrence_Of (Bastyp, Loc), Expressions => New_List (Relocate_Node (Exp))); else return Exp; end if; end Wrap_MA; -- Local variables Base : Node_Id; Ent : Entity_Id; Etyp : Entity_Id; Exp : Node_Id; Exptyp : Entity_Id; Expv : Uint; Rent : RE_Id; Temp : Node_Id; Xnode : Node_Id; -- Start of processing for Expand_N_Op_Expon begin Binary_Op_Validity_Checks (N); -- CodePeer wants to see the unexpanded N_Op_Expon node if CodePeer_Mode then return; end if; -- Relocation of left and right operands must be done after performing -- the validity checks since the generation of validation checks may -- remove side effects. Base := Relocate_Node (Left_Opnd (N)); Bastyp := Etype (Base); Exp := Relocate_Node (Right_Opnd (N)); Exptyp := Etype (Exp); -- If either operand is of a private type, then we have the use of an -- intrinsic operator, and we get rid of the privateness, by using root -- types of underlying types for the actual operation. Otherwise the -- private types will cause trouble if we expand multiplications or -- shifts etc. We also do this transformation if the result type is -- different from the base type. if Is_Private_Type (Etype (Base)) or else Is_Private_Type (Typ) or else Is_Private_Type (Exptyp) or else Rtyp /= Root_Type (Bastyp) then declare Bt : constant Entity_Id := Root_Type (Underlying_Type (Bastyp)); Et : constant Entity_Id := Root_Type (Underlying_Type (Exptyp)); begin Rewrite (N, Unchecked_Convert_To (Typ, Make_Op_Expon (Loc, Left_Opnd => Unchecked_Convert_To (Bt, Base), Right_Opnd => Unchecked_Convert_To (Et, Exp)))); Analyze_And_Resolve (N, Typ); return; end; end if; -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- Test for case of known right argument where we can replace the -- exponentiation by an equivalent expression using multiplication. -- Note: use CRT_Safe version of Compile_Time_Known_Value because in -- configurable run-time mode, we may not have the exponentiation -- routine available, and we don't want the legality of the program -- to depend on how clever the compiler is in knowing values. if CRT_Safe_Compile_Time_Known_Value (Exp) then Expv := Expr_Value (Exp); -- We only fold small non-negative exponents. You might think we -- could fold small negative exponents for the real case, but we -- can't because we are required to raise Constraint_Error for -- the case of 0.0 ** (negative) even if Machine_Overflows = False. -- See ACVC test C4A012B, and it is not worth generating the test. -- For small negative exponents, we return the reciprocal of -- the folding of the exponentiation for the opposite (positive) -- exponent, as required by Ada RM 4.5.6(11/3). if abs Expv <= 4 then -- X ** 0 = 1 (or 1.0) if Expv = 0 then -- Call Remove_Side_Effects to ensure that any side effects -- in the ignored left operand (in particular function calls -- to user defined functions) are properly executed. Remove_Side_Effects (Base); if Ekind (Typ) in Integer_Kind then Xnode := Make_Integer_Literal (Loc, Intval => 1); else Xnode := Make_Real_Literal (Loc, Ureal_1); end if; -- X ** 1 = X elsif Expv = 1 then Xnode := Base; -- X ** 2 = X * X elsif Expv = 2 then Xnode := Wrap_MA ( Make_Op_Multiply (Loc, Left_Opnd => Duplicate_Subexpr (Base), Right_Opnd => Duplicate_Subexpr_No_Checks (Base))); -- X ** 3 = X * X * X elsif Expv = 3 then Xnode := Wrap_MA ( Make_Op_Multiply (Loc, Left_Opnd => Make_Op_Multiply (Loc, Left_Opnd => Duplicate_Subexpr (Base), Right_Opnd => Duplicate_Subexpr_No_Checks (Base)), Right_Opnd => Duplicate_Subexpr_No_Checks (Base))); -- X ** 4 -> -- do -- En : constant base'type := base * base; -- in -- En * En elsif Expv = 4 then Temp := Make_Temporary (Loc, 'E', Base); Xnode := Make_Expression_With_Actions (Loc, Actions => New_List ( Make_Object_Declaration (Loc, Defining_Identifier => Temp, Constant_Present => True, Object_Definition => New_Occurrence_Of (Typ, Loc), Expression => Wrap_MA ( Make_Op_Multiply (Loc, Left_Opnd => Duplicate_Subexpr (Base), Right_Opnd => Duplicate_Subexpr_No_Checks (Base))))), Expression => Wrap_MA ( Make_Op_Multiply (Loc, Left_Opnd => New_Occurrence_Of (Temp, Loc), Right_Opnd => New_Occurrence_Of (Temp, Loc)))); -- X ** N = 1.0 / X ** (-N) -- N in -4 .. -1 else pragma Assert (Expv = -1 or Expv = -2 or Expv = -3 or Expv = -4); Xnode := Make_Op_Divide (Loc, Left_Opnd => Make_Float_Literal (Loc, Radix => Uint_1, Significand => Uint_1, Exponent => Uint_0), Right_Opnd => Make_Op_Expon (Loc, Left_Opnd => Duplicate_Subexpr (Base), Right_Opnd => Make_Integer_Literal (Loc, Intval => -Expv))); end if; Rewrite (N, Xnode); Analyze_And_Resolve (N, Typ); return; end if; end if; -- Deal with optimizing 2 ** expression to shift where possible -- Note: we used to check that Exptyp was an unsigned type. But that is -- an unnecessary check, since if Exp is negative, we have a run-time -- error that is either caught (so we get the right result) or we have -- suppressed the check, in which case the code is erroneous anyway. if Is_Integer_Type (Rtyp) -- The base value must be "safe compile-time known", and exactly 2 and then Nkind (Base) = N_Integer_Literal and then CRT_Safe_Compile_Time_Known_Value (Base) and then Expr_Value (Base) = Uint_2 -- We only handle cases where the right type is a integer and then Is_Integer_Type (Root_Type (Exptyp)) and then Esize (Root_Type (Exptyp)) <= Esize (Standard_Integer) -- This transformation is not applicable for a modular type with a -- nonbinary modulus because we do not handle modular reduction in -- a correct manner if we attempt this transformation in this case. and then not Non_Binary_Modulus (Typ) then -- Handle the cases where our parent is a division or multiplication -- specially. In these cases we can convert to using a shift at the -- parent level if we are not doing overflow checking, since it is -- too tricky to combine the overflow check at the parent level. if not Ovflo and then Nkind_In (Parent (N), N_Op_Divide, N_Op_Multiply) then declare P : constant Node_Id := Parent (N); L : constant Node_Id := Left_Opnd (P); R : constant Node_Id := Right_Opnd (P); begin if (Nkind (P) = N_Op_Multiply and then ((Is_Integer_Type (Etype (L)) and then R = N) or else (Is_Integer_Type (Etype (R)) and then L = N)) and then not Do_Overflow_Check (P)) or else (Nkind (P) = N_Op_Divide and then Is_Integer_Type (Etype (L)) and then Is_Unsigned_Type (Etype (L)) and then R = N and then not Do_Overflow_Check (P)) then Set_Is_Power_Of_2_For_Shift (N); return; end if; end; -- Here we just have 2 ** N on its own, so we can convert this to a -- shift node. We are prepared to deal with overflow here, and we -- also have to handle proper modular reduction for binary modular. else declare OK : Boolean; Lo : Uint; Hi : Uint; MaxS : Uint; -- Maximum shift count with no overflow TestS : Boolean; -- Set True if we must test the shift count Test_Gt : Node_Id; -- Node for test against TestS begin -- Compute maximum shift based on the underlying size. For a -- modular type this is one less than the size. if Is_Modular_Integer_Type (Typ) then -- For modular integer types, this is the size of the value -- being shifted minus one. Any larger values will cause -- modular reduction to a result of zero. Note that we do -- want the RM_Size here (e.g. mod 2 ** 7, we want a result -- of 6, since 2**7 should be reduced to zero). MaxS := RM_Size (Rtyp) - 1; -- For signed integer types, we use the size of the value -- being shifted minus 2. Larger values cause overflow. else MaxS := Esize (Rtyp) - 2; end if; -- Determine range to see if it can be larger than MaxS Determine_Range (Exp, OK, Lo, Hi, Assume_Valid => True); TestS := (not OK) or else Hi > MaxS; -- Signed integer case if Is_Signed_Integer_Type (Typ) then -- Generate overflow check if overflow is active. Note that -- we can simply ignore the possibility of overflow if the -- flag is not set (means that overflow cannot happen or -- that overflow checks are suppressed). if Ovflo and TestS then Insert_Action (N, Make_Raise_Constraint_Error (Loc, Condition => Make_Op_Gt (Loc, Left_Opnd => Duplicate_Subexpr (Exp), Right_Opnd => Make_Integer_Literal (Loc, MaxS)), Reason => CE_Overflow_Check_Failed)); end if; -- Now rewrite node as Shift_Left (1, right-operand) Rewrite (N, Make_Op_Shift_Left (Loc, Left_Opnd => Make_Integer_Literal (Loc, Uint_1), Right_Opnd => Exp)); -- Modular integer case else pragma Assert (Is_Modular_Integer_Type (Typ)); -- If shift count can be greater than MaxS, we need to wrap -- the shift in a test that will reduce the result value to -- zero if this shift count is exceeded. if TestS then -- Note: build node for the comparison first, before we -- reuse the Right_Opnd, so that we have proper parents -- in place for the Duplicate_Subexpr call. Test_Gt := Make_Op_Gt (Loc, Left_Opnd => Duplicate_Subexpr (Exp), Right_Opnd => Make_Integer_Literal (Loc, MaxS)); Rewrite (N, Make_If_Expression (Loc, Expressions => New_List ( Test_Gt, Make_Integer_Literal (Loc, Uint_0), Make_Op_Shift_Left (Loc, Left_Opnd => Make_Integer_Literal (Loc, Uint_1), Right_Opnd => Exp)))); -- If we know shift count cannot be greater than MaxS, then -- it is safe to just rewrite as a shift with no test. else Rewrite (N, Make_Op_Shift_Left (Loc, Left_Opnd => Make_Integer_Literal (Loc, Uint_1), Right_Opnd => Exp)); end if; end if; Analyze_And_Resolve (N, Typ); return; end; end if; end if; -- Fall through if exponentiation must be done using a runtime routine -- First deal with modular case if Is_Modular_Integer_Type (Rtyp) then -- Nonbinary modular case, we call the special exponentiation -- routine for the nonbinary case, converting the argument to -- Long_Long_Integer and passing the modulus value. Then the -- result is converted back to the base type. if Non_Binary_Modulus (Rtyp) then Rewrite (N, Convert_To (Typ, Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (RE_Exp_Modular), Loc), Parameter_Associations => New_List ( Convert_To (RTE (RE_Unsigned), Base), Make_Integer_Literal (Loc, Modulus (Rtyp)), Exp)))); -- Binary modular case, in this case, we call one of two routines, -- either the unsigned integer case, or the unsigned long long -- integer case, with a final "and" operation to do the required mod. else if UI_To_Int (Esize (Rtyp)) <= Standard_Integer_Size then Ent := RTE (RE_Exp_Unsigned); else Ent := RTE (RE_Exp_Long_Long_Unsigned); end if; Rewrite (N, Convert_To (Typ, Make_Op_And (Loc, Left_Opnd => Make_Function_Call (Loc, Name => New_Occurrence_Of (Ent, Loc), Parameter_Associations => New_List ( Convert_To (Etype (First_Formal (Ent)), Base), Exp)), Right_Opnd => Make_Integer_Literal (Loc, Modulus (Rtyp) - 1)))); end if; -- Common exit point for modular type case Analyze_And_Resolve (N, Typ); return; -- Signed integer cases, done using either Integer or Long_Long_Integer. -- It is not worth having routines for Short_[Short_]Integer, since for -- most machines it would not help, and it would generate more code that -- might need certification when a certified run time is required. -- In the integer cases, we have two routines, one for when overflow -- checks are required, and one when they are not required, since there -- is a real gain in omitting checks on many machines. elsif Rtyp = Base_Type (Standard_Long_Long_Integer) or else (Rtyp = Base_Type (Standard_Long_Integer) and then Esize (Standard_Long_Integer) > Esize (Standard_Integer)) or else Rtyp = Universal_Integer then Etyp := Standard_Long_Long_Integer; if Ovflo then Rent := RE_Exp_Long_Long_Integer; else Rent := RE_Exn_Long_Long_Integer; end if; elsif Is_Signed_Integer_Type (Rtyp) then Etyp := Standard_Integer; if Ovflo then Rent := RE_Exp_Integer; else Rent := RE_Exn_Integer; end if; -- Floating-point cases. We do not need separate routines for the -- overflow case here, since in the case of floating-point, we generate -- infinities anyway as a rule (either that or we automatically trap -- overflow), and if there is an infinity generated and a range check -- is required, the check will fail anyway. -- Historical note: we used to convert everything to Long_Long_Float -- and call a single common routine, but this had the undesirable effect -- of giving different results for small static exponent values and the -- same dynamic values. else pragma Assert (Is_Floating_Point_Type (Rtyp)); if Rtyp = Standard_Float then Etyp := Standard_Float; Rent := RE_Exn_Float; elsif Rtyp = Standard_Long_Float then Etyp := Standard_Long_Float; Rent := RE_Exn_Long_Float; else Etyp := Standard_Long_Long_Float; Rent := RE_Exn_Long_Long_Float; end if; end if; -- Common processing for integer cases and floating-point cases. -- If we are in the right type, we can call runtime routine directly if Typ = Etyp and then Rtyp /= Universal_Integer and then Rtyp /= Universal_Real then Rewrite (N, Wrap_MA ( Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (Rent), Loc), Parameter_Associations => New_List (Base, Exp)))); -- Otherwise we have to introduce conversions (conversions are also -- required in the universal cases, since the runtime routine is -- typed using one of the standard types). else Rewrite (N, Convert_To (Typ, Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (Rent), Loc), Parameter_Associations => New_List ( Convert_To (Etyp, Base), Exp)))); end if; Analyze_And_Resolve (N, Typ); return; exception when RE_Not_Available => return; end Expand_N_Op_Expon; -------------------- -- Expand_N_Op_Ge -- -------------------- procedure Expand_N_Op_Ge (N : Node_Id) is Typ : constant Entity_Id := Etype (N); Op1 : constant Node_Id := Left_Opnd (N); Op2 : constant Node_Id := Right_Opnd (N); Typ1 : constant Entity_Id := Base_Type (Etype (Op1)); begin Binary_Op_Validity_Checks (N); -- Deal with overflow checks in MINIMIZED/ELIMINATED mode and if that -- means we no longer have a comparison operation, we are all done. Expand_Compare_Minimize_Eliminate_Overflow (N); if Nkind (N) /= N_Op_Ge then return; end if; -- Array type case if Is_Array_Type (Typ1) then Expand_Array_Comparison (N); return; end if; -- Deal with boolean operands if Is_Boolean_Type (Typ1) then Adjust_Condition (Op1); Adjust_Condition (Op2); Set_Etype (N, Standard_Boolean); Adjust_Result_Type (N, Typ); end if; Rewrite_Comparison (N); -- Try to narrow the operation if Typ1 = Universal_Integer and then Nkind (N) = N_Op_Ge then Narrow_Large_Operation (N); end if; Optimize_Length_Comparison (N); end Expand_N_Op_Ge; -------------------- -- Expand_N_Op_Gt -- -------------------- procedure Expand_N_Op_Gt (N : Node_Id) is Typ : constant Entity_Id := Etype (N); Op1 : constant Node_Id := Left_Opnd (N); Op2 : constant Node_Id := Right_Opnd (N); Typ1 : constant Entity_Id := Base_Type (Etype (Op1)); begin Binary_Op_Validity_Checks (N); -- Deal with overflow checks in MINIMIZED/ELIMINATED mode and if that -- means we no longer have a comparison operation, we are all done. Expand_Compare_Minimize_Eliminate_Overflow (N); if Nkind (N) /= N_Op_Gt then return; end if; -- Deal with array type operands if Is_Array_Type (Typ1) then Expand_Array_Comparison (N); return; end if; -- Deal with boolean type operands if Is_Boolean_Type (Typ1) then Adjust_Condition (Op1); Adjust_Condition (Op2); Set_Etype (N, Standard_Boolean); Adjust_Result_Type (N, Typ); end if; Rewrite_Comparison (N); -- Try to narrow the operation if Typ1 = Universal_Integer and then Nkind (N) = N_Op_Gt then Narrow_Large_Operation (N); end if; Optimize_Length_Comparison (N); end Expand_N_Op_Gt; -------------------- -- Expand_N_Op_Le -- -------------------- procedure Expand_N_Op_Le (N : Node_Id) is Typ : constant Entity_Id := Etype (N); Op1 : constant Node_Id := Left_Opnd (N); Op2 : constant Node_Id := Right_Opnd (N); Typ1 : constant Entity_Id := Base_Type (Etype (Op1)); begin Binary_Op_Validity_Checks (N); -- Deal with overflow checks in MINIMIZED/ELIMINATED mode and if that -- means we no longer have a comparison operation, we are all done. Expand_Compare_Minimize_Eliminate_Overflow (N); if Nkind (N) /= N_Op_Le then return; end if; -- Deal with array type operands if Is_Array_Type (Typ1) then Expand_Array_Comparison (N); return; end if; -- Deal with Boolean type operands if Is_Boolean_Type (Typ1) then Adjust_Condition (Op1); Adjust_Condition (Op2); Set_Etype (N, Standard_Boolean); Adjust_Result_Type (N, Typ); end if; Rewrite_Comparison (N); -- Try to narrow the operation if Typ1 = Universal_Integer and then Nkind (N) = N_Op_Le then Narrow_Large_Operation (N); end if; Optimize_Length_Comparison (N); end Expand_N_Op_Le; -------------------- -- Expand_N_Op_Lt -- -------------------- procedure Expand_N_Op_Lt (N : Node_Id) is Typ : constant Entity_Id := Etype (N); Op1 : constant Node_Id := Left_Opnd (N); Op2 : constant Node_Id := Right_Opnd (N); Typ1 : constant Entity_Id := Base_Type (Etype (Op1)); begin Binary_Op_Validity_Checks (N); -- Deal with overflow checks in MINIMIZED/ELIMINATED mode and if that -- means we no longer have a comparison operation, we are all done. Expand_Compare_Minimize_Eliminate_Overflow (N); if Nkind (N) /= N_Op_Lt then return; end if; -- Deal with array type operands if Is_Array_Type (Typ1) then Expand_Array_Comparison (N); return; end if; -- Deal with Boolean type operands if Is_Boolean_Type (Typ1) then Adjust_Condition (Op1); Adjust_Condition (Op2); Set_Etype (N, Standard_Boolean); Adjust_Result_Type (N, Typ); end if; Rewrite_Comparison (N); -- Try to narrow the operation if Typ1 = Universal_Integer and then Nkind (N) = N_Op_Lt then Narrow_Large_Operation (N); end if; Optimize_Length_Comparison (N); end Expand_N_Op_Lt; ----------------------- -- Expand_N_Op_Minus -- ----------------------- procedure Expand_N_Op_Minus (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); begin Unary_Op_Validity_Checks (N); -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- Try to narrow the operation if Typ = Universal_Integer then Narrow_Large_Operation (N); if Nkind (N) /= N_Op_Minus then return; end if; end if; if not Backend_Overflow_Checks_On_Target and then Is_Signed_Integer_Type (Typ) and then Do_Overflow_Check (N) then -- Software overflow checking expands -expr into (0 - expr) Rewrite (N, Make_Op_Subtract (Loc, Left_Opnd => Make_Integer_Literal (Loc, 0), Right_Opnd => Right_Opnd (N))); Analyze_And_Resolve (N, Typ); end if; Expand_Nonbinary_Modular_Op (N); end Expand_N_Op_Minus; --------------------- -- Expand_N_Op_Mod -- --------------------- procedure Expand_N_Op_Mod (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); DDC : constant Boolean := Do_Division_Check (N); Left : Node_Id; Right : Node_Id; LLB : Uint; Llo : Uint; Lhi : Uint; LOK : Boolean; Rlo : Uint; Rhi : Uint; ROK : Boolean; pragma Warnings (Off, Lhi); begin Binary_Op_Validity_Checks (N); -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- Try to narrow the operation if Typ = Universal_Integer then Narrow_Large_Operation (N); if Nkind (N) /= N_Op_Mod then return; end if; end if; if Is_Integer_Type (Typ) then Apply_Divide_Checks (N); -- All done if we don't have a MOD any more, which can happen as a -- result of overflow expansion in MINIMIZED or ELIMINATED modes. if Nkind (N) /= N_Op_Mod then return; end if; end if; -- Proceed with expansion of mod operator Left := Left_Opnd (N); Right := Right_Opnd (N); Determine_Range (Right, ROK, Rlo, Rhi, Assume_Valid => True); Determine_Range (Left, LOK, Llo, Lhi, Assume_Valid => True); -- Convert mod to rem if operands are both known to be non-negative, or -- both known to be non-positive (these are the cases in which rem and -- mod are the same, see (RM 4.5.5(28-30)). We do this since it is quite -- likely that this will improve the quality of code, (the operation now -- corresponds to the hardware remainder), and it does not seem likely -- that it could be harmful. It also avoids some cases of the elaborate -- expansion in Modify_Tree_For_C mode below (since Ada rem = C %). if (LOK and ROK) and then ((Llo >= 0 and then Rlo >= 0) or else (Lhi <= 0 and then Rhi <= 0)) then Rewrite (N, Make_Op_Rem (Sloc (N), Left_Opnd => Left_Opnd (N), Right_Opnd => Right_Opnd (N))); -- Instead of reanalyzing the node we do the analysis manually. This -- avoids anomalies when the replacement is done in an instance and -- is epsilon more efficient. Set_Entity (N, Standard_Entity (S_Op_Rem)); Set_Etype (N, Typ); Set_Do_Division_Check (N, DDC); Expand_N_Op_Rem (N); Set_Analyzed (N); return; -- Otherwise, normal mod processing else -- Apply optimization x mod 1 = 0. We don't really need that with -- gcc, but it is useful with other back ends and is certainly -- harmless. if Is_Integer_Type (Etype (N)) and then Compile_Time_Known_Value (Right) and then Expr_Value (Right) = Uint_1 then -- Call Remove_Side_Effects to ensure that any side effects in -- the ignored left operand (in particular function calls to -- user defined functions) are properly executed. Remove_Side_Effects (Left); Rewrite (N, Make_Integer_Literal (Loc, 0)); Analyze_And_Resolve (N, Typ); return; end if; -- If we still have a mod operator and we are in Modify_Tree_For_C -- mode, and we have a signed integer type, then here is where we do -- the rewrite in terms of Rem. Note this rewrite bypasses the need -- for the special handling of the annoying case of largest negative -- number mod minus one. if Nkind (N) = N_Op_Mod and then Is_Signed_Integer_Type (Typ) and then Modify_Tree_For_C then -- In the general case, we expand A mod B as -- Tnn : constant typ := A rem B; -- .. -- (if (A >= 0) = (B >= 0) then Tnn -- elsif Tnn = 0 then 0 -- else Tnn + B) -- The comparison can be written simply as A >= 0 if we know that -- B >= 0 which is a very common case. -- An important optimization is when B is known at compile time -- to be 2**K for some constant. In this case we can simply AND -- the left operand with the bit string 2**K-1 (i.e. K 1-bits) -- and that works for both the positive and negative cases. declare P2 : constant Nat := Power_Of_Two (Right); begin if P2 /= 0 then Rewrite (N, Unchecked_Convert_To (Typ, Make_Op_And (Loc, Left_Opnd => Unchecked_Convert_To (Corresponding_Unsigned_Type (Typ), Left), Right_Opnd => Make_Integer_Literal (Loc, 2 ** P2 - 1)))); Analyze_And_Resolve (N, Typ); return; end if; end; -- Here for the full rewrite declare Tnn : constant Entity_Id := Make_Temporary (Sloc (N), 'T', N); Cmp : Node_Id; begin Cmp := Make_Op_Ge (Loc, Left_Opnd => Duplicate_Subexpr_No_Checks (Left), Right_Opnd => Make_Integer_Literal (Loc, 0)); if not LOK or else Rlo < 0 then Cmp := Make_Op_Eq (Loc, Left_Opnd => Cmp, Right_Opnd => Make_Op_Ge (Loc, Left_Opnd => Duplicate_Subexpr_No_Checks (Right), Right_Opnd => Make_Integer_Literal (Loc, 0))); end if; Insert_Action (N, Make_Object_Declaration (Loc, Defining_Identifier => Tnn, Constant_Present => True, Object_Definition => New_Occurrence_Of (Typ, Loc), Expression => Make_Op_Rem (Loc, Left_Opnd => Left, Right_Opnd => Right))); Rewrite (N, Make_If_Expression (Loc, Expressions => New_List ( Cmp, New_Occurrence_Of (Tnn, Loc), Make_If_Expression (Loc, Is_Elsif => True, Expressions => New_List ( Make_Op_Eq (Loc, Left_Opnd => New_Occurrence_Of (Tnn, Loc), Right_Opnd => Make_Integer_Literal (Loc, 0)), Make_Integer_Literal (Loc, 0), Make_Op_Add (Loc, Left_Opnd => New_Occurrence_Of (Tnn, Loc), Right_Opnd => Duplicate_Subexpr_No_Checks (Right))))))); Analyze_And_Resolve (N, Typ); return; end; end if; -- Deal with annoying case of largest negative number mod minus one. -- Gigi may not handle this case correctly, because on some targets, -- the mod value is computed using a divide instruction which gives -- an overflow trap for this case. -- It would be a bit more efficient to figure out which targets -- this is really needed for, but in practice it is reasonable -- to do the following special check in all cases, since it means -- we get a clearer message, and also the overhead is minimal given -- that division is expensive in any case. -- In fact the check is quite easy, if the right operand is -1, then -- the mod value is always 0, and we can just ignore the left operand -- completely in this case. -- This only applies if we still have a mod operator. Skip if we -- have already rewritten this (e.g. in the case of eliminated -- overflow checks which have driven us into bignum mode). if Nkind (N) = N_Op_Mod then -- The operand type may be private (e.g. in the expansion of an -- intrinsic operation) so we must use the underlying type to get -- the bounds, and convert the literals explicitly. LLB := Expr_Value (Type_Low_Bound (Base_Type (Underlying_Type (Etype (Left))))); if ((not ROK) or else (Rlo <= (-1) and then (-1) <= Rhi)) and then ((not LOK) or else (Llo = LLB)) then Rewrite (N, Make_If_Expression (Loc, Expressions => New_List ( Make_Op_Eq (Loc, Left_Opnd => Duplicate_Subexpr (Right), Right_Opnd => Unchecked_Convert_To (Typ, Make_Integer_Literal (Loc, -1))), Unchecked_Convert_To (Typ, Make_Integer_Literal (Loc, Uint_0)), Relocate_Node (N)))); Set_Analyzed (Next (Next (First (Expressions (N))))); Analyze_And_Resolve (N, Typ); end if; end if; end if; end Expand_N_Op_Mod; -------------------------- -- Expand_N_Op_Multiply -- -------------------------- procedure Expand_N_Op_Multiply (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Lop : constant Node_Id := Left_Opnd (N); Rop : constant Node_Id := Right_Opnd (N); Lp2 : constant Boolean := Nkind (Lop) = N_Op_Expon and then Is_Power_Of_2_For_Shift (Lop); Rp2 : constant Boolean := Nkind (Rop) = N_Op_Expon and then Is_Power_Of_2_For_Shift (Rop); Ltyp : constant Entity_Id := Etype (Lop); Rtyp : constant Entity_Id := Etype (Rop); Typ : Entity_Id := Etype (N); begin Binary_Op_Validity_Checks (N); -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- Special optimizations for integer types if Is_Integer_Type (Typ) then -- N * 0 = 0 for integer types if Compile_Time_Known_Value (Rop) and then Expr_Value (Rop) = Uint_0 then -- Call Remove_Side_Effects to ensure that any side effects in -- the ignored left operand (in particular function calls to -- user defined functions) are properly executed. Remove_Side_Effects (Lop); Rewrite (N, Make_Integer_Literal (Loc, Uint_0)); Analyze_And_Resolve (N, Typ); return; end if; -- Similar handling for 0 * N = 0 if Compile_Time_Known_Value (Lop) and then Expr_Value (Lop) = Uint_0 then Remove_Side_Effects (Rop); Rewrite (N, Make_Integer_Literal (Loc, Uint_0)); Analyze_And_Resolve (N, Typ); return; end if; -- N * 1 = 1 * N = N for integer types -- This optimisation is not done if we are going to -- rewrite the product 1 * 2 ** N to a shift. if Compile_Time_Known_Value (Rop) and then Expr_Value (Rop) = Uint_1 and then not Lp2 then Rewrite (N, Lop); return; elsif Compile_Time_Known_Value (Lop) and then Expr_Value (Lop) = Uint_1 and then not Rp2 then Rewrite (N, Rop); return; end if; end if; -- Try to narrow the operation if Typ = Universal_Integer then Narrow_Large_Operation (N); if Nkind (N) /= N_Op_Multiply then return; end if; end if; -- Convert x * 2 ** y to Shift_Left (x, y). Note that the fact that -- Is_Power_Of_2_For_Shift is set means that we know that our left -- operand is an integer, as required for this to work. if Rp2 then if Lp2 then -- Convert 2 ** A * 2 ** B into 2 ** (A + B) Rewrite (N, Make_Op_Expon (Loc, Left_Opnd => Make_Integer_Literal (Loc, 2), Right_Opnd => Make_Op_Add (Loc, Left_Opnd => Right_Opnd (Lop), Right_Opnd => Right_Opnd (Rop)))); Analyze_And_Resolve (N, Typ); return; else -- If the result is modular, perform the reduction of the result -- appropriately. if Is_Modular_Integer_Type (Typ) and then not Non_Binary_Modulus (Typ) then Rewrite (N, Make_Op_And (Loc, Left_Opnd => Make_Op_Shift_Left (Loc, Left_Opnd => Lop, Right_Opnd => Convert_To (Standard_Natural, Right_Opnd (Rop))), Right_Opnd => Make_Integer_Literal (Loc, Modulus (Typ) - 1))); else Rewrite (N, Make_Op_Shift_Left (Loc, Left_Opnd => Lop, Right_Opnd => Convert_To (Standard_Natural, Right_Opnd (Rop)))); end if; Analyze_And_Resolve (N, Typ); return; end if; -- Same processing for the operands the other way round elsif Lp2 then if Is_Modular_Integer_Type (Typ) and then not Non_Binary_Modulus (Typ) then Rewrite (N, Make_Op_And (Loc, Left_Opnd => Make_Op_Shift_Left (Loc, Left_Opnd => Rop, Right_Opnd => Convert_To (Standard_Natural, Right_Opnd (Lop))), Right_Opnd => Make_Integer_Literal (Loc, Modulus (Typ) - 1))); else Rewrite (N, Make_Op_Shift_Left (Loc, Left_Opnd => Rop, Right_Opnd => Convert_To (Standard_Natural, Right_Opnd (Lop)))); end if; Analyze_And_Resolve (N, Typ); return; end if; -- Do required fixup of universal fixed operation if Typ = Universal_Fixed then Fixup_Universal_Fixed_Operation (N); Typ := Etype (N); end if; -- Multiplications with fixed-point results if Is_Fixed_Point_Type (Typ) then -- Case of fixed * integer => fixed if Is_Integer_Type (Rtyp) then Expand_Multiply_Fixed_By_Integer_Giving_Fixed (N); -- Case of integer * fixed => fixed elsif Is_Integer_Type (Ltyp) then Expand_Multiply_Integer_By_Fixed_Giving_Fixed (N); -- Case of fixed * fixed => fixed else Expand_Multiply_Fixed_By_Fixed_Giving_Fixed (N); end if; -- Other cases of multiplication of fixed-point operands elsif Is_Fixed_Point_Type (Ltyp) or else Is_Fixed_Point_Type (Rtyp) then if Is_Integer_Type (Typ) then Expand_Multiply_Fixed_By_Fixed_Giving_Integer (N); else pragma Assert (Is_Floating_Point_Type (Typ)); Expand_Multiply_Fixed_By_Fixed_Giving_Float (N); end if; -- Mixed-mode operations can appear in a non-static universal context, -- in which case the integer argument must be converted explicitly. elsif Typ = Universal_Real and then Is_Integer_Type (Rtyp) then Rewrite (Rop, Convert_To (Universal_Real, Relocate_Node (Rop))); Analyze_And_Resolve (Rop, Universal_Real); elsif Typ = Universal_Real and then Is_Integer_Type (Ltyp) then Rewrite (Lop, Convert_To (Universal_Real, Relocate_Node (Lop))); Analyze_And_Resolve (Lop, Universal_Real); -- Non-fixed point cases, check software overflow checking required elsif Is_Signed_Integer_Type (Etype (N)) then Apply_Arithmetic_Overflow_Check (N); end if; -- Overflow checks for floating-point if -gnateF mode active Check_Float_Op_Overflow (N); Expand_Nonbinary_Modular_Op (N); end Expand_N_Op_Multiply; -------------------- -- Expand_N_Op_Ne -- -------------------- procedure Expand_N_Op_Ne (N : Node_Id) is Typ : constant Entity_Id := Etype (Left_Opnd (N)); begin -- Case of elementary type with standard operator. But if unnesting, -- handle elementary types whose Equivalent_Types are records because -- there may be padding or undefined fields. if Is_Elementary_Type (Typ) and then Sloc (Entity (N)) = Standard_Location and then not (Ekind_In (Typ, E_Class_Wide_Type, E_Class_Wide_Subtype, E_Access_Subprogram_Type, E_Access_Protected_Subprogram_Type, E_Anonymous_Access_Protected_Subprogram_Type, E_Access_Subprogram_Type, E_Exception_Type) and then Present (Equivalent_Type (Typ)) and then Is_Record_Type (Equivalent_Type (Typ))) then Binary_Op_Validity_Checks (N); -- Deal with overflow checks in MINIMIZED/ELIMINATED mode and if -- means we no longer have a /= operation, we are all done. Expand_Compare_Minimize_Eliminate_Overflow (N); if Nkind (N) /= N_Op_Ne then return; end if; -- Boolean types (requiring handling of non-standard case) if Is_Boolean_Type (Typ) then Adjust_Condition (Left_Opnd (N)); Adjust_Condition (Right_Opnd (N)); Set_Etype (N, Standard_Boolean); Adjust_Result_Type (N, Typ); end if; Rewrite_Comparison (N); -- Try to narrow the operation if Typ = Universal_Integer and then Nkind (N) = N_Op_Ne then Narrow_Large_Operation (N); end if; -- For all cases other than elementary types, we rewrite node as the -- negation of an equality operation, and reanalyze. The equality to be -- used is defined in the same scope and has the same signature. This -- signature must be set explicitly since in an instance it may not have -- the same visibility as in the generic unit. This avoids duplicating -- or factoring the complex code for record/array equality tests etc. -- This case is also used for the minimal expansion performed in -- GNATprove mode. else declare Loc : constant Source_Ptr := Sloc (N); Neg : Node_Id; Ne : constant Entity_Id := Entity (N); begin Binary_Op_Validity_Checks (N); Neg := Make_Op_Not (Loc, Right_Opnd => Make_Op_Eq (Loc, Left_Opnd => Left_Opnd (N), Right_Opnd => Right_Opnd (N))); -- The level of parentheses is useless in GNATprove mode, and -- bumping its level here leads to wrong columns being used in -- check messages, hence skip it in this mode. if not GNATprove_Mode then Set_Paren_Count (Right_Opnd (Neg), 1); end if; if Scope (Ne) /= Standard_Standard then Set_Entity (Right_Opnd (Neg), Corresponding_Equality (Ne)); end if; -- For navigation purposes, we want to treat the inequality as an -- implicit reference to the corresponding equality. Preserve the -- Comes_From_ source flag to generate proper Xref entries. Preserve_Comes_From_Source (Neg, N); Preserve_Comes_From_Source (Right_Opnd (Neg), N); Rewrite (N, Neg); Analyze_And_Resolve (N, Standard_Boolean); end; end if; -- No need for optimization in GNATprove mode, where we would rather see -- the original source expression. if not GNATprove_Mode then Optimize_Length_Comparison (N); end if; end Expand_N_Op_Ne; --------------------- -- Expand_N_Op_Not -- --------------------- -- If the argument is other than a Boolean array type, there is no special -- expansion required, except for dealing with validity checks, and non- -- standard boolean representations. -- For the packed array case, we call the special routine in Exp_Pakd, -- except that if the component size is greater than one, we use the -- standard routine generating a gruesome loop (it is so peculiar to have -- packed arrays with non-standard Boolean representations anyway, so it -- does not matter that we do not handle this case efficiently). -- For the unpacked array case (and for the special packed case where we -- have non standard Booleans, as discussed above), we generate and insert -- into the tree the following function definition: -- function Nnnn (A : arr) is -- B : arr; -- begin -- for J in a'range loop -- B (J) := not A (J); -- end loop; -- return B; -- end Nnnn; -- Here arr is the actual subtype of the parameter (and hence always -- constrained). Then we replace the not with a call to this function. procedure Expand_N_Op_Not (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); Opnd : Node_Id; Arr : Entity_Id; A : Entity_Id; B : Entity_Id; J : Entity_Id; A_J : Node_Id; B_J : Node_Id; Func_Name : Entity_Id; Loop_Statement : Node_Id; begin Unary_Op_Validity_Checks (N); -- For boolean operand, deal with non-standard booleans if Is_Boolean_Type (Typ) then Adjust_Condition (Right_Opnd (N)); Set_Etype (N, Standard_Boolean); Adjust_Result_Type (N, Typ); return; end if; -- Only array types need any other processing if not Is_Array_Type (Typ) then return; end if; -- Case of array operand. If bit packed with a component size of 1, -- handle it in Exp_Pakd if the operand is known to be aligned. if Is_Bit_Packed_Array (Typ) and then Component_Size (Typ) = 1 and then not Is_Possibly_Unaligned_Object (Right_Opnd (N)) then Expand_Packed_Not (N); return; end if; -- Case of array operand which is not bit-packed. If the context is -- a safe assignment, call in-place operation, If context is a larger -- boolean expression in the context of a safe assignment, expansion is -- done by enclosing operation. Opnd := Relocate_Node (Right_Opnd (N)); Convert_To_Actual_Subtype (Opnd); Arr := Etype (Opnd); Ensure_Defined (Arr, N); Silly_Boolean_Array_Not_Test (N, Arr); if Nkind (Parent (N)) = N_Assignment_Statement then if Safe_In_Place_Array_Op (Name (Parent (N)), N, Empty) then Build_Boolean_Array_Proc_Call (Parent (N), Opnd, Empty); return; -- Special case the negation of a binary operation elsif Nkind_In (Opnd, N_Op_And, N_Op_Or, N_Op_Xor) and then Safe_In_Place_Array_Op (Name (Parent (N)), Left_Opnd (Opnd), Right_Opnd (Opnd)) then Build_Boolean_Array_Proc_Call (Parent (N), Opnd, Empty); return; end if; elsif Nkind (Parent (N)) in N_Binary_Op and then Nkind (Parent (Parent (N))) = N_Assignment_Statement then declare Op1 : constant Node_Id := Left_Opnd (Parent (N)); Op2 : constant Node_Id := Right_Opnd (Parent (N)); Lhs : constant Node_Id := Name (Parent (Parent (N))); begin if Safe_In_Place_Array_Op (Lhs, Op1, Op2) then -- (not A) op (not B) can be reduced to a single call if N = Op1 and then Nkind (Op2) = N_Op_Not then return; elsif N = Op2 and then Nkind (Op1) = N_Op_Not then return; -- A xor (not B) can also be special-cased elsif N = Op2 and then Nkind (Parent (N)) = N_Op_Xor then return; end if; end if; end; end if; A := Make_Defining_Identifier (Loc, Name_uA); B := Make_Defining_Identifier (Loc, Name_uB); J := Make_Defining_Identifier (Loc, Name_uJ); A_J := Make_Indexed_Component (Loc, Prefix => New_Occurrence_Of (A, Loc), Expressions => New_List (New_Occurrence_Of (J, Loc))); B_J := Make_Indexed_Component (Loc, Prefix => New_Occurrence_Of (B, Loc), Expressions => New_List (New_Occurrence_Of (J, Loc))); Loop_Statement := Make_Implicit_Loop_Statement (N, Identifier => Empty, Iteration_Scheme => Make_Iteration_Scheme (Loc, Loop_Parameter_Specification => Make_Loop_Parameter_Specification (Loc, Defining_Identifier => J, Discrete_Subtype_Definition => Make_Attribute_Reference (Loc, Prefix => Make_Identifier (Loc, Chars (A)), Attribute_Name => Name_Range))), Statements => New_List ( Make_Assignment_Statement (Loc, Name => B_J, Expression => Make_Op_Not (Loc, A_J)))); Func_Name := Make_Temporary (Loc, 'N'); Set_Is_Inlined (Func_Name); Insert_Action (N, Make_Subprogram_Body (Loc, Specification => Make_Function_Specification (Loc, Defining_Unit_Name => Func_Name, Parameter_Specifications => New_List ( Make_Parameter_Specification (Loc, Defining_Identifier => A, Parameter_Type => New_Occurrence_Of (Typ, Loc))), Result_Definition => New_Occurrence_Of (Typ, Loc)), Declarations => New_List ( Make_Object_Declaration (Loc, Defining_Identifier => B, Object_Definition => New_Occurrence_Of (Arr, Loc))), Handled_Statement_Sequence => Make_Handled_Sequence_Of_Statements (Loc, Statements => New_List ( Loop_Statement, Make_Simple_Return_Statement (Loc, Expression => Make_Identifier (Loc, Chars (B))))))); Rewrite (N, Make_Function_Call (Loc, Name => New_Occurrence_Of (Func_Name, Loc), Parameter_Associations => New_List (Opnd))); Analyze_And_Resolve (N, Typ); end Expand_N_Op_Not; -------------------- -- Expand_N_Op_Or -- -------------------- procedure Expand_N_Op_Or (N : Node_Id) is Typ : constant Entity_Id := Etype (N); begin Binary_Op_Validity_Checks (N); if Is_Array_Type (Etype (N)) then Expand_Boolean_Operator (N); elsif Is_Boolean_Type (Etype (N)) then Adjust_Condition (Left_Opnd (N)); Adjust_Condition (Right_Opnd (N)); Set_Etype (N, Standard_Boolean); Adjust_Result_Type (N, Typ); elsif Is_Intrinsic_Subprogram (Entity (N)) then Expand_Intrinsic_Call (N, Entity (N)); end if; Expand_Nonbinary_Modular_Op (N); end Expand_N_Op_Or; ---------------------- -- Expand_N_Op_Plus -- ---------------------- procedure Expand_N_Op_Plus (N : Node_Id) is Typ : constant Entity_Id := Etype (N); begin Unary_Op_Validity_Checks (N); -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- Try to narrow the operation if Typ = Universal_Integer then Narrow_Large_Operation (N); end if; end Expand_N_Op_Plus; --------------------- -- Expand_N_Op_Rem -- --------------------- procedure Expand_N_Op_Rem (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); Left : Node_Id; Right : Node_Id; Lo : Uint; Hi : Uint; OK : Boolean; Lneg : Boolean; Rneg : Boolean; -- Set if corresponding operand can be negative pragma Unreferenced (Hi); begin Binary_Op_Validity_Checks (N); -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- Try to narrow the operation if Typ = Universal_Integer then Narrow_Large_Operation (N); if Nkind (N) /= N_Op_Rem then return; end if; end if; if Is_Integer_Type (Etype (N)) then Apply_Divide_Checks (N); -- All done if we don't have a REM any more, which can happen as a -- result of overflow expansion in MINIMIZED or ELIMINATED modes. if Nkind (N) /= N_Op_Rem then return; end if; end if; -- Proceed with expansion of REM Left := Left_Opnd (N); Right := Right_Opnd (N); -- Apply optimization x rem 1 = 0. We don't really need that with gcc, -- but it is useful with other back ends, and is certainly harmless. if Is_Integer_Type (Etype (N)) and then Compile_Time_Known_Value (Right) and then Expr_Value (Right) = Uint_1 then -- Call Remove_Side_Effects to ensure that any side effects in the -- ignored left operand (in particular function calls to user defined -- functions) are properly executed. Remove_Side_Effects (Left); Rewrite (N, Make_Integer_Literal (Loc, 0)); Analyze_And_Resolve (N, Typ); return; end if; -- Deal with annoying case of largest negative number remainder minus -- one. Gigi may not handle this case correctly, because on some -- targets, the mod value is computed using a divide instruction -- which gives an overflow trap for this case. -- It would be a bit more efficient to figure out which targets this -- is really needed for, but in practice it is reasonable to do the -- following special check in all cases, since it means we get a clearer -- message, and also the overhead is minimal given that division is -- expensive in any case. -- In fact the check is quite easy, if the right operand is -1, then -- the remainder is always 0, and we can just ignore the left operand -- completely in this case. Determine_Range (Right, OK, Lo, Hi, Assume_Valid => True); Lneg := (not OK) or else Lo < 0; Determine_Range (Left, OK, Lo, Hi, Assume_Valid => True); Rneg := (not OK) or else Lo < 0; -- We won't mess with trying to find out if the left operand can really -- be the largest negative number (that's a pain in the case of private -- types and this is really marginal). We will just assume that we need -- the test if the left operand can be negative at all. if Lneg and Rneg then Rewrite (N, Make_If_Expression (Loc, Expressions => New_List ( Make_Op_Eq (Loc, Left_Opnd => Duplicate_Subexpr (Right), Right_Opnd => Unchecked_Convert_To (Typ, Make_Integer_Literal (Loc, -1))), Unchecked_Convert_To (Typ, Make_Integer_Literal (Loc, Uint_0)), Relocate_Node (N)))); Set_Analyzed (Next (Next (First (Expressions (N))))); Analyze_And_Resolve (N, Typ); end if; end Expand_N_Op_Rem; ----------------------------- -- Expand_N_Op_Rotate_Left -- ----------------------------- procedure Expand_N_Op_Rotate_Left (N : Node_Id) is begin Binary_Op_Validity_Checks (N); -- If we are in Modify_Tree_For_C mode, there is no rotate left in C, -- so we rewrite in terms of logical shifts -- Shift_Left (Num, Bits) or Shift_Right (num, Esize - Bits) -- where Bits is the shift count mod Esize (the mod operation here -- deals with ludicrous large shift counts, which are apparently OK). -- What about nonbinary modulus ??? declare Loc : constant Source_Ptr := Sloc (N); Rtp : constant Entity_Id := Etype (Right_Opnd (N)); Typ : constant Entity_Id := Etype (N); begin if Modify_Tree_For_C then Rewrite (Right_Opnd (N), Make_Op_Rem (Loc, Left_Opnd => Relocate_Node (Right_Opnd (N)), Right_Opnd => Make_Integer_Literal (Loc, Esize (Typ)))); Analyze_And_Resolve (Right_Opnd (N), Rtp); Rewrite (N, Make_Op_Or (Loc, Left_Opnd => Make_Op_Shift_Left (Loc, Left_Opnd => Left_Opnd (N), Right_Opnd => Right_Opnd (N)), Right_Opnd => Make_Op_Shift_Right (Loc, Left_Opnd => Duplicate_Subexpr_No_Checks (Left_Opnd (N)), Right_Opnd => Make_Op_Subtract (Loc, Left_Opnd => Make_Integer_Literal (Loc, Esize (Typ)), Right_Opnd => Duplicate_Subexpr_No_Checks (Right_Opnd (N)))))); Analyze_And_Resolve (N, Typ); end if; end; end Expand_N_Op_Rotate_Left; ------------------------------ -- Expand_N_Op_Rotate_Right -- ------------------------------ procedure Expand_N_Op_Rotate_Right (N : Node_Id) is begin Binary_Op_Validity_Checks (N); -- If we are in Modify_Tree_For_C mode, there is no rotate right in C, -- so we rewrite in terms of logical shifts -- Shift_Right (Num, Bits) or Shift_Left (num, Esize - Bits) -- where Bits is the shift count mod Esize (the mod operation here -- deals with ludicrous large shift counts, which are apparently OK). -- What about nonbinary modulus ??? declare Loc : constant Source_Ptr := Sloc (N); Rtp : constant Entity_Id := Etype (Right_Opnd (N)); Typ : constant Entity_Id := Etype (N); begin Rewrite (Right_Opnd (N), Make_Op_Rem (Loc, Left_Opnd => Relocate_Node (Right_Opnd (N)), Right_Opnd => Make_Integer_Literal (Loc, Esize (Typ)))); Analyze_And_Resolve (Right_Opnd (N), Rtp); if Modify_Tree_For_C then Rewrite (N, Make_Op_Or (Loc, Left_Opnd => Make_Op_Shift_Right (Loc, Left_Opnd => Left_Opnd (N), Right_Opnd => Right_Opnd (N)), Right_Opnd => Make_Op_Shift_Left (Loc, Left_Opnd => Duplicate_Subexpr_No_Checks (Left_Opnd (N)), Right_Opnd => Make_Op_Subtract (Loc, Left_Opnd => Make_Integer_Literal (Loc, Esize (Typ)), Right_Opnd => Duplicate_Subexpr_No_Checks (Right_Opnd (N)))))); Analyze_And_Resolve (N, Typ); end if; end; end Expand_N_Op_Rotate_Right; ---------------------------- -- Expand_N_Op_Shift_Left -- ---------------------------- -- Note: nothing in this routine depends on left as opposed to right shifts -- so we share the routine for expanding shift right operations. procedure Expand_N_Op_Shift_Left (N : Node_Id) is begin Binary_Op_Validity_Checks (N); -- If we are in Modify_Tree_For_C mode, then ensure that the right -- operand is not greater than the word size (since that would not -- be defined properly by the corresponding C shift operator). if Modify_Tree_For_C then declare Right : constant Node_Id := Right_Opnd (N); Loc : constant Source_Ptr := Sloc (Right); Typ : constant Entity_Id := Etype (N); Siz : constant Uint := Esize (Typ); Orig : Node_Id; OK : Boolean; Lo : Uint; Hi : Uint; begin if Compile_Time_Known_Value (Right) then if Expr_Value (Right) >= Siz then Rewrite (N, Make_Integer_Literal (Loc, 0)); Analyze_And_Resolve (N, Typ); end if; -- Not compile time known, find range else Determine_Range (Right, OK, Lo, Hi, Assume_Valid => True); -- Nothing to do if known to be OK range, otherwise expand if not OK or else Hi >= Siz then -- Prevent recursion on copy of shift node Orig := Relocate_Node (N); Set_Analyzed (Orig); -- Now do the rewrite Rewrite (N, Make_If_Expression (Loc, Expressions => New_List ( Make_Op_Ge (Loc, Left_Opnd => Duplicate_Subexpr_Move_Checks (Right), Right_Opnd => Make_Integer_Literal (Loc, Siz)), Make_Integer_Literal (Loc, 0), Orig))); Analyze_And_Resolve (N, Typ); end if; end if; end; end if; end Expand_N_Op_Shift_Left; ----------------------------- -- Expand_N_Op_Shift_Right -- ----------------------------- procedure Expand_N_Op_Shift_Right (N : Node_Id) is begin -- Share shift left circuit Expand_N_Op_Shift_Left (N); end Expand_N_Op_Shift_Right; ---------------------------------------- -- Expand_N_Op_Shift_Right_Arithmetic -- ---------------------------------------- procedure Expand_N_Op_Shift_Right_Arithmetic (N : Node_Id) is begin Binary_Op_Validity_Checks (N); -- If we are in Modify_Tree_For_C mode, there is no shift right -- arithmetic in C, so we rewrite in terms of logical shifts. -- Shift_Right (Num, Bits) or -- (if Num >= Sign -- then not (Shift_Right (Mask, bits)) -- else 0) -- Here Mask is all 1 bits (2**size - 1), and Sign is 2**(size - 1) -- Note: in almost all C compilers it would work to just shift a -- signed integer right, but it's undefined and we cannot rely on it. -- Note: the above works fine for shift counts greater than or equal -- to the word size, since in this case (not (Shift_Right (Mask, bits))) -- generates all 1'bits. -- What about nonbinary modulus ??? declare Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); Sign : constant Uint := 2 ** (Esize (Typ) - 1); Mask : constant Uint := (2 ** Esize (Typ)) - 1; Left : constant Node_Id := Left_Opnd (N); Right : constant Node_Id := Right_Opnd (N); Maskx : Node_Id; begin if Modify_Tree_For_C then -- Here if not (Shift_Right (Mask, bits)) can be computed at -- compile time as a single constant. if Compile_Time_Known_Value (Right) then declare Val : constant Uint := Expr_Value (Right); begin if Val >= Esize (Typ) then Maskx := Make_Integer_Literal (Loc, Mask); else Maskx := Make_Integer_Literal (Loc, Intval => Mask - (Mask / (2 ** Expr_Value (Right)))); end if; end; else Maskx := Make_Op_Not (Loc, Right_Opnd => Make_Op_Shift_Right (Loc, Left_Opnd => Make_Integer_Literal (Loc, Mask), Right_Opnd => Duplicate_Subexpr_No_Checks (Right))); end if; -- Now do the rewrite Rewrite (N, Make_Op_Or (Loc, Left_Opnd => Make_Op_Shift_Right (Loc, Left_Opnd => Left, Right_Opnd => Right), Right_Opnd => Make_If_Expression (Loc, Expressions => New_List ( Make_Op_Ge (Loc, Left_Opnd => Duplicate_Subexpr_No_Checks (Left), Right_Opnd => Make_Integer_Literal (Loc, Sign)), Maskx, Make_Integer_Literal (Loc, 0))))); Analyze_And_Resolve (N, Typ); end if; end; end Expand_N_Op_Shift_Right_Arithmetic; -------------------------- -- Expand_N_Op_Subtract -- -------------------------- procedure Expand_N_Op_Subtract (N : Node_Id) is Typ : constant Entity_Id := Etype (N); begin Binary_Op_Validity_Checks (N); -- Check for MINIMIZED/ELIMINATED overflow mode if Minimized_Eliminated_Overflow_Check (N) then Apply_Arithmetic_Overflow_Check (N); return; end if; -- Try to narrow the operation if Typ = Universal_Integer then Narrow_Large_Operation (N); if Nkind (N) /= N_Op_Subtract then return; end if; end if; -- N - 0 = N for integer types if Is_Integer_Type (Typ) and then Compile_Time_Known_Value (Right_Opnd (N)) and then Expr_Value (Right_Opnd (N)) = 0 then Rewrite (N, Left_Opnd (N)); return; end if; -- Arithmetic overflow checks for signed integer/fixed point types if Is_Signed_Integer_Type (Typ) or else Is_Fixed_Point_Type (Typ) then Apply_Arithmetic_Overflow_Check (N); end if; -- Overflow checks for floating-point if -gnateF mode active Check_Float_Op_Overflow (N); Expand_Nonbinary_Modular_Op (N); end Expand_N_Op_Subtract; --------------------- -- Expand_N_Op_Xor -- --------------------- procedure Expand_N_Op_Xor (N : Node_Id) is Typ : constant Entity_Id := Etype (N); begin Binary_Op_Validity_Checks (N); if Is_Array_Type (Etype (N)) then Expand_Boolean_Operator (N); elsif Is_Boolean_Type (Etype (N)) then Adjust_Condition (Left_Opnd (N)); Adjust_Condition (Right_Opnd (N)); Set_Etype (N, Standard_Boolean); Adjust_Result_Type (N, Typ); elsif Is_Intrinsic_Subprogram (Entity (N)) then Expand_Intrinsic_Call (N, Entity (N)); end if; Expand_Nonbinary_Modular_Op (N); end Expand_N_Op_Xor; ---------------------- -- Expand_N_Or_Else -- ---------------------- procedure Expand_N_Or_Else (N : Node_Id) renames Expand_Short_Circuit_Operator; ----------------------------------- -- Expand_N_Qualified_Expression -- ----------------------------------- procedure Expand_N_Qualified_Expression (N : Node_Id) is Operand : constant Node_Id := Expression (N); Target_Type : constant Entity_Id := Entity (Subtype_Mark (N)); begin -- Do validity check if validity checking operands if Validity_Checks_On and Validity_Check_Operands then Ensure_Valid (Operand); end if; -- Apply possible constraint check Apply_Constraint_Check (Operand, Target_Type, No_Sliding => True); -- Apply possible predicate check Apply_Predicate_Check (Operand, Target_Type); if Do_Range_Check (Operand) then Generate_Range_Check (Operand, Target_Type, CE_Range_Check_Failed); end if; end Expand_N_Qualified_Expression; ------------------------------------ -- Expand_N_Quantified_Expression -- ------------------------------------ -- We expand: -- for all X in range => Cond -- into: -- T := True; -- for X in range loop -- if not Cond then -- T := False; -- exit; -- end if; -- end loop; -- Similarly, an existentially quantified expression: -- for some X in range => Cond -- becomes: -- T := False; -- for X in range loop -- if Cond then -- T := True; -- exit; -- end if; -- end loop; -- In both cases, the iteration may be over a container in which case it is -- given by an iterator specification, not a loop parameter specification. procedure Expand_N_Quantified_Expression (N : Node_Id) is Actions : constant List_Id := New_List; For_All : constant Boolean := All_Present (N); Iter_Spec : constant Node_Id := Iterator_Specification (N); Loc : constant Source_Ptr := Sloc (N); Loop_Spec : constant Node_Id := Loop_Parameter_Specification (N); Cond : Node_Id; Flag : Entity_Id; Scheme : Node_Id; Stmts : List_Id; Var : Entity_Id; begin -- Ensure that the bound variable is properly frozen. We must do -- this before expansion because the expression is about to be -- converted into a loop, and resulting freeze nodes may end up -- in the wrong place in the tree. if Present (Iter_Spec) then Var := Defining_Identifier (Iter_Spec); else Var := Defining_Identifier (Loop_Spec); end if; declare P : Node_Id := Parent (N); begin while Nkind (P) in N_Subexpr loop P := Parent (P); end loop; Freeze_Before (P, Etype (Var)); end; -- Create the declaration of the flag which tracks the status of the -- quantified expression. Generate: -- Flag : Boolean := (True | False); Flag := Make_Temporary (Loc, 'T', N); Append_To (Actions, Make_Object_Declaration (Loc, Defining_Identifier => Flag, Object_Definition => New_Occurrence_Of (Standard_Boolean, Loc), Expression => New_Occurrence_Of (Boolean_Literals (For_All), Loc))); -- Construct the circuitry which tracks the status of the quantified -- expression. Generate: -- if [not] Cond then -- Flag := (False | True); -- exit; -- end if; Cond := Relocate_Node (Condition (N)); if For_All then Cond := Make_Op_Not (Loc, Cond); end if; Stmts := New_List ( Make_Implicit_If_Statement (N, Condition => Cond, Then_Statements => New_List ( Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (Flag, Loc), Expression => New_Occurrence_Of (Boolean_Literals (not For_All), Loc)), Make_Exit_Statement (Loc)))); -- Build the loop equivalent of the quantified expression if Present (Iter_Spec) then Scheme := Make_Iteration_Scheme (Loc, Iterator_Specification => Iter_Spec); else Scheme := Make_Iteration_Scheme (Loc, Loop_Parameter_Specification => Loop_Spec); end if; Append_To (Actions, Make_Loop_Statement (Loc, Iteration_Scheme => Scheme, Statements => Stmts, End_Label => Empty)); -- Transform the quantified expression Rewrite (N, Make_Expression_With_Actions (Loc, Expression => New_Occurrence_Of (Flag, Loc), Actions => Actions)); Analyze_And_Resolve (N, Standard_Boolean); end Expand_N_Quantified_Expression; --------------------------------- -- Expand_N_Selected_Component -- --------------------------------- procedure Expand_N_Selected_Component (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Par : constant Node_Id := Parent (N); P : constant Node_Id := Prefix (N); S : constant Node_Id := Selector_Name (N); Ptyp : constant Entity_Id := Underlying_Type (Etype (P)); Disc : Entity_Id; New_N : Node_Id; Dcon : Elmt_Id; Dval : Node_Id; function In_Left_Hand_Side (Comp : Node_Id) return Boolean; -- Gigi needs a temporary for prefixes that depend on a discriminant, -- unless the context of an assignment can provide size information. -- Don't we have a general routine that does this??? function Is_Subtype_Declaration return Boolean; -- The replacement of a discriminant reference by its value is required -- if this is part of the initialization of an temporary generated by a -- change of representation. This shows up as the construction of a -- discriminant constraint for a subtype declared at the same point as -- the entity in the prefix of the selected component. We recognize this -- case when the context of the reference is: -- subtype ST is T(Obj.D); -- where the entity for Obj comes from source, and ST has the same sloc. ----------------------- -- In_Left_Hand_Side -- ----------------------- function In_Left_Hand_Side (Comp : Node_Id) return Boolean is begin return (Nkind (Parent (Comp)) = N_Assignment_Statement and then Comp = Name (Parent (Comp))) or else (Present (Parent (Comp)) and then Nkind (Parent (Comp)) in N_Subexpr and then In_Left_Hand_Side (Parent (Comp))); end In_Left_Hand_Side; ----------------------------- -- Is_Subtype_Declaration -- ----------------------------- function Is_Subtype_Declaration return Boolean is Par : constant Node_Id := Parent (N); begin return Nkind (Par) = N_Index_Or_Discriminant_Constraint and then Nkind (Parent (Parent (Par))) = N_Subtype_Declaration and then Comes_From_Source (Entity (Prefix (N))) and then Sloc (Par) = Sloc (Entity (Prefix (N))); end Is_Subtype_Declaration; -- Start of processing for Expand_N_Selected_Component begin -- Deal with discriminant check required if Do_Discriminant_Check (N) then if Present (Discriminant_Checking_Func (Original_Record_Component (Entity (S)))) then -- Present the discriminant checking function to the backend, so -- that it can inline the call to the function. Add_Inlined_Body (Discriminant_Checking_Func (Original_Record_Component (Entity (S))), N); -- Now reset the flag and generate the call Set_Do_Discriminant_Check (N, False); Generate_Discriminant_Check (N); -- In the case of Unchecked_Union, no discriminant checking is -- actually performed. else Set_Do_Discriminant_Check (N, False); end if; end if; -- Ada 2005 (AI-318-02): If the prefix is a call to a build-in-place -- function, then additional actuals must be passed. if Is_Build_In_Place_Function_Call (P) then Make_Build_In_Place_Call_In_Anonymous_Context (P); -- Ada 2005 (AI-318-02): Specialization of the previous case for prefix -- containing build-in-place function calls whose returned object covers -- interface types. elsif Present (Unqual_BIP_Iface_Function_Call (P)) then Make_Build_In_Place_Iface_Call_In_Anonymous_Context (P); end if; -- Gigi cannot handle unchecked conversions that are the prefix of a -- selected component with discriminants. This must be checked during -- expansion, because during analysis the type of the selector is not -- known at the point the prefix is analyzed. If the conversion is the -- target of an assignment, then we cannot force the evaluation. if Nkind (Prefix (N)) = N_Unchecked_Type_Conversion and then Has_Discriminants (Etype (N)) and then not In_Left_Hand_Side (N) then Force_Evaluation (Prefix (N)); end if; -- Remaining processing applies only if selector is a discriminant if Ekind (Entity (Selector_Name (N))) = E_Discriminant then -- If the selector is a discriminant of a constrained record type, -- we may be able to rewrite the expression with the actual value -- of the discriminant, a useful optimization in some cases. if Is_Record_Type (Ptyp) and then Has_Discriminants (Ptyp) and then Is_Constrained (Ptyp) then -- Do this optimization for discrete types only, and not for -- access types (access discriminants get us into trouble). if not Is_Discrete_Type (Etype (N)) then null; -- Don't do this on the left-hand side of an assignment statement. -- Normally one would think that references like this would not -- occur, but they do in generated code, and mean that we really -- do want to assign the discriminant. elsif Nkind (Par) = N_Assignment_Statement and then Name (Par) = N then null; -- Don't do this optimization for the prefix of an attribute or -- the name of an object renaming declaration since these are -- contexts where we do not want the value anyway. elsif (Nkind (Par) = N_Attribute_Reference and then Prefix (Par) = N) or else Is_Renamed_Object (N) then null; -- Don't do this optimization if we are within the code for a -- discriminant check, since the whole point of such a check may -- be to verify the condition on which the code below depends. elsif Is_In_Discriminant_Check (N) then null; -- Green light to see if we can do the optimization. There is -- still one condition that inhibits the optimization below but -- now is the time to check the particular discriminant. else -- Loop through discriminants to find the matching discriminant -- constraint to see if we can copy it. Disc := First_Discriminant (Ptyp); Dcon := First_Elmt (Discriminant_Constraint (Ptyp)); Discr_Loop : while Present (Dcon) loop Dval := Node (Dcon); -- Check if this is the matching discriminant and if the -- discriminant value is simple enough to make sense to -- copy. We don't want to copy complex expressions, and -- indeed to do so can cause trouble (before we put in -- this guard, a discriminant expression containing an -- AND THEN was copied, causing problems for coverage -- analysis tools). -- However, if the reference is part of the initialization -- code generated for an object declaration, we must use -- the discriminant value from the subtype constraint, -- because the selected component may be a reference to the -- object being initialized, whose discriminant is not yet -- set. This only happens in complex cases involving changes -- or representation. if Disc = Entity (Selector_Name (N)) and then (Is_Entity_Name (Dval) or else Compile_Time_Known_Value (Dval) or else Is_Subtype_Declaration) then -- Here we have the matching discriminant. Check for -- the case of a discriminant of a component that is -- constrained by an outer discriminant, which cannot -- be optimized away. if Denotes_Discriminant (Dval, Check_Concurrent => True) then exit Discr_Loop; elsif Nkind (Original_Node (Dval)) = N_Selected_Component and then Denotes_Discriminant (Selector_Name (Original_Node (Dval)), True) then exit Discr_Loop; -- Do not retrieve value if constraint is not static. It -- is generally not useful, and the constraint may be a -- rewritten outer discriminant in which case it is in -- fact incorrect. elsif Is_Entity_Name (Dval) and then Nkind (Parent (Entity (Dval))) = N_Object_Declaration and then Present (Expression (Parent (Entity (Dval)))) and then not Is_OK_Static_Expression (Expression (Parent (Entity (Dval)))) then exit Discr_Loop; -- In the context of a case statement, the expression may -- have the base type of the discriminant, and we need to -- preserve the constraint to avoid spurious errors on -- missing cases. elsif Nkind (Parent (N)) = N_Case_Statement and then Etype (Dval) /= Etype (Disc) then Rewrite (N, Make_Qualified_Expression (Loc, Subtype_Mark => New_Occurrence_Of (Etype (Disc), Loc), Expression => New_Copy_Tree (Dval))); Analyze_And_Resolve (N, Etype (Disc)); -- In case that comes out as a static expression, -- reset it (a selected component is never static). Set_Is_Static_Expression (N, False); return; -- Otherwise we can just copy the constraint, but the -- result is certainly not static. In some cases the -- discriminant constraint has been analyzed in the -- context of the original subtype indication, but for -- itypes the constraint might not have been analyzed -- yet, and this must be done now. else Rewrite (N, New_Copy_Tree (Dval)); Analyze_And_Resolve (N); Set_Is_Static_Expression (N, False); return; end if; end if; Next_Elmt (Dcon); Next_Discriminant (Disc); end loop Discr_Loop; -- Note: the above loop should always find a matching -- discriminant, but if it does not, we just missed an -- optimization due to some glitch (perhaps a previous -- error), so ignore. end if; end if; -- The only remaining processing is in the case of a discriminant of -- a concurrent object, where we rewrite the prefix to denote the -- corresponding record type. If the type is derived and has renamed -- discriminants, use corresponding discriminant, which is the one -- that appears in the corresponding record. if not Is_Concurrent_Type (Ptyp) then return; end if; Disc := Entity (Selector_Name (N)); if Is_Derived_Type (Ptyp) and then Present (Corresponding_Discriminant (Disc)) then Disc := Corresponding_Discriminant (Disc); end if; New_N := Make_Selected_Component (Loc, Prefix => Unchecked_Convert_To (Corresponding_Record_Type (Ptyp), New_Copy_Tree (P)), Selector_Name => Make_Identifier (Loc, Chars (Disc))); Rewrite (N, New_N); Analyze (N); end if; -- Set Atomic_Sync_Required if necessary for atomic component if Nkind (N) = N_Selected_Component then declare E : constant Entity_Id := Entity (Selector_Name (N)); Set : Boolean; begin -- If component is atomic, but type is not, setting depends on -- disable/enable state for the component. if Is_Atomic (E) and then not Is_Atomic (Etype (E)) then Set := not Atomic_Synchronization_Disabled (E); -- If component is not atomic, but its type is atomic, setting -- depends on disable/enable state for the type. elsif not Is_Atomic (E) and then Is_Atomic (Etype (E)) then Set := not Atomic_Synchronization_Disabled (Etype (E)); -- If both component and type are atomic, we disable if either -- component or its type have sync disabled. elsif Is_Atomic (E) and then Is_Atomic (Etype (E)) then Set := (not Atomic_Synchronization_Disabled (E)) and then (not Atomic_Synchronization_Disabled (Etype (E))); else Set := False; end if; -- Set flag if required if Set then Activate_Atomic_Synchronization (N); end if; end; end if; end Expand_N_Selected_Component; -------------------- -- Expand_N_Slice -- -------------------- procedure Expand_N_Slice (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); function Is_Procedure_Actual (N : Node_Id) return Boolean; -- Check whether the argument is an actual for a procedure call, in -- which case the expansion of a bit-packed slice is deferred until the -- call itself is expanded. The reason this is required is that we might -- have an IN OUT or OUT parameter, and the copy out is essential, and -- that copy out would be missed if we created a temporary here in -- Expand_N_Slice. Note that we don't bother to test specifically for an -- IN OUT or OUT mode parameter, since it is a bit tricky to do, and it -- is harmless to defer expansion in the IN case, since the call -- processing will still generate the appropriate copy in operation, -- which will take care of the slice. procedure Make_Temporary_For_Slice; -- Create a named variable for the value of the slice, in cases where -- the back end cannot handle it properly, e.g. when packed types or -- unaligned slices are involved. ------------------------- -- Is_Procedure_Actual -- ------------------------- function Is_Procedure_Actual (N : Node_Id) return Boolean is Par : Node_Id := Parent (N); begin loop -- If our parent is a procedure call we can return if Nkind (Par) = N_Procedure_Call_Statement then return True; -- If our parent is a type conversion, keep climbing the tree, -- since a type conversion can be a procedure actual. Also keep -- climbing if parameter association or a qualified expression, -- since these are additional cases that do can appear on -- procedure actuals. elsif Nkind_In (Par, N_Type_Conversion, N_Parameter_Association, N_Qualified_Expression) then Par := Parent (Par); -- Any other case is not what we are looking for else return False; end if; end loop; end Is_Procedure_Actual; ------------------------------ -- Make_Temporary_For_Slice -- ------------------------------ procedure Make_Temporary_For_Slice is Ent : constant Entity_Id := Make_Temporary (Loc, 'T', N); Decl : Node_Id; begin Decl := Make_Object_Declaration (Loc, Defining_Identifier => Ent, Object_Definition => New_Occurrence_Of (Typ, Loc)); Set_No_Initialization (Decl); Insert_Actions (N, New_List ( Decl, Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (Ent, Loc), Expression => Relocate_Node (N)))); Rewrite (N, New_Occurrence_Of (Ent, Loc)); Analyze_And_Resolve (N, Typ); end Make_Temporary_For_Slice; -- Local variables Pref : constant Node_Id := Prefix (N); -- Start of processing for Expand_N_Slice begin -- Ada 2005 (AI-318-02): If the prefix is a call to a build-in-place -- function, then additional actuals must be passed. if Is_Build_In_Place_Function_Call (Pref) then Make_Build_In_Place_Call_In_Anonymous_Context (Pref); -- Ada 2005 (AI-318-02): Specialization of the previous case for prefix -- containing build-in-place function calls whose returned object covers -- interface types. elsif Present (Unqual_BIP_Iface_Function_Call (Pref)) then Make_Build_In_Place_Iface_Call_In_Anonymous_Context (Pref); end if; -- The remaining case to be handled is packed slices. We can leave -- packed slices as they are in the following situations: -- 1. Right or left side of an assignment (we can handle this -- situation correctly in the assignment statement expansion). -- 2. Prefix of indexed component (the slide is optimized away in this -- case, see the start of Expand_N_Slice.) -- 3. Object renaming declaration, since we want the name of the -- slice, not the value. -- 4. Argument to procedure call, since copy-in/copy-out handling may -- be required, and this is handled in the expansion of call -- itself. -- 5. Prefix of an address attribute (this is an error which is caught -- elsewhere, and the expansion would interfere with generating the -- error message) or of a size attribute (because 'Size may change -- when applied to the temporary instead of the slice directly). if not Is_Packed (Typ) then -- Apply transformation for actuals of a function call, where -- Expand_Actuals is not used. if Nkind (Parent (N)) = N_Function_Call and then Is_Possibly_Unaligned_Slice (N) then Make_Temporary_For_Slice; end if; elsif Nkind (Parent (N)) = N_Assignment_Statement or else (Nkind (Parent (Parent (N))) = N_Assignment_Statement and then Parent (N) = Name (Parent (Parent (N)))) then return; elsif Nkind (Parent (N)) = N_Indexed_Component or else Is_Renamed_Object (N) or else Is_Procedure_Actual (N) then return; elsif Nkind (Parent (N)) = N_Attribute_Reference and then (Attribute_Name (Parent (N)) = Name_Address or else Attribute_Name (Parent (N)) = Name_Size) then return; else Make_Temporary_For_Slice; end if; end Expand_N_Slice; ------------------------------ -- Expand_N_Type_Conversion -- ------------------------------ procedure Expand_N_Type_Conversion (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Operand : constant Node_Id := Expression (N); Operand_Acc : Node_Id := Operand; Target_Type : Entity_Id := Etype (N); Operand_Type : Entity_Id := Etype (Operand); procedure Discrete_Range_Check; -- Handles generation of range check for discrete target value procedure Handle_Changed_Representation; -- This is called in the case of record and array type conversions to -- see if there is a change of representation to be handled. Change of -- representation is actually handled at the assignment statement level, -- and what this procedure does is rewrite node N conversion as an -- assignment to temporary. If there is no change of representation, -- then the conversion node is unchanged. procedure Raise_Accessibility_Error; -- Called when we know that an accessibility check will fail. Rewrites -- node N to an appropriate raise statement and outputs warning msgs. -- The Etype of the raise node is set to Target_Type. Note that in this -- case the rest of the processing should be skipped (i.e. the call to -- this procedure will be followed by "goto Done"). procedure Real_Range_Check; -- Handles generation of range check for real target value function Has_Extra_Accessibility (Id : Entity_Id) return Boolean; -- True iff Present (Effective_Extra_Accessibility (Id)) successfully -- evaluates to True. -------------------------- -- Discrete_Range_Check -- -------------------------- -- Case of conversions to a discrete type. We let Generate_Range_Check -- do the heavy lifting, after converting a fixed-point operand to an -- appropriate integer type. procedure Discrete_Range_Check is Expr : Node_Id; Ityp : Entity_Id; procedure Generate_Temporary; -- Generate a temporary to facilitate in the C backend the code -- generation of the unchecked conversion since the size of the -- source type may differ from the size of the target type. ------------------------ -- Generate_Temporary -- ------------------------ procedure Generate_Temporary is begin if Esize (Etype (Expr)) < Esize (Etype (Ityp)) then declare Exp_Type : constant Entity_Id := Ityp; Def_Id : constant Entity_Id := Make_Temporary (Loc, 'R', Expr); E : Node_Id; Res : Node_Id; begin Set_Is_Internal (Def_Id); Set_Etype (Def_Id, Exp_Type); Res := New_Occurrence_Of (Def_Id, Loc); E := Make_Object_Declaration (Loc, Defining_Identifier => Def_Id, Object_Definition => New_Occurrence_Of (Exp_Type, Loc), Constant_Present => True, Expression => Relocate_Node (Expr)); Set_Assignment_OK (E); Insert_Action (Expr, E); Set_Assignment_OK (Res, Assignment_OK (Expr)); Rewrite (Expr, Res); Analyze_And_Resolve (Expr, Exp_Type); end; end if; end Generate_Temporary; -- Start of processing for Discrete_Range_Check begin -- Nothing to do if conversion was rewritten if Nkind (N) /= N_Type_Conversion then return; end if; Expr := Expression (N); -- Nothing to do if range checks suppressed if Range_Checks_Suppressed (Target_Type) then return; end if; -- Nothing to do if expression is an entity on which checks have been -- suppressed. if Is_Entity_Name (Expr) and then Range_Checks_Suppressed (Entity (Expr)) then return; end if; -- Before we do a range check, we have to deal with treating -- a fixed-point operand as an integer. The way we do this -- is simply to do an unchecked conversion to an appropriate -- integer type large enough to hold the result. if Is_Fixed_Point_Type (Etype (Expr)) then if Esize (Base_Type (Etype (Expr))) > Esize (Standard_Integer) then Ityp := Standard_Long_Long_Integer; else Ityp := Standard_Integer; end if; -- Generate a temporary with the large type to facilitate in the C -- backend the code generation for the unchecked conversion. if Modify_Tree_For_C then Generate_Temporary; end if; Set_Do_Range_Check (Expr, False); Rewrite (Expr, Unchecked_Convert_To (Ityp, Expr)); end if; -- Reset overflow flag, since the range check will include -- dealing with possible overflow, and generate the check. Set_Do_Overflow_Check (N, False); Generate_Range_Check (Expr, Target_Type, CE_Range_Check_Failed); end Discrete_Range_Check; ----------------------------------- -- Handle_Changed_Representation -- ----------------------------------- procedure Handle_Changed_Representation is Temp : Entity_Id; Decl : Node_Id; Odef : Node_Id; N_Ix : Node_Id; Cons : List_Id; begin -- Nothing else to do if no change of representation if Same_Representation (Operand_Type, Target_Type) then return; -- The real change of representation work is done by the assignment -- statement processing. So if this type conversion is appearing as -- the expression of an assignment statement, nothing needs to be -- done to the conversion. elsif Nkind (Parent (N)) = N_Assignment_Statement then return; -- Otherwise we need to generate a temporary variable, and do the -- change of representation assignment into that temporary variable. -- The conversion is then replaced by a reference to this variable. else Cons := No_List; -- If type is unconstrained we have to add a constraint, copied -- from the actual value of the left-hand side. if not Is_Constrained (Target_Type) then if Has_Discriminants (Operand_Type) then -- A change of representation can only apply to untagged -- types. We need to build the constraint that applies to -- the target type, using the constraints of the operand. -- The analysis is complicated if there are both inherited -- discriminants and constrained discriminants. -- We iterate over the discriminants of the target, and -- find the discriminant of the same name: -- a) If there is a corresponding discriminant in the object -- then the value is a selected component of the operand. -- b) Otherwise the value of a constrained discriminant is -- found in the stored constraint of the operand. declare Stored : constant Elist_Id := Stored_Constraint (Operand_Type); Elmt : Elmt_Id; Disc_O : Entity_Id; -- Discriminant of the operand type. Its value in the -- object is captured in a selected component. Disc_S : Entity_Id; -- Stored discriminant of the operand. If present, it -- corresponds to a constrained discriminant of the -- parent type. Disc_T : Entity_Id; -- Discriminant of the target type begin Disc_T := First_Discriminant (Target_Type); Disc_O := First_Discriminant (Operand_Type); Disc_S := First_Stored_Discriminant (Operand_Type); if Present (Stored) then Elmt := First_Elmt (Stored); else Elmt := No_Elmt; -- init to avoid warning end if; Cons := New_List; while Present (Disc_T) loop if Present (Disc_O) and then Chars (Disc_T) = Chars (Disc_O) then Append_To (Cons, Make_Selected_Component (Loc, Prefix => Duplicate_Subexpr_Move_Checks (Operand), Selector_Name => Make_Identifier (Loc, Chars (Disc_O)))); Next_Discriminant (Disc_O); elsif Present (Disc_S) then Append_To (Cons, New_Copy_Tree (Node (Elmt))); Next_Elmt (Elmt); end if; Next_Discriminant (Disc_T); end loop; end; elsif Is_Array_Type (Operand_Type) then N_Ix := First_Index (Target_Type); Cons := New_List; for J in 1 .. Number_Dimensions (Operand_Type) loop -- We convert the bounds explicitly. We use an unchecked -- conversion because bounds checks are done elsewhere. Append_To (Cons, Make_Range (Loc, Low_Bound => Unchecked_Convert_To (Etype (N_Ix), Make_Attribute_Reference (Loc, Prefix => Duplicate_Subexpr_No_Checks (Operand, Name_Req => True), Attribute_Name => Name_First, Expressions => New_List ( Make_Integer_Literal (Loc, J)))), High_Bound => Unchecked_Convert_To (Etype (N_Ix), Make_Attribute_Reference (Loc, Prefix => Duplicate_Subexpr_No_Checks (Operand, Name_Req => True), Attribute_Name => Name_Last, Expressions => New_List ( Make_Integer_Literal (Loc, J)))))); Next_Index (N_Ix); end loop; end if; end if; Odef := New_Occurrence_Of (Target_Type, Loc); if Present (Cons) then Odef := Make_Subtype_Indication (Loc, Subtype_Mark => Odef, Constraint => Make_Index_Or_Discriminant_Constraint (Loc, Constraints => Cons)); end if; Temp := Make_Temporary (Loc, 'C'); Decl := Make_Object_Declaration (Loc, Defining_Identifier => Temp, Object_Definition => Odef); Set_No_Initialization (Decl, True); -- Insert required actions. It is essential to suppress checks -- since we have suppressed default initialization, which means -- that the variable we create may have no discriminants. Insert_Actions (N, New_List ( Decl, Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (Temp, Loc), Expression => Relocate_Node (N))), Suppress => All_Checks); Rewrite (N, New_Occurrence_Of (Temp, Loc)); return; end if; end Handle_Changed_Representation; ------------------------------- -- Raise_Accessibility_Error -- ------------------------------- procedure Raise_Accessibility_Error is begin Error_Msg_Warn := SPARK_Mode /= On; Rewrite (N, Make_Raise_Program_Error (Sloc (N), Reason => PE_Accessibility_Check_Failed)); Set_Etype (N, Target_Type); Error_Msg_N ("< typ'Last] -- typ (Tnn) -- This is necessary when there is a conversion of integer to float or -- to fixed-point to ensure that the correct checks are made. It is not -- necessary for the float-to-float case where it is enough to just set -- the Do_Range_Check flag on the expression. procedure Real_Range_Check is Btyp : constant Entity_Id := Base_Type (Target_Type); Lo : constant Node_Id := Type_Low_Bound (Target_Type); Hi : constant Node_Id := Type_High_Bound (Target_Type); Conv : Node_Id; Hi_Arg : Node_Id; Hi_Val : Node_Id; Lo_Arg : Node_Id; Lo_Val : Node_Id; Expr : Entity_Id; Tnn : Entity_Id; begin -- Nothing to do if conversion was rewritten if Nkind (N) /= N_Type_Conversion then return; end if; Expr := Expression (N); -- Clear the flag once for all Set_Do_Range_Check (Expr, False); -- Nothing to do if range checks suppressed, or target has the same -- range as the base type (or is the base type). if Range_Checks_Suppressed (Target_Type) or else (Lo = Type_Low_Bound (Btyp) and then Hi = Type_High_Bound (Btyp)) then return; end if; -- Nothing to do if expression is an entity on which checks have been -- suppressed. if Is_Entity_Name (Expr) and then Range_Checks_Suppressed (Entity (Expr)) then return; end if; -- Nothing to do if expression was rewritten into a float-to-float -- conversion, since this kind of conversion is handled elsewhere. if Is_Floating_Point_Type (Etype (Expr)) and then Is_Floating_Point_Type (Target_Type) then return; end if; -- Nothing to do if bounds are all static and we can tell that the -- expression is within the bounds of the target. Note that if the -- operand is of an unconstrained floating-point type, then we do -- not trust it to be in range (might be infinite) declare S_Lo : constant Node_Id := Type_Low_Bound (Etype (Expr)); S_Hi : constant Node_Id := Type_High_Bound (Etype (Expr)); begin if (not Is_Floating_Point_Type (Etype (Expr)) or else Is_Constrained (Etype (Expr))) and then Compile_Time_Known_Value (S_Lo) and then Compile_Time_Known_Value (S_Hi) and then Compile_Time_Known_Value (Hi) and then Compile_Time_Known_Value (Lo) then declare D_Lov : constant Ureal := Expr_Value_R (Lo); D_Hiv : constant Ureal := Expr_Value_R (Hi); S_Lov : Ureal; S_Hiv : Ureal; begin if Is_Real_Type (Etype (Expr)) then S_Lov := Expr_Value_R (S_Lo); S_Hiv := Expr_Value_R (S_Hi); else S_Lov := UR_From_Uint (Expr_Value (S_Lo)); S_Hiv := UR_From_Uint (Expr_Value (S_Hi)); end if; if D_Hiv > D_Lov and then S_Lov >= D_Lov and then S_Hiv <= D_Hiv then return; end if; end; end if; end; -- Otherwise rewrite the conversion as described above Conv := Convert_To (Btyp, Expr); -- If a conversion is necessary, then copy the specific flags from -- the original one and also move the Do_Overflow_Check flag since -- this new conversion is to the base type. if Nkind (Conv) = N_Type_Conversion then Set_Conversion_OK (Conv, Conversion_OK (N)); Set_Float_Truncate (Conv, Float_Truncate (N)); Set_Rounded_Result (Conv, Rounded_Result (N)); if Do_Overflow_Check (N) then Set_Do_Overflow_Check (Conv); Set_Do_Overflow_Check (N, False); end if; end if; Tnn := Make_Temporary (Loc, 'T', Conv); -- For a conversion from Float to Fixed where the bounds of the -- fixed-point type are static, we can obtain a more accurate -- fixed-point value by converting the result of the floating- -- point expression to an appropriate integer type, and then -- performing an unchecked conversion to the target fixed-point -- type. The range check can then use the corresponding integer -- value of the bounds instead of requiring further conversions. -- This preserves the identity: -- Fix_Val = Fixed_Type (Float_Type (Fix_Val)) -- which used to fail when Fix_Val was a bound of the type and -- the 'Small was not a representable number. -- This transformation requires an integer type large enough to -- accommodate a fixed-point value. This will not be the case -- in systems where Duration is larger than Long_Integer. if Is_Ordinary_Fixed_Point_Type (Target_Type) and then Is_Floating_Point_Type (Etype (Expr)) and then RM_Size (Btyp) <= RM_Size (Standard_Long_Integer) and then Nkind (Lo) = N_Real_Literal and then Nkind (Hi) = N_Real_Literal then declare Expr_Id : constant Entity_Id := Make_Temporary (Loc, 'T', Conv); Int_Type : Entity_Id; begin -- Find an integer type of the appropriate size to perform an -- unchecked conversion to the target fixed-point type. if RM_Size (Btyp) > RM_Size (Standard_Integer) then Int_Type := Standard_Long_Integer; elsif RM_Size (Btyp) > RM_Size (Standard_Short_Integer) then Int_Type := Standard_Integer; else Int_Type := Standard_Short_Integer; end if; -- Generate a temporary with the integer value. Required in the -- CCG compiler to ensure that run-time checks reference this -- integer expression (instead of the resulting fixed-point -- value because fixed-point values are handled by means of -- unsigned integer types). Insert_Action (N, Make_Object_Declaration (Loc, Defining_Identifier => Expr_Id, Object_Definition => New_Occurrence_Of (Int_Type, Loc), Constant_Present => True, Expression => Convert_To (Int_Type, Expression (Conv)))); -- Create integer objects for range checking of result. Lo_Arg := Unchecked_Convert_To (Int_Type, New_Occurrence_Of (Expr_Id, Loc)); Lo_Val := Make_Integer_Literal (Loc, Corresponding_Integer_Value (Lo)); Hi_Arg := Unchecked_Convert_To (Int_Type, New_Occurrence_Of (Expr_Id, Loc)); Hi_Val := Make_Integer_Literal (Loc, Corresponding_Integer_Value (Hi)); -- Rewrite conversion as an integer conversion of the -- original floating-point expression, followed by an -- unchecked conversion to the target fixed-point type. Conv := Make_Unchecked_Type_Conversion (Loc, Subtype_Mark => New_Occurrence_Of (Target_Type, Loc), Expression => New_Occurrence_Of (Expr_Id, Loc)); end; -- All other conversions else Lo_Arg := New_Occurrence_Of (Tnn, Loc); Lo_Val := Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Target_Type, Loc), Attribute_Name => Name_First); Hi_Arg := New_Occurrence_Of (Tnn, Loc); Hi_Val := Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Target_Type, Loc), Attribute_Name => Name_Last); end if; -- Build code for range checking. Note that checks are suppressed -- here since we don't want a recursive range check popping up. Insert_Actions (N, New_List ( Make_Object_Declaration (Loc, Defining_Identifier => Tnn, Object_Definition => New_Occurrence_Of (Btyp, Loc), Constant_Present => True, Expression => Conv), Make_Raise_Constraint_Error (Loc, Condition => Make_Or_Else (Loc, Left_Opnd => Make_Op_Lt (Loc, Left_Opnd => Lo_Arg, Right_Opnd => Lo_Val), Right_Opnd => Make_Op_Gt (Loc, Left_Opnd => Hi_Arg, Right_Opnd => Hi_Val)), Reason => CE_Range_Check_Failed)), Suppress => All_Checks); Rewrite (Expr, New_Occurrence_Of (Tnn, Loc)); end Real_Range_Check; ----------------------------- -- Has_Extra_Accessibility -- ----------------------------- -- Returns true for a formal of an anonymous access type or for an Ada -- 2012-style stand-alone object of an anonymous access type. function Has_Extra_Accessibility (Id : Entity_Id) return Boolean is begin if Is_Formal (Id) or else Ekind_In (Id, E_Constant, E_Variable) then return Present (Effective_Extra_Accessibility (Id)); else return False; end if; end Has_Extra_Accessibility; -- Start of processing for Expand_N_Type_Conversion begin -- First remove check marks put by the semantic analysis on the type -- conversion between array types. We need these checks, and they will -- be generated by this expansion routine, but we do not depend on these -- flags being set, and since we do intend to expand the checks in the -- front end, we don't want them on the tree passed to the back end. if Is_Array_Type (Target_Type) then if Is_Constrained (Target_Type) then Set_Do_Length_Check (N, False); else Set_Do_Range_Check (Operand, False); end if; end if; -- Nothing at all to do if conversion is to the identical type so remove -- the conversion completely, it is useless, except that it may carry -- an Assignment_OK attribute, which must be propagated to the operand. if Operand_Type = Target_Type then if Assignment_OK (N) then Set_Assignment_OK (Operand); end if; Rewrite (N, Relocate_Node (Operand)); goto Done; end if; -- Nothing to do if this is the second argument of read. This is a -- "backwards" conversion that will be handled by the specialized code -- in attribute processing. if Nkind (Parent (N)) = N_Attribute_Reference and then Attribute_Name (Parent (N)) = Name_Read and then Next (First (Expressions (Parent (N)))) = N then goto Done; end if; -- Check for case of converting to a type that has an invariant -- associated with it. This requires an invariant check. We insert -- a call: -- invariant_check (typ (expr)) -- in the code, after removing side effects from the expression. -- This is clearer than replacing the conversion into an expression -- with actions, because the context may impose additional actions -- (tag checks, membership tests, etc.) that conflict with this -- rewriting (used previously). -- Note: the Comes_From_Source check, and then the resetting of this -- flag prevents what would otherwise be an infinite recursion. if Has_Invariants (Target_Type) and then Present (Invariant_Procedure (Target_Type)) and then Comes_From_Source (N) then Set_Comes_From_Source (N, False); Remove_Side_Effects (N); Insert_Action (N, Make_Invariant_Call (Duplicate_Subexpr (N))); goto Done; end if; -- Here if we may need to expand conversion -- If the operand of the type conversion is an arithmetic operation on -- signed integers, and the based type of the signed integer type in -- question is smaller than Standard.Integer, we promote both of the -- operands to type Integer. -- For example, if we have -- target-type (opnd1 + opnd2) -- and opnd1 and opnd2 are of type short integer, then we rewrite -- this as: -- target-type (integer(opnd1) + integer(opnd2)) -- We do this because we are always allowed to compute in a larger type -- if we do the right thing with the result, and in this case we are -- going to do a conversion which will do an appropriate check to make -- sure that things are in range of the target type in any case. This -- avoids some unnecessary intermediate overflows. -- We might consider a similar transformation in the case where the -- target is a real type or a 64-bit integer type, and the operand -- is an arithmetic operation using a 32-bit integer type. However, -- we do not bother with this case, because it could cause significant -- inefficiencies on 32-bit machines. On a 64-bit machine it would be -- much cheaper, but we don't want different behavior on 32-bit and -- 64-bit machines. Note that the exclusion of the 64-bit case also -- handles the configurable run-time cases where 64-bit arithmetic -- may simply be unavailable. -- Note: this circuit is partially redundant with respect to the circuit -- in Checks.Apply_Arithmetic_Overflow_Check, but we catch more cases in -- the processing here. Also we still need the Checks circuit, since we -- have to be sure not to generate junk overflow checks in the first -- place, since it would be trick to remove them here. if Integer_Promotion_Possible (N) then -- All conditions met, go ahead with transformation declare Opnd : Node_Id; L, R : Node_Id; begin Opnd := New_Op_Node (Nkind (Operand), Loc); R := Convert_To (Standard_Integer, Right_Opnd (Operand)); Set_Right_Opnd (Opnd, R); if Nkind (Operand) in N_Binary_Op then L := Convert_To (Standard_Integer, Left_Opnd (Operand)); Set_Left_Opnd (Opnd, L); end if; Rewrite (N, Make_Type_Conversion (Loc, Subtype_Mark => Relocate_Node (Subtype_Mark (N)), Expression => Opnd)); Analyze_And_Resolve (N, Target_Type); goto Done; end; end if; -- Do validity check if validity checking operands if Validity_Checks_On and Validity_Check_Operands then Ensure_Valid (Operand); end if; -- Special case of converting from non-standard boolean type if Is_Boolean_Type (Operand_Type) and then (Nonzero_Is_True (Operand_Type)) then Adjust_Condition (Operand); Set_Etype (Operand, Standard_Boolean); Operand_Type := Standard_Boolean; end if; -- Case of converting to an access type if Is_Access_Type (Target_Type) then -- In terms of accessibility rules, an anonymous access discriminant -- is not considered separate from its parent object. if Nkind (Operand) = N_Selected_Component and then Ekind (Entity (Selector_Name (Operand))) = E_Discriminant and then Ekind (Operand_Type) = E_Anonymous_Access_Type then Operand_Acc := Original_Node (Prefix (Operand)); end if; -- If this type conversion was internally generated by the front end -- to displace the pointer to the object to reference an interface -- type and the original node was an Unrestricted_Access attribute, -- then skip applying accessibility checks (because, according to the -- GNAT Reference Manual, this attribute is similar to 'Access except -- that all accessibility and aliased view checks are omitted). if not Comes_From_Source (N) and then Is_Interface (Designated_Type (Target_Type)) and then Nkind (Original_Node (N)) = N_Attribute_Reference and then Attribute_Name (Original_Node (N)) = Name_Unrestricted_Access then null; -- Apply an accessibility check when the conversion operand is an -- access parameter (or a renaming thereof), unless conversion was -- expanded from an Unchecked_ or Unrestricted_Access attribute, -- or for the actual of a class-wide interface parameter. Note that -- other checks may still need to be applied below (such as tagged -- type checks). elsif Is_Entity_Name (Operand_Acc) and then Has_Extra_Accessibility (Entity (Operand_Acc)) and then Ekind (Etype (Operand_Acc)) = E_Anonymous_Access_Type and then (Nkind (Original_Node (N)) /= N_Attribute_Reference or else Attribute_Name (Original_Node (N)) = Name_Access) then if not Comes_From_Source (N) and then Nkind_In (Parent (N), N_Function_Call, N_Parameter_Association, N_Procedure_Call_Statement) and then Is_Interface (Designated_Type (Target_Type)) and then Is_Class_Wide_Type (Designated_Type (Target_Type)) then null; else Apply_Accessibility_Check (Operand_Acc, Target_Type, Insert_Node => Operand); end if; -- If the level of the operand type is statically deeper than the -- level of the target type, then force Program_Error. Note that this -- can only occur for cases where the attribute is within the body of -- an instantiation, otherwise the conversion will already have been -- rejected as illegal. -- Note: warnings are issued by the analyzer for the instance cases elsif In_Instance_Body -- The case where the target type is an anonymous access type of -- a discriminant is excluded, because the level of such a type -- depends on the context and currently the level returned for such -- types is zero, resulting in warnings about check failures -- in certain legal cases involving class-wide interfaces as the -- designated type (some cases, such as return statements, are -- checked at run time, but not clear if these are handled right -- in general, see 3.10.2(12/2-12.5/3) ???). and then not (Ekind (Target_Type) = E_Anonymous_Access_Type and then Present (Associated_Node_For_Itype (Target_Type)) and then Nkind (Associated_Node_For_Itype (Target_Type)) = N_Discriminant_Specification) and then Type_Access_Level (Operand_Type) > Type_Access_Level (Target_Type) then Raise_Accessibility_Error; goto Done; -- When the operand is a selected access discriminant the check needs -- to be made against the level of the object denoted by the prefix -- of the selected name. Force Program_Error for this case as well -- (this accessibility violation can only happen if within the body -- of an instantiation). elsif In_Instance_Body and then Ekind (Operand_Type) = E_Anonymous_Access_Type and then Nkind (Operand) = N_Selected_Component and then Ekind (Entity (Selector_Name (Operand))) = E_Discriminant and then Object_Access_Level (Operand) > Type_Access_Level (Target_Type) then Raise_Accessibility_Error; goto Done; end if; end if; -- Case of conversions of tagged types and access to tagged types -- When needed, that is to say when the expression is class-wide, Add -- runtime a tag check for (strict) downward conversion by using the -- membership test, generating: -- [constraint_error when Operand not in Target_Type'Class] -- or in the access type case -- [constraint_error -- when Operand /= null -- and then Operand.all not in -- Designated_Type (Target_Type)'Class] if (Is_Access_Type (Target_Type) and then Is_Tagged_Type (Designated_Type (Target_Type))) or else Is_Tagged_Type (Target_Type) then -- Do not do any expansion in the access type case if the parent is a -- renaming, since this is an error situation which will be caught by -- Sem_Ch8, and the expansion can interfere with this error check. if Is_Access_Type (Target_Type) and then Is_Renamed_Object (N) then goto Done; end if; -- Otherwise, proceed with processing tagged conversion Tagged_Conversion : declare Actual_Op_Typ : Entity_Id; Actual_Targ_Typ : Entity_Id; Root_Op_Typ : Entity_Id; procedure Make_Tag_Check (Targ_Typ : Entity_Id); -- Create a membership check to test whether Operand is a member -- of Targ_Typ. If the original Target_Type is an access, include -- a test for null value. The check is inserted at N. -------------------- -- Make_Tag_Check -- -------------------- procedure Make_Tag_Check (Targ_Typ : Entity_Id) is Cond : Node_Id; begin -- Generate: -- [Constraint_Error -- when Operand /= null -- and then Operand.all not in Targ_Typ] if Is_Access_Type (Target_Type) then Cond := Make_And_Then (Loc, Left_Opnd => Make_Op_Ne (Loc, Left_Opnd => Duplicate_Subexpr_No_Checks (Operand), Right_Opnd => Make_Null (Loc)), Right_Opnd => Make_Not_In (Loc, Left_Opnd => Make_Explicit_Dereference (Loc, Prefix => Duplicate_Subexpr_No_Checks (Operand)), Right_Opnd => New_Occurrence_Of (Targ_Typ, Loc))); -- Generate: -- [Constraint_Error when Operand not in Targ_Typ] else Cond := Make_Not_In (Loc, Left_Opnd => Duplicate_Subexpr_No_Checks (Operand), Right_Opnd => New_Occurrence_Of (Targ_Typ, Loc)); end if; Insert_Action (N, Make_Raise_Constraint_Error (Loc, Condition => Cond, Reason => CE_Tag_Check_Failed), Suppress => All_Checks); end Make_Tag_Check; -- Start of processing for Tagged_Conversion begin -- Handle entities from the limited view if Is_Access_Type (Operand_Type) then Actual_Op_Typ := Available_View (Designated_Type (Operand_Type)); else Actual_Op_Typ := Operand_Type; end if; if Is_Access_Type (Target_Type) then Actual_Targ_Typ := Available_View (Designated_Type (Target_Type)); else Actual_Targ_Typ := Target_Type; end if; Root_Op_Typ := Root_Type (Actual_Op_Typ); -- Ada 2005 (AI-251): Handle interface type conversion if Is_Interface (Actual_Op_Typ) or else Is_Interface (Actual_Targ_Typ) then Expand_Interface_Conversion (N); goto Done; end if; -- Create a runtime tag check for a downward CW type conversion if Is_Class_Wide_Type (Actual_Op_Typ) and then Actual_Op_Typ /= Actual_Targ_Typ and then Root_Op_Typ /= Actual_Targ_Typ and then Is_Ancestor (Root_Op_Typ, Actual_Targ_Typ, Use_Full_View => True) and then not Tag_Checks_Suppressed (Actual_Targ_Typ) then declare Conv : Node_Id; begin Make_Tag_Check (Class_Wide_Type (Actual_Targ_Typ)); Conv := Make_Unchecked_Type_Conversion (Loc, Subtype_Mark => New_Occurrence_Of (Target_Type, Loc), Expression => Relocate_Node (Expression (N))); Rewrite (N, Conv); Analyze_And_Resolve (N, Target_Type); end; end if; end Tagged_Conversion; -- Case of other access type conversions elsif Is_Access_Type (Target_Type) then Apply_Constraint_Check (Operand, Target_Type); -- Case of conversions from a fixed-point type -- These conversions require special expansion and processing, found in -- the Exp_Fixd package. We ignore cases where Conversion_OK is set, -- since from a semantic point of view, these are simple integer -- conversions, which do not need further processing. elsif Is_Fixed_Point_Type (Operand_Type) and then not Conversion_OK (N) then -- We should never see universal fixed at this case, since the -- expansion of the constituent divide or multiply should have -- eliminated the explicit mention of universal fixed. pragma Assert (Operand_Type /= Universal_Fixed); -- Check for special case of the conversion to universal real that -- occurs as a result of the use of a round attribute. In this case, -- the real type for the conversion is taken from the target type of -- the Round attribute and the result must be marked as rounded. if Target_Type = Universal_Real and then Nkind (Parent (N)) = N_Attribute_Reference and then Attribute_Name (Parent (N)) = Name_Round then Set_Rounded_Result (N); Set_Etype (N, Etype (Parent (N))); Target_Type := Etype (N); end if; if Is_Fixed_Point_Type (Target_Type) then Expand_Convert_Fixed_To_Fixed (N); Real_Range_Check; elsif Is_Integer_Type (Target_Type) then Expand_Convert_Fixed_To_Integer (N); Discrete_Range_Check; else pragma Assert (Is_Floating_Point_Type (Target_Type)); Expand_Convert_Fixed_To_Float (N); Real_Range_Check; end if; -- Case of conversions to a fixed-point type -- These conversions require special expansion and processing, found in -- the Exp_Fixd package. Again, ignore cases where Conversion_OK is set, -- since from a semantic point of view, these are simple integer -- conversions, which do not need further processing. elsif Is_Fixed_Point_Type (Target_Type) and then not Conversion_OK (N) then if Is_Integer_Type (Operand_Type) then Expand_Convert_Integer_To_Fixed (N); Real_Range_Check; else pragma Assert (Is_Floating_Point_Type (Operand_Type)); Expand_Convert_Float_To_Fixed (N); Real_Range_Check; end if; -- Case of array conversions -- Expansion of array conversions, add required length/range checks but -- only do this if there is no change of representation. For handling of -- this case, see Handle_Changed_Representation. elsif Is_Array_Type (Target_Type) then if Is_Constrained (Target_Type) then Apply_Length_Check (Operand, Target_Type); else Apply_Range_Check (Operand, Target_Type); end if; Handle_Changed_Representation; -- Case of conversions of discriminated types -- Add required discriminant checks if target is constrained. Again this -- change is skipped if we have a change of representation. elsif Has_Discriminants (Target_Type) and then Is_Constrained (Target_Type) then Apply_Discriminant_Check (Operand, Target_Type); Handle_Changed_Representation; -- Case of all other record conversions. The only processing required -- is to check for a change of representation requiring the special -- assignment processing. elsif Is_Record_Type (Target_Type) then -- Ada 2005 (AI-216): Program_Error is raised when converting from -- a derived Unchecked_Union type to an unconstrained type that is -- not Unchecked_Union if the operand lacks inferable discriminants. if Is_Derived_Type (Operand_Type) and then Is_Unchecked_Union (Base_Type (Operand_Type)) and then not Is_Constrained (Target_Type) and then not Is_Unchecked_Union (Base_Type (Target_Type)) and then not Has_Inferable_Discriminants (Operand) then -- To prevent Gigi from generating illegal code, we generate a -- Program_Error node, but we give it the target type of the -- conversion (is this requirement documented somewhere ???) declare PE : constant Node_Id := Make_Raise_Program_Error (Loc, Reason => PE_Unchecked_Union_Restriction); begin Set_Etype (PE, Target_Type); Rewrite (N, PE); end; else Handle_Changed_Representation; end if; -- Case of conversions of enumeration types elsif Is_Enumeration_Type (Target_Type) then -- Special processing is required if there is a change of -- representation (from enumeration representation clauses). if not Same_Representation (Target_Type, Operand_Type) and then not Conversion_OK (N) then -- Convert: x(y) to x'val (ytyp'pos (y)) Rewrite (N, Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Target_Type, Loc), Attribute_Name => Name_Val, Expressions => New_List ( Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Operand_Type, Loc), Attribute_Name => Name_Pos, Expressions => New_List (Operand))))); Analyze_And_Resolve (N, Target_Type); end if; end if; -- At this stage, either the conversion node has been transformed into -- some other equivalent expression, or left as a conversion that can be -- handled by Gigi, in the following cases: -- Conversions with no change of representation or type -- Numeric conversions involving integer, floating- and fixed-point -- values. Fixed-point values are allowed only if Conversion_OK is -- set, i.e. if the fixed-point values are to be treated as integers. -- No other conversions should be passed to Gigi -- Check: are these rules stated in sinfo??? if so, why restate here??? -- The only remaining step is to generate a range check if we still have -- a type conversion at this stage and Do_Range_Check is set. Note that -- we need to deal with at most 8 out of the 9 possible cases of numeric -- conversions here, because the float-to-integer case is entirely dealt -- with by Apply_Float_Conversion_Check. if Nkind (N) = N_Type_Conversion and then Do_Range_Check (Expression (N)) then -- Float-to-float conversions if Is_Floating_Point_Type (Target_Type) and then Is_Floating_Point_Type (Etype (Expression (N))) then -- Reset overflow flag, since the range check will include -- dealing with possible overflow, and generate the check. Set_Do_Overflow_Check (N, False); Generate_Range_Check (Expression (N), Target_Type, CE_Range_Check_Failed); -- Discrete-to-discrete conversions or fixed-point-to-discrete -- conversions when Conversion_OK is set. elsif Is_Discrete_Type (Target_Type) and then (Is_Discrete_Type (Etype (Expression (N))) or else (Is_Fixed_Point_Type (Etype (Expression (N))) and then Conversion_OK (N))) then -- If Address is either a source type or target type, -- suppress range check to avoid typing anomalies when -- it is a visible integer type. if Is_Descendant_Of_Address (Etype (Expression (N))) or else Is_Descendant_Of_Address (Target_Type) then Set_Do_Range_Check (Expression (N), False); else Discrete_Range_Check; end if; -- Conversions to floating- or fixed-point when Conversion_OK is set elsif Is_Floating_Point_Type (Target_Type) or else (Is_Fixed_Point_Type (Target_Type) and then Conversion_OK (N)) then Real_Range_Check; end if; end if; -- Here at end of processing <> -- Apply predicate check if required. Note that we can't just call -- Apply_Predicate_Check here, because the type looks right after -- the conversion and it would omit the check. The Comes_From_Source -- guard is necessary to prevent infinite recursions when we generate -- internal conversions for the purpose of checking predicates. if Present (Predicate_Function (Target_Type)) and then not Predicates_Ignored (Target_Type) and then Target_Type /= Operand_Type and then Comes_From_Source (N) then declare New_Expr : constant Node_Id := Duplicate_Subexpr (N); begin -- Avoid infinite recursion on the subsequent expansion of -- of the copy of the original type conversion. When needed, -- a range check has already been applied to the expression. Set_Comes_From_Source (New_Expr, False); Insert_Action (N, Make_Predicate_Check (Target_Type, New_Expr), Suppress => Range_Check); end; end if; end Expand_N_Type_Conversion; ----------------------------------- -- Expand_N_Unchecked_Expression -- ----------------------------------- -- Remove the unchecked expression node from the tree. Its job was simply -- to make sure that its constituent expression was handled with checks -- off, and now that is done, we can remove it from the tree, and indeed -- must, since Gigi does not expect to see these nodes. procedure Expand_N_Unchecked_Expression (N : Node_Id) is Exp : constant Node_Id := Expression (N); begin Set_Assignment_OK (Exp, Assignment_OK (N) or else Assignment_OK (Exp)); Rewrite (N, Exp); end Expand_N_Unchecked_Expression; ---------------------------------------- -- Expand_N_Unchecked_Type_Conversion -- ---------------------------------------- -- If this cannot be handled by Gigi and we haven't already made a -- temporary for it, do it now. procedure Expand_N_Unchecked_Type_Conversion (N : Node_Id) is Target_Type : constant Entity_Id := Etype (N); Operand : constant Node_Id := Expression (N); Operand_Type : constant Entity_Id := Etype (Operand); begin -- Nothing at all to do if conversion is to the identical type so remove -- the conversion completely, it is useless, except that it may carry -- an Assignment_OK indication which must be propagated to the operand. if Operand_Type = Target_Type then -- Code duplicates Expand_N_Unchecked_Expression above, factor??? if Assignment_OK (N) then Set_Assignment_OK (Operand); end if; Rewrite (N, Relocate_Node (Operand)); return; end if; -- If we have a conversion of a compile time known value to a target -- type and the value is in range of the target type, then we can simply -- replace the construct by an integer literal of the correct type. We -- only apply this to discrete types being converted. Possibly it may -- apply in other cases, but it is too much trouble to worry about. -- Note that we do not do this transformation if the Kill_Range_Check -- flag is set, since then the value may be outside the expected range. -- This happens in the Normalize_Scalars case. -- We also skip this if either the target or operand type is biased -- because in this case, the unchecked conversion is supposed to -- preserve the bit pattern, not the integer value. if Is_Integer_Type (Target_Type) and then not Has_Biased_Representation (Target_Type) and then Is_Discrete_Type (Operand_Type) and then not Has_Biased_Representation (Operand_Type) and then Compile_Time_Known_Value (Operand) and then not Kill_Range_Check (N) then declare Val : constant Uint := Expr_Rep_Value (Operand); begin if Compile_Time_Known_Value (Type_Low_Bound (Target_Type)) and then Compile_Time_Known_Value (Type_High_Bound (Target_Type)) and then Val >= Expr_Value (Type_Low_Bound (Target_Type)) and then Val <= Expr_Value (Type_High_Bound (Target_Type)) then Rewrite (N, Make_Integer_Literal (Sloc (N), Val)); -- If Address is the target type, just set the type to avoid a -- spurious type error on the literal when Address is a visible -- integer type. if Is_Descendant_Of_Address (Target_Type) then Set_Etype (N, Target_Type); else Analyze_And_Resolve (N, Target_Type); end if; return; end if; end; end if; -- Generate an extra temporary for cases unsupported by the C backend if Modify_Tree_For_C then declare Source : constant Node_Id := Unqual_Conv (Expression (N)); Source_Typ : Entity_Id := Get_Full_View (Etype (Source)); begin if Is_Packed_Array (Source_Typ) then Source_Typ := Packed_Array_Impl_Type (Source_Typ); end if; if Nkind (Source) = N_Function_Call and then (Is_Composite_Type (Etype (Source)) or else Is_Composite_Type (Target_Type)) then Force_Evaluation (Source); end if; end; end if; -- Nothing to do if conversion is safe if Safe_Unchecked_Type_Conversion (N) then return; end if; -- Otherwise force evaluation unless Assignment_OK flag is set (this -- flag indicates ??? More comments needed here) if Assignment_OK (N) then null; else Force_Evaluation (N); end if; end Expand_N_Unchecked_Type_Conversion; ---------------------------- -- Expand_Record_Equality -- ---------------------------- -- For non-variant records, Equality is expanded when needed into: -- and then Lhs.Discr1 = Rhs.Discr1 -- and then ... -- and then Lhs.Discrn = Rhs.Discrn -- and then Lhs.Cmp1 = Rhs.Cmp1 -- and then ... -- and then Lhs.Cmpn = Rhs.Cmpn -- The expression is folded by the back end for adjacent fields. This -- function is called for tagged record in only one occasion: for imple- -- menting predefined primitive equality (see Predefined_Primitives_Bodies) -- otherwise the primitive "=" is used directly. function Expand_Record_Equality (Nod : Node_Id; Typ : Entity_Id; Lhs : Node_Id; Rhs : Node_Id; Bodies : List_Id) return Node_Id is Loc : constant Source_Ptr := Sloc (Nod); Result : Node_Id; C : Entity_Id; First_Time : Boolean := True; function Element_To_Compare (C : Entity_Id) return Entity_Id; -- Return the next discriminant or component to compare, starting with -- C, skipping inherited components. ------------------------ -- Element_To_Compare -- ------------------------ function Element_To_Compare (C : Entity_Id) return Entity_Id is Comp : Entity_Id; begin Comp := C; loop -- Exit loop when the next element to be compared is found, or -- there is no more such element. exit when No (Comp); exit when Ekind_In (Comp, E_Discriminant, E_Component) and then not ( -- Skip inherited components -- Note: for a tagged type, we always generate the "=" primitive -- for the base type (not on the first subtype), so the test for -- Comp /= Original_Record_Component (Comp) is True for -- inherited components only. (Is_Tagged_Type (Typ) and then Comp /= Original_Record_Component (Comp)) -- Skip _Tag or else Chars (Comp) = Name_uTag -- Skip interface elements (secondary tags???) or else Is_Interface (Etype (Comp))); Next_Entity (Comp); end loop; return Comp; end Element_To_Compare; -- Start of processing for Expand_Record_Equality begin -- Generates the following code: (assuming that Typ has one Discr and -- component C2 is also a record) -- Lhs.Discr1 = Rhs.Discr1 -- and then Lhs.C1 = Rhs.C1 -- and then Lhs.C2.C1=Rhs.C2.C1 and then ... Lhs.C2.Cn=Rhs.C2.Cn -- and then ... -- and then Lhs.Cmpn = Rhs.Cmpn Result := New_Occurrence_Of (Standard_True, Loc); C := Element_To_Compare (First_Entity (Typ)); while Present (C) loop declare New_Lhs : Node_Id; New_Rhs : Node_Id; Check : Node_Id; begin if First_Time then New_Lhs := Lhs; New_Rhs := Rhs; else New_Lhs := New_Copy_Tree (Lhs); New_Rhs := New_Copy_Tree (Rhs); end if; Check := Expand_Composite_Equality (Nod, Etype (C), Lhs => Make_Selected_Component (Loc, Prefix => New_Lhs, Selector_Name => New_Occurrence_Of (C, Loc)), Rhs => Make_Selected_Component (Loc, Prefix => New_Rhs, Selector_Name => New_Occurrence_Of (C, Loc)), Bodies => Bodies); -- If some (sub)component is an unchecked_union, the whole -- operation will raise program error. if Nkind (Check) = N_Raise_Program_Error then Result := Check; Set_Etype (Result, Standard_Boolean); exit; else if First_Time then Result := Check; -- Generate logical "and" for CodePeer to simplify the -- generated code and analysis. elsif CodePeer_Mode then Result := Make_Op_And (Loc, Left_Opnd => Result, Right_Opnd => Check); else Result := Make_And_Then (Loc, Left_Opnd => Result, Right_Opnd => Check); end if; end if; end; First_Time := False; C := Element_To_Compare (Next_Entity (C)); end loop; return Result; end Expand_Record_Equality; --------------------------- -- Expand_Set_Membership -- --------------------------- procedure Expand_Set_Membership (N : Node_Id) is Lop : constant Node_Id := Left_Opnd (N); Alt : Node_Id; Res : Node_Id; function Make_Cond (Alt : Node_Id) return Node_Id; -- If the alternative is a subtype mark, create a simple membership -- test. Otherwise create an equality test for it. --------------- -- Make_Cond -- --------------- function Make_Cond (Alt : Node_Id) return Node_Id is Cond : Node_Id; L : constant Node_Id := New_Copy_Tree (Lop); R : constant Node_Id := Relocate_Node (Alt); begin if (Is_Entity_Name (Alt) and then Is_Type (Entity (Alt))) or else Nkind (Alt) = N_Range then Cond := Make_In (Sloc (Alt), Left_Opnd => L, Right_Opnd => R); else Cond := Make_Op_Eq (Sloc (Alt), Left_Opnd => L, Right_Opnd => R); if Is_Record_Or_Limited_Type (Etype (Alt)) then -- We reset the Entity in order to use the primitive equality -- of the type, as per RM 4.5.2 (28.1/4). Set_Entity (Cond, Empty); end if; end if; return Cond; end Make_Cond; -- Start of processing for Expand_Set_Membership begin Remove_Side_Effects (Lop); Alt := Last (Alternatives (N)); Res := Make_Cond (Alt); Prev (Alt); while Present (Alt) loop Res := Make_Or_Else (Sloc (Alt), Left_Opnd => Make_Cond (Alt), Right_Opnd => Res); Prev (Alt); end loop; Rewrite (N, Res); Analyze_And_Resolve (N, Standard_Boolean); end Expand_Set_Membership; ----------------------------------- -- Expand_Short_Circuit_Operator -- ----------------------------------- -- Deal with special expansion if actions are present for the right operand -- and deal with optimizing case of arguments being True or False. We also -- deal with the special case of non-standard boolean values. procedure Expand_Short_Circuit_Operator (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); Left : constant Node_Id := Left_Opnd (N); Right : constant Node_Id := Right_Opnd (N); LocR : constant Source_Ptr := Sloc (Right); Actlist : List_Id; Shortcut_Value : constant Boolean := Nkind (N) = N_Or_Else; Shortcut_Ent : constant Entity_Id := Boolean_Literals (Shortcut_Value); -- If Left = Shortcut_Value then Right need not be evaluated function Make_Test_Expr (Opnd : Node_Id) return Node_Id; -- For Opnd a boolean expression, return a Boolean expression equivalent -- to Opnd /= Shortcut_Value. function Useful (Actions : List_Id) return Boolean; -- Return True if Actions is not empty and contains useful nodes to -- process. -------------------- -- Make_Test_Expr -- -------------------- function Make_Test_Expr (Opnd : Node_Id) return Node_Id is begin if Shortcut_Value then return Make_Op_Not (Sloc (Opnd), Opnd); else return Opnd; end if; end Make_Test_Expr; ------------ -- Useful -- ------------ function Useful (Actions : List_Id) return Boolean is L : Node_Id; begin if Present (Actions) then L := First (Actions); -- For now "useful" means not N_Variable_Reference_Marker. -- Consider stripping other nodes in the future. while Present (L) loop if Nkind (L) /= N_Variable_Reference_Marker then return True; end if; Next (L); end loop; end if; return False; end Useful; -- Local variables Op_Var : Entity_Id; -- Entity for a temporary variable holding the value of the operator, -- used for expansion in the case where actions are present. -- Start of processing for Expand_Short_Circuit_Operator begin -- Deal with non-standard booleans if Is_Boolean_Type (Typ) then Adjust_Condition (Left); Adjust_Condition (Right); Set_Etype (N, Standard_Boolean); end if; -- Check for cases where left argument is known to be True or False if Compile_Time_Known_Value (Left) then -- Mark SCO for left condition as compile time known if Generate_SCO and then Comes_From_Source (Left) then Set_SCO_Condition (Left, Expr_Value_E (Left) = Standard_True); end if; -- Rewrite True AND THEN Right / False OR ELSE Right to Right. -- Any actions associated with Right will be executed unconditionally -- and can thus be inserted into the tree unconditionally. if Expr_Value_E (Left) /= Shortcut_Ent then if Present (Actions (N)) then Insert_Actions (N, Actions (N)); end if; Rewrite (N, Right); -- Rewrite False AND THEN Right / True OR ELSE Right to Left. -- In this case we can forget the actions associated with Right, -- since they will never be executed. else Kill_Dead_Code (Right); Kill_Dead_Code (Actions (N)); Rewrite (N, New_Occurrence_Of (Shortcut_Ent, Loc)); end if; Adjust_Result_Type (N, Typ); return; end if; -- If Actions are present for the right operand, we have to do some -- special processing. We can't just let these actions filter back into -- code preceding the short circuit (which is what would have happened -- if we had not trapped them in the short-circuit form), since they -- must only be executed if the right operand of the short circuit is -- executed and not otherwise. if Useful (Actions (N)) then Actlist := Actions (N); -- The old approach is to expand: -- left AND THEN right -- into -- C : Boolean := False; -- IF left THEN -- Actions; -- IF right THEN -- C := True; -- END IF; -- END IF; -- and finally rewrite the operator into a reference to C. Similarly -- for left OR ELSE right, with negated values. Note that this -- rewrite causes some difficulties for coverage analysis because -- of the introduction of the new variable C, which obscures the -- structure of the test. -- We use this "old approach" if Minimize_Expression_With_Actions -- is True. if Minimize_Expression_With_Actions then Op_Var := Make_Temporary (Loc, 'C', Related_Node => N); Insert_Action (N, Make_Object_Declaration (Loc, Defining_Identifier => Op_Var, Object_Definition => New_Occurrence_Of (Standard_Boolean, Loc), Expression => New_Occurrence_Of (Shortcut_Ent, Loc))); Append_To (Actlist, Make_Implicit_If_Statement (Right, Condition => Make_Test_Expr (Right), Then_Statements => New_List ( Make_Assignment_Statement (LocR, Name => New_Occurrence_Of (Op_Var, LocR), Expression => New_Occurrence_Of (Boolean_Literals (not Shortcut_Value), LocR))))); Insert_Action (N, Make_Implicit_If_Statement (Left, Condition => Make_Test_Expr (Left), Then_Statements => Actlist)); Rewrite (N, New_Occurrence_Of (Op_Var, Loc)); Analyze_And_Resolve (N, Standard_Boolean); -- The new approach (the default) is to use an -- Expression_With_Actions node for the right operand of the -- short-circuit form. Note that this solves the traceability -- problems for coverage analysis. else Rewrite (Right, Make_Expression_With_Actions (LocR, Expression => Relocate_Node (Right), Actions => Actlist)); Set_Actions (N, No_List); Analyze_And_Resolve (Right, Standard_Boolean); end if; Adjust_Result_Type (N, Typ); return; end if; -- No actions present, check for cases of right argument True/False if Compile_Time_Known_Value (Right) then -- Mark SCO for left condition as compile time known if Generate_SCO and then Comes_From_Source (Right) then Set_SCO_Condition (Right, Expr_Value_E (Right) = Standard_True); end if; -- Change (Left and then True), (Left or else False) to Left. Note -- that we know there are no actions associated with the right -- operand, since we just checked for this case above. if Expr_Value_E (Right) /= Shortcut_Ent then Rewrite (N, Left); -- Change (Left and then False), (Left or else True) to Right, -- making sure to preserve any side effects associated with the Left -- operand. else Remove_Side_Effects (Left); Rewrite (N, New_Occurrence_Of (Shortcut_Ent, Loc)); end if; end if; Adjust_Result_Type (N, Typ); end Expand_Short_Circuit_Operator; ------------------------------------ -- Fixup_Universal_Fixed_Operation -- ------------------------------------- procedure Fixup_Universal_Fixed_Operation (N : Node_Id) is Conv : constant Node_Id := Parent (N); begin -- We must have a type conversion immediately above us pragma Assert (Nkind (Conv) = N_Type_Conversion); -- Normally the type conversion gives our target type. The exception -- occurs in the case of the Round attribute, where the conversion -- will be to universal real, and our real type comes from the Round -- attribute (as well as an indication that we must round the result) if Nkind (Parent (Conv)) = N_Attribute_Reference and then Attribute_Name (Parent (Conv)) = Name_Round then Set_Etype (N, Base_Type (Etype (Parent (Conv)))); Set_Rounded_Result (N); -- Normal case where type comes from conversion above us else Set_Etype (N, Base_Type (Etype (Conv))); end if; end Fixup_Universal_Fixed_Operation; --------------------------------- -- Has_Inferable_Discriminants -- --------------------------------- function Has_Inferable_Discriminants (N : Node_Id) return Boolean is function Prefix_Is_Formal_Parameter (N : Node_Id) return Boolean; -- Determines whether the left-most prefix of a selected component is a -- formal parameter in a subprogram. Assumes N is a selected component. -------------------------------- -- Prefix_Is_Formal_Parameter -- -------------------------------- function Prefix_Is_Formal_Parameter (N : Node_Id) return Boolean is Sel_Comp : Node_Id; begin -- Move to the left-most prefix by climbing up the tree Sel_Comp := N; while Present (Parent (Sel_Comp)) and then Nkind (Parent (Sel_Comp)) = N_Selected_Component loop Sel_Comp := Parent (Sel_Comp); end loop; return Is_Formal (Entity (Prefix (Sel_Comp))); end Prefix_Is_Formal_Parameter; -- Start of processing for Has_Inferable_Discriminants begin -- For selected components, the subtype of the selector must be a -- constrained Unchecked_Union. If the component is subject to a -- per-object constraint, then the enclosing object must have inferable -- discriminants. if Nkind (N) = N_Selected_Component then if Has_Per_Object_Constraint (Entity (Selector_Name (N))) then -- A small hack. If we have a per-object constrained selected -- component of a formal parameter, return True since we do not -- know the actual parameter association yet. if Prefix_Is_Formal_Parameter (N) then return True; -- Otherwise, check the enclosing object and the selector else return Has_Inferable_Discriminants (Prefix (N)) and then Has_Inferable_Discriminants (Selector_Name (N)); end if; -- The call to Has_Inferable_Discriminants will determine whether -- the selector has a constrained Unchecked_Union nominal type. else return Has_Inferable_Discriminants (Selector_Name (N)); end if; -- A qualified expression has inferable discriminants if its subtype -- mark is a constrained Unchecked_Union subtype. elsif Nkind (N) = N_Qualified_Expression then return Is_Unchecked_Union (Etype (Subtype_Mark (N))) and then Is_Constrained (Etype (Subtype_Mark (N))); -- For all other names, it is sufficient to have a constrained -- Unchecked_Union nominal subtype. else return Is_Unchecked_Union (Base_Type (Etype (N))) and then Is_Constrained (Etype (N)); end if; end Has_Inferable_Discriminants; ------------------------------- -- Insert_Dereference_Action -- ------------------------------- procedure Insert_Dereference_Action (N : Node_Id) is function Is_Checked_Storage_Pool (P : Entity_Id) return Boolean; -- Return true if type of P is derived from Checked_Pool; ----------------------------- -- Is_Checked_Storage_Pool -- ----------------------------- function Is_Checked_Storage_Pool (P : Entity_Id) return Boolean is T : Entity_Id; begin if No (P) then return False; end if; T := Etype (P); while T /= Etype (T) loop if Is_RTE (T, RE_Checked_Pool) then return True; else T := Etype (T); end if; end loop; return False; end Is_Checked_Storage_Pool; -- Local variables Context : constant Node_Id := Parent (N); Ptr_Typ : constant Entity_Id := Etype (N); Desig_Typ : constant Entity_Id := Available_View (Designated_Type (Ptr_Typ)); Loc : constant Source_Ptr := Sloc (N); Pool : constant Entity_Id := Associated_Storage_Pool (Ptr_Typ); Addr : Entity_Id; Alig : Entity_Id; Deref : Node_Id; Size : Entity_Id; Size_Bits : Node_Id; Stmt : Node_Id; -- Start of processing for Insert_Dereference_Action begin pragma Assert (Nkind (Context) = N_Explicit_Dereference); -- Do not re-expand a dereference which has already been processed by -- this routine. if Has_Dereference_Action (Context) then return; -- Do not perform this type of expansion for internally-generated -- dereferences. elsif not Comes_From_Source (Original_Node (Context)) then return; -- A dereference action is only applicable to objects which have been -- allocated on a checked pool. elsif not Is_Checked_Storage_Pool (Pool) then return; end if; -- Extract the address of the dereferenced object. Generate: -- Addr : System.Address := 'Pool_Address; Addr := Make_Temporary (Loc, 'P'); Insert_Action (N, Make_Object_Declaration (Loc, Defining_Identifier => Addr, Object_Definition => New_Occurrence_Of (RTE (RE_Address), Loc), Expression => Make_Attribute_Reference (Loc, Prefix => Duplicate_Subexpr_Move_Checks (N), Attribute_Name => Name_Pool_Address))); -- Calculate the size of the dereferenced object. Generate: -- Size : Storage_Count := .all'Size / Storage_Unit; Deref := Make_Explicit_Dereference (Loc, Prefix => Duplicate_Subexpr_Move_Checks (N)); Set_Has_Dereference_Action (Deref); Size_Bits := Make_Attribute_Reference (Loc, Prefix => Deref, Attribute_Name => Name_Size); -- Special case of an unconstrained array: need to add descriptor size if Is_Array_Type (Desig_Typ) and then not Is_Constrained (First_Subtype (Desig_Typ)) then Size_Bits := Make_Op_Add (Loc, Left_Opnd => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (First_Subtype (Desig_Typ), Loc), Attribute_Name => Name_Descriptor_Size), Right_Opnd => Size_Bits); end if; Size := Make_Temporary (Loc, 'S'); Insert_Action (N, Make_Object_Declaration (Loc, Defining_Identifier => Size, Object_Definition => New_Occurrence_Of (RTE (RE_Storage_Count), Loc), Expression => Make_Op_Divide (Loc, Left_Opnd => Size_Bits, Right_Opnd => Make_Integer_Literal (Loc, System_Storage_Unit)))); -- Calculate the alignment of the dereferenced object. Generate: -- Alig : constant Storage_Count := .all'Alignment; Deref := Make_Explicit_Dereference (Loc, Prefix => Duplicate_Subexpr_Move_Checks (N)); Set_Has_Dereference_Action (Deref); Alig := Make_Temporary (Loc, 'A'); Insert_Action (N, Make_Object_Declaration (Loc, Defining_Identifier => Alig, Object_Definition => New_Occurrence_Of (RTE (RE_Storage_Count), Loc), Expression => Make_Attribute_Reference (Loc, Prefix => Deref, Attribute_Name => Name_Alignment))); -- A dereference of a controlled object requires special processing. The -- finalization machinery requests additional space from the underlying -- pool to allocate and hide two pointers. As a result, a checked pool -- may mark the wrong memory as valid. Since checked pools do not have -- knowledge of hidden pointers, we have to bring the two pointers back -- in view in order to restore the original state of the object. -- The address manipulation is not performed for access types that are -- subject to pragma No_Heap_Finalization because the two pointers do -- not exist in the first place. if No_Heap_Finalization (Ptr_Typ) then null; elsif Needs_Finalization (Desig_Typ) then -- Adjust the address and size of the dereferenced object. Generate: -- Adjust_Controlled_Dereference (Addr, Size, Alig); Stmt := Make_Procedure_Call_Statement (Loc, Name => New_Occurrence_Of (RTE (RE_Adjust_Controlled_Dereference), Loc), Parameter_Associations => New_List ( New_Occurrence_Of (Addr, Loc), New_Occurrence_Of (Size, Loc), New_Occurrence_Of (Alig, Loc))); -- Class-wide types complicate things because we cannot determine -- statically whether the actual object is truly controlled. We must -- generate a runtime check to detect this property. Generate: -- -- if Needs_Finalization (.all'Tag) then -- ; -- end if; if Is_Class_Wide_Type (Desig_Typ) then Deref := Make_Explicit_Dereference (Loc, Prefix => Duplicate_Subexpr_Move_Checks (N)); Set_Has_Dereference_Action (Deref); Stmt := Make_Implicit_If_Statement (N, Condition => Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (RE_Needs_Finalization), Loc), Parameter_Associations => New_List ( Make_Attribute_Reference (Loc, Prefix => Deref, Attribute_Name => Name_Tag))), Then_Statements => New_List (Stmt)); end if; Insert_Action (N, Stmt); end if; -- Generate: -- Dereference (Pool, Addr, Size, Alig); Insert_Action (N, Make_Procedure_Call_Statement (Loc, Name => New_Occurrence_Of (Find_Prim_Op (Etype (Pool), Name_Dereference), Loc), Parameter_Associations => New_List ( New_Occurrence_Of (Pool, Loc), New_Occurrence_Of (Addr, Loc), New_Occurrence_Of (Size, Loc), New_Occurrence_Of (Alig, Loc)))); -- Mark the explicit dereference as processed to avoid potential -- infinite expansion. Set_Has_Dereference_Action (Context); exception when RE_Not_Available => return; end Insert_Dereference_Action; -------------------------------- -- Integer_Promotion_Possible -- -------------------------------- function Integer_Promotion_Possible (N : Node_Id) return Boolean is Operand : constant Node_Id := Expression (N); Operand_Type : constant Entity_Id := Etype (Operand); Root_Operand_Type : constant Entity_Id := Root_Type (Operand_Type); begin pragma Assert (Nkind (N) = N_Type_Conversion); return -- We only do the transformation for source constructs. We assume -- that the expander knows what it is doing when it generates code. Comes_From_Source (N) -- If the operand type is Short_Integer or Short_Short_Integer, -- then we will promote to Integer, which is available on all -- targets, and is sufficient to ensure no intermediate overflow. -- Furthermore it is likely to be as efficient or more efficient -- than using the smaller type for the computation so we do this -- unconditionally. and then (Root_Operand_Type = Base_Type (Standard_Short_Integer) or else Root_Operand_Type = Base_Type (Standard_Short_Short_Integer)) -- Test for interesting operation, which includes addition, -- division, exponentiation, multiplication, subtraction, absolute -- value and unary negation. Unary "+" is omitted since it is a -- no-op and thus can't overflow. and then Nkind_In (Operand, N_Op_Abs, N_Op_Add, N_Op_Divide, N_Op_Expon, N_Op_Minus, N_Op_Multiply, N_Op_Subtract); end Integer_Promotion_Possible; ------------------------------ -- Make_Array_Comparison_Op -- ------------------------------ -- This is a hand-coded expansion of the following generic function: -- generic -- type elem is (<>); -- type index is (<>); -- type a is array (index range <>) of elem; -- function Gnnn (X : a; Y: a) return boolean is -- J : index := Y'first; -- begin -- if X'length = 0 then -- return false; -- elsif Y'length = 0 then -- return true; -- else -- for I in X'range loop -- if X (I) = Y (J) then -- if J = Y'last then -- exit; -- else -- J := index'succ (J); -- end if; -- else -- return X (I) > Y (J); -- end if; -- end loop; -- return X'length > Y'length; -- end if; -- end Gnnn; -- Note that since we are essentially doing this expansion by hand, we -- do not need to generate an actual or formal generic part, just the -- instantiated function itself. -- Perhaps we could have the actual generic available in the run-time, -- obtained by rtsfind, and actually expand a real instantiation ??? function Make_Array_Comparison_Op (Typ : Entity_Id; Nod : Node_Id) return Node_Id is Loc : constant Source_Ptr := Sloc (Nod); X : constant Entity_Id := Make_Defining_Identifier (Loc, Name_uX); Y : constant Entity_Id := Make_Defining_Identifier (Loc, Name_uY); I : constant Entity_Id := Make_Defining_Identifier (Loc, Name_uI); J : constant Entity_Id := Make_Defining_Identifier (Loc, Name_uJ); Index : constant Entity_Id := Base_Type (Etype (First_Index (Typ))); Loop_Statement : Node_Id; Loop_Body : Node_Id; If_Stat : Node_Id; Inner_If : Node_Id; Final_Expr : Node_Id; Func_Body : Node_Id; Func_Name : Entity_Id; Formals : List_Id; Length1 : Node_Id; Length2 : Node_Id; begin -- if J = Y'last then -- exit; -- else -- J := index'succ (J); -- end if; Inner_If := Make_Implicit_If_Statement (Nod, Condition => Make_Op_Eq (Loc, Left_Opnd => New_Occurrence_Of (J, Loc), Right_Opnd => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Y, Loc), Attribute_Name => Name_Last)), Then_Statements => New_List ( Make_Exit_Statement (Loc)), Else_Statements => New_List ( Make_Assignment_Statement (Loc, Name => New_Occurrence_Of (J, Loc), Expression => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Index, Loc), Attribute_Name => Name_Succ, Expressions => New_List (New_Occurrence_Of (J, Loc)))))); -- if X (I) = Y (J) then -- if ... end if; -- else -- return X (I) > Y (J); -- end if; Loop_Body := Make_Implicit_If_Statement (Nod, Condition => Make_Op_Eq (Loc, Left_Opnd => Make_Indexed_Component (Loc, Prefix => New_Occurrence_Of (X, Loc), Expressions => New_List (New_Occurrence_Of (I, Loc))), Right_Opnd => Make_Indexed_Component (Loc, Prefix => New_Occurrence_Of (Y, Loc), Expressions => New_List (New_Occurrence_Of (J, Loc)))), Then_Statements => New_List (Inner_If), Else_Statements => New_List ( Make_Simple_Return_Statement (Loc, Expression => Make_Op_Gt (Loc, Left_Opnd => Make_Indexed_Component (Loc, Prefix => New_Occurrence_Of (X, Loc), Expressions => New_List (New_Occurrence_Of (I, Loc))), Right_Opnd => Make_Indexed_Component (Loc, Prefix => New_Occurrence_Of (Y, Loc), Expressions => New_List ( New_Occurrence_Of (J, Loc))))))); -- for I in X'range loop -- if ... end if; -- end loop; Loop_Statement := Make_Implicit_Loop_Statement (Nod, Identifier => Empty, Iteration_Scheme => Make_Iteration_Scheme (Loc, Loop_Parameter_Specification => Make_Loop_Parameter_Specification (Loc, Defining_Identifier => I, Discrete_Subtype_Definition => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (X, Loc), Attribute_Name => Name_Range))), Statements => New_List (Loop_Body)); -- if X'length = 0 then -- return false; -- elsif Y'length = 0 then -- return true; -- else -- for ... loop ... end loop; -- return X'length > Y'length; -- end if; Length1 := Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (X, Loc), Attribute_Name => Name_Length); Length2 := Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Y, Loc), Attribute_Name => Name_Length); Final_Expr := Make_Op_Gt (Loc, Left_Opnd => Length1, Right_Opnd => Length2); If_Stat := Make_Implicit_If_Statement (Nod, Condition => Make_Op_Eq (Loc, Left_Opnd => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (X, Loc), Attribute_Name => Name_Length), Right_Opnd => Make_Integer_Literal (Loc, 0)), Then_Statements => New_List ( Make_Simple_Return_Statement (Loc, Expression => New_Occurrence_Of (Standard_False, Loc))), Elsif_Parts => New_List ( Make_Elsif_Part (Loc, Condition => Make_Op_Eq (Loc, Left_Opnd => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Y, Loc), Attribute_Name => Name_Length), Right_Opnd => Make_Integer_Literal (Loc, 0)), Then_Statements => New_List ( Make_Simple_Return_Statement (Loc, Expression => New_Occurrence_Of (Standard_True, Loc))))), Else_Statements => New_List ( Loop_Statement, Make_Simple_Return_Statement (Loc, Expression => Final_Expr))); -- (X : a; Y: a) Formals := New_List ( Make_Parameter_Specification (Loc, Defining_Identifier => X, Parameter_Type => New_Occurrence_Of (Typ, Loc)), Make_Parameter_Specification (Loc, Defining_Identifier => Y, Parameter_Type => New_Occurrence_Of (Typ, Loc))); -- function Gnnn (...) return boolean is -- J : index := Y'first; -- begin -- if ... end if; -- end Gnnn; Func_Name := Make_Temporary (Loc, 'G'); Func_Body := Make_Subprogram_Body (Loc, Specification => Make_Function_Specification (Loc, Defining_Unit_Name => Func_Name, Parameter_Specifications => Formals, Result_Definition => New_Occurrence_Of (Standard_Boolean, Loc)), Declarations => New_List ( Make_Object_Declaration (Loc, Defining_Identifier => J, Object_Definition => New_Occurrence_Of (Index, Loc), Expression => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Y, Loc), Attribute_Name => Name_First))), Handled_Statement_Sequence => Make_Handled_Sequence_Of_Statements (Loc, Statements => New_List (If_Stat))); return Func_Body; end Make_Array_Comparison_Op; --------------------------- -- Make_Boolean_Array_Op -- --------------------------- -- For logical operations on boolean arrays, expand in line the following, -- replacing 'and' with 'or' or 'xor' where needed: -- function Annn (A : typ; B: typ) return typ is -- C : typ; -- begin -- for J in A'range loop -- C (J) := A (J) op B (J); -- end loop; -- return C; -- end Annn; -- Here typ is the boolean array type function Make_Boolean_Array_Op (Typ : Entity_Id; N : Node_Id) return Node_Id is Loc : constant Source_Ptr := Sloc (N); A : constant Entity_Id := Make_Defining_Identifier (Loc, Name_uA); B : constant Entity_Id := Make_Defining_Identifier (Loc, Name_uB); C : constant Entity_Id := Make_Defining_Identifier (Loc, Name_uC); J : constant Entity_Id := Make_Defining_Identifier (Loc, Name_uJ); A_J : Node_Id; B_J : Node_Id; C_J : Node_Id; Op : Node_Id; Formals : List_Id; Func_Name : Entity_Id; Func_Body : Node_Id; Loop_Statement : Node_Id; begin A_J := Make_Indexed_Component (Loc, Prefix => New_Occurrence_Of (A, Loc), Expressions => New_List (New_Occurrence_Of (J, Loc))); B_J := Make_Indexed_Component (Loc, Prefix => New_Occurrence_Of (B, Loc), Expressions => New_List (New_Occurrence_Of (J, Loc))); C_J := Make_Indexed_Component (Loc, Prefix => New_Occurrence_Of (C, Loc), Expressions => New_List (New_Occurrence_Of (J, Loc))); if Nkind (N) = N_Op_And then Op := Make_Op_And (Loc, Left_Opnd => A_J, Right_Opnd => B_J); elsif Nkind (N) = N_Op_Or then Op := Make_Op_Or (Loc, Left_Opnd => A_J, Right_Opnd => B_J); else Op := Make_Op_Xor (Loc, Left_Opnd => A_J, Right_Opnd => B_J); end if; Loop_Statement := Make_Implicit_Loop_Statement (N, Identifier => Empty, Iteration_Scheme => Make_Iteration_Scheme (Loc, Loop_Parameter_Specification => Make_Loop_Parameter_Specification (Loc, Defining_Identifier => J, Discrete_Subtype_Definition => Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (A, Loc), Attribute_Name => Name_Range))), Statements => New_List ( Make_Assignment_Statement (Loc, Name => C_J, Expression => Op))); Formals := New_List ( Make_Parameter_Specification (Loc, Defining_Identifier => A, Parameter_Type => New_Occurrence_Of (Typ, Loc)), Make_Parameter_Specification (Loc, Defining_Identifier => B, Parameter_Type => New_Occurrence_Of (Typ, Loc))); Func_Name := Make_Temporary (Loc, 'A'); Set_Is_Inlined (Func_Name); Func_Body := Make_Subprogram_Body (Loc, Specification => Make_Function_Specification (Loc, Defining_Unit_Name => Func_Name, Parameter_Specifications => Formals, Result_Definition => New_Occurrence_Of (Typ, Loc)), Declarations => New_List ( Make_Object_Declaration (Loc, Defining_Identifier => C, Object_Definition => New_Occurrence_Of (Typ, Loc))), Handled_Statement_Sequence => Make_Handled_Sequence_Of_Statements (Loc, Statements => New_List ( Loop_Statement, Make_Simple_Return_Statement (Loc, Expression => New_Occurrence_Of (C, Loc))))); return Func_Body; end Make_Boolean_Array_Op; ----------------------------------------- -- Minimized_Eliminated_Overflow_Check -- ----------------------------------------- function Minimized_Eliminated_Overflow_Check (N : Node_Id) return Boolean is begin return Is_Signed_Integer_Type (Etype (N)) and then Overflow_Check_Mode in Minimized_Or_Eliminated; end Minimized_Eliminated_Overflow_Check; ---------------------------- -- Narrow_Large_Operation -- ---------------------------- procedure Narrow_Large_Operation (N : Node_Id) is Kind : constant Node_Kind := Nkind (N); In_Rng : constant Boolean := Kind = N_In; Binary : constant Boolean := Kind in N_Binary_Op or else In_Rng; Compar : constant Boolean := Kind in N_Op_Compare or else In_Rng; R : constant Node_Id := Right_Opnd (N); Typ : constant Entity_Id := Etype (R); function Get_Size_For_Range (Lo, Hi : Uint) return Nat; -- Return the size of a small signed integer type covering Lo .. Hi. -- The important thing is to return a size lower than that of Typ. ------------------------ -- Get_Size_For_Range -- ------------------------ function Get_Size_For_Range (Lo, Hi : Uint) return Nat is function Is_OK_For_Range (Siz : Nat) return Boolean; -- Return True if a signed integer with given size can cover Lo .. Hi -------------------------- -- Is_OK_For_Range -- -------------------------- function Is_OK_For_Range (Siz : Nat) return Boolean is B : constant Uint := Uint_2 ** (Siz - 1); begin -- Test B = 2 ** (size - 1) (can accommodate -B .. +(B - 1)) return Lo >= -B and then Hi >= -B and then Lo < B and then Hi < B; end Is_OK_For_Range; begin -- This is (almost always) the size of Integer if Is_OK_For_Range (32) then return 32; -- If the size of Typ is 64 then check 63 elsif RM_Size (Typ) = 64 and then Is_OK_For_Range (63) then return 63; -- This is (almost always) the size of Long_Long_Integer elsif Is_OK_For_Range (64) then return 64; else return 128; end if; end Get_Size_For_Range; -- Local variables L : Node_Id; Llo, Lhi : Uint; Rlo, Rhi : Uint; Lsiz, Rsiz : Nat; Nlo, Nhi : Uint; Nsiz : Nat; Ntyp : Entity_Id; Nop : Node_Id; OK : Boolean; -- Start of processing for Narrow_Large_Operation begin -- First, determine the range of the left operand, if any if Binary then L := Left_Opnd (N); Determine_Range (L, OK, Llo, Lhi, Assume_Valid => True); if not OK then return; end if; else L := Empty; Llo := Uint_0; Lhi := Uint_0; end if; -- Second, determine the range of the right operand, which can itself -- be a range, in which case we take the lower bound of the low bound -- and the upper bound of the high bound. if In_Rng then declare Zlo, Zhi : Uint; begin Determine_Range (Low_Bound (R), OK, Rlo, Zhi, Assume_Valid => True); if not OK then return; end if; Determine_Range (High_Bound (R), OK, Zlo, Rhi, Assume_Valid => True); if not OK then return; end if; end; else Determine_Range (R, OK, Rlo, Rhi, Assume_Valid => True); if not OK then return; end if; end if; -- Then compute a size suitable for each range if Binary then Lsiz := Get_Size_For_Range (Llo, Lhi); else Lsiz := 0; end if; Rsiz := Get_Size_For_Range (Rlo, Rhi); -- Now compute the size of the narrower type if Compar then -- The type must be able to accomodate the operands Nsiz := Nat'Max (Lsiz, Rsiz); else -- The type must be able to accomodate the operand(s) and the result. -- Note that Determine_Range typically does not report the bounds of -- the value as being larger than those of the base type, which means -- that it does not report overflow (see also Enable_Overflow_Check). Determine_Range (N, OK, Nlo, Nhi, Assume_Valid => True); if not OK then return; end if; -- Therefore, if Nsiz is not lower than the size of the original type -- here, we cannot be sure that the operation does not overflow. Nsiz := Get_Size_For_Range (Nlo, Nhi); Nsiz := Nat'Max (Nsiz, Lsiz); Nsiz := Nat'Max (Nsiz, Rsiz); end if; -- If the size is not lower than the size of the original type, then -- there is no point in changing the type, except in the case where -- we can remove a conversion to the original type from an operand. if Nsiz >= RM_Size (Typ) and then not (Binary and then Nkind (L) = N_Type_Conversion and then Entity (Subtype_Mark (L)) = Typ) and then not (Nkind (R) = N_Type_Conversion and then Entity (Subtype_Mark (R)) = Typ) then return; end if; -- Now pick the narrower type according to the size if Nsiz <= RM_Size (Standard_Integer) then Ntyp := Standard_Integer; elsif Nsiz <= RM_Size (Standard_Long_Long_Integer) then Ntyp := Standard_Long_Long_Integer; else return; end if; -- Finally rewrite the operation in the narrower type Nop := New_Op_Node (Kind, Sloc (N)); if Binary then Set_Left_Opnd (Nop, Convert_To (Ntyp, L)); end if; if In_Rng then Set_Right_Opnd (Nop, Make_Range (Sloc (N), Convert_To (Ntyp, Low_Bound (R)), Convert_To (Ntyp, High_Bound (R)))); else Set_Right_Opnd (Nop, Convert_To (Ntyp, R)); end if; Rewrite (N, Nop); if Compar then -- Analyze it with the comparison type and checks suppressed since -- the conversions of the operands cannot overflow. Analyze_And_Resolve (N, Etype (Original_Node (N)), Suppress => Overflow_Check); else -- Analyze it with the narrower type and checks suppressed, but only -- when we are sure that the operation does not overflow, see above. if Nsiz < RM_Size (Typ) then Analyze_And_Resolve (N, Ntyp, Suppress => Overflow_Check); else Analyze_And_Resolve (N, Ntyp); end if; -- Put back a conversion to the original type Convert_To_And_Rewrite (Typ, N); end if; end Narrow_Large_Operation; -------------------------------- -- Optimize_Length_Comparison -- -------------------------------- procedure Optimize_Length_Comparison (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Typ : constant Entity_Id := Etype (N); Result : Node_Id; Left : Node_Id; Right : Node_Id; -- First and Last attribute reference nodes, which end up as left and -- right operands of the optimized result. Is_Zero : Boolean; -- True for comparison operand of zero Maybe_Superflat : Boolean; -- True if we may be in the dynamic superflat case, i.e. Is_Zero is set -- to false but the comparison operand can be zero at run time. In this -- case, we normally cannot do anything because the canonical formula of -- the length is not valid, but there is one exception: when the operand -- is itself the length of an array with the same bounds as the array on -- the LHS, we can entirely optimize away the comparison. Comp : Node_Id; -- Comparison operand, set only if Is_Zero is false Ent : array (Pos range 1 .. 2) of Entity_Id := (Empty, Empty); -- Entities whose length is being compared Index : array (Pos range 1 .. 2) of Node_Id := (Empty, Empty); -- Integer_Literal nodes for length attribute expressions, or Empty -- if there is no such expression present. Op : Node_Kind := Nkind (N); -- Kind of comparison operator, gets flipped if operands backwards function Convert_To_Long_Long_Integer (N : Node_Id) return Node_Id; -- Given a discrete expression, returns a Long_Long_Integer typed -- expression representing the underlying value of the expression. -- This is done with an unchecked conversion to Long_Long_Integer. -- We use unchecked conversion to handle the enumeration type case. function Is_Entity_Length (N : Node_Id; Num : Pos) return Boolean; -- Tests if N is a length attribute applied to a simple entity. If so, -- returns True, and sets Ent to the entity, and Index to the integer -- literal provided as an attribute expression, or to Empty if none. -- Num is the index designating the relevant slot in Ent and Index. -- Also returns True if the expression is a generated type conversion -- whose expression is of the desired form. This latter case arises -- when Apply_Universal_Integer_Attribute_Check installs a conversion -- to check for being in range, which is not needed in this context. -- Returns False if neither condition holds. function Is_Optimizable (N : Node_Id) return Boolean; -- Tests N to see if it is an optimizable comparison value (defined as -- constant zero or one, or something else where the value is known to -- be nonnegative and in the 32-bit range and where the corresponding -- Length value is also known to be 32 bits). If result is true, sets -- Is_Zero, Maybe_Superflat and Comp accordingly. procedure Rewrite_For_Equal_Lengths; -- Rewrite the comparison of two equal lengths into either True or False ---------------------------------- -- Convert_To_Long_Long_Integer -- ---------------------------------- function Convert_To_Long_Long_Integer (N : Node_Id) return Node_Id is begin return Unchecked_Convert_To (Standard_Long_Long_Integer, N); end Convert_To_Long_Long_Integer; ---------------------- -- Is_Entity_Length -- ---------------------- function Is_Entity_Length (N : Node_Id; Num : Pos) return Boolean is begin if Nkind (N) = N_Attribute_Reference and then Attribute_Name (N) = Name_Length and then Is_Entity_Name (Prefix (N)) then Ent (Num) := Entity (Prefix (N)); if Present (Expressions (N)) then Index (Num) := First (Expressions (N)); else Index (Num) := Empty; end if; return True; elsif Nkind (N) = N_Type_Conversion and then not Comes_From_Source (N) then return Is_Entity_Length (Expression (N), Num); else return False; end if; end Is_Entity_Length; -------------------- -- Is_Optimizable -- -------------------- function Is_Optimizable (N : Node_Id) return Boolean is Val : Uint; OK : Boolean; Lo : Uint; Hi : Uint; Indx : Node_Id; Dbl : Boolean; Ityp : Entity_Id; begin if Compile_Time_Known_Value (N) then Val := Expr_Value (N); if Val = Uint_0 then Is_Zero := True; Maybe_Superflat := False; Comp := Empty; return True; elsif Val = Uint_1 then Is_Zero := False; Maybe_Superflat := False; Comp := Empty; return True; end if; end if; -- Here we have to make sure of being within a 32-bit range (take the -- full unsigned range so the length of 32-bit arrays is accepted). Determine_Range (N, OK, Lo, Hi, Assume_Valid => True); if not OK or else Lo < Uint_0 or else Hi > Uint_2 ** 32 then return False; end if; Maybe_Superflat := (Lo = Uint_0); -- Tests if N is also a length attribute applied to a simple entity Dbl := Is_Entity_Length (N, 2); -- We can deal with the superflat case only if N is also a length if Maybe_Superflat and then not Dbl then return False; end if; -- Comparison value was within range, so now we must check the index -- value to make sure it is also within 32 bits. for K in Pos range 1 .. 2 loop Indx := First_Index (Etype (Ent (K))); if Present (Index (K)) then for J in 2 .. UI_To_Int (Intval (Index (K))) loop Next_Index (Indx); end loop; end if; Ityp := Etype (Indx); if Esize (Ityp) > 32 then return False; end if; exit when not Dbl; end loop; Is_Zero := False; Comp := N; return True; end Is_Optimizable; ------------------------------- -- Rewrite_For_Equal_Lengths -- ------------------------------- procedure Rewrite_For_Equal_Lengths is begin case Op is when N_Op_Eq | N_Op_Ge | N_Op_Le => Rewrite (N, Convert_To (Typ, New_Occurrence_Of (Standard_True, Sloc (N)))); when N_Op_Ne | N_Op_Gt | N_Op_Lt => Rewrite (N, Convert_To (Typ, New_Occurrence_Of (Standard_False, Sloc (N)))); when others => raise Program_Error; end case; Analyze_And_Resolve (N, Typ); end Rewrite_For_Equal_Lengths; -- Start of processing for Optimize_Length_Comparison begin -- Nothing to do if not a comparison if Op not in N_Op_Compare then return; end if; -- Nothing to do if special -gnatd.P debug flag set. if Debug_Flag_Dot_PP then return; end if; -- Ent'Length op 0/1 if Is_Entity_Length (Left_Opnd (N), 1) and then Is_Optimizable (Right_Opnd (N)) then null; -- 0/1 op Ent'Length elsif Is_Entity_Length (Right_Opnd (N), 1) and then Is_Optimizable (Left_Opnd (N)) then -- Flip comparison to opposite sense case Op is when N_Op_Lt => Op := N_Op_Gt; when N_Op_Le => Op := N_Op_Ge; when N_Op_Gt => Op := N_Op_Lt; when N_Op_Ge => Op := N_Op_Le; when others => null; end case; -- Else optimization not possible else return; end if; -- Fall through if we will do the optimization -- Cases to handle: -- X'Length = 0 => X'First > X'Last -- X'Length = 1 => X'First = X'Last -- X'Length = n => X'First + (n - 1) = X'Last -- X'Length /= 0 => X'First <= X'Last -- X'Length /= 1 => X'First /= X'Last -- X'Length /= n => X'First + (n - 1) /= X'Last -- X'Length >= 0 => always true, warn -- X'Length >= 1 => X'First <= X'Last -- X'Length >= n => X'First + (n - 1) <= X'Last -- X'Length > 0 => X'First <= X'Last -- X'Length > 1 => X'First < X'Last -- X'Length > n => X'First + (n - 1) < X'Last -- X'Length <= 0 => X'First > X'Last (warn, could be =) -- X'Length <= 1 => X'First >= X'Last -- X'Length <= n => X'First + (n - 1) >= X'Last -- X'Length < 0 => always false (warn) -- X'Length < 1 => X'First > X'Last -- X'Length < n => X'First + (n - 1) > X'Last -- Note: for the cases of n (not constant 0,1), we require that the -- corresponding index type be integer or shorter (i.e. not 64-bit), -- and the same for the comparison value. Then we do the comparison -- using 64-bit arithmetic (actually long long integer), so that we -- cannot have overflow intefering with the result. -- First deal with warning cases if Is_Zero then case Op is -- X'Length >= 0 when N_Op_Ge => Rewrite (N, Convert_To (Typ, New_Occurrence_Of (Standard_True, Loc))); Analyze_And_Resolve (N, Typ); Warn_On_Known_Condition (N); return; -- X'Length < 0 when N_Op_Lt => Rewrite (N, Convert_To (Typ, New_Occurrence_Of (Standard_False, Loc))); Analyze_And_Resolve (N, Typ); Warn_On_Known_Condition (N); return; when N_Op_Le => if Constant_Condition_Warnings and then Comes_From_Source (Original_Node (N)) then Error_Msg_N ("could replace by ""'=""?c?", N); end if; Op := N_Op_Eq; when others => null; end case; end if; -- Build the First reference we will use Left := Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Ent (1), Loc), Attribute_Name => Name_First); if Present (Index (1)) then Set_Expressions (Left, New_List (New_Copy (Index (1)))); end if; -- Build the Last reference we will use Right := Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Ent (1), Loc), Attribute_Name => Name_Last); if Present (Index (1)) then Set_Expressions (Right, New_List (New_Copy (Index (1)))); end if; -- If general value case, then do the addition of (n - 1), and -- also add the needed conversions to type Long_Long_Integer. -- If n = Y'Length, we rewrite X'First + (n - 1) op X'Last into: -- Y'Last + (X'First - Y'First) op X'Last -- in the hope that X'First - Y'First can be computed statically. if Present (Comp) then if Present (Ent (2)) then declare Y_First : constant Node_Id := Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Ent (2), Loc), Attribute_Name => Name_First); Y_Last : constant Node_Id := Make_Attribute_Reference (Loc, Prefix => New_Occurrence_Of (Ent (2), Loc), Attribute_Name => Name_Last); R : Compare_Result; begin if Present (Index (2)) then Set_Expressions (Y_First, New_List (New_Copy (Index (2)))); Set_Expressions (Y_Last, New_List (New_Copy (Index (2)))); end if; Analyze (Left); Analyze (Y_First); -- If X'First = Y'First, simplify the above formula into a -- direct comparison of Y'Last and X'Last. R := Compile_Time_Compare (Left, Y_First, Assume_Valid => True); if R = EQ then Analyze (Right); Analyze (Y_Last); R := Compile_Time_Compare (Right, Y_Last, Assume_Valid => True); -- If the pairs of attributes are equal, we are done if R = EQ then Rewrite_For_Equal_Lengths; return; end if; -- If the base types are different, convert both operands to -- Long_Long_Integer, else compare them directly. if Base_Type (Etype (Right)) /= Base_Type (Etype (Y_Last)) then Left := Convert_To_Long_Long_Integer (Y_Last); else Left := Y_Last; Comp := Empty; end if; -- Otherwise, use the above formula as-is else Left := Make_Op_Add (Loc, Left_Opnd => Convert_To_Long_Long_Integer (Y_Last), Right_Opnd => Make_Op_Subtract (Loc, Left_Opnd => Convert_To_Long_Long_Integer (Left), Right_Opnd => Convert_To_Long_Long_Integer (Y_First))); end if; end; -- General value case else Left := Make_Op_Add (Loc, Left_Opnd => Convert_To_Long_Long_Integer (Left), Right_Opnd => Make_Op_Subtract (Loc, Left_Opnd => Convert_To_Long_Long_Integer (Comp), Right_Opnd => Make_Integer_Literal (Loc, 1))); end if; end if; -- We cannot do anything in the superflat case past this point if Maybe_Superflat then return; end if; -- If general operand, convert Last reference to Long_Long_Integer if Present (Comp) then Right := Convert_To_Long_Long_Integer (Right); end if; -- Check for cases to optimize -- X'Length = 0 => X'First > X'Last -- X'Length < 1 => X'First > X'Last -- X'Length < n => X'First + (n - 1) > X'Last if (Is_Zero and then Op = N_Op_Eq) or else (not Is_Zero and then Op = N_Op_Lt) then Result := Make_Op_Gt (Loc, Left_Opnd => Left, Right_Opnd => Right); -- X'Length = 1 => X'First = X'Last -- X'Length = n => X'First + (n - 1) = X'Last elsif not Is_Zero and then Op = N_Op_Eq then Result := Make_Op_Eq (Loc, Left_Opnd => Left, Right_Opnd => Right); -- X'Length /= 0 => X'First <= X'Last -- X'Length > 0 => X'First <= X'Last elsif Is_Zero and (Op = N_Op_Ne or else Op = N_Op_Gt) then Result := Make_Op_Le (Loc, Left_Opnd => Left, Right_Opnd => Right); -- X'Length /= 1 => X'First /= X'Last -- X'Length /= n => X'First + (n - 1) /= X'Last elsif not Is_Zero and then Op = N_Op_Ne then Result := Make_Op_Ne (Loc, Left_Opnd => Left, Right_Opnd => Right); -- X'Length >= 1 => X'First <= X'Last -- X'Length >= n => X'First + (n - 1) <= X'Last elsif not Is_Zero and then Op = N_Op_Ge then Result := Make_Op_Le (Loc, Left_Opnd => Left, Right_Opnd => Right); -- X'Length > 1 => X'First < X'Last -- X'Length > n => X'First + (n = 1) < X'Last elsif not Is_Zero and then Op = N_Op_Gt then Result := Make_Op_Lt (Loc, Left_Opnd => Left, Right_Opnd => Right); -- X'Length <= 1 => X'First >= X'Last -- X'Length <= n => X'First + (n - 1) >= X'Last elsif not Is_Zero and then Op = N_Op_Le then Result := Make_Op_Ge (Loc, Left_Opnd => Left, Right_Opnd => Right); -- Should not happen at this stage else raise Program_Error; end if; -- Rewrite and finish up (we can suppress overflow checks, see above) Rewrite (N, Result); Analyze_And_Resolve (N, Typ, Suppress => Overflow_Check); end Optimize_Length_Comparison; -------------------------------- -- Process_If_Case_Statements -- -------------------------------- procedure Process_If_Case_Statements (N : Node_Id; Stmts : List_Id) is Decl : Node_Id; begin Decl := First (Stmts); while Present (Decl) loop if Nkind (Decl) = N_Object_Declaration and then Is_Finalizable_Transient (Decl, N) then Process_Transient_In_Expression (Decl, N, Stmts); end if; Next (Decl); end loop; end Process_If_Case_Statements; ------------------------------------- -- Process_Transient_In_Expression -- ------------------------------------- procedure Process_Transient_In_Expression (Obj_Decl : Node_Id; Expr : Node_Id; Stmts : List_Id) is Loc : constant Source_Ptr := Sloc (Obj_Decl); Obj_Id : constant Entity_Id := Defining_Identifier (Obj_Decl); Hook_Context : constant Node_Id := Find_Hook_Context (Expr); -- The node on which to insert the hook as an action. This is usually -- the innermost enclosing non-transient construct. Fin_Call : Node_Id; Hook_Assign : Node_Id; Hook_Clear : Node_Id; Hook_Decl : Node_Id; Hook_Insert : Node_Id; Ptr_Decl : Node_Id; Fin_Context : Node_Id; -- The node after which to insert the finalization actions of the -- transient object. begin pragma Assert (Nkind_In (Expr, N_Case_Expression, N_Expression_With_Actions, N_If_Expression)); -- When the context is a Boolean evaluation, all three nodes capture the -- result of their computation in a local temporary: -- do -- Trans_Id : Ctrl_Typ := ...; -- Result : constant Boolean := ... Trans_Id ...; -- -- in Result end; -- As a result, the finalization of any transient objects can safely -- take place after the result capture. -- ??? could this be extended to elementary types? if Is_Boolean_Type (Etype (Expr)) then Fin_Context := Last (Stmts); -- Otherwise the immediate context may not be safe enough to carry -- out transient object finalization due to aliasing and nesting of -- constructs. Insert calls to [Deep_]Finalize after the innermost -- enclosing non-transient construct. else Fin_Context := Hook_Context; end if; -- Mark the transient object as successfully processed to avoid double -- finalization. Set_Is_Finalized_Transient (Obj_Id); -- Construct all the pieces necessary to hook and finalize a transient -- object. Build_Transient_Object_Statements (Obj_Decl => Obj_Decl, Fin_Call => Fin_Call, Hook_Assign => Hook_Assign, Hook_Clear => Hook_Clear, Hook_Decl => Hook_Decl, Ptr_Decl => Ptr_Decl, Finalize_Obj => False); -- Add the access type which provides a reference to the transient -- object. Generate: -- type Ptr_Typ is access all Desig_Typ; Insert_Action (Hook_Context, Ptr_Decl); -- Add the temporary which acts as a hook to the transient object. -- Generate: -- Hook : Ptr_Id := null; Insert_Action (Hook_Context, Hook_Decl); -- When the transient object is initialized by an aggregate, the hook -- must capture the object after the last aggregate assignment takes -- place. Only then is the object considered initialized. Generate: -- Hook := Ptr_Typ (Obj_Id); -- -- Hook := Obj_Id'Unrestricted_Access; if Ekind_In (Obj_Id, E_Constant, E_Variable) and then Present (Last_Aggregate_Assignment (Obj_Id)) then Hook_Insert := Last_Aggregate_Assignment (Obj_Id); -- Otherwise the hook seizes the related object immediately else Hook_Insert := Obj_Decl; end if; Insert_After_And_Analyze (Hook_Insert, Hook_Assign); -- When the node is part of a return statement, there is no need to -- insert a finalization call, as the general finalization mechanism -- (see Build_Finalizer) would take care of the transient object on -- subprogram exit. Note that it would also be impossible to insert the -- finalization code after the return statement as this will render it -- unreachable. if Nkind (Fin_Context) = N_Simple_Return_Statement then null; -- Finalize the hook after the context has been evaluated. Generate: -- if Hook /= null then -- [Deep_]Finalize (Hook.all); -- Hook := null; -- end if; else Insert_Action_After (Fin_Context, Make_Implicit_If_Statement (Obj_Decl, Condition => Make_Op_Ne (Loc, Left_Opnd => New_Occurrence_Of (Defining_Entity (Hook_Decl), Loc), Right_Opnd => Make_Null (Loc)), Then_Statements => New_List ( Fin_Call, Hook_Clear))); end if; end Process_Transient_In_Expression; ------------------------ -- Rewrite_Comparison -- ------------------------ procedure Rewrite_Comparison (N : Node_Id) is Typ : constant Entity_Id := Etype (N); False_Result : Boolean; True_Result : Boolean; begin if Nkind (N) = N_Type_Conversion then Rewrite_Comparison (Expression (N)); return; elsif Nkind (N) not in N_Op_Compare then return; end if; -- Determine the potential outcome of the comparison assuming that the -- operands are valid and emit a warning when the comparison evaluates -- to True or False only in the presence of invalid values. Warn_On_Constant_Valid_Condition (N); -- Determine the potential outcome of the comparison assuming that the -- operands are not valid. Test_Comparison (Op => N, Assume_Valid => False, True_Result => True_Result, False_Result => False_Result); -- The outcome is a decisive False or True, rewrite the operator if False_Result or True_Result then Rewrite (N, Convert_To (Typ, New_Occurrence_Of (Boolean_Literals (True_Result), Sloc (N)))); Analyze_And_Resolve (N, Typ); Warn_On_Known_Condition (N); end if; end Rewrite_Comparison; ---------------------------- -- Safe_In_Place_Array_Op -- ---------------------------- function Safe_In_Place_Array_Op (Lhs : Node_Id; Op1 : Node_Id; Op2 : Node_Id) return Boolean is Target : Entity_Id; function Is_Safe_Operand (Op : Node_Id) return Boolean; -- Operand is safe if it cannot overlap part of the target of the -- operation. If the operand and the target are identical, the operand -- is safe. The operand can be empty in the case of negation. function Is_Unaliased (N : Node_Id) return Boolean; -- Check that N is a stand-alone entity ------------------ -- Is_Unaliased -- ------------------ function Is_Unaliased (N : Node_Id) return Boolean is begin return Is_Entity_Name (N) and then No (Address_Clause (Entity (N))) and then No (Renamed_Object (Entity (N))); end Is_Unaliased; --------------------- -- Is_Safe_Operand -- --------------------- function Is_Safe_Operand (Op : Node_Id) return Boolean is begin if No (Op) then return True; elsif Is_Entity_Name (Op) then return Is_Unaliased (Op); elsif Nkind_In (Op, N_Indexed_Component, N_Selected_Component) then return Is_Unaliased (Prefix (Op)); elsif Nkind (Op) = N_Slice then return Is_Unaliased (Prefix (Op)) and then Entity (Prefix (Op)) /= Target; elsif Nkind (Op) = N_Op_Not then return Is_Safe_Operand (Right_Opnd (Op)); else return False; end if; end Is_Safe_Operand; -- Start of processing for Safe_In_Place_Array_Op begin -- Skip this processing if the component size is different from system -- storage unit (since at least for NOT this would cause problems). if Component_Size (Etype (Lhs)) /= System_Storage_Unit then return False; -- Cannot do in place stuff if non-standard Boolean representation elsif Has_Non_Standard_Rep (Component_Type (Etype (Lhs))) then return False; elsif not Is_Unaliased (Lhs) then return False; else Target := Entity (Lhs); return Is_Safe_Operand (Op1) and then Is_Safe_Operand (Op2); end if; end Safe_In_Place_Array_Op; ----------------------- -- Tagged_Membership -- ----------------------- -- There are two different cases to consider depending on whether the right -- operand is a class-wide type or not. If not we just compare the actual -- tag of the left expr to the target type tag: -- -- Left_Expr.Tag = Right_Type'Tag; -- -- If it is a class-wide type we use the RT function CW_Membership which is -- usually implemented by looking in the ancestor tables contained in the -- dispatch table pointed by Left_Expr.Tag for Typ'Tag -- Ada 2005 (AI-251): If it is a class-wide interface type we use the RT -- function IW_Membership which is usually implemented by looking in the -- table of abstract interface types plus the ancestor table contained in -- the dispatch table pointed by Left_Expr.Tag for Typ'Tag procedure Tagged_Membership (N : Node_Id; SCIL_Node : out Node_Id; Result : out Node_Id) is Left : constant Node_Id := Left_Opnd (N); Right : constant Node_Id := Right_Opnd (N); Loc : constant Source_Ptr := Sloc (N); Full_R_Typ : Entity_Id; Left_Type : Entity_Id; New_Node : Node_Id; Right_Type : Entity_Id; Obj_Tag : Node_Id; begin SCIL_Node := Empty; -- Handle entities from the limited view Left_Type := Available_View (Etype (Left)); Right_Type := Available_View (Etype (Right)); -- In the case where the type is an access type, the test is applied -- using the designated types (needed in Ada 2012 for implicit anonymous -- access conversions, for AI05-0149). if Is_Access_Type (Right_Type) then Left_Type := Designated_Type (Left_Type); Right_Type := Designated_Type (Right_Type); end if; if Is_Class_Wide_Type (Left_Type) then Left_Type := Root_Type (Left_Type); end if; if Is_Class_Wide_Type (Right_Type) then Full_R_Typ := Underlying_Type (Root_Type (Right_Type)); else Full_R_Typ := Underlying_Type (Right_Type); end if; Obj_Tag := Make_Selected_Component (Loc, Prefix => Relocate_Node (Left), Selector_Name => New_Occurrence_Of (First_Tag_Component (Left_Type), Loc)); if Is_Class_Wide_Type (Right_Type) or else Is_Interface (Left_Type) then -- No need to issue a run-time check if we statically know that the -- result of this membership test is always true. For example, -- considering the following declarations: -- type Iface is interface; -- type T is tagged null record; -- type DT is new T and Iface with null record; -- Obj1 : T; -- Obj2 : DT; -- These membership tests are always true: -- Obj1 in T'Class -- Obj2 in T'Class; -- Obj2 in Iface'Class; -- We do not need to handle cases where the membership is illegal. -- For example: -- Obj1 in DT'Class; -- Compile time error -- Obj1 in Iface'Class; -- Compile time error if not Is_Interface (Left_Type) and then not Is_Class_Wide_Type (Left_Type) and then (Is_Ancestor (Etype (Right_Type), Left_Type, Use_Full_View => True) or else (Is_Interface (Etype (Right_Type)) and then Interface_Present_In_Ancestor (Typ => Left_Type, Iface => Etype (Right_Type)))) then Result := New_Occurrence_Of (Standard_True, Loc); return; end if; -- Ada 2005 (AI-251): Class-wide applied to interfaces if Is_Interface (Etype (Class_Wide_Type (Right_Type))) -- Support to: "Iface_CW_Typ in Typ'Class" or else Is_Interface (Left_Type) then -- Issue error if IW_Membership operation not available in a -- configurable run time setting. if not RTE_Available (RE_IW_Membership) then Error_Msg_CRT ("dynamic membership test on interface types", N); Result := Empty; return; end if; Result := Make_Function_Call (Loc, Name => New_Occurrence_Of (RTE (RE_IW_Membership), Loc), Parameter_Associations => New_List ( Make_Attribute_Reference (Loc, Prefix => Obj_Tag, Attribute_Name => Name_Address), New_Occurrence_Of ( Node (First_Elmt (Access_Disp_Table (Full_R_Typ))), Loc))); -- Ada 95: Normal case else Build_CW_Membership (Loc, Obj_Tag_Node => Obj_Tag, Typ_Tag_Node => New_Occurrence_Of ( Node (First_Elmt (Access_Disp_Table (Full_R_Typ))), Loc), Related_Nod => N, New_Node => New_Node); -- Generate the SCIL node for this class-wide membership test. -- Done here because the previous call to Build_CW_Membership -- relocates Obj_Tag. if Generate_SCIL then SCIL_Node := Make_SCIL_Membership_Test (Sloc (N)); Set_SCIL_Entity (SCIL_Node, Etype (Right_Type)); Set_SCIL_Tag_Value (SCIL_Node, Obj_Tag); end if; Result := New_Node; end if; -- Right_Type is not a class-wide type else -- No need to check the tag of the object if Right_Typ is abstract if Is_Abstract_Type (Right_Type) then Result := New_Occurrence_Of (Standard_False, Loc); else Result := Make_Op_Eq (Loc, Left_Opnd => Obj_Tag, Right_Opnd => New_Occurrence_Of (Node (First_Elmt (Access_Disp_Table (Full_R_Typ))), Loc)); end if; end if; end Tagged_Membership; ------------------------------ -- Unary_Op_Validity_Checks -- ------------------------------ procedure Unary_Op_Validity_Checks (N : Node_Id) is begin if Validity_Checks_On and Validity_Check_Operands then Ensure_Valid (Right_Opnd (N)); end if; end Unary_Op_Validity_Checks; end Exp_Ch4;