diff options
Diffstat (limited to 'gcc-4.8/gcc/ada/exp_ch7.adb')
-rw-r--r-- | gcc-4.8/gcc/ada/exp_ch7.adb | 7917 |
1 files changed, 0 insertions, 7917 deletions
diff --git a/gcc-4.8/gcc/ada/exp_ch7.adb b/gcc-4.8/gcc/ada/exp_ch7.adb deleted file mode 100644 index 72892828b..000000000 --- a/gcc-4.8/gcc/ada/exp_ch7.adb +++ /dev/null @@ -1,7917 +0,0 @@ ------------------------------------------------------------------------------- --- -- --- GNAT COMPILER COMPONENTS -- --- -- --- E X P _ C H 7 -- --- -- --- B o d y -- --- -- --- Copyright (C) 1992-2012, 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. -- --- -- ------------------------------------------------------------------------------- - --- This package contains virtually all expansion mechanisms related to --- - controlled types --- - transient scopes - -with Atree; use Atree; -with Debug; use Debug; -with Einfo; use Einfo; -with Elists; use Elists; -with Errout; use Errout; -with Exp_Ch6; use Exp_Ch6; -with Exp_Ch9; use Exp_Ch9; -with Exp_Ch11; use Exp_Ch11; -with Exp_Dbug; use Exp_Dbug; -with Exp_Dist; use Exp_Dist; -with Exp_Disp; use Exp_Disp; -with Exp_Tss; use Exp_Tss; -with Exp_Util; use Exp_Util; -with Freeze; use Freeze; -with Lib; use Lib; -with Nlists; use Nlists; -with Nmake; use Nmake; -with Opt; use Opt; -with Output; use Output; -with Restrict; use Restrict; -with Rident; use Rident; -with Rtsfind; use Rtsfind; -with Sinfo; use Sinfo; -with Sem; use Sem; -with Sem_Aux; use Sem_Aux; -with Sem_Ch3; use Sem_Ch3; -with Sem_Ch7; use Sem_Ch7; -with Sem_Ch8; use Sem_Ch8; -with Sem_Res; use Sem_Res; -with Sem_Util; use Sem_Util; -with Snames; use Snames; -with Stand; use Stand; -with Targparm; use Targparm; -with Tbuild; use Tbuild; -with Ttypes; use Ttypes; -with Uintp; use Uintp; - -package body Exp_Ch7 is - - -------------------------------- - -- Transient Scope Management -- - -------------------------------- - - -- A transient scope is created when temporary objects are created by the - -- compiler. These temporary objects are allocated on the secondary stack - -- and the transient scope is responsible for finalizing the object when - -- appropriate and reclaiming the memory at the right time. The temporary - -- objects are generally the objects allocated to store the result of a - -- function returning an unconstrained or a tagged value. Expressions - -- needing to be wrapped in a transient scope (functions calls returning - -- unconstrained or tagged values) may appear in 3 different contexts which - -- lead to 3 different kinds of transient scope expansion: - - -- 1. In a simple statement (procedure call, assignment, ...). In this - -- case the instruction is wrapped into a transient block. See - -- Wrap_Transient_Statement for details. - - -- 2. In an expression of a control structure (test in a IF statement, - -- expression in a CASE statement, ...). See Wrap_Transient_Expression - -- for details. - - -- 3. In a expression of an object_declaration. No wrapping is possible - -- here, so the finalization actions, if any, are done right after the - -- declaration and the secondary stack deallocation is done in the - -- proper enclosing scope. See Wrap_Transient_Declaration for details. - - -- Note about functions returning tagged types: it has been decided to - -- always allocate their result in the secondary stack, even though is not - -- absolutely mandatory when the tagged type is constrained because the - -- caller knows the size of the returned object and thus could allocate the - -- result in the primary stack. An exception to this is when the function - -- builds its result in place, as is done for functions with inherently - -- limited result types for Ada 2005. In that case, certain callers may - -- pass the address of a constrained object as the target object for the - -- function result. - - -- By allocating tagged results in the secondary stack a number of - -- implementation difficulties are avoided: - - -- - If it is a dispatching function call, the computation of the size of - -- the result is possible but complex from the outside. - - -- - If the returned type is controlled, the assignment of the returned - -- value to the anonymous object involves an Adjust, and we have no - -- easy way to access the anonymous object created by the back end. - - -- - If the returned type is class-wide, this is an unconstrained type - -- anyway. - - -- Furthermore, the small loss in efficiency which is the result of this - -- decision is not such a big deal because functions returning tagged types - -- are not as common in practice compared to functions returning access to - -- a tagged type. - - -------------------------------------------------- - -- Transient Blocks and Finalization Management -- - -------------------------------------------------- - - function Find_Node_To_Be_Wrapped (N : Node_Id) return Node_Id; - -- N is a node which may generate a transient scope. Loop over the parent - -- pointers of N until it find the appropriate node to wrap. If it returns - -- Empty, it means that no transient scope is needed in this context. - - procedure Insert_Actions_In_Scope_Around (N : Node_Id); - -- Insert the before-actions kept in the scope stack before N, and the - -- after-actions after N, which must be a member of a list. - - function Make_Transient_Block - (Loc : Source_Ptr; - Action : Node_Id; - Par : Node_Id) return Node_Id; - -- Action is a single statement or object declaration. Par is the proper - -- parent of the generated block. Create a transient block whose name is - -- the current scope and the only handled statement is Action. If Action - -- involves controlled objects or secondary stack usage, the corresponding - -- cleanup actions are performed at the end of the block. - - procedure Set_Node_To_Be_Wrapped (N : Node_Id); - -- Set the field Node_To_Be_Wrapped of the current scope - - -- ??? The entire comment needs to be rewritten - -- ??? which entire comment? - - ----------------------------- - -- Finalization Management -- - ----------------------------- - - -- This part describe how Initialization/Adjustment/Finalization procedures - -- are generated and called. Two cases must be considered, types that are - -- Controlled (Is_Controlled flag set) and composite types that contain - -- controlled components (Has_Controlled_Component flag set). In the first - -- case the procedures to call are the user-defined primitive operations - -- Initialize/Adjust/Finalize. In the second case, GNAT generates - -- Deep_Initialize, Deep_Adjust and Deep_Finalize that are in charge - -- of calling the former procedures on the controlled components. - - -- For records with Has_Controlled_Component set, a hidden "controller" - -- component is inserted. This controller component contains its own - -- finalization list on which all controlled components are attached - -- creating an indirection on the upper-level Finalization list. This - -- technique facilitates the management of objects whose number of - -- controlled components changes during execution. This controller - -- component is itself controlled and is attached to the upper-level - -- finalization chain. Its adjust primitive is in charge of calling adjust - -- on the components and adjusting the finalization pointer to match their - -- new location (see a-finali.adb). - - -- It is not possible to use a similar technique for arrays that have - -- Has_Controlled_Component set. In this case, deep procedures are - -- generated that call initialize/adjust/finalize + attachment or - -- detachment on the finalization list for all component. - - -- Initialize calls: they are generated for declarations or dynamic - -- allocations of Controlled objects with no initial value. They are always - -- followed by an attachment to the current Finalization Chain. For the - -- dynamic allocation case this the chain attached to the scope of the - -- access type definition otherwise, this is the chain of the current - -- scope. - - -- Adjust Calls: They are generated on 2 occasions: (1) for declarations - -- or dynamic allocations of Controlled objects with an initial value. - -- (2) after an assignment. In the first case they are followed by an - -- attachment to the final chain, in the second case they are not. - - -- Finalization Calls: They are generated on (1) scope exit, (2) - -- assignments, (3) unchecked deallocations. In case (3) they have to - -- be detached from the final chain, in case (2) they must not and in - -- case (1) this is not important since we are exiting the scope anyway. - - -- Other details: - - -- Type extensions will have a new record controller at each derivation - -- level containing controlled components. The record controller for - -- the parent/ancestor is attached to the finalization list of the - -- extension's record controller (i.e. the parent is like a component - -- of the extension). - - -- For types that are both Is_Controlled and Has_Controlled_Components, - -- the record controller and the object itself are handled separately. - -- It could seem simpler to attach the object at the end of its record - -- controller but this would not tackle view conversions properly. - - -- A classwide type can always potentially have controlled components - -- but the record controller of the corresponding actual type may not - -- be known at compile time so the dispatch table contains a special - -- field that allows to compute the offset of the record controller - -- dynamically. See s-finimp.Deep_Tag_Attach and a-tags.RC_Offset. - - -- Here is a simple example of the expansion of a controlled block : - - -- declare - -- X : Controlled; - -- Y : Controlled := Init; - -- - -- type R is record - -- C : Controlled; - -- end record; - -- W : R; - -- Z : R := (C => X); - - -- begin - -- X := Y; - -- W := Z; - -- end; - -- - -- is expanded into - -- - -- declare - -- _L : System.FI.Finalizable_Ptr; - - -- procedure _Clean is - -- begin - -- Abort_Defer; - -- System.FI.Finalize_List (_L); - -- Abort_Undefer; - -- end _Clean; - - -- X : Controlled; - -- begin - -- Abort_Defer; - -- Initialize (X); - -- Attach_To_Final_List (_L, Finalizable (X), 1); - -- at end: Abort_Undefer; - -- Y : Controlled := Init; - -- Adjust (Y); - -- Attach_To_Final_List (_L, Finalizable (Y), 1); - -- - -- type R is record - -- C : Controlled; - -- end record; - -- W : R; - -- begin - -- Abort_Defer; - -- Deep_Initialize (W, _L, 1); - -- at end: Abort_Under; - -- Z : R := (C => X); - -- Deep_Adjust (Z, _L, 1); - - -- begin - -- _Assign (X, Y); - -- Deep_Finalize (W, False); - -- <save W's final pointers> - -- W := Z; - -- <restore W's final pointers> - -- Deep_Adjust (W, _L, 0); - -- at end - -- _Clean; - -- end; - - type Final_Primitives is - (Initialize_Case, Adjust_Case, Finalize_Case, Address_Case); - -- This enumeration type is defined in order to ease sharing code for - -- building finalization procedures for composite types. - - Name_Of : constant array (Final_Primitives) of Name_Id := - (Initialize_Case => Name_Initialize, - Adjust_Case => Name_Adjust, - Finalize_Case => Name_Finalize, - Address_Case => Name_Finalize_Address); - Deep_Name_Of : constant array (Final_Primitives) of TSS_Name_Type := - (Initialize_Case => TSS_Deep_Initialize, - Adjust_Case => TSS_Deep_Adjust, - Finalize_Case => TSS_Deep_Finalize, - Address_Case => TSS_Finalize_Address); - - procedure Build_Array_Deep_Procs (Typ : Entity_Id); - -- Build the deep Initialize/Adjust/Finalize for a record Typ with - -- Has_Controlled_Component set and store them using the TSS mechanism. - - function Build_Cleanup_Statements (N : Node_Id) return List_Id; - -- Create the clean up calls for an asynchronous call block, task master, - -- protected subprogram body, task allocation block or task body. If the - -- context does not contain the above constructs, the routine returns an - -- empty list. - - procedure Build_Finalizer - (N : Node_Id; - Clean_Stmts : List_Id; - Mark_Id : Entity_Id; - Top_Decls : List_Id; - Defer_Abort : Boolean; - Fin_Id : out Entity_Id); - -- N may denote an accept statement, block, entry body, package body, - -- package spec, protected body, subprogram body, and a task body. Create - -- a procedure which contains finalization calls for all controlled objects - -- declared in the declarative or statement region of N. The calls are - -- built in reverse order relative to the original declarations. In the - -- case of a tack body, the routine delays the creation of the finalizer - -- until all statements have been moved to the task body procedure. - -- Clean_Stmts may contain additional context-dependent code used to abort - -- asynchronous calls or complete tasks (see Build_Cleanup_Statements). - -- Mark_Id is the secondary stack used in the current context or Empty if - -- missing. Top_Decls is the list on which the declaration of the finalizer - -- is attached in the non-package case. Defer_Abort indicates that the - -- statements passed in perform actions that require abort to be deferred, - -- such as for task termination. Fin_Id is the finalizer declaration - -- entity. - - procedure Build_Finalizer_Call (N : Node_Id; Fin_Id : Entity_Id); - -- N is a construct which contains a handled sequence of statements, Fin_Id - -- is the entity of a finalizer. Create an At_End handler which covers the - -- statements of N and calls Fin_Id. If the handled statement sequence has - -- an exception handler, the statements will be wrapped in a block to avoid - -- unwanted interaction with the new At_End handler. - - procedure Build_Record_Deep_Procs (Typ : Entity_Id); - -- Build the deep Initialize/Adjust/Finalize for a record Typ with - -- Has_Component_Component set and store them using the TSS mechanism. - - procedure Check_Visibly_Controlled - (Prim : Final_Primitives; - Typ : Entity_Id; - E : in out Entity_Id; - Cref : in out Node_Id); - -- The controlled operation declared for a derived type may not be - -- overriding, if the controlled operations of the parent type are hidden, - -- for example when the parent is a private type whose full view is - -- controlled. For other primitive operations we modify the name of the - -- operation to indicate that it is not overriding, but this is not - -- possible for Initialize, etc. because they have to be retrievable by - -- name. Before generating the proper call to one of these operations we - -- check whether Typ is known to be controlled at the point of definition. - -- If it is not then we must retrieve the hidden operation of the parent - -- and use it instead. This is one case that might be solved more cleanly - -- once Overriding pragmas or declarations are in place. - - function Convert_View - (Proc : Entity_Id; - Arg : Node_Id; - Ind : Pos := 1) return Node_Id; - -- Proc is one of the Initialize/Adjust/Finalize operations, and Arg is the - -- argument being passed to it. Ind indicates which formal of procedure - -- Proc we are trying to match. This function will, if necessary, generate - -- a conversion between the partial and full view of Arg to match the type - -- of the formal of Proc, or force a conversion to the class-wide type in - -- the case where the operation is abstract. - - function Enclosing_Function (E : Entity_Id) return Entity_Id; - -- Given an arbitrary entity, traverse the scope chain looking for the - -- first enclosing function. Return Empty if no function was found. - - function Make_Call - (Loc : Source_Ptr; - Proc_Id : Entity_Id; - Param : Node_Id; - For_Parent : Boolean := False) return Node_Id; - -- Subsidiary to Make_Adjust_Call and Make_Final_Call. Given the entity of - -- routine [Deep_]Adjust / Finalize and an object parameter, create an - -- adjust / finalization call. Flag For_Parent should be set when field - -- _parent is being processed. - - function Make_Deep_Proc - (Prim : Final_Primitives; - Typ : Entity_Id; - Stmts : List_Id) return Node_Id; - -- This function generates the tree for Deep_Initialize, Deep_Adjust or - -- Deep_Finalize procedures according to the first parameter, these - -- procedures operate on the type Typ. The Stmts parameter gives the body - -- of the procedure. - - function Make_Deep_Array_Body - (Prim : Final_Primitives; - Typ : Entity_Id) return List_Id; - -- This function generates the list of statements for implementing - -- Deep_Initialize, Deep_Adjust or Deep_Finalize procedures according to - -- the first parameter, these procedures operate on the array type Typ. - - function Make_Deep_Record_Body - (Prim : Final_Primitives; - Typ : Entity_Id; - Is_Local : Boolean := False) return List_Id; - -- This function generates the list of statements for implementing - -- Deep_Initialize, Deep_Adjust or Deep_Finalize procedures according to - -- the first parameter, these procedures operate on the record type Typ. - -- Flag Is_Local is used in conjunction with Deep_Finalize to designate - -- whether the inner logic should be dictated by state counters. - - function Make_Finalize_Address_Stmts (Typ : Entity_Id) return List_Id; - -- Subsidiary to Make_Finalize_Address_Body, Make_Deep_Array_Body and - -- Make_Deep_Record_Body. Generate the following statements: - -- - -- declare - -- type Acc_Typ is access all Typ; - -- for Acc_Typ'Storage_Size use 0; - -- begin - -- [Deep_]Finalize (Acc_Typ (V).all); - -- end; - - ---------------------------- - -- Build_Array_Deep_Procs -- - ---------------------------- - - procedure Build_Array_Deep_Procs (Typ : Entity_Id) is - begin - Set_TSS (Typ, - Make_Deep_Proc - (Prim => Initialize_Case, - Typ => Typ, - Stmts => Make_Deep_Array_Body (Initialize_Case, Typ))); - - if not Is_Immutably_Limited_Type (Typ) then - Set_TSS (Typ, - Make_Deep_Proc - (Prim => Adjust_Case, - Typ => Typ, - Stmts => Make_Deep_Array_Body (Adjust_Case, Typ))); - end if; - - -- Do not generate Deep_Finalize and Finalize_Address if finalization is - -- suppressed since these routine will not be used. - - if not Restriction_Active (No_Finalization) then - Set_TSS (Typ, - Make_Deep_Proc - (Prim => Finalize_Case, - Typ => Typ, - Stmts => Make_Deep_Array_Body (Finalize_Case, Typ))); - - -- Create TSS primitive Finalize_Address for non-VM targets. JVM and - -- .NET do not support address arithmetic and unchecked conversions. - - if VM_Target = No_VM then - Set_TSS (Typ, - Make_Deep_Proc - (Prim => Address_Case, - Typ => Typ, - Stmts => Make_Deep_Array_Body (Address_Case, Typ))); - end if; - end if; - end Build_Array_Deep_Procs; - - ------------------------------ - -- Build_Cleanup_Statements -- - ------------------------------ - - function Build_Cleanup_Statements (N : Node_Id) return List_Id is - Is_Asynchronous_Call : constant Boolean := - Nkind (N) = N_Block_Statement - and then Is_Asynchronous_Call_Block (N); - Is_Master : constant Boolean := - Nkind (N) /= N_Entry_Body - and then Is_Task_Master (N); - Is_Protected_Body : constant Boolean := - Nkind (N) = N_Subprogram_Body - and then Is_Protected_Subprogram_Body (N); - Is_Task_Allocation : constant Boolean := - Nkind (N) = N_Block_Statement - and then Is_Task_Allocation_Block (N); - Is_Task_Body : constant Boolean := - Nkind (Original_Node (N)) = N_Task_Body; - - Loc : constant Source_Ptr := Sloc (N); - Stmts : constant List_Id := New_List; - - begin - if Is_Task_Body then - if Restricted_Profile then - Append_To (Stmts, - Build_Runtime_Call (Loc, RE_Complete_Restricted_Task)); - else - Append_To (Stmts, Build_Runtime_Call (Loc, RE_Complete_Task)); - end if; - - elsif Is_Master then - if Restriction_Active (No_Task_Hierarchy) = False then - Append_To (Stmts, Build_Runtime_Call (Loc, RE_Complete_Master)); - end if; - - -- Add statements to unlock the protected object parameter and to - -- undefer abort. If the context is a protected procedure and the object - -- has entries, call the entry service routine. - - -- NOTE: The generated code references _object, a parameter to the - -- procedure. - - elsif Is_Protected_Body then - declare - Spec : constant Node_Id := Parent (Corresponding_Spec (N)); - Conc_Typ : Entity_Id; - Nam : Node_Id; - Param : Node_Id; - Param_Typ : Entity_Id; - - begin - -- Find the _object parameter representing the protected object - - Param := First (Parameter_Specifications (Spec)); - loop - Param_Typ := Etype (Parameter_Type (Param)); - - if Ekind (Param_Typ) = E_Record_Type then - Conc_Typ := Corresponding_Concurrent_Type (Param_Typ); - end if; - - exit when No (Param) or else Present (Conc_Typ); - Next (Param); - end loop; - - pragma Assert (Present (Param)); - - -- If the associated protected object has entries, a protected - -- procedure has to service entry queues. In this case generate: - - -- Service_Entries (_object._object'Access); - - if Nkind (Specification (N)) = N_Procedure_Specification - and then Has_Entries (Conc_Typ) - then - case Corresponding_Runtime_Package (Conc_Typ) is - when System_Tasking_Protected_Objects_Entries => - Nam := New_Reference_To (RTE (RE_Service_Entries), Loc); - - when System_Tasking_Protected_Objects_Single_Entry => - Nam := New_Reference_To (RTE (RE_Service_Entry), Loc); - - when others => - raise Program_Error; - end case; - - Append_To (Stmts, - Make_Procedure_Call_Statement (Loc, - Name => Nam, - Parameter_Associations => New_List ( - Make_Attribute_Reference (Loc, - Prefix => - Make_Selected_Component (Loc, - Prefix => New_Reference_To ( - Defining_Identifier (Param), Loc), - Selector_Name => - Make_Identifier (Loc, Name_uObject)), - Attribute_Name => Name_Unchecked_Access)))); - - else - -- Generate: - -- Unlock (_object._object'Access); - - case Corresponding_Runtime_Package (Conc_Typ) is - when System_Tasking_Protected_Objects_Entries => - Nam := New_Reference_To (RTE (RE_Unlock_Entries), Loc); - - when System_Tasking_Protected_Objects_Single_Entry => - Nam := New_Reference_To (RTE (RE_Unlock_Entry), Loc); - - when System_Tasking_Protected_Objects => - Nam := New_Reference_To (RTE (RE_Unlock), Loc); - - when others => - raise Program_Error; - end case; - - Append_To (Stmts, - Make_Procedure_Call_Statement (Loc, - Name => Nam, - Parameter_Associations => New_List ( - Make_Attribute_Reference (Loc, - Prefix => - Make_Selected_Component (Loc, - Prefix => - New_Reference_To - (Defining_Identifier (Param), Loc), - Selector_Name => - Make_Identifier (Loc, Name_uObject)), - Attribute_Name => Name_Unchecked_Access)))); - end if; - - -- Generate: - -- Abort_Undefer; - - if Abort_Allowed then - Append_To (Stmts, - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (RTE (RE_Abort_Undefer), Loc), - Parameter_Associations => Empty_List)); - end if; - end; - - -- Add a call to Expunge_Unactivated_Tasks for dynamically allocated - -- tasks. Other unactivated tasks are completed by Complete_Task or - -- Complete_Master. - - -- NOTE: The generated code references _chain, a local object - - elsif Is_Task_Allocation then - - -- Generate: - -- Expunge_Unactivated_Tasks (_chain); - - -- where _chain is the list of tasks created by the allocator but not - -- yet activated. This list will be empty unless the block completes - -- abnormally. - - Append_To (Stmts, - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To - (RTE (RE_Expunge_Unactivated_Tasks), Loc), - Parameter_Associations => New_List ( - New_Reference_To (Activation_Chain_Entity (N), Loc)))); - - -- Attempt to cancel an asynchronous entry call whenever the block which - -- contains the abortable part is exited. - - -- NOTE: The generated code references Cnn, a local object - - elsif Is_Asynchronous_Call then - declare - Cancel_Param : constant Entity_Id := - Entry_Cancel_Parameter (Entity (Identifier (N))); - - begin - -- If it is of type Communication_Block, this must be a protected - -- entry call. Generate: - - -- if Enqueued (Cancel_Param) then - -- Cancel_Protected_Entry_Call (Cancel_Param); - -- end if; - - if Is_RTE (Etype (Cancel_Param), RE_Communication_Block) then - Append_To (Stmts, - Make_If_Statement (Loc, - Condition => - Make_Function_Call (Loc, - Name => - New_Reference_To (RTE (RE_Enqueued), Loc), - Parameter_Associations => New_List ( - New_Reference_To (Cancel_Param, Loc))), - - Then_Statements => New_List ( - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To - (RTE (RE_Cancel_Protected_Entry_Call), Loc), - Parameter_Associations => New_List ( - New_Reference_To (Cancel_Param, Loc)))))); - - -- Asynchronous delay, generate: - -- Cancel_Async_Delay (Cancel_Param); - - elsif Is_RTE (Etype (Cancel_Param), RE_Delay_Block) then - Append_To (Stmts, - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (RTE (RE_Cancel_Async_Delay), Loc), - Parameter_Associations => New_List ( - Make_Attribute_Reference (Loc, - Prefix => - New_Reference_To (Cancel_Param, Loc), - Attribute_Name => Name_Unchecked_Access)))); - - -- Task entry call, generate: - -- Cancel_Task_Entry_Call (Cancel_Param); - - else - Append_To (Stmts, - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (RTE (RE_Cancel_Task_Entry_Call), Loc), - Parameter_Associations => New_List ( - New_Reference_To (Cancel_Param, Loc)))); - end if; - end; - end if; - - return Stmts; - end Build_Cleanup_Statements; - - ----------------------------- - -- Build_Controlling_Procs -- - ----------------------------- - - procedure Build_Controlling_Procs (Typ : Entity_Id) is - begin - if Is_Array_Type (Typ) then - Build_Array_Deep_Procs (Typ); - else pragma Assert (Is_Record_Type (Typ)); - Build_Record_Deep_Procs (Typ); - end if; - end Build_Controlling_Procs; - - ----------------------------- - -- Build_Exception_Handler -- - ----------------------------- - - function Build_Exception_Handler - (Data : Finalization_Exception_Data; - For_Library : Boolean := False) return Node_Id - is - Actuals : List_Id; - Proc_To_Call : Entity_Id; - Except : Node_Id; - Stmts : List_Id; - - begin - pragma Assert (Present (Data.Raised_Id)); - - if Exception_Extra_Info - or else (For_Library and not Restricted_Profile) - then - if Exception_Extra_Info then - - -- Generate: - - -- Get_Current_Excep.all - - Except := - Make_Function_Call (Data.Loc, - Name => - Make_Explicit_Dereference (Data.Loc, - Prefix => - New_Reference_To - (RTE (RE_Get_Current_Excep), Data.Loc))); - - else - -- Generate: - - -- null - - Except := Make_Null (Data.Loc); - end if; - - if For_Library and then not Restricted_Profile then - Proc_To_Call := RTE (RE_Save_Library_Occurrence); - Actuals := New_List (Except); - - else - Proc_To_Call := RTE (RE_Save_Occurrence); - - -- The dereference occurs only when Exception_Extra_Info is true, - -- and therefore Except is not null. - - Actuals := - New_List ( - New_Reference_To (Data.E_Id, Data.Loc), - Make_Explicit_Dereference (Data.Loc, Except)); - end if; - - -- Generate: - - -- when others => - -- if not Raised_Id then - -- Raised_Id := True; - - -- Save_Occurrence (E_Id, Get_Current_Excep.all.all); - -- or - -- Save_Library_Occurrence (Get_Current_Excep.all); - -- end if; - - Stmts := - New_List ( - Make_If_Statement (Data.Loc, - Condition => - Make_Op_Not (Data.Loc, - Right_Opnd => New_Reference_To (Data.Raised_Id, Data.Loc)), - - Then_Statements => New_List ( - Make_Assignment_Statement (Data.Loc, - Name => New_Reference_To (Data.Raised_Id, Data.Loc), - Expression => New_Reference_To (Standard_True, Data.Loc)), - - Make_Procedure_Call_Statement (Data.Loc, - Name => - New_Reference_To (Proc_To_Call, Data.Loc), - Parameter_Associations => Actuals)))); - - else - -- Generate: - - -- Raised_Id := True; - - Stmts := New_List ( - Make_Assignment_Statement (Data.Loc, - Name => New_Reference_To (Data.Raised_Id, Data.Loc), - Expression => New_Reference_To (Standard_True, Data.Loc))); - end if; - - -- Generate: - - -- when others => - - return - Make_Exception_Handler (Data.Loc, - Exception_Choices => New_List (Make_Others_Choice (Data.Loc)), - Statements => Stmts); - end Build_Exception_Handler; - - ------------------------------- - -- Build_Finalization_Master -- - ------------------------------- - - procedure Build_Finalization_Master - (Typ : Entity_Id; - Ins_Node : Node_Id := Empty; - Encl_Scope : Entity_Id := Empty) - is - Desig_Typ : constant Entity_Id := Directly_Designated_Type (Typ); - Ptr_Typ : Entity_Id := Root_Type (Base_Type (Typ)); - - function In_Deallocation_Instance (E : Entity_Id) return Boolean; - -- Determine whether entity E is inside a wrapper package created for - -- an instance of Ada.Unchecked_Deallocation. - - ------------------------------ - -- In_Deallocation_Instance -- - ------------------------------ - - function In_Deallocation_Instance (E : Entity_Id) return Boolean is - Pkg : constant Entity_Id := Scope (E); - Par : Node_Id := Empty; - - begin - if Ekind (Pkg) = E_Package - and then Present (Related_Instance (Pkg)) - and then Ekind (Related_Instance (Pkg)) = E_Procedure - then - Par := Generic_Parent (Parent (Related_Instance (Pkg))); - - return - Present (Par) - and then Chars (Par) = Name_Unchecked_Deallocation - and then Chars (Scope (Par)) = Name_Ada - and then Scope (Scope (Par)) = Standard_Standard; - end if; - - return False; - end In_Deallocation_Instance; - - -- Start of processing for Build_Finalization_Master - - begin - if Is_Private_Type (Ptr_Typ) - and then Present (Full_View (Ptr_Typ)) - then - Ptr_Typ := Full_View (Ptr_Typ); - end if; - - -- Certain run-time configurations and targets do not provide support - -- for controlled types. - - if Restriction_Active (No_Finalization) then - return; - - -- Do not process C, C++, CIL and Java types since it is assumend that - -- the non-Ada side will handle their clean up. - - elsif Convention (Desig_Typ) = Convention_C - or else Convention (Desig_Typ) = Convention_CIL - or else Convention (Desig_Typ) = Convention_CPP - or else Convention (Desig_Typ) = Convention_Java - then - return; - - -- Various machinery such as freezing may have already created a - -- finalization master. - - elsif Present (Finalization_Master (Ptr_Typ)) then - return; - - -- Do not process types that return on the secondary stack - - elsif Present (Associated_Storage_Pool (Ptr_Typ)) - and then Is_RTE (Associated_Storage_Pool (Ptr_Typ), RE_SS_Pool) - then - return; - - -- Do not process types which may never allocate an object - - elsif No_Pool_Assigned (Ptr_Typ) then - return; - - -- Do not process access types coming from Ada.Unchecked_Deallocation - -- instances. Even though the designated type may be controlled, the - -- access type will never participate in allocation. - - elsif In_Deallocation_Instance (Ptr_Typ) then - return; - - -- Ignore the general use of anonymous access types unless the context - -- requires a finalization master. - - elsif Ekind (Ptr_Typ) = E_Anonymous_Access_Type - and then No (Ins_Node) - then - return; - - -- Do not process non-library access types when restriction No_Nested_ - -- Finalization is in effect since masters are controlled objects. - - elsif Restriction_Active (No_Nested_Finalization) - and then not Is_Library_Level_Entity (Ptr_Typ) - then - return; - - -- For .NET/JVM targets, allow the processing of access-to-controlled - -- types where the designated type is explicitly derived from [Limited_] - -- Controlled. - - elsif VM_Target /= No_VM - and then not Is_Controlled (Desig_Typ) - then - return; - - -- Do not create finalization masters in Alfa mode because they result - -- in unwanted expansion. - - elsif Alfa_Mode then - return; - end if; - - declare - Loc : constant Source_Ptr := Sloc (Ptr_Typ); - Actions : constant List_Id := New_List; - Fin_Mas_Id : Entity_Id; - Pool_Id : Entity_Id; - - begin - -- Generate: - -- Fnn : aliased Finalization_Master; - - -- Source access types use fixed master names since the master is - -- inserted in the same source unit only once. The only exception to - -- this are instances using the same access type as generic actual. - - if Comes_From_Source (Ptr_Typ) - and then not Inside_A_Generic - then - Fin_Mas_Id := - Make_Defining_Identifier (Loc, - Chars => New_External_Name (Chars (Ptr_Typ), "FM")); - - -- Internally generated access types use temporaries as their names - -- due to possible collision with identical names coming from other - -- packages. - - else - Fin_Mas_Id := Make_Temporary (Loc, 'F'); - end if; - - Append_To (Actions, - Make_Object_Declaration (Loc, - Defining_Identifier => Fin_Mas_Id, - Aliased_Present => True, - Object_Definition => - New_Reference_To (RTE (RE_Finalization_Master), Loc))); - - -- Storage pool selection and attribute decoration of the generated - -- master. Since .NET/JVM compilers do not support pools, this step - -- is skipped. - - if VM_Target = No_VM then - - -- If the access type has a user-defined pool, use it as the base - -- storage medium for the finalization pool. - - if Present (Associated_Storage_Pool (Ptr_Typ)) then - Pool_Id := Associated_Storage_Pool (Ptr_Typ); - - -- The default choice is the global pool - - else - Pool_Id := Get_Global_Pool_For_Access_Type (Ptr_Typ); - Set_Associated_Storage_Pool (Ptr_Typ, Pool_Id); - end if; - - -- Generate: - -- Set_Base_Pool (Fnn, Pool_Id'Unchecked_Access); - - Append_To (Actions, - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (RTE (RE_Set_Base_Pool), Loc), - Parameter_Associations => New_List ( - New_Reference_To (Fin_Mas_Id, Loc), - Make_Attribute_Reference (Loc, - Prefix => New_Reference_To (Pool_Id, Loc), - Attribute_Name => Name_Unrestricted_Access)))); - end if; - - Set_Finalization_Master (Ptr_Typ, Fin_Mas_Id); - - -- A finalization master created for an anonymous access type must be - -- inserted before a context-dependent node. - - if Present (Ins_Node) then - Push_Scope (Encl_Scope); - - -- Treat use clauses as declarations and insert directly in front - -- of them. - - if Nkind_In (Ins_Node, N_Use_Package_Clause, - N_Use_Type_Clause) - then - Insert_List_Before_And_Analyze (Ins_Node, Actions); - else - Insert_Actions (Ins_Node, Actions); - end if; - - Pop_Scope; - - elsif Ekind (Desig_Typ) = E_Incomplete_Type - and then Has_Completion_In_Body (Desig_Typ) - then - Insert_Actions (Parent (Ptr_Typ), Actions); - - -- If the designated type is not yet frozen, then append the actions - -- to that type's freeze actions. The actions need to be appended to - -- whichever type is frozen later, similarly to what Freeze_Type does - -- for appending the storage pool declaration for an access type. - -- Otherwise, the call to Set_Storage_Pool_Ptr might reference the - -- pool object before it's declared. However, it's not clear that - -- this is exactly the right test to accomplish that here. ??? - - elsif Present (Freeze_Node (Desig_Typ)) - and then not Analyzed (Freeze_Node (Desig_Typ)) - then - Append_Freeze_Actions (Desig_Typ, Actions); - - elsif Present (Freeze_Node (Ptr_Typ)) - and then not Analyzed (Freeze_Node (Ptr_Typ)) - then - Append_Freeze_Actions (Ptr_Typ, Actions); - - -- If there's a pool created locally for the access type, then we - -- need to ensure that the master gets created after the pool object, - -- because otherwise we can have a forward reference, so we force the - -- master actions to be inserted and analyzed after the pool entity. - -- Note that both the access type and its designated type may have - -- already been frozen and had their freezing actions analyzed at - -- this point. (This seems a little unclean.???) - - elsif VM_Target = No_VM - and then Scope (Pool_Id) = Scope (Ptr_Typ) - then - Insert_List_After_And_Analyze (Parent (Pool_Id), Actions); - - else - Insert_Actions (Parent (Ptr_Typ), Actions); - end if; - end; - end Build_Finalization_Master; - - --------------------- - -- Build_Finalizer -- - --------------------- - - procedure Build_Finalizer - (N : Node_Id; - Clean_Stmts : List_Id; - Mark_Id : Entity_Id; - Top_Decls : List_Id; - Defer_Abort : Boolean; - Fin_Id : out Entity_Id) - is - Acts_As_Clean : constant Boolean := - Present (Mark_Id) - or else - (Present (Clean_Stmts) - and then Is_Non_Empty_List (Clean_Stmts)); - Exceptions_OK : constant Boolean := - not Restriction_Active (No_Exception_Propagation); - For_Package_Body : constant Boolean := Nkind (N) = N_Package_Body; - For_Package_Spec : constant Boolean := Nkind (N) = N_Package_Declaration; - For_Package : constant Boolean := - For_Package_Body or else For_Package_Spec; - Loc : constant Source_Ptr := Sloc (N); - - -- NOTE: Local variable declarations are conservative and do not create - -- structures right from the start. Entities and lists are created once - -- it has been established that N has at least one controlled object. - - Components_Built : Boolean := False; - -- A flag used to avoid double initialization of entities and lists. If - -- the flag is set then the following variables have been initialized: - -- Counter_Id - -- Finalizer_Decls - -- Finalizer_Stmts - -- Jump_Alts - - Counter_Id : Entity_Id := Empty; - Counter_Val : Int := 0; - -- Name and value of the state counter - - Decls : List_Id := No_List; - -- Declarative region of N (if available). If N is a package declaration - -- Decls denotes the visible declarations. - - Finalizer_Data : Finalization_Exception_Data; - -- Data for the exception - - Finalizer_Decls : List_Id := No_List; - -- Local variable declarations. This list holds the label declarations - -- of all jump block alternatives as well as the declaration of the - -- local exception occurence and the raised flag: - -- E : Exception_Occurrence; - -- Raised : Boolean := False; - -- L<counter value> : label; - - Finalizer_Insert_Nod : Node_Id := Empty; - -- Insertion point for the finalizer body. Depending on the context - -- (Nkind of N) and the individual grouping of controlled objects, this - -- node may denote a package declaration or body, package instantiation, - -- block statement or a counter update statement. - - Finalizer_Stmts : List_Id := No_List; - -- The statement list of the finalizer body. It contains the following: - -- - -- Abort_Defer; -- Added if abort is allowed - -- <call to Prev_At_End> -- Added if exists - -- <cleanup statements> -- Added if Acts_As_Clean - -- <jump block> -- Added if Has_Ctrl_Objs - -- <finalization statements> -- Added if Has_Ctrl_Objs - -- <stack release> -- Added if Mark_Id exists - -- Abort_Undefer; -- Added if abort is allowed - - Has_Ctrl_Objs : Boolean := False; - -- A general flag which denotes whether N has at least one controlled - -- object. - - Has_Tagged_Types : Boolean := False; - -- A general flag which indicates whether N has at least one library- - -- level tagged type declaration. - - HSS : Node_Id := Empty; - -- The sequence of statements of N (if available) - - Jump_Alts : List_Id := No_List; - -- Jump block alternatives. Depending on the value of the state counter, - -- the control flow jumps to a sequence of finalization statements. This - -- list contains the following: - -- - -- when <counter value> => - -- goto L<counter value>; - - Jump_Block_Insert_Nod : Node_Id := Empty; - -- Specific point in the finalizer statements where the jump block is - -- inserted. - - Last_Top_Level_Ctrl_Construct : Node_Id := Empty; - -- The last controlled construct encountered when processing the top - -- level lists of N. This can be a nested package, an instantiation or - -- an object declaration. - - Prev_At_End : Entity_Id := Empty; - -- The previous at end procedure of the handled statements block of N - - Priv_Decls : List_Id := No_List; - -- The private declarations of N if N is a package declaration - - Spec_Id : Entity_Id := Empty; - Spec_Decls : List_Id := Top_Decls; - Stmts : List_Id := No_List; - - Tagged_Type_Stmts : List_Id := No_List; - -- Contains calls to Ada.Tags.Unregister_Tag for all library-level - -- tagged types found in N. - - ----------------------- - -- Local subprograms -- - ----------------------- - - procedure Build_Components; - -- Create all entites and initialize all lists used in the creation of - -- the finalizer. - - procedure Create_Finalizer; - -- Create the spec and body of the finalizer and insert them in the - -- proper place in the tree depending on the context. - - procedure Process_Declarations - (Decls : List_Id; - Preprocess : Boolean := False; - Top_Level : Boolean := False); - -- Inspect a list of declarations or statements which may contain - -- objects that need finalization. When flag Preprocess is set, the - -- routine will simply count the total number of controlled objects in - -- Decls. Flag Top_Level denotes whether the processing is done for - -- objects in nested package declarations or instances. - - procedure Process_Object_Declaration - (Decl : Node_Id; - Has_No_Init : Boolean := False; - Is_Protected : Boolean := False); - -- Generate all the machinery associated with the finalization of a - -- single object. Flag Has_No_Init is used to denote certain contexts - -- where Decl does not have initialization call(s). Flag Is_Protected - -- is set when Decl denotes a simple protected object. - - procedure Process_Tagged_Type_Declaration (Decl : Node_Id); - -- Generate all the code necessary to unregister the external tag of a - -- tagged type. - - ---------------------- - -- Build_Components -- - ---------------------- - - procedure Build_Components is - Counter_Decl : Node_Id; - Counter_Typ : Entity_Id; - Counter_Typ_Decl : Node_Id; - - begin - pragma Assert (Present (Decls)); - - -- This routine might be invoked several times when dealing with - -- constructs that have two lists (either two declarative regions - -- or declarations and statements). Avoid double initialization. - - if Components_Built then - return; - end if; - - Components_Built := True; - - if Has_Ctrl_Objs then - - -- Create entities for the counter, its type, the local exception - -- and the raised flag. - - Counter_Id := Make_Temporary (Loc, 'C'); - Counter_Typ := Make_Temporary (Loc, 'T'); - - Finalizer_Decls := New_List; - - Build_Object_Declarations - (Finalizer_Data, Finalizer_Decls, Loc, For_Package); - - -- Since the total number of controlled objects is always known, - -- build a subtype of Natural with precise bounds. This allows - -- the backend to optimize the case statement. Generate: - -- - -- subtype Tnn is Natural range 0 .. Counter_Val; - - Counter_Typ_Decl := - Make_Subtype_Declaration (Loc, - Defining_Identifier => Counter_Typ, - Subtype_Indication => - Make_Subtype_Indication (Loc, - Subtype_Mark => New_Reference_To (Standard_Natural, Loc), - Constraint => - Make_Range_Constraint (Loc, - Range_Expression => - Make_Range (Loc, - Low_Bound => - Make_Integer_Literal (Loc, Uint_0), - High_Bound => - Make_Integer_Literal (Loc, Counter_Val))))); - - -- Generate the declaration of the counter itself: - -- - -- Counter : Integer := 0; - - Counter_Decl := - Make_Object_Declaration (Loc, - Defining_Identifier => Counter_Id, - Object_Definition => New_Reference_To (Counter_Typ, Loc), - Expression => Make_Integer_Literal (Loc, 0)); - - -- Set the type of the counter explicitly to prevent errors when - -- examining object declarations later on. - - Set_Etype (Counter_Id, Counter_Typ); - - -- The counter and its type are inserted before the source - -- declarations of N. - - Prepend_To (Decls, Counter_Decl); - Prepend_To (Decls, Counter_Typ_Decl); - - -- The counter and its associated type must be manually analized - -- since N has already been analyzed. Use the scope of the spec - -- when inserting in a package. - - if For_Package then - Push_Scope (Spec_Id); - Analyze (Counter_Typ_Decl); - Analyze (Counter_Decl); - Pop_Scope; - - else - Analyze (Counter_Typ_Decl); - Analyze (Counter_Decl); - end if; - - Jump_Alts := New_List; - end if; - - -- If the context requires additional clean up, the finalization - -- machinery is added after the clean up code. - - if Acts_As_Clean then - Finalizer_Stmts := Clean_Stmts; - Jump_Block_Insert_Nod := Last (Finalizer_Stmts); - else - Finalizer_Stmts := New_List; - end if; - - if Has_Tagged_Types then - Tagged_Type_Stmts := New_List; - end if; - end Build_Components; - - ---------------------- - -- Create_Finalizer -- - ---------------------- - - procedure Create_Finalizer is - Body_Id : Entity_Id; - Fin_Body : Node_Id; - Fin_Spec : Node_Id; - Jump_Block : Node_Id; - Label : Node_Id; - Label_Id : Entity_Id; - - function New_Finalizer_Name return Name_Id; - -- Create a fully qualified name of a package spec or body finalizer. - -- The generated name is of the form: xx__yy__finalize_[spec|body]. - - ------------------------ - -- New_Finalizer_Name -- - ------------------------ - - function New_Finalizer_Name return Name_Id is - procedure New_Finalizer_Name (Id : Entity_Id); - -- Place "__<name-of-Id>" in the name buffer. If the identifier - -- has a non-standard scope, process the scope first. - - ------------------------ - -- New_Finalizer_Name -- - ------------------------ - - procedure New_Finalizer_Name (Id : Entity_Id) is - begin - if Scope (Id) = Standard_Standard then - Get_Name_String (Chars (Id)); - - else - New_Finalizer_Name (Scope (Id)); - Add_Str_To_Name_Buffer ("__"); - Add_Str_To_Name_Buffer (Get_Name_String (Chars (Id))); - end if; - end New_Finalizer_Name; - - -- Start of processing for New_Finalizer_Name - - begin - -- Create the fully qualified name of the enclosing scope - - New_Finalizer_Name (Spec_Id); - - -- Generate: - -- __finalize_[spec|body] - - Add_Str_To_Name_Buffer ("__finalize_"); - - if For_Package_Spec then - Add_Str_To_Name_Buffer ("spec"); - else - Add_Str_To_Name_Buffer ("body"); - end if; - - return Name_Find; - end New_Finalizer_Name; - - -- Start of processing for Create_Finalizer - - begin - -- Step 1: Creation of the finalizer name - - -- Packages must use a distinct name for their finalizers since the - -- binder will have to generate calls to them by name. The name is - -- of the following form: - - -- xx__yy__finalize_[spec|body] - - if For_Package then - Fin_Id := Make_Defining_Identifier (Loc, New_Finalizer_Name); - Set_Has_Qualified_Name (Fin_Id); - Set_Has_Fully_Qualified_Name (Fin_Id); - - -- The default name is _finalizer - - else - Fin_Id := - Make_Defining_Identifier (Loc, - Chars => New_External_Name (Name_uFinalizer)); - - -- The visibility semantics of AT_END handlers force a strange - -- separation of spec and body for stack-related finalizers: - - -- declare : Enclosing_Scope - -- procedure _finalizer; - -- begin - -- <controlled objects> - -- procedure _finalizer is - -- ... - -- at end - -- _finalizer; - -- end; - - -- Both spec and body are within the same construct and scope, but - -- the body is part of the handled sequence of statements. This - -- placement confuses the elaboration mechanism on targets where - -- AT_END handlers are expanded into "when all others" handlers: - - -- exception - -- when all others => - -- _finalizer; -- appears to require elab checks - -- at end - -- _finalizer; - -- end; - - -- Since the compiler guarantees that the body of a _finalizer is - -- always inserted in the same construct where the AT_END handler - -- resides, there is no need for elaboration checks. - - Set_Kill_Elaboration_Checks (Fin_Id); - end if; - - -- Step 2: Creation of the finalizer specification - - -- Generate: - -- procedure Fin_Id; - - Fin_Spec := - Make_Subprogram_Declaration (Loc, - Specification => - Make_Procedure_Specification (Loc, - Defining_Unit_Name => Fin_Id)); - - -- Step 3: Creation of the finalizer body - - if Has_Ctrl_Objs then - - -- Add L0, the default destination to the jump block - - Label_Id := Make_Identifier (Loc, New_External_Name ('L', 0)); - Set_Entity (Label_Id, - Make_Defining_Identifier (Loc, Chars (Label_Id))); - Label := Make_Label (Loc, Label_Id); - - -- Generate: - -- L0 : label; - - Prepend_To (Finalizer_Decls, - Make_Implicit_Label_Declaration (Loc, - Defining_Identifier => Entity (Label_Id), - Label_Construct => Label)); - - -- Generate: - -- when others => - -- goto L0; - - Append_To (Jump_Alts, - Make_Case_Statement_Alternative (Loc, - Discrete_Choices => New_List (Make_Others_Choice (Loc)), - Statements => New_List ( - Make_Goto_Statement (Loc, - Name => New_Reference_To (Entity (Label_Id), Loc))))); - - -- Generate: - -- <<L0>> - - Append_To (Finalizer_Stmts, Label); - - -- Create the jump block which controls the finalization flow - -- depending on the value of the state counter. - - Jump_Block := - Make_Case_Statement (Loc, - Expression => Make_Identifier (Loc, Chars (Counter_Id)), - Alternatives => Jump_Alts); - - if Acts_As_Clean - and then Present (Jump_Block_Insert_Nod) - then - Insert_After (Jump_Block_Insert_Nod, Jump_Block); - else - Prepend_To (Finalizer_Stmts, Jump_Block); - end if; - end if; - - -- Add the library-level tagged type unregistration machinery before - -- the jump block circuitry. This ensures that external tags will be - -- removed even if a finalization exception occurs at some point. - - if Has_Tagged_Types then - Prepend_List_To (Finalizer_Stmts, Tagged_Type_Stmts); - end if; - - -- Add a call to the previous At_End handler if it exists. The call - -- must always precede the jump block. - - if Present (Prev_At_End) then - Prepend_To (Finalizer_Stmts, - Make_Procedure_Call_Statement (Loc, Prev_At_End)); - - -- Clear the At_End handler since we have already generated the - -- proper replacement call for it. - - Set_At_End_Proc (HSS, Empty); - end if; - - -- Release the secondary stack mark - - if Present (Mark_Id) then - Append_To (Finalizer_Stmts, - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (RTE (RE_SS_Release), Loc), - Parameter_Associations => New_List ( - New_Reference_To (Mark_Id, Loc)))); - end if; - - -- Protect the statements with abort defer/undefer. This is only when - -- aborts are allowed and the clean up statements require deferral or - -- there are controlled objects to be finalized. - - if Abort_Allowed - and then - (Defer_Abort or else Has_Ctrl_Objs) - then - Prepend_To (Finalizer_Stmts, - Make_Procedure_Call_Statement (Loc, - Name => New_Reference_To (RTE (RE_Abort_Defer), Loc))); - - Append_To (Finalizer_Stmts, - Make_Procedure_Call_Statement (Loc, - Name => New_Reference_To (RTE (RE_Abort_Undefer), Loc))); - end if; - - -- The local exception does not need to be reraised for library-level - -- finalizers. Note that this action must be carried out after object - -- clean up, secondary stack release and abort undeferral. Generate: - - -- if Raised and then not Abort then - -- Raise_From_Controlled_Operation (E); - -- end if; - - if Has_Ctrl_Objs - and then Exceptions_OK - and then not For_Package - then - Append_To (Finalizer_Stmts, - Build_Raise_Statement (Finalizer_Data)); - end if; - - -- Generate: - -- procedure Fin_Id is - -- Abort : constant Boolean := Triggered_By_Abort; - -- <or> - -- Abort : constant Boolean := False; -- no abort - - -- E : Exception_Occurrence; -- All added if flag - -- Raised : Boolean := False; -- Has_Ctrl_Objs is set - -- L0 : label; - -- ... - -- Lnn : label; - - -- begin - -- Abort_Defer; -- Added if abort is allowed - -- <call to Prev_At_End> -- Added if exists - -- <cleanup statements> -- Added if Acts_As_Clean - -- <jump block> -- Added if Has_Ctrl_Objs - -- <finalization statements> -- Added if Has_Ctrl_Objs - -- <stack release> -- Added if Mark_Id exists - -- Abort_Undefer; -- Added if abort is allowed - -- <exception propagation> -- Added if Has_Ctrl_Objs - -- end Fin_Id; - - -- Create the body of the finalizer - - Body_Id := Make_Defining_Identifier (Loc, Chars (Fin_Id)); - - if For_Package then - Set_Has_Qualified_Name (Body_Id); - Set_Has_Fully_Qualified_Name (Body_Id); - end if; - - Fin_Body := - Make_Subprogram_Body (Loc, - Specification => - Make_Procedure_Specification (Loc, - Defining_Unit_Name => Body_Id), - Declarations => Finalizer_Decls, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, Finalizer_Stmts)); - - -- Step 4: Spec and body insertion, analysis - - if For_Package then - - -- If the package spec has private declarations, the finalizer - -- body must be added to the end of the list in order to have - -- visibility of all private controlled objects. - - if For_Package_Spec then - if Present (Priv_Decls) then - Append_To (Priv_Decls, Fin_Spec); - Append_To (Priv_Decls, Fin_Body); - else - Append_To (Decls, Fin_Spec); - Append_To (Decls, Fin_Body); - end if; - - -- For package bodies, both the finalizer spec and body are - -- inserted at the end of the package declarations. - - else - Append_To (Decls, Fin_Spec); - Append_To (Decls, Fin_Body); - end if; - - -- Push the name of the package - - Push_Scope (Spec_Id); - Analyze (Fin_Spec); - Analyze (Fin_Body); - Pop_Scope; - - -- Non-package case - - else - -- Create the spec for the finalizer. The At_End handler must be - -- able to call the body which resides in a nested structure. - - -- Generate: - -- declare - -- procedure Fin_Id; -- Spec - -- begin - -- <objects and possibly statements> - -- procedure Fin_Id is ... -- Body - -- <statements> - -- at end - -- Fin_Id; -- At_End handler - -- end; - - pragma Assert (Present (Spec_Decls)); - - Append_To (Spec_Decls, Fin_Spec); - Analyze (Fin_Spec); - - -- When the finalizer acts solely as a clean up routine, the body - -- is inserted right after the spec. - - if Acts_As_Clean - and then not Has_Ctrl_Objs - then - Insert_After (Fin_Spec, Fin_Body); - - -- In all other cases the body is inserted after either: - -- - -- 1) The counter update statement of the last controlled object - -- 2) The last top level nested controlled package - -- 3) The last top level controlled instantiation - - else - -- Manually freeze the spec. This is somewhat of a hack because - -- a subprogram is frozen when its body is seen and the freeze - -- node appears right before the body. However, in this case, - -- the spec must be frozen earlier since the At_End handler - -- must be able to call it. - -- - -- declare - -- procedure Fin_Id; -- Spec - -- [Fin_Id] -- Freeze node - -- begin - -- ... - -- at end - -- Fin_Id; -- At_End handler - -- end; - - Ensure_Freeze_Node (Fin_Id); - Insert_After (Fin_Spec, Freeze_Node (Fin_Id)); - Set_Is_Frozen (Fin_Id); - - -- In the case where the last construct to contain a controlled - -- object is either a nested package, an instantiation or a - -- freeze node, the body must be inserted directly after the - -- construct. - - if Nkind_In (Last_Top_Level_Ctrl_Construct, - N_Freeze_Entity, - N_Package_Declaration, - N_Package_Body) - then - Finalizer_Insert_Nod := Last_Top_Level_Ctrl_Construct; - end if; - - Insert_After (Finalizer_Insert_Nod, Fin_Body); - end if; - - Analyze (Fin_Body); - end if; - end Create_Finalizer; - - -------------------------- - -- Process_Declarations -- - -------------------------- - - procedure Process_Declarations - (Decls : List_Id; - Preprocess : Boolean := False; - Top_Level : Boolean := False) - is - Decl : Node_Id; - Expr : Node_Id; - Obj_Id : Entity_Id; - Obj_Typ : Entity_Id; - Pack_Id : Entity_Id; - Spec : Node_Id; - Typ : Entity_Id; - - Old_Counter_Val : Int; - -- This variable is used to determine whether a nested package or - -- instance contains at least one controlled object. - - procedure Processing_Actions - (Has_No_Init : Boolean := False; - Is_Protected : Boolean := False); - -- Depending on the mode of operation of Process_Declarations, either - -- increment the controlled object counter, set the controlled object - -- flag and store the last top level construct or process the current - -- declaration. Flag Has_No_Init is used to propagate scenarios where - -- the current declaration may not have initialization proc(s). Flag - -- Is_Protected should be set when the current declaration denotes a - -- simple protected object. - - ------------------------ - -- Processing_Actions -- - ------------------------ - - procedure Processing_Actions - (Has_No_Init : Boolean := False; - Is_Protected : Boolean := False) - is - begin - -- Library-level tagged type - - if Nkind (Decl) = N_Full_Type_Declaration then - if Preprocess then - Has_Tagged_Types := True; - - if Top_Level - and then No (Last_Top_Level_Ctrl_Construct) - then - Last_Top_Level_Ctrl_Construct := Decl; - end if; - - else - Process_Tagged_Type_Declaration (Decl); - end if; - - -- Controlled object declaration - - else - if Preprocess then - Counter_Val := Counter_Val + 1; - Has_Ctrl_Objs := True; - - if Top_Level - and then No (Last_Top_Level_Ctrl_Construct) - then - Last_Top_Level_Ctrl_Construct := Decl; - end if; - - else - Process_Object_Declaration (Decl, Has_No_Init, Is_Protected); - end if; - end if; - end Processing_Actions; - - -- Start of processing for Process_Declarations - - begin - if No (Decls) or else Is_Empty_List (Decls) then - return; - end if; - - -- Process all declarations in reverse order - - Decl := Last_Non_Pragma (Decls); - while Present (Decl) loop - - -- Library-level tagged types - - if Nkind (Decl) = N_Full_Type_Declaration then - Typ := Defining_Identifier (Decl); - - if Is_Tagged_Type (Typ) - and then Is_Library_Level_Entity (Typ) - and then Convention (Typ) = Convention_Ada - and then Present (Access_Disp_Table (Typ)) - and then RTE_Available (RE_Register_Tag) - and then not No_Run_Time_Mode - and then not Is_Abstract_Type (Typ) - then - Processing_Actions; - end if; - - -- Regular object declarations - - elsif Nkind (Decl) = N_Object_Declaration then - Obj_Id := Defining_Identifier (Decl); - Obj_Typ := Base_Type (Etype (Obj_Id)); - Expr := Expression (Decl); - - -- Bypass any form of processing for objects which have their - -- finalization disabled. This applies only to objects at the - -- library level. - - if For_Package - and then Finalize_Storage_Only (Obj_Typ) - then - null; - - -- Transient variables are treated separately in order to - -- minimize the size of the generated code. For details, see - -- Process_Transient_Objects. - - elsif Is_Processed_Transient (Obj_Id) then - null; - - -- The object is of the form: - -- Obj : Typ [:= Expr]; - - -- Do not process the incomplete view of a deferred constant. - -- Do not consider tag-to-class-wide conversions. - - elsif not Is_Imported (Obj_Id) - and then Needs_Finalization (Obj_Typ) - and then not (Ekind (Obj_Id) = E_Constant - and then not Has_Completion (Obj_Id)) - and then not Is_Tag_To_Class_Wide_Conversion (Obj_Id) - then - Processing_Actions; - - -- The object is of the form: - -- Obj : Access_Typ := Non_BIP_Function_Call'reference; - - -- Obj : Access_Typ := - -- BIP_Function_Call (BIPalloc => 2, ...)'reference; - - elsif Is_Access_Type (Obj_Typ) - and then Needs_Finalization - (Available_View (Designated_Type (Obj_Typ))) - and then Present (Expr) - and then - (Is_Secondary_Stack_BIP_Func_Call (Expr) - or else - (Is_Non_BIP_Func_Call (Expr) - and then not Is_Related_To_Func_Return (Obj_Id))) - then - Processing_Actions (Has_No_Init => True); - - -- Processing for "hook" objects generated for controlled - -- transients declared inside an Expression_With_Actions. - - elsif Is_Access_Type (Obj_Typ) - and then Present (Status_Flag_Or_Transient_Decl (Obj_Id)) - and then Nkind (Status_Flag_Or_Transient_Decl (Obj_Id)) = - N_Object_Declaration - and then Is_Finalizable_Transient - (Status_Flag_Or_Transient_Decl (Obj_Id), Decl) - then - Processing_Actions (Has_No_Init => True); - - -- Process intermediate results of an if expression with one - -- of the alternatives using a controlled function call. - - elsif Is_Access_Type (Obj_Typ) - and then Present (Status_Flag_Or_Transient_Decl (Obj_Id)) - and then Nkind (Status_Flag_Or_Transient_Decl (Obj_Id)) = - N_Defining_Identifier - and then Present (Expr) - and then Nkind (Expr) = N_Null - then - Processing_Actions (Has_No_Init => True); - - -- Simple protected objects which use type System.Tasking. - -- Protected_Objects.Protection to manage their locks should - -- be treated as controlled since they require manual cleanup. - -- The only exception is illustrated in the following example: - - -- package Pkg is - -- type Ctrl is new Controlled ... - -- procedure Finalize (Obj : in out Ctrl); - -- Lib_Obj : Ctrl; - -- end Pkg; - - -- package body Pkg is - -- protected Prot is - -- procedure Do_Something (Obj : in out Ctrl); - -- end Prot; - - -- protected body Prot is - -- procedure Do_Something (Obj : in out Ctrl) is ... - -- end Prot; - - -- procedure Finalize (Obj : in out Ctrl) is - -- begin - -- Prot.Do_Something (Obj); - -- end Finalize; - -- end Pkg; - - -- Since for the most part entities in package bodies depend on - -- those in package specs, Prot's lock should be cleaned up - -- first. The subsequent cleanup of the spec finalizes Lib_Obj. - -- This act however attempts to invoke Do_Something and fails - -- because the lock has disappeared. - - elsif Ekind (Obj_Id) = E_Variable - and then not In_Library_Level_Package_Body (Obj_Id) - and then - (Is_Simple_Protected_Type (Obj_Typ) - or else Has_Simple_Protected_Object (Obj_Typ)) - then - Processing_Actions (Is_Protected => True); - end if; - - -- Specific cases of object renamings - - elsif Nkind (Decl) = N_Object_Renaming_Declaration then - Obj_Id := Defining_Identifier (Decl); - Obj_Typ := Base_Type (Etype (Obj_Id)); - - -- Bypass any form of processing for objects which have their - -- finalization disabled. This applies only to objects at the - -- library level. - - if For_Package - and then Finalize_Storage_Only (Obj_Typ) - then - null; - - -- Return object of a build-in-place function. This case is - -- recognized and marked by the expansion of an extended return - -- statement (see Expand_N_Extended_Return_Statement). - - elsif Needs_Finalization (Obj_Typ) - and then Is_Return_Object (Obj_Id) - and then Present (Status_Flag_Or_Transient_Decl (Obj_Id)) - then - Processing_Actions (Has_No_Init => True); - - -- Detect a case where a source object has been initialized by - -- a controlled function call or another object which was later - -- rewritten as a class-wide conversion of Ada.Tags.Displace. - - -- Obj1 : CW_Type := Src_Obj; - -- Obj2 : CW_Type := Function_Call (...); - - -- Obj1 : CW_Type renames (... Ada.Tags.Displace (Src_Obj)); - -- Tmp : ... := Function_Call (...)'reference; - -- Obj2 : CW_Type renames (... Ada.Tags.Displace (Tmp)); - - elsif Is_Displacement_Of_Object_Or_Function_Result (Obj_Id) then - Processing_Actions (Has_No_Init => True); - end if; - - -- Inspect the freeze node of an access-to-controlled type and - -- look for a delayed finalization master. This case arises when - -- the freeze actions are inserted at a later time than the - -- expansion of the context. Since Build_Finalizer is never called - -- on a single construct twice, the master will be ultimately - -- left out and never finalized. This is also needed for freeze - -- actions of designated types themselves, since in some cases the - -- finalization master is associated with a designated type's - -- freeze node rather than that of the access type (see handling - -- for freeze actions in Build_Finalization_Master). - - elsif Nkind (Decl) = N_Freeze_Entity - and then Present (Actions (Decl)) - then - Typ := Entity (Decl); - - if (Is_Access_Type (Typ) - and then not Is_Access_Subprogram_Type (Typ) - and then Needs_Finalization - (Available_View (Designated_Type (Typ)))) - or else (Is_Type (Typ) and then Needs_Finalization (Typ)) - then - Old_Counter_Val := Counter_Val; - - -- Freeze nodes are considered to be identical to packages - -- and blocks in terms of nesting. The difference is that - -- a finalization master created inside the freeze node is - -- at the same nesting level as the node itself. - - Process_Declarations (Actions (Decl), Preprocess); - - -- The freeze node contains a finalization master - - if Preprocess - and then Top_Level - and then No (Last_Top_Level_Ctrl_Construct) - and then Counter_Val > Old_Counter_Val - then - Last_Top_Level_Ctrl_Construct := Decl; - end if; - end if; - - -- Nested package declarations, avoid generics - - elsif Nkind (Decl) = N_Package_Declaration then - Spec := Specification (Decl); - Pack_Id := Defining_Unit_Name (Spec); - - if Nkind (Pack_Id) = N_Defining_Program_Unit_Name then - Pack_Id := Defining_Identifier (Pack_Id); - end if; - - if Ekind (Pack_Id) /= E_Generic_Package then - Old_Counter_Val := Counter_Val; - Process_Declarations - (Private_Declarations (Spec), Preprocess); - Process_Declarations - (Visible_Declarations (Spec), Preprocess); - - -- Either the visible or the private declarations contain a - -- controlled object. The nested package declaration is the - -- last such construct. - - if Preprocess - and then Top_Level - and then No (Last_Top_Level_Ctrl_Construct) - and then Counter_Val > Old_Counter_Val - then - Last_Top_Level_Ctrl_Construct := Decl; - end if; - end if; - - -- Nested package bodies, avoid generics - - elsif Nkind (Decl) = N_Package_Body then - Spec := Corresponding_Spec (Decl); - - if Ekind (Spec) /= E_Generic_Package then - Old_Counter_Val := Counter_Val; - Process_Declarations (Declarations (Decl), Preprocess); - - -- The nested package body is the last construct to contain - -- a controlled object. - - if Preprocess - and then Top_Level - and then No (Last_Top_Level_Ctrl_Construct) - and then Counter_Val > Old_Counter_Val - then - Last_Top_Level_Ctrl_Construct := Decl; - end if; - end if; - - -- Handle a rare case caused by a controlled transient variable - -- created as part of a record init proc. The variable is wrapped - -- in a block, but the block is not associated with a transient - -- scope. - - elsif Nkind (Decl) = N_Block_Statement - and then Inside_Init_Proc - then - Old_Counter_Val := Counter_Val; - - if Present (Handled_Statement_Sequence (Decl)) then - Process_Declarations - (Statements (Handled_Statement_Sequence (Decl)), - Preprocess); - end if; - - Process_Declarations (Declarations (Decl), Preprocess); - - -- Either the declaration or statement list of the block has a - -- controlled object. - - if Preprocess - and then Top_Level - and then No (Last_Top_Level_Ctrl_Construct) - and then Counter_Val > Old_Counter_Val - then - Last_Top_Level_Ctrl_Construct := Decl; - end if; - - -- Handle the case where the original context has been wrapped in - -- a block to avoid interference between exception handlers and - -- At_End handlers. Treat the block as transparent and process its - -- contents. - - elsif Nkind (Decl) = N_Block_Statement - and then Is_Finalization_Wrapper (Decl) - then - if Present (Handled_Statement_Sequence (Decl)) then - Process_Declarations - (Statements (Handled_Statement_Sequence (Decl)), - Preprocess); - end if; - - Process_Declarations (Declarations (Decl), Preprocess); - end if; - - Prev_Non_Pragma (Decl); - end loop; - end Process_Declarations; - - -------------------------------- - -- Process_Object_Declaration -- - -------------------------------- - - procedure Process_Object_Declaration - (Decl : Node_Id; - Has_No_Init : Boolean := False; - Is_Protected : Boolean := False) - is - Obj_Id : constant Entity_Id := Defining_Identifier (Decl); - Loc : constant Source_Ptr := Sloc (Decl); - Body_Ins : Node_Id; - Count_Ins : Node_Id; - Fin_Call : Node_Id; - Fin_Stmts : List_Id; - Inc_Decl : Node_Id; - Label : Node_Id; - Label_Id : Entity_Id; - Obj_Ref : Node_Id; - Obj_Typ : Entity_Id; - - function Build_BIP_Cleanup_Stmts (Func_Id : Entity_Id) return Node_Id; - -- Once it has been established that the current object is in fact a - -- return object of build-in-place function Func_Id, generate the - -- following cleanup code: - -- - -- if BIPallocfrom > Secondary_Stack'Pos - -- and then BIPfinalizationmaster /= null - -- then - -- declare - -- type Ptr_Typ is access Obj_Typ; - -- for Ptr_Typ'Storage_Pool - -- use Base_Pool (BIPfinalizationmaster); - -- begin - -- Free (Ptr_Typ (Temp)); - -- end; - -- end if; - -- - -- Obj_Typ is the type of the current object, Temp is the original - -- allocation which Obj_Id renames. - - procedure Find_Last_Init - (Decl : Node_Id; - Typ : Entity_Id; - Last_Init : out Node_Id; - Body_Insert : out Node_Id); - -- An object declaration has at least one and at most two init calls: - -- that of the type and the user-defined initialize. Given an object - -- declaration, Last_Init denotes the last initialization call which - -- follows the declaration. Body_Insert denotes the place where the - -- finalizer body could be potentially inserted. - - ----------------------------- - -- Build_BIP_Cleanup_Stmts -- - ----------------------------- - - function Build_BIP_Cleanup_Stmts - (Func_Id : Entity_Id) return Node_Id - is - Decls : constant List_Id := New_List; - Fin_Mas_Id : constant Entity_Id := - Build_In_Place_Formal - (Func_Id, BIP_Finalization_Master); - Obj_Typ : constant Entity_Id := Etype (Func_Id); - Temp_Id : constant Entity_Id := - Entity (Prefix (Name (Parent (Obj_Id)))); - - Cond : Node_Id; - Free_Blk : Node_Id; - Free_Stmt : Node_Id; - Pool_Id : Entity_Id; - Ptr_Typ : Entity_Id; - - begin - -- Generate: - -- Pool_Id renames Base_Pool (BIPfinalizationmaster.all).all; - - Pool_Id := Make_Temporary (Loc, 'P'); - - Append_To (Decls, - Make_Object_Renaming_Declaration (Loc, - Defining_Identifier => Pool_Id, - Subtype_Mark => - New_Reference_To (RTE (RE_Root_Storage_Pool), Loc), - Name => - Make_Explicit_Dereference (Loc, - Prefix => - Make_Function_Call (Loc, - Name => - New_Reference_To (RTE (RE_Base_Pool), Loc), - Parameter_Associations => New_List ( - Make_Explicit_Dereference (Loc, - Prefix => New_Reference_To (Fin_Mas_Id, Loc))))))); - - -- Create an access type which uses the storage pool of the - -- caller's finalization master. - - -- Generate: - -- type Ptr_Typ is access Obj_Typ; - - Ptr_Typ := Make_Temporary (Loc, 'P'); - - Append_To (Decls, - Make_Full_Type_Declaration (Loc, - Defining_Identifier => Ptr_Typ, - Type_Definition => - Make_Access_To_Object_Definition (Loc, - Subtype_Indication => New_Reference_To (Obj_Typ, Loc)))); - - -- Perform minor decoration in order to set the master and the - -- storage pool attributes. - - Set_Ekind (Ptr_Typ, E_Access_Type); - Set_Finalization_Master (Ptr_Typ, Fin_Mas_Id); - Set_Associated_Storage_Pool (Ptr_Typ, Pool_Id); - - -- Create an explicit free statement. Note that the free uses the - -- caller's pool expressed as a renaming. - - Free_Stmt := - Make_Free_Statement (Loc, - Expression => - Unchecked_Convert_To (Ptr_Typ, - New_Reference_To (Temp_Id, Loc))); - - Set_Storage_Pool (Free_Stmt, Pool_Id); - - -- Create a block to house the dummy type and the instantiation as - -- well as to perform the cleanup the temporary. - - -- Generate: - -- declare - -- <Decls> - -- begin - -- Free (Ptr_Typ (Temp_Id)); - -- end; - - Free_Blk := - Make_Block_Statement (Loc, - Declarations => Decls, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Free_Stmt))); - - -- Generate: - -- if BIPfinalizationmaster /= null then - - Cond := - Make_Op_Ne (Loc, - Left_Opnd => New_Reference_To (Fin_Mas_Id, Loc), - Right_Opnd => Make_Null (Loc)); - - -- For constrained or tagged results escalate the condition to - -- include the allocation format. Generate: - -- - -- if BIPallocform > Secondary_Stack'Pos - -- and then BIPfinalizationmaster /= null - -- then - - if not Is_Constrained (Obj_Typ) - or else Is_Tagged_Type (Obj_Typ) - then - declare - Alloc : constant Entity_Id := - Build_In_Place_Formal (Func_Id, BIP_Alloc_Form); - begin - Cond := - Make_And_Then (Loc, - Left_Opnd => - Make_Op_Gt (Loc, - Left_Opnd => New_Reference_To (Alloc, Loc), - Right_Opnd => - Make_Integer_Literal (Loc, - UI_From_Int - (BIP_Allocation_Form'Pos (Secondary_Stack)))), - - Right_Opnd => Cond); - end; - end if; - - -- Generate: - -- if <Cond> then - -- <Free_Blk> - -- end if; - - return - Make_If_Statement (Loc, - Condition => Cond, - Then_Statements => New_List (Free_Blk)); - end Build_BIP_Cleanup_Stmts; - - -------------------- - -- Find_Last_Init -- - -------------------- - - procedure Find_Last_Init - (Decl : Node_Id; - Typ : Entity_Id; - Last_Init : out Node_Id; - Body_Insert : out Node_Id) - is - Nod_1 : Node_Id := Empty; - Nod_2 : Node_Id := Empty; - Utyp : Entity_Id; - - function Is_Init_Call - (N : Node_Id; - Typ : Entity_Id) return Boolean; - -- Given an arbitrary node, determine whether N is a procedure - -- call and if it is, try to match the name of the call with the - -- [Deep_]Initialize proc of Typ. - - function Next_Suitable_Statement (Stmt : Node_Id) return Node_Id; - -- Given a statement which is part of a list, return the next - -- real statement while skipping over dynamic elab checks. - - ------------------ - -- Is_Init_Call -- - ------------------ - - function Is_Init_Call - (N : Node_Id; - Typ : Entity_Id) return Boolean - is - begin - -- A call to [Deep_]Initialize is always direct - - if Nkind (N) = N_Procedure_Call_Statement - and then Nkind (Name (N)) = N_Identifier - then - declare - Call_Ent : constant Entity_Id := Entity (Name (N)); - Deep_Init : constant Entity_Id := - TSS (Typ, TSS_Deep_Initialize); - Init : Entity_Id := Empty; - - begin - -- A type may have controlled components but not be - -- controlled. - - if Is_Controlled (Typ) then - Init := Find_Prim_Op (Typ, Name_Initialize); - - if Present (Init) then - Init := Ultimate_Alias (Init); - end if; - end if; - - return - (Present (Deep_Init) and then Call_Ent = Deep_Init) - or else - (Present (Init) and then Call_Ent = Init); - end; - end if; - - return False; - end Is_Init_Call; - - ----------------------------- - -- Next_Suitable_Statement -- - ----------------------------- - - function Next_Suitable_Statement (Stmt : Node_Id) return Node_Id is - Result : Node_Id := Next (Stmt); - - begin - -- Skip over access-before-elaboration checks - - if Dynamic_Elaboration_Checks - and then Nkind (Result) = N_Raise_Program_Error - then - Result := Next (Result); - end if; - - return Result; - end Next_Suitable_Statement; - - -- Start of processing for Find_Last_Init - - begin - Last_Init := Decl; - Body_Insert := Empty; - - -- Object renamings and objects associated with controlled - -- function results do not have initialization calls. - - if Has_No_Init then - return; - end if; - - if Is_Concurrent_Type (Typ) then - Utyp := Corresponding_Record_Type (Typ); - else - Utyp := Typ; - end if; - - if Is_Private_Type (Utyp) - and then Present (Full_View (Utyp)) - then - Utyp := Full_View (Utyp); - end if; - - -- The init procedures are arranged as follows: - - -- Object : Controlled_Type; - -- Controlled_TypeIP (Object); - -- [[Deep_]Initialize (Object);] - - -- where the user-defined initialize may be optional or may appear - -- inside a block when abort deferral is needed. - - Nod_1 := Next_Suitable_Statement (Decl); - if Present (Nod_1) then - Nod_2 := Next_Suitable_Statement (Nod_1); - - -- The statement following an object declaration is always a - -- call to the type init proc. - - Last_Init := Nod_1; - end if; - - -- Optional user-defined init or deep init processing - - if Present (Nod_2) then - - -- The statement following the type init proc may be a block - -- statement in cases where abort deferral is required. - - if Nkind (Nod_2) = N_Block_Statement then - declare - HSS : constant Node_Id := - Handled_Statement_Sequence (Nod_2); - Stmt : Node_Id; - - begin - if Present (HSS) - and then Present (Statements (HSS)) - then - Stmt := First (Statements (HSS)); - - -- Examine individual block statements and locate the - -- call to [Deep_]Initialze. - - while Present (Stmt) loop - if Is_Init_Call (Stmt, Utyp) then - Last_Init := Stmt; - Body_Insert := Nod_2; - - exit; - end if; - - Next (Stmt); - end loop; - end if; - end; - - elsif Is_Init_Call (Nod_2, Utyp) then - Last_Init := Nod_2; - end if; - end if; - end Find_Last_Init; - - -- Start of processing for Process_Object_Declaration - - begin - Obj_Ref := New_Reference_To (Obj_Id, Loc); - Obj_Typ := Base_Type (Etype (Obj_Id)); - - -- Handle access types - - if Is_Access_Type (Obj_Typ) then - Obj_Ref := Make_Explicit_Dereference (Loc, Obj_Ref); - Obj_Typ := Directly_Designated_Type (Obj_Typ); - end if; - - Set_Etype (Obj_Ref, Obj_Typ); - - -- Set a new value for the state counter and insert the statement - -- after the object declaration. Generate: - -- - -- Counter := <value>; - - Inc_Decl := - Make_Assignment_Statement (Loc, - Name => New_Reference_To (Counter_Id, Loc), - Expression => Make_Integer_Literal (Loc, Counter_Val)); - - -- Insert the counter after all initialization has been done. The - -- place of insertion depends on the context. When dealing with a - -- controlled function, the counter is inserted directly after the - -- declaration because such objects lack init calls. - - Find_Last_Init (Decl, Obj_Typ, Count_Ins, Body_Ins); - - Insert_After (Count_Ins, Inc_Decl); - Analyze (Inc_Decl); - - -- If the current declaration is the last in the list, the finalizer - -- body needs to be inserted after the set counter statement for the - -- current object declaration. This is complicated by the fact that - -- the set counter statement may appear in abort deferred block. In - -- that case, the proper insertion place is after the block. - - if No (Finalizer_Insert_Nod) then - - -- Insertion after an abort deffered block - - if Present (Body_Ins) then - Finalizer_Insert_Nod := Body_Ins; - else - Finalizer_Insert_Nod := Inc_Decl; - end if; - end if; - - -- Create the associated label with this object, generate: - -- - -- L<counter> : label; - - Label_Id := - Make_Identifier (Loc, New_External_Name ('L', Counter_Val)); - Set_Entity - (Label_Id, Make_Defining_Identifier (Loc, Chars (Label_Id))); - Label := Make_Label (Loc, Label_Id); - - Prepend_To (Finalizer_Decls, - Make_Implicit_Label_Declaration (Loc, - Defining_Identifier => Entity (Label_Id), - Label_Construct => Label)); - - -- Create the associated jump with this object, generate: - -- - -- when <counter> => - -- goto L<counter>; - - Prepend_To (Jump_Alts, - Make_Case_Statement_Alternative (Loc, - Discrete_Choices => New_List ( - Make_Integer_Literal (Loc, Counter_Val)), - Statements => New_List ( - Make_Goto_Statement (Loc, - Name => New_Reference_To (Entity (Label_Id), Loc))))); - - -- Insert the jump destination, generate: - -- - -- <<L<counter>>> - - Append_To (Finalizer_Stmts, Label); - - -- Processing for simple protected objects. Such objects require - -- manual finalization of their lock managers. - - if Is_Protected then - Fin_Stmts := No_List; - - if Is_Simple_Protected_Type (Obj_Typ) then - Fin_Call := Cleanup_Protected_Object (Decl, Obj_Ref); - - if Present (Fin_Call) then - Fin_Stmts := New_List (Fin_Call); - end if; - - elsif Has_Simple_Protected_Object (Obj_Typ) then - if Is_Record_Type (Obj_Typ) then - Fin_Stmts := Cleanup_Record (Decl, Obj_Ref, Obj_Typ); - elsif Is_Array_Type (Obj_Typ) then - Fin_Stmts := Cleanup_Array (Decl, Obj_Ref, Obj_Typ); - end if; - end if; - - -- Generate: - -- begin - -- System.Tasking.Protected_Objects.Finalize_Protection - -- (Obj._object); - - -- exception - -- when others => - -- null; - -- end; - - if Present (Fin_Stmts) then - Append_To (Finalizer_Stmts, - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => Fin_Stmts, - - Exception_Handlers => New_List ( - Make_Exception_Handler (Loc, - Exception_Choices => New_List ( - Make_Others_Choice (Loc)), - - Statements => New_List ( - Make_Null_Statement (Loc))))))); - end if; - - -- Processing for regular controlled objects - - else - -- Generate: - -- [Deep_]Finalize (Obj); -- No_Exception_Propagation - - -- begin -- Exception handlers allowed - -- [Deep_]Finalize (Obj); - - -- exception - -- when Id : others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, Id); - -- end if; - -- end; - - Fin_Call := - Make_Final_Call ( - Obj_Ref => Obj_Ref, - Typ => Obj_Typ); - - -- For CodePeer, the exception handlers normally generated here - -- generate complex flowgraphs which result in capacity problems. - -- Omitting these handlers for CodePeer is justified as follows: - - -- If a handler is dead, then omitting it is surely ok - - -- If a handler is live, then CodePeer should flag the - -- potentially-exception-raising construct that causes it - -- to be live. That is what we are interested in, not what - -- happens after the exception is raised. - - if Exceptions_OK and not CodePeer_Mode then - Fin_Stmts := New_List ( - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Fin_Call), - - Exception_Handlers => New_List ( - Build_Exception_Handler - (Finalizer_Data, For_Package))))); - - -- When exception handlers are prohibited, the finalization call - -- appears unprotected. Any exception raised during finalization - -- will bypass the circuitry which ensures the cleanup of all - -- remaining objects. - - else - Fin_Stmts := New_List (Fin_Call); - end if; - - -- If we are dealing with a return object of a build-in-place - -- function, generate the following cleanup statements: - - -- if BIPallocfrom > Secondary_Stack'Pos - -- and then BIPfinalizationmaster /= null - -- then - -- declare - -- type Ptr_Typ is access Obj_Typ; - -- for Ptr_Typ'Storage_Pool use - -- Base_Pool (BIPfinalizationmaster.all).all; - -- begin - -- Free (Ptr_Typ (Temp)); - -- end; - -- end if; - -- - -- The generated code effectively detaches the temporary from the - -- caller finalization master and deallocates the object. This is - -- disabled on .NET/JVM because pools are not supported. - - if VM_Target = No_VM and then Is_Return_Object (Obj_Id) then - declare - Func_Id : constant Entity_Id := Enclosing_Function (Obj_Id); - begin - if Is_Build_In_Place_Function (Func_Id) - and then Needs_BIP_Finalization_Master (Func_Id) - then - Append_To (Fin_Stmts, Build_BIP_Cleanup_Stmts (Func_Id)); - end if; - end; - end if; - - if Ekind_In (Obj_Id, E_Constant, E_Variable) - and then Present (Status_Flag_Or_Transient_Decl (Obj_Id)) - then - -- Temporaries created for the purpose of "exporting" a - -- controlled transient out of an Expression_With_Actions (EWA) - -- need guards. The following illustrates the usage of such - -- temporaries. - - -- Access_Typ : access [all] Obj_Typ; - -- Temp : Access_Typ := null; - -- <Counter> := ...; - - -- do - -- Ctrl_Trans : [access [all]] Obj_Typ := ...; - -- Temp := Access_Typ (Ctrl_Trans); -- when a pointer - -- <or> - -- Temp := Ctrl_Trans'Unchecked_Access; - -- in ... end; - - -- The finalization machinery does not process EWA nodes as - -- this may lead to premature finalization of expressions. Note - -- that Temp is marked as being properly initialized regardless - -- of whether the initialization of Ctrl_Trans succeeded. Since - -- a failed initialization may leave Temp with a value of null, - -- add a guard to handle this case: - - -- if Obj /= null then - -- <object finalization statements> - -- end if; - - if Nkind (Status_Flag_Or_Transient_Decl (Obj_Id)) = - N_Object_Declaration - then - Fin_Stmts := New_List ( - Make_If_Statement (Loc, - Condition => - Make_Op_Ne (Loc, - Left_Opnd => New_Reference_To (Obj_Id, Loc), - Right_Opnd => Make_Null (Loc)), - Then_Statements => Fin_Stmts)); - - -- Return objects use a flag to aid in processing their - -- potential finalization when the enclosing function fails - -- to return properly. Generate: - - -- if not Flag then - -- <object finalization statements> - -- end if; - - else - Fin_Stmts := New_List ( - Make_If_Statement (Loc, - Condition => - Make_Op_Not (Loc, - Right_Opnd => - New_Reference_To - (Status_Flag_Or_Transient_Decl (Obj_Id), Loc)), - - Then_Statements => Fin_Stmts)); - end if; - end if; - end if; - - Append_List_To (Finalizer_Stmts, Fin_Stmts); - - -- Since the declarations are examined in reverse, the state counter - -- must be decremented in order to keep with the true position of - -- objects. - - Counter_Val := Counter_Val - 1; - end Process_Object_Declaration; - - ------------------------------------- - -- Process_Tagged_Type_Declaration -- - ------------------------------------- - - procedure Process_Tagged_Type_Declaration (Decl : Node_Id) is - Typ : constant Entity_Id := Defining_Identifier (Decl); - DT_Ptr : constant Entity_Id := - Node (First_Elmt (Access_Disp_Table (Typ))); - begin - -- Generate: - -- Ada.Tags.Unregister_Tag (<Typ>P); - - Append_To (Tagged_Type_Stmts, - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (RTE (RE_Unregister_Tag), Loc), - Parameter_Associations => New_List ( - New_Reference_To (DT_Ptr, Loc)))); - end Process_Tagged_Type_Declaration; - - -- Start of processing for Build_Finalizer - - begin - Fin_Id := Empty; - - -- Do not perform this expansion in Alfa mode because it is not - -- necessary. - - if Alfa_Mode then - return; - end if; - - -- Step 1: Extract all lists which may contain controlled objects or - -- library-level tagged types. - - if For_Package_Spec then - Decls := Visible_Declarations (Specification (N)); - Priv_Decls := Private_Declarations (Specification (N)); - - -- Retrieve the package spec id - - Spec_Id := Defining_Unit_Name (Specification (N)); - - if Nkind (Spec_Id) = N_Defining_Program_Unit_Name then - Spec_Id := Defining_Identifier (Spec_Id); - end if; - - -- Accept statement, block, entry body, package body, protected body, - -- subprogram body or task body. - - else - Decls := Declarations (N); - HSS := Handled_Statement_Sequence (N); - - if Present (HSS) then - if Present (Statements (HSS)) then - Stmts := Statements (HSS); - end if; - - if Present (At_End_Proc (HSS)) then - Prev_At_End := At_End_Proc (HSS); - end if; - end if; - - -- Retrieve the package spec id for package bodies - - if For_Package_Body then - Spec_Id := Corresponding_Spec (N); - end if; - end if; - - -- Do not process nested packages since those are handled by the - -- enclosing scope's finalizer. Do not process non-expanded package - -- instantiations since those will be re-analyzed and re-expanded. - - if For_Package - and then - (not Is_Library_Level_Entity (Spec_Id) - - -- Nested packages are considered to be library level entities, - -- but do not need to be processed separately. True library level - -- packages have a scope value of 1. - - or else Scope_Depth_Value (Spec_Id) /= Uint_1 - or else (Is_Generic_Instance (Spec_Id) - and then Package_Instantiation (Spec_Id) /= N)) - then - return; - end if; - - -- Step 2: Object [pre]processing - - if For_Package then - - -- Preprocess the visible declarations now in order to obtain the - -- correct number of controlled object by the time the private - -- declarations are processed. - - Process_Declarations (Decls, Preprocess => True, Top_Level => True); - - -- From all the possible contexts, only package specifications may - -- have private declarations. - - if For_Package_Spec then - Process_Declarations - (Priv_Decls, Preprocess => True, Top_Level => True); - end if; - - -- The current context may lack controlled objects, but require some - -- other form of completion (task termination for instance). In such - -- cases, the finalizer must be created and carry the additional - -- statements. - - if Acts_As_Clean or Has_Ctrl_Objs or Has_Tagged_Types then - Build_Components; - end if; - - -- The preprocessing has determined that the context has controlled - -- objects or library-level tagged types. - - if Has_Ctrl_Objs or Has_Tagged_Types then - - -- Private declarations are processed first in order to preserve - -- possible dependencies between public and private objects. - - if For_Package_Spec then - Process_Declarations (Priv_Decls); - end if; - - Process_Declarations (Decls); - end if; - - -- Non-package case - - else - -- Preprocess both declarations and statements - - Process_Declarations (Decls, Preprocess => True, Top_Level => True); - Process_Declarations (Stmts, Preprocess => True, Top_Level => True); - - -- At this point it is known that N has controlled objects. Ensure - -- that N has a declarative list since the finalizer spec will be - -- attached to it. - - if Has_Ctrl_Objs and then No (Decls) then - Set_Declarations (N, New_List); - Decls := Declarations (N); - Spec_Decls := Decls; - end if; - - -- The current context may lack controlled objects, but require some - -- other form of completion (task termination for instance). In such - -- cases, the finalizer must be created and carry the additional - -- statements. - - if Acts_As_Clean or Has_Ctrl_Objs or Has_Tagged_Types then - Build_Components; - end if; - - if Has_Ctrl_Objs or Has_Tagged_Types then - Process_Declarations (Stmts); - Process_Declarations (Decls); - end if; - end if; - - -- Step 3: Finalizer creation - - if Acts_As_Clean or Has_Ctrl_Objs or Has_Tagged_Types then - Create_Finalizer; - end if; - end Build_Finalizer; - - -------------------------- - -- Build_Finalizer_Call -- - -------------------------- - - procedure Build_Finalizer_Call (N : Node_Id; Fin_Id : Entity_Id) is - Is_Prot_Body : constant Boolean := - Nkind (N) = N_Subprogram_Body - and then Is_Protected_Subprogram_Body (N); - -- Determine whether N denotes the protected version of a subprogram - -- which belongs to a protected type. - - Loc : constant Source_Ptr := Sloc (N); - HSS : Node_Id; - - begin - -- Do not perform this expansion in Alfa mode because we do not create - -- finalizers in the first place. - - if Alfa_Mode then - return; - end if; - - -- The At_End handler should have been assimilated by the finalizer - - HSS := Handled_Statement_Sequence (N); - pragma Assert (No (At_End_Proc (HSS))); - - -- If the construct to be cleaned up is a protected subprogram body, the - -- finalizer call needs to be associated with the block which wraps the - -- unprotected version of the subprogram. The following illustrates this - -- scenario: - - -- procedure Prot_SubpP is - -- procedure finalizer is - -- begin - -- Service_Entries (Prot_Obj); - -- Abort_Undefer; - -- end finalizer; - - -- begin - -- . . . - -- begin - -- Prot_SubpN (Prot_Obj); - -- at end - -- finalizer; - -- end; - -- end Prot_SubpP; - - if Is_Prot_Body then - HSS := Handled_Statement_Sequence (Last (Statements (HSS))); - - -- An At_End handler and regular exception handlers cannot coexist in - -- the same statement sequence. Wrap the original statements in a block. - - elsif Present (Exception_Handlers (HSS)) then - declare - End_Lab : constant Node_Id := End_Label (HSS); - Block : Node_Id; - - begin - Block := - Make_Block_Statement (Loc, Handled_Statement_Sequence => HSS); - - Set_Handled_Statement_Sequence (N, - Make_Handled_Sequence_Of_Statements (Loc, New_List (Block))); - - HSS := Handled_Statement_Sequence (N); - Set_End_Label (HSS, End_Lab); - end; - end if; - - Set_At_End_Proc (HSS, New_Reference_To (Fin_Id, Loc)); - - Analyze (At_End_Proc (HSS)); - Expand_At_End_Handler (HSS, Empty); - end Build_Finalizer_Call; - - --------------------- - -- Build_Late_Proc -- - --------------------- - - procedure Build_Late_Proc (Typ : Entity_Id; Nam : Name_Id) is - begin - for Final_Prim in Name_Of'Range loop - if Name_Of (Final_Prim) = Nam then - Set_TSS (Typ, - Make_Deep_Proc - (Prim => Final_Prim, - Typ => Typ, - Stmts => Make_Deep_Record_Body (Final_Prim, Typ))); - end if; - end loop; - end Build_Late_Proc; - - ------------------------------- - -- Build_Object_Declarations -- - ------------------------------- - - procedure Build_Object_Declarations - (Data : out Finalization_Exception_Data; - Decls : List_Id; - Loc : Source_Ptr; - For_Package : Boolean := False) - is - A_Expr : Node_Id; - E_Decl : Node_Id; - - begin - pragma Assert (Decls /= No_List); - - -- Always set the proper location as it may be needed even when - -- exception propagation is forbidden. - - Data.Loc := Loc; - - if Restriction_Active (No_Exception_Propagation) then - Data.Abort_Id := Empty; - Data.E_Id := Empty; - Data.Raised_Id := Empty; - return; - end if; - - Data.Raised_Id := Make_Temporary (Loc, 'R'); - - -- In certain scenarios, finalization can be triggered by an abort. If - -- the finalization itself fails and raises an exception, the resulting - -- Program_Error must be supressed and replaced by an abort signal. In - -- order to detect this scenario, save the state of entry into the - -- finalization code. - - -- No need to do this for VM case, since VM version of Ada.Exceptions - -- does not include routine Raise_From_Controlled_Operation which is the - -- the sole user of flag Abort. - - -- This is not needed for library-level finalizers as they are called - -- by the environment task and cannot be aborted. - - if Abort_Allowed - and then VM_Target = No_VM - and then not For_Package - then - Data.Abort_Id := Make_Temporary (Loc, 'A'); - - A_Expr := New_Reference_To (RTE (RE_Triggered_By_Abort), Loc); - - -- Generate: - - -- Abort_Id : constant Boolean := <A_Expr>; - - Append_To (Decls, - Make_Object_Declaration (Loc, - Defining_Identifier => Data.Abort_Id, - Constant_Present => True, - Object_Definition => New_Reference_To (Standard_Boolean, Loc), - Expression => A_Expr)); - - else - -- No abort, .NET/JVM or library-level finalizers - - Data.Abort_Id := Empty; - end if; - - if Exception_Extra_Info then - Data.E_Id := Make_Temporary (Loc, 'E'); - - -- Generate: - - -- E_Id : Exception_Occurrence; - - E_Decl := - Make_Object_Declaration (Loc, - Defining_Identifier => Data.E_Id, - Object_Definition => - New_Reference_To (RTE (RE_Exception_Occurrence), Loc)); - Set_No_Initialization (E_Decl); - - Append_To (Decls, E_Decl); - - else - Data.E_Id := Empty; - end if; - - -- Generate: - - -- Raised_Id : Boolean := False; - - Append_To (Decls, - Make_Object_Declaration (Loc, - Defining_Identifier => Data.Raised_Id, - Object_Definition => New_Reference_To (Standard_Boolean, Loc), - Expression => New_Reference_To (Standard_False, Loc))); - end Build_Object_Declarations; - - --------------------------- - -- Build_Raise_Statement -- - --------------------------- - - function Build_Raise_Statement - (Data : Finalization_Exception_Data) return Node_Id - is - Stmt : Node_Id; - Expr : Node_Id; - - begin - -- Standard run-time and .NET/JVM targets use the specialized routine - -- Raise_From_Controlled_Operation. - - if Exception_Extra_Info - and then RTE_Available (RE_Raise_From_Controlled_Operation) - then - Stmt := - Make_Procedure_Call_Statement (Data.Loc, - Name => - New_Reference_To - (RTE (RE_Raise_From_Controlled_Operation), Data.Loc), - Parameter_Associations => - New_List (New_Reference_To (Data.E_Id, Data.Loc))); - - -- Restricted run-time: exception messages are not supported and hence - -- Raise_From_Controlled_Operation is not supported. Raise Program_Error - -- instead. - - else - Stmt := - Make_Raise_Program_Error (Data.Loc, - Reason => PE_Finalize_Raised_Exception); - end if; - - -- Generate: - - -- Raised_Id and then not Abort_Id - -- <or> - -- Raised_Id - - Expr := New_Reference_To (Data.Raised_Id, Data.Loc); - - if Present (Data.Abort_Id) then - Expr := Make_And_Then (Data.Loc, - Left_Opnd => Expr, - Right_Opnd => - Make_Op_Not (Data.Loc, - Right_Opnd => New_Reference_To (Data.Abort_Id, Data.Loc))); - end if; - - -- Generate: - - -- if Raised_Id and then not Abort_Id then - -- Raise_From_Controlled_Operation (E_Id); - -- <or> - -- raise Program_Error; -- restricted runtime - -- end if; - - return - Make_If_Statement (Data.Loc, - Condition => Expr, - Then_Statements => New_List (Stmt)); - end Build_Raise_Statement; - - ----------------------------- - -- Build_Record_Deep_Procs -- - ----------------------------- - - procedure Build_Record_Deep_Procs (Typ : Entity_Id) is - begin - Set_TSS (Typ, - Make_Deep_Proc - (Prim => Initialize_Case, - Typ => Typ, - Stmts => Make_Deep_Record_Body (Initialize_Case, Typ))); - - if not Is_Immutably_Limited_Type (Typ) then - Set_TSS (Typ, - Make_Deep_Proc - (Prim => Adjust_Case, - Typ => Typ, - Stmts => Make_Deep_Record_Body (Adjust_Case, Typ))); - end if; - - -- Do not generate Deep_Finalize and Finalize_Address if finalization is - -- suppressed since these routine will not be used. - - if not Restriction_Active (No_Finalization) then - Set_TSS (Typ, - Make_Deep_Proc - (Prim => Finalize_Case, - Typ => Typ, - Stmts => Make_Deep_Record_Body (Finalize_Case, Typ))); - - -- Create TSS primitive Finalize_Address for non-VM targets. JVM and - -- .NET do not support address arithmetic and unchecked conversions. - - if VM_Target = No_VM then - Set_TSS (Typ, - Make_Deep_Proc - (Prim => Address_Case, - Typ => Typ, - Stmts => Make_Deep_Record_Body (Address_Case, Typ))); - end if; - end if; - end Build_Record_Deep_Procs; - - ------------------- - -- Cleanup_Array -- - ------------------- - - function Cleanup_Array - (N : Node_Id; - Obj : Node_Id; - Typ : Entity_Id) return List_Id - is - Loc : constant Source_Ptr := Sloc (N); - Index_List : constant List_Id := New_List; - - function Free_Component return List_Id; - -- Generate the code to finalize the task or protected subcomponents - -- of a single component of the array. - - function Free_One_Dimension (Dim : Int) return List_Id; - -- Generate a loop over one dimension of the array - - -------------------- - -- Free_Component -- - -------------------- - - function Free_Component return List_Id is - Stmts : List_Id := New_List; - Tsk : Node_Id; - C_Typ : constant Entity_Id := Component_Type (Typ); - - begin - -- Component type is known to contain tasks or protected objects - - Tsk := - Make_Indexed_Component (Loc, - Prefix => Duplicate_Subexpr_No_Checks (Obj), - Expressions => Index_List); - - Set_Etype (Tsk, C_Typ); - - if Is_Task_Type (C_Typ) then - Append_To (Stmts, Cleanup_Task (N, Tsk)); - - elsif Is_Simple_Protected_Type (C_Typ) then - Append_To (Stmts, Cleanup_Protected_Object (N, Tsk)); - - elsif Is_Record_Type (C_Typ) then - Stmts := Cleanup_Record (N, Tsk, C_Typ); - - elsif Is_Array_Type (C_Typ) then - Stmts := Cleanup_Array (N, Tsk, C_Typ); - end if; - - return Stmts; - end Free_Component; - - ------------------------ - -- Free_One_Dimension -- - ------------------------ - - function Free_One_Dimension (Dim : Int) return List_Id is - Index : Entity_Id; - - begin - if Dim > Number_Dimensions (Typ) then - return Free_Component; - - -- Here we generate the required loop - - else - Index := Make_Temporary (Loc, 'J'); - Append (New_Reference_To (Index, Loc), Index_List); - - return New_List ( - Make_Implicit_Loop_Statement (N, - Identifier => Empty, - Iteration_Scheme => - Make_Iteration_Scheme (Loc, - Loop_Parameter_Specification => - Make_Loop_Parameter_Specification (Loc, - Defining_Identifier => Index, - Discrete_Subtype_Definition => - Make_Attribute_Reference (Loc, - Prefix => Duplicate_Subexpr (Obj), - Attribute_Name => Name_Range, - Expressions => New_List ( - Make_Integer_Literal (Loc, Dim))))), - Statements => Free_One_Dimension (Dim + 1))); - end if; - end Free_One_Dimension; - - -- Start of processing for Cleanup_Array - - begin - return Free_One_Dimension (1); - end Cleanup_Array; - - -------------------- - -- Cleanup_Record -- - -------------------- - - function Cleanup_Record - (N : Node_Id; - Obj : Node_Id; - Typ : Entity_Id) return List_Id - is - Loc : constant Source_Ptr := Sloc (N); - Tsk : Node_Id; - Comp : Entity_Id; - Stmts : constant List_Id := New_List; - U_Typ : constant Entity_Id := Underlying_Type (Typ); - - begin - if Has_Discriminants (U_Typ) - and then Nkind (Parent (U_Typ)) = N_Full_Type_Declaration - and then - Nkind (Type_Definition (Parent (U_Typ))) = N_Record_Definition - and then - Present - (Variant_Part (Component_List (Type_Definition (Parent (U_Typ))))) - then - -- For now, do not attempt to free a component that may appear in a - -- variant, and instead issue a warning. Doing this "properly" would - -- require building a case statement and would be quite a mess. Note - -- that the RM only requires that free "work" for the case of a task - -- access value, so already we go way beyond this in that we deal - -- with the array case and non-discriminated record cases. - - Error_Msg_N - ("task/protected object in variant record will not be freed??", N); - return New_List (Make_Null_Statement (Loc)); - end if; - - Comp := First_Component (Typ); - while Present (Comp) loop - if Has_Task (Etype (Comp)) - or else Has_Simple_Protected_Object (Etype (Comp)) - then - Tsk := - Make_Selected_Component (Loc, - Prefix => Duplicate_Subexpr_No_Checks (Obj), - Selector_Name => New_Occurrence_Of (Comp, Loc)); - Set_Etype (Tsk, Etype (Comp)); - - if Is_Task_Type (Etype (Comp)) then - Append_To (Stmts, Cleanup_Task (N, Tsk)); - - elsif Is_Simple_Protected_Type (Etype (Comp)) then - Append_To (Stmts, Cleanup_Protected_Object (N, Tsk)); - - elsif Is_Record_Type (Etype (Comp)) then - - -- Recurse, by generating the prefix of the argument to - -- the eventual cleanup call. - - Append_List_To (Stmts, Cleanup_Record (N, Tsk, Etype (Comp))); - - elsif Is_Array_Type (Etype (Comp)) then - Append_List_To (Stmts, Cleanup_Array (N, Tsk, Etype (Comp))); - end if; - end if; - - Next_Component (Comp); - end loop; - - return Stmts; - end Cleanup_Record; - - ------------------------------ - -- Cleanup_Protected_Object -- - ------------------------------ - - function Cleanup_Protected_Object - (N : Node_Id; - Ref : Node_Id) return Node_Id - is - Loc : constant Source_Ptr := Sloc (N); - - begin - -- For restricted run-time libraries (Ravenscar), tasks are - -- non-terminating, and protected objects can only appear at library - -- level, so we do not want finalization of protected objects. - - if Restricted_Profile then - return Empty; - - else - return - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (RTE (RE_Finalize_Protection), Loc), - Parameter_Associations => New_List (Concurrent_Ref (Ref))); - end if; - end Cleanup_Protected_Object; - - ------------------ - -- Cleanup_Task -- - ------------------ - - function Cleanup_Task - (N : Node_Id; - Ref : Node_Id) return Node_Id - is - Loc : constant Source_Ptr := Sloc (N); - - begin - -- For restricted run-time libraries (Ravenscar), tasks are - -- non-terminating and they can only appear at library level, so we do - -- not want finalization of task objects. - - if Restricted_Profile then - return Empty; - - else - return - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (RTE (RE_Free_Task), Loc), - Parameter_Associations => New_List (Concurrent_Ref (Ref))); - end if; - end Cleanup_Task; - - ------------------------------ - -- Check_Visibly_Controlled -- - ------------------------------ - - procedure Check_Visibly_Controlled - (Prim : Final_Primitives; - Typ : Entity_Id; - E : in out Entity_Id; - Cref : in out Node_Id) - is - Parent_Type : Entity_Id; - Op : Entity_Id; - - begin - if Is_Derived_Type (Typ) - and then Comes_From_Source (E) - and then not Present (Overridden_Operation (E)) - then - -- We know that the explicit operation on the type does not override - -- the inherited operation of the parent, and that the derivation - -- is from a private type that is not visibly controlled. - - Parent_Type := Etype (Typ); - Op := Find_Prim_Op (Parent_Type, Name_Of (Prim)); - - if Present (Op) then - E := Op; - - -- Wrap the object to be initialized into the proper - -- unchecked conversion, to be compatible with the operation - -- to be called. - - if Nkind (Cref) = N_Unchecked_Type_Conversion then - Cref := Unchecked_Convert_To (Parent_Type, Expression (Cref)); - else - Cref := Unchecked_Convert_To (Parent_Type, Cref); - end if; - end if; - end if; - end Check_Visibly_Controlled; - - ------------------------------- - -- CW_Or_Has_Controlled_Part -- - ------------------------------- - - function CW_Or_Has_Controlled_Part (T : Entity_Id) return Boolean is - begin - return Is_Class_Wide_Type (T) or else Needs_Finalization (T); - end CW_Or_Has_Controlled_Part; - - ------------------ - -- Convert_View -- - ------------------ - - function Convert_View - (Proc : Entity_Id; - Arg : Node_Id; - Ind : Pos := 1) return Node_Id - is - Fent : Entity_Id := First_Entity (Proc); - Ftyp : Entity_Id; - Atyp : Entity_Id; - - begin - for J in 2 .. Ind loop - Next_Entity (Fent); - end loop; - - Ftyp := Etype (Fent); - - if Nkind_In (Arg, N_Type_Conversion, N_Unchecked_Type_Conversion) then - Atyp := Entity (Subtype_Mark (Arg)); - else - Atyp := Etype (Arg); - end if; - - if Is_Abstract_Subprogram (Proc) and then Is_Tagged_Type (Ftyp) then - return Unchecked_Convert_To (Class_Wide_Type (Ftyp), Arg); - - elsif Ftyp /= Atyp - and then Present (Atyp) - and then (Is_Private_Type (Ftyp) or else Is_Private_Type (Atyp)) - and then Base_Type (Underlying_Type (Atyp)) = - Base_Type (Underlying_Type (Ftyp)) - then - return Unchecked_Convert_To (Ftyp, Arg); - - -- If the argument is already a conversion, as generated by - -- Make_Init_Call, set the target type to the type of the formal - -- directly, to avoid spurious typing problems. - - elsif Nkind_In (Arg, N_Unchecked_Type_Conversion, N_Type_Conversion) - and then not Is_Class_Wide_Type (Atyp) - then - Set_Subtype_Mark (Arg, New_Occurrence_Of (Ftyp, Sloc (Arg))); - Set_Etype (Arg, Ftyp); - return Arg; - - else - return Arg; - end if; - end Convert_View; - - ------------------------ - -- Enclosing_Function -- - ------------------------ - - function Enclosing_Function (E : Entity_Id) return Entity_Id is - Func_Id : Entity_Id; - - begin - Func_Id := E; - while Present (Func_Id) - and then Func_Id /= Standard_Standard - loop - if Ekind (Func_Id) = E_Function then - return Func_Id; - end if; - - Func_Id := Scope (Func_Id); - end loop; - - return Empty; - end Enclosing_Function; - - ------------------------------- - -- Establish_Transient_Scope -- - ------------------------------- - - -- This procedure is called each time a transient block has to be inserted - -- that is to say for each call to a function with unconstrained or tagged - -- result. It creates a new scope on the stack scope in order to enclose - -- all transient variables generated - - procedure Establish_Transient_Scope (N : Node_Id; Sec_Stack : Boolean) is - Loc : constant Source_Ptr := Sloc (N); - Wrap_Node : Node_Id; - - begin - -- Do not create a transient scope if we are already inside one - - for S in reverse Scope_Stack.First .. Scope_Stack.Last loop - if Scope_Stack.Table (S).Is_Transient then - if Sec_Stack then - Set_Uses_Sec_Stack (Scope_Stack.Table (S).Entity); - end if; - - return; - - -- If we have encountered Standard there are no enclosing - -- transient scopes. - - elsif Scope_Stack.Table (S).Entity = Standard_Standard then - exit; - end if; - end loop; - - Wrap_Node := Find_Node_To_Be_Wrapped (N); - - -- Case of no wrap node, false alert, no transient scope needed - - if No (Wrap_Node) then - null; - - -- If the node to wrap is an iteration_scheme, the expression is - -- one of the bounds, and the expansion will make an explicit - -- declaration for it (see Analyze_Iteration_Scheme, sem_ch5.adb), - -- so do not apply any transformations here. Same for an Ada 2012 - -- iterator specification, where a block is created for the expression - -- that build the container. - - elsif Nkind_In (Wrap_Node, N_Iteration_Scheme, - N_Iterator_Specification) - then - null; - - -- In formal verification mode, if the node to wrap is a pragma check, - -- this node and enclosed expression are not expanded, so do not apply - -- any transformations here. - - elsif Alfa_Mode - and then Nkind (Wrap_Node) = N_Pragma - and then Get_Pragma_Id (Wrap_Node) = Pragma_Check - then - null; - - else - Push_Scope (New_Internal_Entity (E_Block, Current_Scope, Loc, 'B')); - Set_Scope_Is_Transient; - - if Sec_Stack then - Set_Uses_Sec_Stack (Current_Scope); - Check_Restriction (No_Secondary_Stack, N); - end if; - - Set_Etype (Current_Scope, Standard_Void_Type); - Set_Node_To_Be_Wrapped (Wrap_Node); - - if Debug_Flag_W then - Write_Str (" <Transient>"); - Write_Eol; - end if; - end if; - end Establish_Transient_Scope; - - ---------------------------- - -- Expand_Cleanup_Actions -- - ---------------------------- - - procedure Expand_Cleanup_Actions (N : Node_Id) is - Scop : constant Entity_Id := Current_Scope; - - Is_Asynchronous_Call : constant Boolean := - Nkind (N) = N_Block_Statement - and then Is_Asynchronous_Call_Block (N); - Is_Master : constant Boolean := - Nkind (N) /= N_Entry_Body - and then Is_Task_Master (N); - Is_Protected_Body : constant Boolean := - Nkind (N) = N_Subprogram_Body - and then Is_Protected_Subprogram_Body (N); - Is_Task_Allocation : constant Boolean := - Nkind (N) = N_Block_Statement - and then Is_Task_Allocation_Block (N); - Is_Task_Body : constant Boolean := - Nkind (Original_Node (N)) = N_Task_Body; - Needs_Sec_Stack_Mark : constant Boolean := - Uses_Sec_Stack (Scop) - and then - not Sec_Stack_Needed_For_Return (Scop) - and then VM_Target = No_VM; - - Actions_Required : constant Boolean := - Requires_Cleanup_Actions (N, True) - or else Is_Asynchronous_Call - or else Is_Master - or else Is_Protected_Body - or else Is_Task_Allocation - or else Is_Task_Body - or else Needs_Sec_Stack_Mark; - - HSS : Node_Id := Handled_Statement_Sequence (N); - Loc : Source_Ptr; - - procedure Wrap_HSS_In_Block; - -- Move HSS inside a new block along with the original exception - -- handlers. Make the newly generated block the sole statement of HSS. - - ----------------------- - -- Wrap_HSS_In_Block -- - ----------------------- - - procedure Wrap_HSS_In_Block is - Block : Node_Id; - End_Lab : Node_Id; - - begin - -- Preserve end label to provide proper cross-reference information - - End_Lab := End_Label (HSS); - Block := - Make_Block_Statement (Loc, - Handled_Statement_Sequence => HSS); - - -- Signal the finalization machinery that this particular block - -- contains the original context. - - Set_Is_Finalization_Wrapper (Block); - - Set_Handled_Statement_Sequence (N, - Make_Handled_Sequence_Of_Statements (Loc, New_List (Block))); - HSS := Handled_Statement_Sequence (N); - - Set_First_Real_Statement (HSS, Block); - Set_End_Label (HSS, End_Lab); - - -- Comment needed here, see RH for 1.306 ??? - - if Nkind (N) = N_Subprogram_Body then - Set_Has_Nested_Block_With_Handler (Scop); - end if; - end Wrap_HSS_In_Block; - - -- Start of processing for Expand_Cleanup_Actions - - begin - -- The current construct does not need any form of servicing - - if not Actions_Required then - return; - - -- If the current node is a rewritten task body and the descriptors have - -- not been delayed (due to some nested instantiations), do not generate - -- redundant cleanup actions. - - elsif Is_Task_Body - and then Nkind (N) = N_Subprogram_Body - and then not Delay_Subprogram_Descriptors (Corresponding_Spec (N)) - then - return; - end if; - - declare - Decls : List_Id := Declarations (N); - Fin_Id : Entity_Id; - Mark : Entity_Id := Empty; - New_Decls : List_Id; - Old_Poll : Boolean; - - begin - -- If we are generating expanded code for debugging purposes, use the - -- Sloc of the point of insertion for the cleanup code. The Sloc will - -- be updated subsequently to reference the proper line in .dg files. - -- If we are not debugging generated code, use No_Location instead, - -- so that no debug information is generated for the cleanup code. - -- This makes the behavior of the NEXT command in GDB monotonic, and - -- makes the placement of breakpoints more accurate. - - if Debug_Generated_Code then - Loc := Sloc (Scop); - else - Loc := No_Location; - end if; - - -- Set polling off. The finalization and cleanup code is executed - -- with aborts deferred. - - Old_Poll := Polling_Required; - Polling_Required := False; - - -- A task activation call has already been built for a task - -- allocation block. - - if not Is_Task_Allocation then - Build_Task_Activation_Call (N); - end if; - - if Is_Master then - Establish_Task_Master (N); - end if; - - New_Decls := New_List; - - -- If secondary stack is in use, generate: - -- - -- Mnn : constant Mark_Id := SS_Mark; - - -- Suppress calls to SS_Mark and SS_Release if VM_Target, since the - -- secondary stack is never used on a VM. - - if Needs_Sec_Stack_Mark then - Mark := Make_Temporary (Loc, 'M'); - - Append_To (New_Decls, - Make_Object_Declaration (Loc, - Defining_Identifier => Mark, - Object_Definition => - New_Reference_To (RTE (RE_Mark_Id), Loc), - Expression => - Make_Function_Call (Loc, - Name => New_Reference_To (RTE (RE_SS_Mark), Loc)))); - - Set_Uses_Sec_Stack (Scop, False); - end if; - - -- If exception handlers are present, wrap the sequence of statements - -- in a block since it is not possible to have exception handlers and - -- an At_End handler in the same construct. - - if Present (Exception_Handlers (HSS)) then - Wrap_HSS_In_Block; - - -- Ensure that the First_Real_Statement field is set - - elsif No (First_Real_Statement (HSS)) then - Set_First_Real_Statement (HSS, First (Statements (HSS))); - end if; - - -- Do not move the Activation_Chain declaration in the context of - -- task allocation blocks. Task allocation blocks use _chain in their - -- cleanup handlers and gigi complains if it is declared in the - -- sequence of statements of the scope that declares the handler. - - if Is_Task_Allocation then - declare - Chain : constant Entity_Id := Activation_Chain_Entity (N); - Decl : Node_Id; - - begin - Decl := First (Decls); - while Nkind (Decl) /= N_Object_Declaration - or else Defining_Identifier (Decl) /= Chain - loop - Next (Decl); - - -- A task allocation block should always include a _chain - -- declaration. - - pragma Assert (Present (Decl)); - end loop; - - Remove (Decl); - Prepend_To (New_Decls, Decl); - end; - end if; - - -- Ensure the presence of a declaration list in order to successfully - -- append all original statements to it. - - if No (Decls) then - Set_Declarations (N, New_List); - Decls := Declarations (N); - end if; - - -- Move the declarations into the sequence of statements in order to - -- have them protected by the At_End handler. It may seem weird to - -- put declarations in the sequence of statement but in fact nothing - -- forbids that at the tree level. - - Append_List_To (Decls, Statements (HSS)); - Set_Statements (HSS, Decls); - - -- Reset the Sloc of the handled statement sequence to properly - -- reflect the new initial "statement" in the sequence. - - Set_Sloc (HSS, Sloc (First (Decls))); - - -- The declarations of finalizer spec and auxiliary variables replace - -- the old declarations that have been moved inward. - - Set_Declarations (N, New_Decls); - Analyze_Declarations (New_Decls); - - -- Generate finalization calls for all controlled objects appearing - -- in the statements of N. Add context specific cleanup for various - -- constructs. - - Build_Finalizer - (N => N, - Clean_Stmts => Build_Cleanup_Statements (N), - Mark_Id => Mark, - Top_Decls => New_Decls, - Defer_Abort => Nkind (Original_Node (N)) = N_Task_Body - or else Is_Master, - Fin_Id => Fin_Id); - - if Present (Fin_Id) then - Build_Finalizer_Call (N, Fin_Id); - end if; - - -- Restore saved polling mode - - Polling_Required := Old_Poll; - end; - end Expand_Cleanup_Actions; - - --------------------------- - -- Expand_N_Package_Body -- - --------------------------- - - -- Add call to Activate_Tasks if body is an activator (actual processing - -- is in chapter 9). - - -- Generate subprogram descriptor for elaboration routine - - -- Encode entity names in package body - - procedure Expand_N_Package_Body (N : Node_Id) is - Spec_Ent : constant Entity_Id := Corresponding_Spec (N); - Fin_Id : Entity_Id; - - begin - -- This is done only for non-generic packages - - if Ekind (Spec_Ent) = E_Package then - Push_Scope (Corresponding_Spec (N)); - - -- Build dispatch tables of library level tagged types - - if Tagged_Type_Expansion - and then Is_Library_Level_Entity (Spec_Ent) - then - Build_Static_Dispatch_Tables (N); - end if; - - Build_Task_Activation_Call (N); - Pop_Scope; - end if; - - Set_Elaboration_Flag (N, Corresponding_Spec (N)); - Set_In_Package_Body (Spec_Ent, False); - - -- Set to encode entity names in package body before gigi is called - - Qualify_Entity_Names (N); - - if Ekind (Spec_Ent) /= E_Generic_Package then - Build_Finalizer - (N => N, - Clean_Stmts => No_List, - Mark_Id => Empty, - Top_Decls => No_List, - Defer_Abort => False, - Fin_Id => Fin_Id); - - if Present (Fin_Id) then - declare - Body_Ent : Node_Id := Defining_Unit_Name (N); - - begin - if Nkind (Body_Ent) = N_Defining_Program_Unit_Name then - Body_Ent := Defining_Identifier (Body_Ent); - end if; - - Set_Finalizer (Body_Ent, Fin_Id); - end; - end if; - end if; - end Expand_N_Package_Body; - - ---------------------------------- - -- Expand_N_Package_Declaration -- - ---------------------------------- - - -- Add call to Activate_Tasks if there are tasks declared and the package - -- has no body. Note that in Ada 83 this may result in premature activation - -- of some tasks, given that we cannot tell whether a body will eventually - -- appear. - - procedure Expand_N_Package_Declaration (N : Node_Id) is - Id : constant Entity_Id := Defining_Entity (N); - Spec : constant Node_Id := Specification (N); - Decls : List_Id; - Fin_Id : Entity_Id; - - No_Body : Boolean := False; - -- True in the case of a package declaration that is a compilation - -- unit and for which no associated body will be compiled in this - -- compilation. - - begin - -- Case of a package declaration other than a compilation unit - - if Nkind (Parent (N)) /= N_Compilation_Unit then - null; - - -- Case of a compilation unit that does not require a body - - elsif not Body_Required (Parent (N)) - and then not Unit_Requires_Body (Id) - then - No_Body := True; - - -- Special case of generating calling stubs for a remote call interface - -- package: even though the package declaration requires one, the body - -- won't be processed in this compilation (so any stubs for RACWs - -- declared in the package must be generated here, along with the spec). - - elsif Parent (N) = Cunit (Main_Unit) - and then Is_Remote_Call_Interface (Id) - and then Distribution_Stub_Mode = Generate_Caller_Stub_Body - then - No_Body := True; - end if; - - -- For a nested instance, delay processing until freeze point - - if Has_Delayed_Freeze (Id) - and then Nkind (Parent (N)) /= N_Compilation_Unit - then - return; - end if; - - -- For a package declaration that implies no associated body, generate - -- task activation call and RACW supporting bodies now (since we won't - -- have a specific separate compilation unit for that). - - if No_Body then - Push_Scope (Id); - - if Has_RACW (Id) then - - -- Generate RACW subprogram bodies - - Decls := Private_Declarations (Spec); - - if No (Decls) then - Decls := Visible_Declarations (Spec); - end if; - - if No (Decls) then - Decls := New_List; - Set_Visible_Declarations (Spec, Decls); - end if; - - Append_RACW_Bodies (Decls, Id); - Analyze_List (Decls); - end if; - - if Present (Activation_Chain_Entity (N)) then - - -- Generate task activation call as last step of elaboration - - Build_Task_Activation_Call (N); - end if; - - Pop_Scope; - end if; - - -- Build dispatch tables of library level tagged types - - if Tagged_Type_Expansion - and then (Is_Compilation_Unit (Id) - or else (Is_Generic_Instance (Id) - and then Is_Library_Level_Entity (Id))) - then - Build_Static_Dispatch_Tables (N); - end if; - - -- Note: it is not necessary to worry about generating a subprogram - -- descriptor, since the only way to get exception handlers into a - -- package spec is to include instantiations, and that would cause - -- generation of subprogram descriptors to be delayed in any case. - - -- Set to encode entity names in package spec before gigi is called - - Qualify_Entity_Names (N); - - if Ekind (Id) /= E_Generic_Package then - Build_Finalizer - (N => N, - Clean_Stmts => No_List, - Mark_Id => Empty, - Top_Decls => No_List, - Defer_Abort => False, - Fin_Id => Fin_Id); - - Set_Finalizer (Id, Fin_Id); - end if; - end Expand_N_Package_Declaration; - - ----------------------------- - -- Find_Node_To_Be_Wrapped -- - ----------------------------- - - function Find_Node_To_Be_Wrapped (N : Node_Id) return Node_Id is - P : Node_Id; - The_Parent : Node_Id; - - begin - The_Parent := N; - loop - P := The_Parent; - pragma Assert (P /= Empty); - The_Parent := Parent (P); - - case Nkind (The_Parent) is - - -- Simple statement can be wrapped - - when N_Pragma => - return The_Parent; - - -- Usually assignments are good candidate for wrapping except - -- when they have been generated as part of a controlled aggregate - -- where the wrapping should take place more globally. - - when N_Assignment_Statement => - if No_Ctrl_Actions (The_Parent) then - null; - else - return The_Parent; - end if; - - -- An entry call statement is a special case if it occurs in the - -- context of a Timed_Entry_Call. In this case we wrap the entire - -- timed entry call. - - when N_Entry_Call_Statement | - N_Procedure_Call_Statement => - if Nkind (Parent (The_Parent)) = N_Entry_Call_Alternative - and then Nkind_In (Parent (Parent (The_Parent)), - N_Timed_Entry_Call, - N_Conditional_Entry_Call) - then - return Parent (Parent (The_Parent)); - else - return The_Parent; - end if; - - -- Object declarations are also a boundary for the transient scope - -- even if they are not really wrapped. For further details, see - -- Wrap_Transient_Declaration. - - when N_Object_Declaration | - N_Object_Renaming_Declaration | - N_Subtype_Declaration => - return The_Parent; - - -- The expression itself is to be wrapped if its parent is a - -- compound statement or any other statement where the expression - -- is known to be scalar - - when N_Accept_Alternative | - N_Attribute_Definition_Clause | - N_Case_Statement | - N_Code_Statement | - N_Delay_Alternative | - N_Delay_Until_Statement | - N_Delay_Relative_Statement | - N_Discriminant_Association | - N_Elsif_Part | - N_Entry_Body_Formal_Part | - N_Exit_Statement | - N_If_Statement | - N_Iteration_Scheme | - N_Terminate_Alternative => - return P; - - when N_Attribute_Reference => - - if Is_Procedure_Attribute_Name - (Attribute_Name (The_Parent)) - then - return The_Parent; - end if; - - -- A raise statement can be wrapped. This will arise when the - -- expression in a raise_with_expression uses the secondary - -- stack, for example. - - when N_Raise_Statement => - return The_Parent; - - -- If the expression is within the iteration scheme of a loop, - -- we must create a declaration for it, followed by an assignment - -- in order to have a usable statement to wrap. - - when N_Loop_Parameter_Specification => - return Parent (The_Parent); - - -- The following nodes contains "dummy calls" which don't need to - -- be wrapped. - - when N_Parameter_Specification | - N_Discriminant_Specification | - N_Component_Declaration => - return Empty; - - -- The return statement is not to be wrapped when the function - -- itself needs wrapping at the outer-level - - when N_Simple_Return_Statement => - declare - Applies_To : constant Entity_Id := - Return_Applies_To - (Return_Statement_Entity (The_Parent)); - Return_Type : constant Entity_Id := Etype (Applies_To); - begin - if Requires_Transient_Scope (Return_Type) then - return Empty; - else - return The_Parent; - end if; - end; - - -- If we leave a scope without having been able to find a node to - -- wrap, something is going wrong but this can happen in error - -- situation that are not detected yet (such as a dynamic string - -- in a pragma export) - - when N_Subprogram_Body | - N_Package_Declaration | - N_Package_Body | - N_Block_Statement => - return Empty; - - -- Otherwise continue the search - - when others => - null; - end case; - end loop; - end Find_Node_To_Be_Wrapped; - - ------------------------------------- - -- Get_Global_Pool_For_Access_Type -- - ------------------------------------- - - function Get_Global_Pool_For_Access_Type (T : Entity_Id) return Entity_Id is - begin - -- Access types whose size is smaller than System.Address size can exist - -- only on VMS. We can't use the usual global pool which returns an - -- object of type Address as truncation will make it invalid. To handle - -- this case, VMS has a dedicated global pool that returns addresses - -- that fit into 32 bit accesses. - - if Opt.True_VMS_Target and then Esize (T) = 32 then - return RTE (RE_Global_Pool_32_Object); - else - return RTE (RE_Global_Pool_Object); - end if; - end Get_Global_Pool_For_Access_Type; - - ---------------------------------- - -- Has_New_Controlled_Component -- - ---------------------------------- - - function Has_New_Controlled_Component (E : Entity_Id) return Boolean is - Comp : Entity_Id; - - begin - if not Is_Tagged_Type (E) then - return Has_Controlled_Component (E); - elsif not Is_Derived_Type (E) then - return Has_Controlled_Component (E); - end if; - - Comp := First_Component (E); - while Present (Comp) loop - if Chars (Comp) = Name_uParent then - null; - - elsif Scope (Original_Record_Component (Comp)) = E - and then Needs_Finalization (Etype (Comp)) - then - return True; - end if; - - Next_Component (Comp); - end loop; - - return False; - end Has_New_Controlled_Component; - - --------------------------------- - -- Has_Simple_Protected_Object -- - --------------------------------- - - function Has_Simple_Protected_Object (T : Entity_Id) return Boolean is - begin - if Has_Task (T) then - return False; - - elsif Is_Simple_Protected_Type (T) then - return True; - - elsif Is_Array_Type (T) then - return Has_Simple_Protected_Object (Component_Type (T)); - - elsif Is_Record_Type (T) then - declare - Comp : Entity_Id; - - begin - Comp := First_Component (T); - while Present (Comp) loop - if Has_Simple_Protected_Object (Etype (Comp)) then - return True; - end if; - - Next_Component (Comp); - end loop; - - return False; - end; - - else - return False; - end if; - end Has_Simple_Protected_Object; - - ------------------------------------ - -- Insert_Actions_In_Scope_Around -- - ------------------------------------ - - procedure Insert_Actions_In_Scope_Around (N : Node_Id) is - After : constant List_Id := - Scope_Stack.Table (Scope_Stack.Last).Actions_To_Be_Wrapped_After; - Before : constant List_Id := - Scope_Stack.Table (Scope_Stack.Last).Actions_To_Be_Wrapped_Before; - -- Note: We used to use renamings of Scope_Stack.Table (Scope_Stack. - -- Last), but this was incorrect as Process_Transient_Object may - -- introduce new scopes and cause a reallocation of Scope_Stack.Table. - - procedure Process_Transient_Objects - (First_Object : Node_Id; - Last_Object : Node_Id; - Related_Node : Node_Id); - -- First_Object and Last_Object define a list which contains potential - -- controlled transient objects. Finalization flags are inserted before - -- First_Object and finalization calls are inserted after Last_Object. - -- Related_Node is the node for which transient objects have been - -- created. - - ------------------------------- - -- Process_Transient_Objects -- - ------------------------------- - - procedure Process_Transient_Objects - (First_Object : Node_Id; - Last_Object : Node_Id; - Related_Node : Node_Id) - is - function Requires_Hooking return Boolean; - -- Determine whether the context requires transient variable export - -- to the outer finalizer. This scenario arises when the context may - -- raise an exception. - - ---------------------- - -- Requires_Hooking -- - ---------------------- - - function Requires_Hooking return Boolean is - begin - -- The context is either a procedure or function call or an object - -- declaration initialized by a function call. Note that in the - -- latter case, a function call that returns on the secondary - -- stack is usually rewritten into something else. Its proper - -- detection requires examination of the original initialization - -- expression. - - return Nkind (N) in N_Subprogram_Call - or else (Nkind (N) = N_Object_Declaration - and then Nkind (Original_Node (Expression (N))) = - N_Function_Call); - end Requires_Hooking; - - -- Local variables - - Must_Hook : constant Boolean := Requires_Hooking; - Built : Boolean := False; - Desig_Typ : Entity_Id; - Fin_Block : Node_Id; - Fin_Data : Finalization_Exception_Data; - Fin_Decls : List_Id; - Last_Fin : Node_Id := Empty; - Loc : Source_Ptr; - Obj_Id : Entity_Id; - Obj_Ref : Node_Id; - Obj_Typ : Entity_Id; - Prev_Fin : Node_Id := Empty; - Stmt : Node_Id; - Stmts : List_Id; - Temp_Id : Entity_Id; - - -- Start of processing for Process_Transient_Objects - - begin - -- Examine all objects in the list First_Object .. Last_Object - - Stmt := First_Object; - while Present (Stmt) loop - if Nkind (Stmt) = N_Object_Declaration - and then Analyzed (Stmt) - and then Is_Finalizable_Transient (Stmt, N) - - -- Do not process the node to be wrapped since it will be - -- handled by the enclosing finalizer. - - and then Stmt /= Related_Node - then - Loc := Sloc (Stmt); - Obj_Id := Defining_Identifier (Stmt); - Obj_Typ := Base_Type (Etype (Obj_Id)); - Desig_Typ := Obj_Typ; - - Set_Is_Processed_Transient (Obj_Id); - - -- Handle access types - - if Is_Access_Type (Desig_Typ) then - Desig_Typ := Available_View (Designated_Type (Desig_Typ)); - end if; - - -- Create the necessary entities and declarations the first - -- time around. - - if not Built then - Fin_Decls := New_List; - - Build_Object_Declarations (Fin_Data, Fin_Decls, Loc); - - Built := True; - end if; - - -- Transient variables associated with subprogram calls need - -- extra processing. These variables are usually created right - -- before the call and finalized immediately after the call. - -- If an exception occurs during the call, the clean up code - -- is skipped due to the sudden change in control and the - -- transient is never finalized. - - -- To handle this case, such variables are "exported" to the - -- enclosing sequence of statements where their corresponding - -- "hooks" are picked up by the finalization machinery. - - if Must_Hook then - declare - Expr : Node_Id; - Ptr_Id : Entity_Id; - - begin - -- Step 1: Create an access type which provides a - -- reference to the transient object. Generate: - - -- Ann : access [all] <Desig_Typ>; - - Ptr_Id := Make_Temporary (Loc, 'A'); - - Insert_Action (Stmt, - Make_Full_Type_Declaration (Loc, - Defining_Identifier => Ptr_Id, - Type_Definition => - Make_Access_To_Object_Definition (Loc, - All_Present => - Ekind (Obj_Typ) = E_General_Access_Type, - Subtype_Indication => - New_Reference_To (Desig_Typ, Loc)))); - - -- Step 2: Create a temporary which acts as a hook to - -- the transient object. Generate: - - -- Temp : Ptr_Id := null; - - Temp_Id := Make_Temporary (Loc, 'T'); - - Insert_Action (Stmt, - Make_Object_Declaration (Loc, - Defining_Identifier => Temp_Id, - Object_Definition => - New_Reference_To (Ptr_Id, Loc))); - - -- Mark the temporary as a transient hook. This signals - -- the machinery in Build_Finalizer to recognize this - -- special case. - - Set_Status_Flag_Or_Transient_Decl (Temp_Id, Stmt); - - -- Step 3: Hook the transient object to the temporary - - if Is_Access_Type (Obj_Typ) then - Expr := - Convert_To (Ptr_Id, New_Reference_To (Obj_Id, Loc)); - else - Expr := - Make_Attribute_Reference (Loc, - Prefix => New_Reference_To (Obj_Id, Loc), - Attribute_Name => Name_Unrestricted_Access); - end if; - - -- Generate: - -- Temp := Ptr_Id (Obj_Id); - -- <or> - -- Temp := Obj_Id'Unrestricted_Access; - - Insert_After_And_Analyze (Stmt, - Make_Assignment_Statement (Loc, - Name => New_Reference_To (Temp_Id, Loc), - Expression => Expr)); - end; - end if; - - Stmts := New_List; - - -- The transient object is about to be finalized by the clean - -- up code following the subprogram call. In order to avoid - -- double finalization, clear the hook. - - -- Generate: - -- Temp := null; - - if Must_Hook then - Append_To (Stmts, - Make_Assignment_Statement (Loc, - Name => New_Reference_To (Temp_Id, Loc), - Expression => Make_Null (Loc))); - end if; - - -- Generate: - -- [Deep_]Finalize (Obj_Ref); - - Obj_Ref := New_Reference_To (Obj_Id, Loc); - - if Is_Access_Type (Obj_Typ) then - Obj_Ref := Make_Explicit_Dereference (Loc, Obj_Ref); - end if; - - Append_To (Stmts, - Make_Final_Call (Obj_Ref => Obj_Ref, Typ => Desig_Typ)); - - -- Generate: - -- [Temp := null;] - -- begin - -- [Deep_]Finalize (Obj_Ref); - - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence - -- (Enn, Get_Current_Excep.all.all); - -- end if; - -- end; - - Fin_Block := - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => Stmts, - Exception_Handlers => New_List ( - Build_Exception_Handler (Fin_Data)))); - - -- The single raise statement must be inserted after all the - -- finalization blocks, and we put everything into a wrapper - -- block to clearly expose the construct to the back-end. - - if Present (Prev_Fin) then - Insert_Before_And_Analyze (Prev_Fin, Fin_Block); - else - Insert_After_And_Analyze (Last_Object, - Make_Block_Statement (Loc, - Declarations => Fin_Decls, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Fin_Block)))); - - Last_Fin := Fin_Block; - end if; - - Prev_Fin := Fin_Block; - end if; - - -- Terminate the scan after the last object has been processed to - -- avoid touching unrelated code. - - if Stmt = Last_Object then - exit; - end if; - - Next (Stmt); - end loop; - - -- Generate: - -- if Raised and then not Abort then - -- Raise_From_Controlled_Operation (E); - -- end if; - - if Built - and then Present (Last_Fin) - then - Insert_After_And_Analyze (Last_Fin, - Build_Raise_Statement (Fin_Data)); - end if; - end Process_Transient_Objects; - - -- Start of processing for Insert_Actions_In_Scope_Around - - begin - if No (Before) and then No (After) then - return; - end if; - - declare - Node_To_Wrap : constant Node_Id := Node_To_Be_Wrapped; - First_Obj : Node_Id; - Last_Obj : Node_Id; - Target : Node_Id; - - begin - -- If the node to be wrapped is the trigger of an asynchronous - -- select, it is not part of a statement list. The actions must be - -- inserted before the select itself, which is part of some list of - -- statements. Note that the triggering alternative includes the - -- triggering statement and an optional statement list. If the node - -- to be wrapped is part of that list, the normal insertion applies. - - if Nkind (Parent (Node_To_Wrap)) = N_Triggering_Alternative - and then not Is_List_Member (Node_To_Wrap) - then - Target := Parent (Parent (Node_To_Wrap)); - else - Target := N; - end if; - - First_Obj := Target; - Last_Obj := Target; - - -- Add all actions associated with a transient scope into the main - -- tree. There are several scenarios here: - - -- +--- Before ----+ +----- After ---+ - -- 1) First_Obj ....... Target ........ Last_Obj - - -- 2) First_Obj ....... Target - - -- 3) Target ........ Last_Obj - - if Present (Before) then - - -- Flag declarations are inserted before the first object - - First_Obj := First (Before); - - Insert_List_Before (Target, Before); - end if; - - if Present (After) then - - -- Finalization calls are inserted after the last object - - Last_Obj := Last (After); - - Insert_List_After (Target, After); - end if; - - -- Check for transient controlled objects associated with Target and - -- generate the appropriate finalization actions for them. - - Process_Transient_Objects - (First_Object => First_Obj, - Last_Object => Last_Obj, - Related_Node => Target); - - -- Reset the action lists - - if Present (Before) then - Scope_Stack.Table (Scope_Stack.Last). - Actions_To_Be_Wrapped_Before := No_List; - end if; - - if Present (After) then - Scope_Stack.Table (Scope_Stack.Last). - Actions_To_Be_Wrapped_After := No_List; - end if; - end; - end Insert_Actions_In_Scope_Around; - - ------------------------------ - -- Is_Simple_Protected_Type -- - ------------------------------ - - function Is_Simple_Protected_Type (T : Entity_Id) return Boolean is - begin - return - Is_Protected_Type (T) - and then not Uses_Lock_Free (T) - and then not Has_Entries (T) - and then Is_RTE (Find_Protection_Type (T), RE_Protection); - end Is_Simple_Protected_Type; - - ----------------------- - -- Make_Adjust_Call -- - ----------------------- - - function Make_Adjust_Call - (Obj_Ref : Node_Id; - Typ : Entity_Id; - For_Parent : Boolean := False) return Node_Id - is - Loc : constant Source_Ptr := Sloc (Obj_Ref); - Adj_Id : Entity_Id := Empty; - Ref : Node_Id := Obj_Ref; - Utyp : Entity_Id; - - begin - -- Recover the proper type which contains Deep_Adjust - - if Is_Class_Wide_Type (Typ) then - Utyp := Root_Type (Typ); - else - Utyp := Typ; - end if; - - Utyp := Underlying_Type (Base_Type (Utyp)); - Set_Assignment_OK (Ref); - - -- Deal with non-tagged derivation of private views - - if Is_Untagged_Derivation (Typ) then - Utyp := Underlying_Type (Root_Type (Base_Type (Typ))); - Ref := Unchecked_Convert_To (Utyp, Ref); - Set_Assignment_OK (Ref); - end if; - - -- When dealing with the completion of a private type, use the base - -- type instead. - - if Utyp /= Base_Type (Utyp) then - pragma Assert (Is_Private_Type (Typ)); - - Utyp := Base_Type (Utyp); - Ref := Unchecked_Convert_To (Utyp, Ref); - end if; - - -- Select the appropriate version of adjust - - if For_Parent then - if Has_Controlled_Component (Utyp) then - Adj_Id := Find_Prim_Op (Utyp, TSS_Deep_Adjust); - end if; - - -- Class-wide types, interfaces and types with controlled components - - elsif Is_Class_Wide_Type (Typ) - or else Is_Interface (Typ) - or else Has_Controlled_Component (Utyp) - then - if Is_Tagged_Type (Utyp) then - Adj_Id := Find_Prim_Op (Utyp, TSS_Deep_Adjust); - else - Adj_Id := TSS (Utyp, TSS_Deep_Adjust); - end if; - - -- Derivations from [Limited_]Controlled - - elsif Is_Controlled (Utyp) then - if Has_Controlled_Component (Utyp) then - Adj_Id := Find_Prim_Op (Utyp, TSS_Deep_Adjust); - else - Adj_Id := Find_Prim_Op (Utyp, Name_Of (Adjust_Case)); - end if; - - -- Tagged types - - elsif Is_Tagged_Type (Utyp) then - Adj_Id := Find_Prim_Op (Utyp, TSS_Deep_Adjust); - - else - raise Program_Error; - end if; - - if Present (Adj_Id) then - - -- If the object is unanalyzed, set its expected type for use in - -- Convert_View in case an additional conversion is needed. - - if No (Etype (Ref)) - and then Nkind (Ref) /= N_Unchecked_Type_Conversion - then - Set_Etype (Ref, Typ); - end if; - - -- The object reference may need another conversion depending on the - -- type of the formal and that of the actual. - - if not Is_Class_Wide_Type (Typ) then - Ref := Convert_View (Adj_Id, Ref); - end if; - - return Make_Call (Loc, Adj_Id, New_Copy_Tree (Ref), For_Parent); - else - return Empty; - end if; - end Make_Adjust_Call; - - ---------------------- - -- Make_Attach_Call -- - ---------------------- - - function Make_Attach_Call - (Obj_Ref : Node_Id; - Ptr_Typ : Entity_Id) return Node_Id - is - pragma Assert (VM_Target /= No_VM); - - Loc : constant Source_Ptr := Sloc (Obj_Ref); - begin - return - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (RTE (RE_Attach), Loc), - Parameter_Associations => New_List ( - New_Reference_To (Finalization_Master (Ptr_Typ), Loc), - Unchecked_Convert_To (RTE (RE_Root_Controlled_Ptr), Obj_Ref))); - end Make_Attach_Call; - - ---------------------- - -- Make_Detach_Call -- - ---------------------- - - function Make_Detach_Call (Obj_Ref : Node_Id) return Node_Id is - Loc : constant Source_Ptr := Sloc (Obj_Ref); - - begin - return - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (RTE (RE_Detach), Loc), - Parameter_Associations => New_List ( - Unchecked_Convert_To (RTE (RE_Root_Controlled_Ptr), Obj_Ref))); - end Make_Detach_Call; - - --------------- - -- Make_Call -- - --------------- - - function Make_Call - (Loc : Source_Ptr; - Proc_Id : Entity_Id; - Param : Node_Id; - For_Parent : Boolean := False) return Node_Id - is - Params : constant List_Id := New_List (Param); - - begin - -- When creating a call to Deep_Finalize for a _parent field of a - -- derived type, disable the invocation of the nested Finalize by giving - -- the corresponding flag a False value. - - if For_Parent then - Append_To (Params, New_Reference_To (Standard_False, Loc)); - end if; - - return - Make_Procedure_Call_Statement (Loc, - Name => New_Reference_To (Proc_Id, Loc), - Parameter_Associations => Params); - end Make_Call; - - -------------------------- - -- Make_Deep_Array_Body -- - -------------------------- - - function Make_Deep_Array_Body - (Prim : Final_Primitives; - Typ : Entity_Id) return List_Id - is - function Build_Adjust_Or_Finalize_Statements - (Typ : Entity_Id) return List_Id; - -- Create the statements necessary to adjust or finalize an array of - -- controlled elements. Generate: - -- - -- declare - -- Abort : constant Boolean := Triggered_By_Abort; - -- <or> - -- Abort : constant Boolean := False; -- no abort - -- - -- E : Exception_Occurrence; - -- Raised : Boolean := False; - -- - -- begin - -- for J1 in [reverse] Typ'First (1) .. Typ'Last (1) loop - -- ^-- in the finalization case - -- ... - -- for Jn in [reverse] Typ'First (n) .. Typ'Last (n) loop - -- begin - -- [Deep_]Adjust / Finalize (V (J1, ..., Jn)); - -- - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - -- end loop; - -- ... - -- end loop; - -- - -- if Raised and then not Abort then - -- Raise_From_Controlled_Operation (E); - -- end if; - -- end; - - function Build_Initialize_Statements (Typ : Entity_Id) return List_Id; - -- Create the statements necessary to initialize an array of controlled - -- elements. Include a mechanism to carry out partial finalization if an - -- exception occurs. Generate: - -- - -- declare - -- Counter : Integer := 0; - -- - -- begin - -- for J1 in V'Range (1) loop - -- ... - -- for JN in V'Range (N) loop - -- begin - -- [Deep_]Initialize (V (J1, ..., JN)); - -- - -- Counter := Counter + 1; - -- - -- exception - -- when others => - -- declare - -- Abort : constant Boolean := Triggered_By_Abort; - -- <or> - -- Abort : constant Boolean := False; -- no abort - -- E : Exception_Occurence; - -- Raised : Boolean := False; - - -- begin - -- Counter := - -- V'Length (1) * - -- V'Length (2) * - -- ... - -- V'Length (N) - Counter; - - -- for F1 in reverse V'Range (1) loop - -- ... - -- for FN in reverse V'Range (N) loop - -- if Counter > 0 then - -- Counter := Counter - 1; - -- else - -- begin - -- [Deep_]Finalize (V (F1, ..., FN)); - - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, - -- Get_Current_Excep.all.all); - -- end if; - -- end; - -- end if; - -- end loop; - -- ... - -- end loop; - -- end; - -- - -- if Raised and then not Abort then - -- Raise_From_Controlled_Operation (E); - -- end if; - -- - -- raise; - -- end; - -- end loop; - -- end loop; - -- end; - - function New_References_To - (L : List_Id; - Loc : Source_Ptr) return List_Id; - -- Given a list of defining identifiers, return a list of references to - -- the original identifiers, in the same order as they appear. - - ----------------------------------------- - -- Build_Adjust_Or_Finalize_Statements -- - ----------------------------------------- - - function Build_Adjust_Or_Finalize_Statements - (Typ : Entity_Id) return List_Id - is - Comp_Typ : constant Entity_Id := Component_Type (Typ); - Index_List : constant List_Id := New_List; - Loc : constant Source_Ptr := Sloc (Typ); - Num_Dims : constant Int := Number_Dimensions (Typ); - Finalizer_Decls : List_Id := No_List; - Finalizer_Data : Finalization_Exception_Data; - Call : Node_Id; - Comp_Ref : Node_Id; - Core_Loop : Node_Id; - Dim : Int; - J : Entity_Id; - Loop_Id : Entity_Id; - Stmts : List_Id; - - Exceptions_OK : constant Boolean := - not Restriction_Active (No_Exception_Propagation); - - procedure Build_Indices; - -- Generate the indices used in the dimension loops - - ------------------- - -- Build_Indices -- - ------------------- - - procedure Build_Indices is - begin - -- Generate the following identifiers: - -- Jnn - for initialization - - for Dim in 1 .. Num_Dims loop - Append_To (Index_List, - Make_Defining_Identifier (Loc, New_External_Name ('J', Dim))); - end loop; - end Build_Indices; - - -- Start of processing for Build_Adjust_Or_Finalize_Statements - - begin - Finalizer_Decls := New_List; - - Build_Indices; - Build_Object_Declarations (Finalizer_Data, Finalizer_Decls, Loc); - - Comp_Ref := - Make_Indexed_Component (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Expressions => New_References_To (Index_List, Loc)); - Set_Etype (Comp_Ref, Comp_Typ); - - -- Generate: - -- [Deep_]Adjust (V (J1, ..., JN)) - - if Prim = Adjust_Case then - Call := Make_Adjust_Call (Obj_Ref => Comp_Ref, Typ => Comp_Typ); - - -- Generate: - -- [Deep_]Finalize (V (J1, ..., JN)) - - else pragma Assert (Prim = Finalize_Case); - Call := Make_Final_Call (Obj_Ref => Comp_Ref, Typ => Comp_Typ); - end if; - - -- Generate the block which houses the adjust or finalize call: - - -- <adjust or finalize call>; -- No_Exception_Propagation - - -- begin -- Exception handlers allowed - -- <adjust or finalize call> - - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - - if Exceptions_OK then - Core_Loop := - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Call), - Exception_Handlers => New_List ( - Build_Exception_Handler (Finalizer_Data)))); - else - Core_Loop := Call; - end if; - - -- Generate the dimension loops starting from the innermost one - - -- for Jnn in [reverse] V'Range (Dim) loop - -- <core loop> - -- end loop; - - J := Last (Index_List); - Dim := Num_Dims; - while Present (J) and then Dim > 0 loop - Loop_Id := J; - Prev (J); - Remove (Loop_Id); - - Core_Loop := - Make_Loop_Statement (Loc, - Iteration_Scheme => - Make_Iteration_Scheme (Loc, - Loop_Parameter_Specification => - Make_Loop_Parameter_Specification (Loc, - Defining_Identifier => Loop_Id, - Discrete_Subtype_Definition => - Make_Attribute_Reference (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Attribute_Name => Name_Range, - Expressions => New_List ( - Make_Integer_Literal (Loc, Dim))), - - Reverse_Present => Prim = Finalize_Case)), - - Statements => New_List (Core_Loop), - End_Label => Empty); - - Dim := Dim - 1; - end loop; - - -- Generate the block which contains the core loop, the declarations - -- of the abort flag, the exception occurrence, the raised flag and - -- the conditional raise: - - -- declare - -- Abort : constant Boolean := Triggered_By_Abort; - -- <or> - -- Abort : constant Boolean := False; -- no abort - - -- E : Exception_Occurrence; - -- Raised : Boolean := False; - - -- begin - -- <core loop> - - -- if Raised and then not Abort then -- Expection handlers OK - -- Raise_From_Controlled_Operation (E); - -- end if; - -- end; - - Stmts := New_List (Core_Loop); - - if Exceptions_OK then - Append_To (Stmts, - Build_Raise_Statement (Finalizer_Data)); - end if; - - return - New_List ( - Make_Block_Statement (Loc, - Declarations => - Finalizer_Decls, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, Stmts))); - end Build_Adjust_Or_Finalize_Statements; - - --------------------------------- - -- Build_Initialize_Statements -- - --------------------------------- - - function Build_Initialize_Statements (Typ : Entity_Id) return List_Id is - Comp_Typ : constant Entity_Id := Component_Type (Typ); - Final_List : constant List_Id := New_List; - Index_List : constant List_Id := New_List; - Loc : constant Source_Ptr := Sloc (Typ); - Num_Dims : constant Int := Number_Dimensions (Typ); - Counter_Id : Entity_Id; - Dim : Int; - F : Node_Id; - Fin_Stmt : Node_Id; - Final_Block : Node_Id; - Final_Loop : Node_Id; - Finalizer_Data : Finalization_Exception_Data; - Finalizer_Decls : List_Id := No_List; - Init_Loop : Node_Id; - J : Node_Id; - Loop_Id : Node_Id; - Stmts : List_Id; - - Exceptions_OK : constant Boolean := - not Restriction_Active (No_Exception_Propagation); - - function Build_Counter_Assignment return Node_Id; - -- Generate the following assignment: - -- Counter := V'Length (1) * - -- ... - -- V'Length (N) - Counter; - - function Build_Finalization_Call return Node_Id; - -- Generate a deep finalization call for an array element - - procedure Build_Indices; - -- Generate the initialization and finalization indices used in the - -- dimension loops. - - function Build_Initialization_Call return Node_Id; - -- Generate a deep initialization call for an array element - - ------------------------------ - -- Build_Counter_Assignment -- - ------------------------------ - - function Build_Counter_Assignment return Node_Id is - Dim : Int; - Expr : Node_Id; - - begin - -- Start from the first dimension and generate: - -- V'Length (1) - - Dim := 1; - Expr := - Make_Attribute_Reference (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Attribute_Name => Name_Length, - Expressions => New_List (Make_Integer_Literal (Loc, Dim))); - - -- Process the rest of the dimensions, generate: - -- Expr * V'Length (N) - - Dim := Dim + 1; - while Dim <= Num_Dims loop - Expr := - Make_Op_Multiply (Loc, - Left_Opnd => Expr, - Right_Opnd => - Make_Attribute_Reference (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Attribute_Name => Name_Length, - Expressions => New_List ( - Make_Integer_Literal (Loc, Dim)))); - - Dim := Dim + 1; - end loop; - - -- Generate: - -- Counter := Expr - Counter; - - return - Make_Assignment_Statement (Loc, - Name => New_Reference_To (Counter_Id, Loc), - Expression => - Make_Op_Subtract (Loc, - Left_Opnd => Expr, - Right_Opnd => New_Reference_To (Counter_Id, Loc))); - end Build_Counter_Assignment; - - ----------------------------- - -- Build_Finalization_Call -- - ----------------------------- - - function Build_Finalization_Call return Node_Id is - Comp_Ref : constant Node_Id := - Make_Indexed_Component (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Expressions => New_References_To (Final_List, Loc)); - - begin - Set_Etype (Comp_Ref, Comp_Typ); - - -- Generate: - -- [Deep_]Finalize (V); - - return Make_Final_Call (Obj_Ref => Comp_Ref, Typ => Comp_Typ); - end Build_Finalization_Call; - - ------------------- - -- Build_Indices -- - ------------------- - - procedure Build_Indices is - begin - -- Generate the following identifiers: - -- Jnn - for initialization - -- Fnn - for finalization - - for Dim in 1 .. Num_Dims loop - Append_To (Index_List, - Make_Defining_Identifier (Loc, New_External_Name ('J', Dim))); - - Append_To (Final_List, - Make_Defining_Identifier (Loc, New_External_Name ('F', Dim))); - end loop; - end Build_Indices; - - ------------------------------- - -- Build_Initialization_Call -- - ------------------------------- - - function Build_Initialization_Call return Node_Id is - Comp_Ref : constant Node_Id := - Make_Indexed_Component (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Expressions => New_References_To (Index_List, Loc)); - - begin - Set_Etype (Comp_Ref, Comp_Typ); - - -- Generate: - -- [Deep_]Initialize (V (J1, ..., JN)); - - return Make_Init_Call (Obj_Ref => Comp_Ref, Typ => Comp_Typ); - end Build_Initialization_Call; - - -- Start of processing for Build_Initialize_Statements - - begin - Counter_Id := Make_Temporary (Loc, 'C'); - Finalizer_Decls := New_List; - - Build_Indices; - Build_Object_Declarations (Finalizer_Data, Finalizer_Decls, Loc); - - -- Generate the block which houses the finalization call, the index - -- guard and the handler which triggers Program_Error later on. - - -- if Counter > 0 then - -- Counter := Counter - 1; - -- else - -- [Deep_]Finalize (V (F1, ..., FN)); -- No_Except_Propagation - - -- begin -- Exceptions allowed - -- [Deep_]Finalize (V (F1, ..., FN)); - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - -- end if; - - if Exceptions_OK then - Fin_Stmt := - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Build_Finalization_Call), - Exception_Handlers => New_List ( - Build_Exception_Handler (Finalizer_Data)))); - else - Fin_Stmt := Build_Finalization_Call; - end if; - - -- This is the core of the loop, the dimension iterators are added - -- one by one in reverse. - - Final_Loop := - Make_If_Statement (Loc, - Condition => - Make_Op_Gt (Loc, - Left_Opnd => New_Reference_To (Counter_Id, Loc), - Right_Opnd => Make_Integer_Literal (Loc, 0)), - - Then_Statements => New_List ( - Make_Assignment_Statement (Loc, - Name => New_Reference_To (Counter_Id, Loc), - Expression => - Make_Op_Subtract (Loc, - Left_Opnd => New_Reference_To (Counter_Id, Loc), - Right_Opnd => Make_Integer_Literal (Loc, 1)))), - - Else_Statements => New_List (Fin_Stmt)); - - -- Generate all finalization loops starting from the innermost - -- dimension. - - -- for Fnn in reverse V'Range (Dim) loop - -- <final loop> - -- end loop; - - F := Last (Final_List); - Dim := Num_Dims; - while Present (F) and then Dim > 0 loop - Loop_Id := F; - Prev (F); - Remove (Loop_Id); - - Final_Loop := - Make_Loop_Statement (Loc, - Iteration_Scheme => - Make_Iteration_Scheme (Loc, - Loop_Parameter_Specification => - Make_Loop_Parameter_Specification (Loc, - Defining_Identifier => Loop_Id, - Discrete_Subtype_Definition => - Make_Attribute_Reference (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Attribute_Name => Name_Range, - Expressions => New_List ( - Make_Integer_Literal (Loc, Dim))), - - Reverse_Present => True)), - - Statements => New_List (Final_Loop), - End_Label => Empty); - - Dim := Dim - 1; - end loop; - - -- Generate the block which contains the finalization loops, the - -- declarations of the abort flag, the exception occurrence, the - -- raised flag and the conditional raise. - - -- declare - -- Abort : constant Boolean := Triggered_By_Abort; - -- <or> - -- Abort : constant Boolean := False; -- no abort - - -- E : Exception_Occurrence; - -- Raised : Boolean := False; - - -- begin - -- Counter := - -- V'Length (1) * - -- ... - -- V'Length (N) - Counter; - - -- <final loop> - - -- if Raised and then not Abort then -- Exception handlers OK - -- Raise_From_Controlled_Operation (E); - -- end if; - - -- raise; -- Exception handlers OK - -- end; - - Stmts := New_List (Build_Counter_Assignment, Final_Loop); - - if Exceptions_OK then - Append_To (Stmts, - Build_Raise_Statement (Finalizer_Data)); - Append_To (Stmts, Make_Raise_Statement (Loc)); - end if; - - Final_Block := - Make_Block_Statement (Loc, - Declarations => - Finalizer_Decls, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, Statements => Stmts)); - - -- Generate the block which contains the initialization call and - -- the partial finalization code. - - -- begin - -- [Deep_]Initialize (V (J1, ..., JN)); - - -- Counter := Counter + 1; - - -- exception - -- when others => - -- <finalization code> - -- end; - - Init_Loop := - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Build_Initialization_Call), - Exception_Handlers => New_List ( - Make_Exception_Handler (Loc, - Exception_Choices => New_List (Make_Others_Choice (Loc)), - Statements => New_List (Final_Block))))); - - Append_To (Statements (Handled_Statement_Sequence (Init_Loop)), - Make_Assignment_Statement (Loc, - Name => New_Reference_To (Counter_Id, Loc), - Expression => - Make_Op_Add (Loc, - Left_Opnd => New_Reference_To (Counter_Id, Loc), - Right_Opnd => Make_Integer_Literal (Loc, 1)))); - - -- Generate all initialization loops starting from the innermost - -- dimension. - - -- for Jnn in V'Range (Dim) loop - -- <init loop> - -- end loop; - - J := Last (Index_List); - Dim := Num_Dims; - while Present (J) and then Dim > 0 loop - Loop_Id := J; - Prev (J); - Remove (Loop_Id); - - Init_Loop := - Make_Loop_Statement (Loc, - Iteration_Scheme => - Make_Iteration_Scheme (Loc, - Loop_Parameter_Specification => - Make_Loop_Parameter_Specification (Loc, - Defining_Identifier => Loop_Id, - Discrete_Subtype_Definition => - Make_Attribute_Reference (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Attribute_Name => Name_Range, - Expressions => New_List ( - Make_Integer_Literal (Loc, Dim))))), - - Statements => New_List (Init_Loop), - End_Label => Empty); - - Dim := Dim - 1; - end loop; - - -- Generate the block which contains the counter variable and the - -- initialization loops. - - -- declare - -- Counter : Integer := 0; - -- begin - -- <init loop> - -- end; - - return - New_List ( - Make_Block_Statement (Loc, - Declarations => New_List ( - Make_Object_Declaration (Loc, - Defining_Identifier => Counter_Id, - Object_Definition => - New_Reference_To (Standard_Integer, Loc), - Expression => Make_Integer_Literal (Loc, 0))), - - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Init_Loop)))); - end Build_Initialize_Statements; - - ----------------------- - -- New_References_To -- - ----------------------- - - function New_References_To - (L : List_Id; - Loc : Source_Ptr) return List_Id - is - Refs : constant List_Id := New_List; - Id : Node_Id; - - begin - Id := First (L); - while Present (Id) loop - Append_To (Refs, New_Reference_To (Id, Loc)); - Next (Id); - end loop; - - return Refs; - end New_References_To; - - -- Start of processing for Make_Deep_Array_Body - - begin - case Prim is - when Address_Case => - return Make_Finalize_Address_Stmts (Typ); - - when Adjust_Case | - Finalize_Case => - return Build_Adjust_Or_Finalize_Statements (Typ); - - when Initialize_Case => - return Build_Initialize_Statements (Typ); - end case; - end Make_Deep_Array_Body; - - -------------------- - -- Make_Deep_Proc -- - -------------------- - - function Make_Deep_Proc - (Prim : Final_Primitives; - Typ : Entity_Id; - Stmts : List_Id) return Entity_Id - is - Loc : constant Source_Ptr := Sloc (Typ); - Formals : List_Id; - Proc_Id : Entity_Id; - - begin - -- Create the object formal, generate: - -- V : System.Address - - if Prim = Address_Case then - Formals := New_List ( - Make_Parameter_Specification (Loc, - Defining_Identifier => Make_Defining_Identifier (Loc, Name_V), - Parameter_Type => New_Reference_To (RTE (RE_Address), Loc))); - - -- Default case - - else - -- V : in out Typ - - Formals := New_List ( - Make_Parameter_Specification (Loc, - Defining_Identifier => Make_Defining_Identifier (Loc, Name_V), - In_Present => True, - Out_Present => True, - Parameter_Type => New_Reference_To (Typ, Loc))); - - -- F : Boolean := True - - if Prim = Adjust_Case - or else Prim = Finalize_Case - then - Append_To (Formals, - Make_Parameter_Specification (Loc, - Defining_Identifier => Make_Defining_Identifier (Loc, Name_F), - Parameter_Type => - New_Reference_To (Standard_Boolean, Loc), - Expression => - New_Reference_To (Standard_True, Loc))); - end if; - end if; - - Proc_Id := - Make_Defining_Identifier (Loc, - Chars => Make_TSS_Name (Typ, Deep_Name_Of (Prim))); - - -- Generate: - -- procedure Deep_Initialize / Adjust / Finalize (V : in out <typ>) is - -- begin - -- <stmts> - -- exception -- Finalize and Adjust cases only - -- raise Program_Error; - -- end Deep_Initialize / Adjust / Finalize; - - -- or - - -- procedure Finalize_Address (V : System.Address) is - -- begin - -- <stmts> - -- end Finalize_Address; - - Discard_Node ( - Make_Subprogram_Body (Loc, - Specification => - Make_Procedure_Specification (Loc, - Defining_Unit_Name => Proc_Id, - Parameter_Specifications => Formals), - - Declarations => Empty_List, - - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, Statements => Stmts))); - - return Proc_Id; - end Make_Deep_Proc; - - --------------------------- - -- Make_Deep_Record_Body -- - --------------------------- - - function Make_Deep_Record_Body - (Prim : Final_Primitives; - Typ : Entity_Id; - Is_Local : Boolean := False) return List_Id - is - function Build_Adjust_Statements (Typ : Entity_Id) return List_Id; - -- Build the statements necessary to adjust a record type. The type may - -- have discriminants and contain variant parts. Generate: - -- - -- begin - -- begin - -- [Deep_]Adjust (V.Comp_1); - -- exception - -- when Id : others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - -- . . . - -- begin - -- [Deep_]Adjust (V.Comp_N); - -- exception - -- when Id : others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - -- - -- begin - -- Deep_Adjust (V._parent, False); -- If applicable - -- exception - -- when Id : others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - -- - -- if F then - -- begin - -- Adjust (V); -- If applicable - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - -- end if; - -- - -- if Raised and then not Abort then - -- Raise_From_Controlled_Operation (E); - -- end if; - -- end; - - function Build_Finalize_Statements (Typ : Entity_Id) return List_Id; - -- Build the statements necessary to finalize a record type. The type - -- may have discriminants and contain variant parts. Generate: - -- - -- declare - -- Abort : constant Boolean := Triggered_By_Abort; - -- <or> - -- Abort : constant Boolean := False; -- no abort - -- E : Exception_Occurence; - -- Raised : Boolean := False; - -- - -- begin - -- if F then - -- begin - -- Finalize (V); -- If applicable - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - -- end if; - -- - -- case Variant_1 is - -- when Value_1 => - -- case State_Counter_N => -- If Is_Local is enabled - -- when N => . - -- goto LN; . - -- ... . - -- when 1 => . - -- goto L1; . - -- when others => . - -- goto L0; . - -- end case; . - -- - -- <<LN>> -- If Is_Local is enabled - -- begin - -- [Deep_]Finalize (V.Comp_N); - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - -- . . . - -- <<L1>> - -- begin - -- [Deep_]Finalize (V.Comp_1); - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - -- <<L0>> - -- end case; - -- - -- case State_Counter_1 => -- If Is_Local is enabled - -- when M => . - -- goto LM; . - -- ... - -- - -- begin - -- Deep_Finalize (V._parent, False); -- If applicable - -- exception - -- when Id : others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - -- - -- if Raised and then not Abort then - -- Raise_From_Controlled_Operation (E); - -- end if; - -- end; - - function Parent_Field_Type (Typ : Entity_Id) return Entity_Id; - -- Given a derived tagged type Typ, traverse all components, find field - -- _parent and return its type. - - procedure Preprocess_Components - (Comps : Node_Id; - Num_Comps : out Int; - Has_POC : out Boolean); - -- Examine all components in component list Comps, count all controlled - -- components and determine whether at least one of them is per-object - -- constrained. Component _parent is always skipped. - - ----------------------------- - -- Build_Adjust_Statements -- - ----------------------------- - - function Build_Adjust_Statements (Typ : Entity_Id) return List_Id is - Loc : constant Source_Ptr := Sloc (Typ); - Typ_Def : constant Node_Id := Type_Definition (Parent (Typ)); - Bod_Stmts : List_Id; - Finalizer_Data : Finalization_Exception_Data; - Finalizer_Decls : List_Id := No_List; - Rec_Def : Node_Id; - Var_Case : Node_Id; - - Exceptions_OK : constant Boolean := - not Restriction_Active (No_Exception_Propagation); - - function Process_Component_List_For_Adjust - (Comps : Node_Id) return List_Id; - -- Build all necessary adjust statements for a single component list - - --------------------------------------- - -- Process_Component_List_For_Adjust -- - --------------------------------------- - - function Process_Component_List_For_Adjust - (Comps : Node_Id) return List_Id - is - Stmts : constant List_Id := New_List; - Decl : Node_Id; - Decl_Id : Entity_Id; - Decl_Typ : Entity_Id; - Has_POC : Boolean; - Num_Comps : Int; - - procedure Process_Component_For_Adjust (Decl : Node_Id); - -- Process the declaration of a single controlled component - - ---------------------------------- - -- Process_Component_For_Adjust -- - ---------------------------------- - - procedure Process_Component_For_Adjust (Decl : Node_Id) is - Id : constant Entity_Id := Defining_Identifier (Decl); - Typ : constant Entity_Id := Etype (Id); - Adj_Stmt : Node_Id; - - begin - -- Generate: - -- [Deep_]Adjust (V.Id); -- No_Exception_Propagation - - -- begin -- Exception handlers allowed - -- [Deep_]Adjust (V.Id); - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, Get_Current_Excep.all.all); - -- end if; - -- end; - - Adj_Stmt := - Make_Adjust_Call ( - Obj_Ref => - Make_Selected_Component (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Selector_Name => Make_Identifier (Loc, Chars (Id))), - Typ => Typ); - - if Exceptions_OK then - Adj_Stmt := - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Adj_Stmt), - Exception_Handlers => New_List ( - Build_Exception_Handler (Finalizer_Data)))); - end if; - - Append_To (Stmts, Adj_Stmt); - end Process_Component_For_Adjust; - - -- Start of processing for Process_Component_List_For_Adjust - - begin - -- Perform an initial check, determine the number of controlled - -- components in the current list and whether at least one of them - -- is per-object constrained. - - Preprocess_Components (Comps, Num_Comps, Has_POC); - - -- The processing in this routine is done in the following order: - -- 1) Regular components - -- 2) Per-object constrained components - -- 3) Variant parts - - if Num_Comps > 0 then - - -- Process all regular components in order of declarations - - Decl := First_Non_Pragma (Component_Items (Comps)); - while Present (Decl) loop - Decl_Id := Defining_Identifier (Decl); - Decl_Typ := Etype (Decl_Id); - - -- Skip _parent as well as per-object constrained components - - if Chars (Decl_Id) /= Name_uParent - and then Needs_Finalization (Decl_Typ) - then - if Has_Access_Constraint (Decl_Id) - and then No (Expression (Decl)) - then - null; - else - Process_Component_For_Adjust (Decl); - end if; - end if; - - Next_Non_Pragma (Decl); - end loop; - - -- Process all per-object constrained components in order of - -- declarations. - - if Has_POC then - Decl := First_Non_Pragma (Component_Items (Comps)); - while Present (Decl) loop - Decl_Id := Defining_Identifier (Decl); - Decl_Typ := Etype (Decl_Id); - - -- Skip _parent - - if Chars (Decl_Id) /= Name_uParent - and then Needs_Finalization (Decl_Typ) - and then Has_Access_Constraint (Decl_Id) - and then No (Expression (Decl)) - then - Process_Component_For_Adjust (Decl); - end if; - - Next_Non_Pragma (Decl); - end loop; - end if; - end if; - - -- Process all variants, if any - - Var_Case := Empty; - if Present (Variant_Part (Comps)) then - declare - Var_Alts : constant List_Id := New_List; - Var : Node_Id; - - begin - Var := First_Non_Pragma (Variants (Variant_Part (Comps))); - while Present (Var) loop - - -- Generate: - -- when <discrete choices> => - -- <adjust statements> - - Append_To (Var_Alts, - Make_Case_Statement_Alternative (Loc, - Discrete_Choices => - New_Copy_List (Discrete_Choices (Var)), - Statements => - Process_Component_List_For_Adjust ( - Component_List (Var)))); - - Next_Non_Pragma (Var); - end loop; - - -- Generate: - -- case V.<discriminant> is - -- when <discrete choices 1> => - -- <adjust statements 1> - -- ... - -- when <discrete choices N> => - -- <adjust statements N> - -- end case; - - Var_Case := - Make_Case_Statement (Loc, - Expression => - Make_Selected_Component (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Selector_Name => - Make_Identifier (Loc, - Chars => Chars (Name (Variant_Part (Comps))))), - Alternatives => Var_Alts); - end; - end if; - - -- Add the variant case statement to the list of statements - - if Present (Var_Case) then - Append_To (Stmts, Var_Case); - end if; - - -- If the component list did not have any controlled components - -- nor variants, return null. - - if Is_Empty_List (Stmts) then - Append_To (Stmts, Make_Null_Statement (Loc)); - end if; - - return Stmts; - end Process_Component_List_For_Adjust; - - -- Start of processing for Build_Adjust_Statements - - begin - Finalizer_Decls := New_List; - Build_Object_Declarations (Finalizer_Data, Finalizer_Decls, Loc); - - if Nkind (Typ_Def) = N_Derived_Type_Definition then - Rec_Def := Record_Extension_Part (Typ_Def); - else - Rec_Def := Typ_Def; - end if; - - -- Create an adjust sequence for all record components - - if Present (Component_List (Rec_Def)) then - Bod_Stmts := - Process_Component_List_For_Adjust (Component_List (Rec_Def)); - end if; - - -- A derived record type must adjust all inherited components. This - -- action poses the following problem: - - -- procedure Deep_Adjust (Obj : in out Parent_Typ) is - -- begin - -- Adjust (Obj); - -- ... - - -- procedure Deep_Adjust (Obj : in out Derived_Typ) is - -- begin - -- Deep_Adjust (Obj._parent); - -- ... - -- Adjust (Obj); - -- ... - - -- Adjusting the derived type will invoke Adjust of the parent and - -- then that of the derived type. This is undesirable because both - -- routines may modify shared components. Only the Adjust of the - -- derived type should be invoked. - - -- To prevent this double adjustment of shared components, - -- Deep_Adjust uses a flag to control the invocation of Adjust: - - -- procedure Deep_Adjust - -- (Obj : in out Some_Type; - -- Flag : Boolean := True) - -- is - -- begin - -- if Flag then - -- Adjust (Obj); - -- end if; - -- ... - - -- When Deep_Adjust is invokes for field _parent, a value of False is - -- provided for the flag: - - -- Deep_Adjust (Obj._parent, False); - - if Is_Tagged_Type (Typ) and then Is_Derived_Type (Typ) then - declare - Par_Typ : constant Entity_Id := Parent_Field_Type (Typ); - Adj_Stmt : Node_Id; - Call : Node_Id; - - begin - if Needs_Finalization (Par_Typ) then - Call := - Make_Adjust_Call - (Obj_Ref => - Make_Selected_Component (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Selector_Name => - Make_Identifier (Loc, Name_uParent)), - Typ => Par_Typ, - For_Parent => True); - - -- Generate: - -- Deep_Adjust (V._parent, False); -- No_Except_Propagat - - -- begin -- Exceptions OK - -- Deep_Adjust (V._parent, False); - -- exception - -- when Id : others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, - -- Get_Current_Excep.all.all); - -- end if; - -- end; - - if Present (Call) then - Adj_Stmt := Call; - - if Exceptions_OK then - Adj_Stmt := - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Adj_Stmt), - Exception_Handlers => New_List ( - Build_Exception_Handler (Finalizer_Data)))); - end if; - - Prepend_To (Bod_Stmts, Adj_Stmt); - end if; - end if; - end; - end if; - - -- Adjust the object. This action must be performed last after all - -- components have been adjusted. - - if Is_Controlled (Typ) then - declare - Adj_Stmt : Node_Id; - Proc : Entity_Id; - - begin - Proc := Find_Prim_Op (Typ, Name_Adjust); - - -- Generate: - -- if F then - -- Adjust (V); -- No_Exception_Propagation - - -- begin -- Exception handlers allowed - -- Adjust (V); - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, - -- Get_Current_Excep.all.all); - -- end if; - -- end; - -- end if; - - if Present (Proc) then - Adj_Stmt := - Make_Procedure_Call_Statement (Loc, - Name => New_Reference_To (Proc, Loc), - Parameter_Associations => New_List ( - Make_Identifier (Loc, Name_V))); - - if Exceptions_OK then - Adj_Stmt := - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Adj_Stmt), - Exception_Handlers => New_List ( - Build_Exception_Handler - (Finalizer_Data)))); - end if; - - Append_To (Bod_Stmts, - Make_If_Statement (Loc, - Condition => Make_Identifier (Loc, Name_F), - Then_Statements => New_List (Adj_Stmt))); - end if; - end; - end if; - - -- At this point either all adjustment statements have been generated - -- or the type is not controlled. - - if Is_Empty_List (Bod_Stmts) then - Append_To (Bod_Stmts, Make_Null_Statement (Loc)); - - return Bod_Stmts; - - -- Generate: - -- declare - -- Abort : constant Boolean := Triggered_By_Abort; - -- <or> - -- Abort : constant Boolean := False; -- no abort - - -- E : Exception_Occurence; - -- Raised : Boolean := False; - - -- begin - -- <adjust statements> - - -- if Raised and then not Abort then - -- Raise_From_Controlled_Operation (E); - -- end if; - -- end; - - else - if Exceptions_OK then - Append_To (Bod_Stmts, - Build_Raise_Statement (Finalizer_Data)); - end if; - - return - New_List ( - Make_Block_Statement (Loc, - Declarations => - Finalizer_Decls, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, Bod_Stmts))); - end if; - end Build_Adjust_Statements; - - ------------------------------- - -- Build_Finalize_Statements -- - ------------------------------- - - function Build_Finalize_Statements (Typ : Entity_Id) return List_Id is - Loc : constant Source_Ptr := Sloc (Typ); - Typ_Def : constant Node_Id := Type_Definition (Parent (Typ)); - Bod_Stmts : List_Id; - Counter : Int := 0; - Finalizer_Data : Finalization_Exception_Data; - Finalizer_Decls : List_Id := No_List; - Rec_Def : Node_Id; - Var_Case : Node_Id; - - Exceptions_OK : constant Boolean := - not Restriction_Active (No_Exception_Propagation); - - function Process_Component_List_For_Finalize - (Comps : Node_Id) return List_Id; - -- Build all necessary finalization statements for a single component - -- list. The statements may include a jump circuitry if flag Is_Local - -- is enabled. - - ----------------------------------------- - -- Process_Component_List_For_Finalize -- - ----------------------------------------- - - function Process_Component_List_For_Finalize - (Comps : Node_Id) return List_Id - is - Alts : List_Id; - Counter_Id : Entity_Id; - Decl : Node_Id; - Decl_Id : Entity_Id; - Decl_Typ : Entity_Id; - Decls : List_Id; - Has_POC : Boolean; - Jump_Block : Node_Id; - Label : Node_Id; - Label_Id : Entity_Id; - Num_Comps : Int; - Stmts : List_Id; - - procedure Process_Component_For_Finalize - (Decl : Node_Id; - Alts : List_Id; - Decls : List_Id; - Stmts : List_Id); - -- Process the declaration of a single controlled component. If - -- flag Is_Local is enabled, create the corresponding label and - -- jump circuitry. Alts is the list of case alternatives, Decls - -- is the top level declaration list where labels are declared - -- and Stmts is the list of finalization actions. - - ------------------------------------ - -- Process_Component_For_Finalize -- - ------------------------------------ - - procedure Process_Component_For_Finalize - (Decl : Node_Id; - Alts : List_Id; - Decls : List_Id; - Stmts : List_Id) - is - Id : constant Entity_Id := Defining_Identifier (Decl); - Typ : constant Entity_Id := Etype (Id); - Fin_Stmt : Node_Id; - - begin - if Is_Local then - declare - Label : Node_Id; - Label_Id : Entity_Id; - - begin - -- Generate: - -- LN : label; - - Label_Id := - Make_Identifier (Loc, - Chars => New_External_Name ('L', Num_Comps)); - Set_Entity (Label_Id, - Make_Defining_Identifier (Loc, Chars (Label_Id))); - Label := Make_Label (Loc, Label_Id); - - Append_To (Decls, - Make_Implicit_Label_Declaration (Loc, - Defining_Identifier => Entity (Label_Id), - Label_Construct => Label)); - - -- Generate: - -- when N => - -- goto LN; - - Append_To (Alts, - Make_Case_Statement_Alternative (Loc, - Discrete_Choices => New_List ( - Make_Integer_Literal (Loc, Num_Comps)), - - Statements => New_List ( - Make_Goto_Statement (Loc, - Name => - New_Reference_To (Entity (Label_Id), Loc))))); - - -- Generate: - -- <<LN>> - - Append_To (Stmts, Label); - - -- Decrease the number of components to be processed. - -- This action yields a new Label_Id in future calls. - - Num_Comps := Num_Comps - 1; - end; - end if; - - -- Generate: - -- [Deep_]Finalize (V.Id); -- No_Exception_Propagation - - -- begin -- Exception handlers allowed - -- [Deep_]Finalize (V.Id); - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, - -- Get_Current_Excep.all.all); - -- end if; - -- end; - - Fin_Stmt := - Make_Final_Call - (Obj_Ref => - Make_Selected_Component (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Selector_Name => Make_Identifier (Loc, Chars (Id))), - Typ => Typ); - - if not Restriction_Active (No_Exception_Propagation) then - Fin_Stmt := - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Fin_Stmt), - Exception_Handlers => New_List ( - Build_Exception_Handler (Finalizer_Data)))); - end if; - - Append_To (Stmts, Fin_Stmt); - end Process_Component_For_Finalize; - - -- Start of processing for Process_Component_List_For_Finalize - - begin - -- Perform an initial check, look for controlled and per-object - -- constrained components. - - Preprocess_Components (Comps, Num_Comps, Has_POC); - - -- Create a state counter to service the current component list. - -- This step is performed before the variants are inspected in - -- order to generate the same state counter names as those from - -- Build_Initialize_Statements. - - if Num_Comps > 0 - and then Is_Local - then - Counter := Counter + 1; - - Counter_Id := - Make_Defining_Identifier (Loc, - Chars => New_External_Name ('C', Counter)); - end if; - - -- Process the component in the following order: - -- 1) Variants - -- 2) Per-object constrained components - -- 3) Regular components - - -- Start with the variant parts - - Var_Case := Empty; - if Present (Variant_Part (Comps)) then - declare - Var_Alts : constant List_Id := New_List; - Var : Node_Id; - - begin - Var := First_Non_Pragma (Variants (Variant_Part (Comps))); - while Present (Var) loop - - -- Generate: - -- when <discrete choices> => - -- <finalize statements> - - Append_To (Var_Alts, - Make_Case_Statement_Alternative (Loc, - Discrete_Choices => - New_Copy_List (Discrete_Choices (Var)), - Statements => - Process_Component_List_For_Finalize ( - Component_List (Var)))); - - Next_Non_Pragma (Var); - end loop; - - -- Generate: - -- case V.<discriminant> is - -- when <discrete choices 1> => - -- <finalize statements 1> - -- ... - -- when <discrete choices N> => - -- <finalize statements N> - -- end case; - - Var_Case := - Make_Case_Statement (Loc, - Expression => - Make_Selected_Component (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Selector_Name => - Make_Identifier (Loc, - Chars => Chars (Name (Variant_Part (Comps))))), - Alternatives => Var_Alts); - end; - end if; - - -- The current component list does not have a single controlled - -- component, however it may contain variants. Return the case - -- statement for the variants or nothing. - - if Num_Comps = 0 then - if Present (Var_Case) then - return New_List (Var_Case); - else - return New_List (Make_Null_Statement (Loc)); - end if; - end if; - - -- Prepare all lists - - Alts := New_List; - Decls := New_List; - Stmts := New_List; - - -- Process all per-object constrained components in reverse order - - if Has_POC then - Decl := Last_Non_Pragma (Component_Items (Comps)); - while Present (Decl) loop - Decl_Id := Defining_Identifier (Decl); - Decl_Typ := Etype (Decl_Id); - - -- Skip _parent - - if Chars (Decl_Id) /= Name_uParent - and then Needs_Finalization (Decl_Typ) - and then Has_Access_Constraint (Decl_Id) - and then No (Expression (Decl)) - then - Process_Component_For_Finalize (Decl, Alts, Decls, Stmts); - end if; - - Prev_Non_Pragma (Decl); - end loop; - end if; - - -- Process the rest of the components in reverse order - - Decl := Last_Non_Pragma (Component_Items (Comps)); - while Present (Decl) loop - Decl_Id := Defining_Identifier (Decl); - Decl_Typ := Etype (Decl_Id); - - -- Skip _parent - - if Chars (Decl_Id) /= Name_uParent - and then Needs_Finalization (Decl_Typ) - then - -- Skip per-object constrained components since they were - -- handled in the above step. - - if Has_Access_Constraint (Decl_Id) - and then No (Expression (Decl)) - then - null; - else - Process_Component_For_Finalize (Decl, Alts, Decls, Stmts); - end if; - end if; - - Prev_Non_Pragma (Decl); - end loop; - - -- Generate: - -- declare - -- LN : label; -- If Is_Local is enabled - -- ... . - -- L0 : label; . - - -- begin . - -- case CounterX is . - -- when N => . - -- goto LN; . - -- ... . - -- when 1 => . - -- goto L1; . - -- when others => . - -- goto L0; . - -- end case; . - - -- <<LN>> -- If Is_Local is enabled - -- begin - -- [Deep_]Finalize (V.CompY); - -- exception - -- when Id : others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, - -- Get_Current_Excep.all.all); - -- end if; - -- end; - -- ... - -- <<L0>> -- If Is_Local is enabled - -- end; - - if Is_Local then - - -- Add the declaration of default jump location L0, its - -- corresponding alternative and its place in the statements. - - Label_Id := Make_Identifier (Loc, New_External_Name ('L', 0)); - Set_Entity (Label_Id, - Make_Defining_Identifier (Loc, Chars (Label_Id))); - Label := Make_Label (Loc, Label_Id); - - Append_To (Decls, -- declaration - Make_Implicit_Label_Declaration (Loc, - Defining_Identifier => Entity (Label_Id), - Label_Construct => Label)); - - Append_To (Alts, -- alternative - Make_Case_Statement_Alternative (Loc, - Discrete_Choices => New_List ( - Make_Others_Choice (Loc)), - - Statements => New_List ( - Make_Goto_Statement (Loc, - Name => New_Reference_To (Entity (Label_Id), Loc))))); - - Append_To (Stmts, Label); -- statement - - -- Create the jump block - - Prepend_To (Stmts, - Make_Case_Statement (Loc, - Expression => Make_Identifier (Loc, Chars (Counter_Id)), - Alternatives => Alts)); - end if; - - Jump_Block := - Make_Block_Statement (Loc, - Declarations => Decls, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, Stmts)); - - if Present (Var_Case) then - return New_List (Var_Case, Jump_Block); - else - return New_List (Jump_Block); - end if; - end Process_Component_List_For_Finalize; - - -- Start of processing for Build_Finalize_Statements - - begin - Finalizer_Decls := New_List; - Build_Object_Declarations (Finalizer_Data, Finalizer_Decls, Loc); - - if Nkind (Typ_Def) = N_Derived_Type_Definition then - Rec_Def := Record_Extension_Part (Typ_Def); - else - Rec_Def := Typ_Def; - end if; - - -- Create a finalization sequence for all record components - - if Present (Component_List (Rec_Def)) then - Bod_Stmts := - Process_Component_List_For_Finalize (Component_List (Rec_Def)); - end if; - - -- A derived record type must finalize all inherited components. This - -- action poses the following problem: - - -- procedure Deep_Finalize (Obj : in out Parent_Typ) is - -- begin - -- Finalize (Obj); - -- ... - - -- procedure Deep_Finalize (Obj : in out Derived_Typ) is - -- begin - -- Deep_Finalize (Obj._parent); - -- ... - -- Finalize (Obj); - -- ... - - -- Finalizing the derived type will invoke Finalize of the parent and - -- then that of the derived type. This is undesirable because both - -- routines may modify shared components. Only the Finalize of the - -- derived type should be invoked. - - -- To prevent this double adjustment of shared components, - -- Deep_Finalize uses a flag to control the invocation of Finalize: - - -- procedure Deep_Finalize - -- (Obj : in out Some_Type; - -- Flag : Boolean := True) - -- is - -- begin - -- if Flag then - -- Finalize (Obj); - -- end if; - -- ... - - -- When Deep_Finalize is invokes for field _parent, a value of False - -- is provided for the flag: - - -- Deep_Finalize (Obj._parent, False); - - if Is_Tagged_Type (Typ) - and then Is_Derived_Type (Typ) - then - declare - Par_Typ : constant Entity_Id := Parent_Field_Type (Typ); - Call : Node_Id; - Fin_Stmt : Node_Id; - - begin - if Needs_Finalization (Par_Typ) then - Call := - Make_Final_Call - (Obj_Ref => - Make_Selected_Component (Loc, - Prefix => Make_Identifier (Loc, Name_V), - Selector_Name => - Make_Identifier (Loc, Name_uParent)), - Typ => Par_Typ, - For_Parent => True); - - -- Generate: - -- Deep_Finalize (V._parent, False); -- No_Except_Propag - - -- begin -- Exceptions OK - -- Deep_Finalize (V._parent, False); - -- exception - -- when Id : others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, - -- Get_Current_Excep.all.all); - -- end if; - -- end; - - if Present (Call) then - Fin_Stmt := Call; - - if Exceptions_OK then - Fin_Stmt := - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Fin_Stmt), - Exception_Handlers => New_List ( - Build_Exception_Handler - (Finalizer_Data)))); - end if; - - Append_To (Bod_Stmts, Fin_Stmt); - end if; - end if; - end; - end if; - - -- Finalize the object. This action must be performed first before - -- all components have been finalized. - - if Is_Controlled (Typ) - and then not Is_Local - then - declare - Fin_Stmt : Node_Id; - Proc : Entity_Id; - - begin - Proc := Find_Prim_Op (Typ, Name_Finalize); - - -- Generate: - -- if F then - -- Finalize (V); -- No_Exception_Propagation - - -- begin - -- Finalize (V); - -- exception - -- when others => - -- if not Raised then - -- Raised := True; - -- Save_Occurrence (E, - -- Get_Current_Excep.all.all); - -- end if; - -- end; - -- end if; - - if Present (Proc) then - Fin_Stmt := - Make_Procedure_Call_Statement (Loc, - Name => New_Reference_To (Proc, Loc), - Parameter_Associations => New_List ( - Make_Identifier (Loc, Name_V))); - - if Exceptions_OK then - Fin_Stmt := - Make_Block_Statement (Loc, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List (Fin_Stmt), - Exception_Handlers => New_List ( - Build_Exception_Handler - (Finalizer_Data)))); - end if; - - Prepend_To (Bod_Stmts, - Make_If_Statement (Loc, - Condition => Make_Identifier (Loc, Name_F), - Then_Statements => New_List (Fin_Stmt))); - end if; - end; - end if; - - -- At this point either all finalization statements have been - -- generated or the type is not controlled. - - if No (Bod_Stmts) then - return New_List (Make_Null_Statement (Loc)); - - -- Generate: - -- declare - -- Abort : constant Boolean := Triggered_By_Abort; - -- <or> - -- Abort : constant Boolean := False; -- no abort - - -- E : Exception_Occurence; - -- Raised : Boolean := False; - - -- begin - -- <finalize statements> - - -- if Raised and then not Abort then - -- Raise_From_Controlled_Operation (E); - -- end if; - -- end; - - else - if Exceptions_OK then - Append_To (Bod_Stmts, - Build_Raise_Statement (Finalizer_Data)); - end if; - - return - New_List ( - Make_Block_Statement (Loc, - Declarations => - Finalizer_Decls, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, Bod_Stmts))); - end if; - end Build_Finalize_Statements; - - ----------------------- - -- Parent_Field_Type -- - ----------------------- - - function Parent_Field_Type (Typ : Entity_Id) return Entity_Id is - Field : Entity_Id; - - begin - Field := First_Entity (Typ); - while Present (Field) loop - if Chars (Field) = Name_uParent then - return Etype (Field); - end if; - - Next_Entity (Field); - end loop; - - -- A derived tagged type should always have a parent field - - raise Program_Error; - end Parent_Field_Type; - - --------------------------- - -- Preprocess_Components -- - --------------------------- - - procedure Preprocess_Components - (Comps : Node_Id; - Num_Comps : out Int; - Has_POC : out Boolean) - is - Decl : Node_Id; - Id : Entity_Id; - Typ : Entity_Id; - - begin - Num_Comps := 0; - Has_POC := False; - - Decl := First_Non_Pragma (Component_Items (Comps)); - while Present (Decl) loop - Id := Defining_Identifier (Decl); - Typ := Etype (Id); - - -- Skip field _parent - - if Chars (Id) /= Name_uParent - and then Needs_Finalization (Typ) - then - Num_Comps := Num_Comps + 1; - - if Has_Access_Constraint (Id) - and then No (Expression (Decl)) - then - Has_POC := True; - end if; - end if; - - Next_Non_Pragma (Decl); - end loop; - end Preprocess_Components; - - -- Start of processing for Make_Deep_Record_Body - - begin - case Prim is - when Address_Case => - return Make_Finalize_Address_Stmts (Typ); - - when Adjust_Case => - return Build_Adjust_Statements (Typ); - - when Finalize_Case => - return Build_Finalize_Statements (Typ); - - when Initialize_Case => - declare - Loc : constant Source_Ptr := Sloc (Typ); - - begin - if Is_Controlled (Typ) then - return New_List ( - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To - (Find_Prim_Op (Typ, Name_Of (Prim)), Loc), - Parameter_Associations => New_List ( - Make_Identifier (Loc, Name_V)))); - else - return Empty_List; - end if; - end; - end case; - end Make_Deep_Record_Body; - - ---------------------- - -- Make_Final_Call -- - ---------------------- - - function Make_Final_Call - (Obj_Ref : Node_Id; - Typ : Entity_Id; - For_Parent : Boolean := False) return Node_Id - is - Loc : constant Source_Ptr := Sloc (Obj_Ref); - Atyp : Entity_Id; - Fin_Id : Entity_Id := Empty; - Ref : Node_Id; - Utyp : Entity_Id; - - begin - -- Recover the proper type which contains [Deep_]Finalize - - if Is_Class_Wide_Type (Typ) then - Utyp := Root_Type (Typ); - Atyp := Utyp; - Ref := Obj_Ref; - - elsif Is_Concurrent_Type (Typ) then - Utyp := Corresponding_Record_Type (Typ); - Atyp := Empty; - Ref := Convert_Concurrent (Obj_Ref, Typ); - - elsif Is_Private_Type (Typ) - and then Present (Full_View (Typ)) - and then Is_Concurrent_Type (Full_View (Typ)) - then - Utyp := Corresponding_Record_Type (Full_View (Typ)); - Atyp := Typ; - Ref := Convert_Concurrent (Obj_Ref, Full_View (Typ)); - - else - Utyp := Typ; - Atyp := Typ; - Ref := Obj_Ref; - end if; - - Utyp := Underlying_Type (Base_Type (Utyp)); - Set_Assignment_OK (Ref); - - -- Deal with non-tagged derivation of private views. If the parent type - -- is a protected type, Deep_Finalize is found on the corresponding - -- record of the ancestor. - - if Is_Untagged_Derivation (Typ) then - if Is_Protected_Type (Typ) then - Utyp := Corresponding_Record_Type (Root_Type (Base_Type (Typ))); - else - Utyp := Underlying_Type (Root_Type (Base_Type (Typ))); - - if Is_Protected_Type (Utyp) then - Utyp := Corresponding_Record_Type (Utyp); - end if; - end if; - - Ref := Unchecked_Convert_To (Utyp, Ref); - Set_Assignment_OK (Ref); - end if; - - -- Deal with derived private types which do not inherit primitives from - -- their parents. In this case, [Deep_]Finalize can be found in the full - -- view of the parent type. - - if Is_Tagged_Type (Utyp) - and then Is_Derived_Type (Utyp) - and then Is_Empty_Elmt_List (Primitive_Operations (Utyp)) - and then Is_Private_Type (Etype (Utyp)) - and then Present (Full_View (Etype (Utyp))) - then - Utyp := Full_View (Etype (Utyp)); - Ref := Unchecked_Convert_To (Utyp, Ref); - Set_Assignment_OK (Ref); - end if; - - -- When dealing with the completion of a private type, use the base type - -- instead. - - if Utyp /= Base_Type (Utyp) then - pragma Assert (Present (Atyp) and then Is_Private_Type (Atyp)); - - Utyp := Base_Type (Utyp); - Ref := Unchecked_Convert_To (Utyp, Ref); - Set_Assignment_OK (Ref); - end if; - - -- Select the appropriate version of Finalize - - if For_Parent then - if Has_Controlled_Component (Utyp) then - Fin_Id := Find_Prim_Op (Utyp, TSS_Deep_Finalize); - end if; - - -- Class-wide types, interfaces and types with controlled components - - elsif Is_Class_Wide_Type (Typ) - or else Is_Interface (Typ) - or else Has_Controlled_Component (Utyp) - then - if Is_Tagged_Type (Utyp) then - Fin_Id := Find_Prim_Op (Utyp, TSS_Deep_Finalize); - else - Fin_Id := TSS (Utyp, TSS_Deep_Finalize); - end if; - - -- Derivations from [Limited_]Controlled - - elsif Is_Controlled (Utyp) then - if Has_Controlled_Component (Utyp) then - Fin_Id := Find_Prim_Op (Utyp, TSS_Deep_Finalize); - else - Fin_Id := Find_Prim_Op (Utyp, Name_Of (Finalize_Case)); - end if; - - -- Tagged types - - elsif Is_Tagged_Type (Utyp) then - Fin_Id := Find_Prim_Op (Utyp, TSS_Deep_Finalize); - - else - raise Program_Error; - end if; - - if Present (Fin_Id) then - - -- When finalizing a class-wide object, do not convert to the root - -- type in order to produce a dispatching call. - - if Is_Class_Wide_Type (Typ) then - null; - - -- Ensure that a finalization routine is at least decorated in order - -- to inspect the object parameter. - - elsif Analyzed (Fin_Id) - or else Ekind (Fin_Id) = E_Procedure - then - -- In certain cases, such as the creation of Stream_Read, the - -- visible entity of the type is its full view. Since Stream_Read - -- will have to create an object of type Typ, the local object - -- will be finalzed by the scope finalizer generated later on. The - -- object parameter of Deep_Finalize will always use the private - -- view of the type. To avoid such a clash between a private and a - -- full view, perform an unchecked conversion of the object - -- reference to the private view. - - declare - Formal_Typ : constant Entity_Id := - Etype (First_Formal (Fin_Id)); - begin - if Is_Private_Type (Formal_Typ) - and then Present (Full_View (Formal_Typ)) - and then Full_View (Formal_Typ) = Utyp - then - Ref := Unchecked_Convert_To (Formal_Typ, Ref); - end if; - end; - - Ref := Convert_View (Fin_Id, Ref); - end if; - - return Make_Call (Loc, Fin_Id, New_Copy_Tree (Ref), For_Parent); - else - return Empty; - end if; - end Make_Final_Call; - - -------------------------------- - -- Make_Finalize_Address_Body -- - -------------------------------- - - procedure Make_Finalize_Address_Body (Typ : Entity_Id) is - Is_Task : constant Boolean := - Ekind (Typ) = E_Record_Type - and then Is_Concurrent_Record_Type (Typ) - and then Ekind (Corresponding_Concurrent_Type (Typ)) = - E_Task_Type; - Loc : constant Source_Ptr := Sloc (Typ); - Proc_Id : Entity_Id; - Stmts : List_Id; - - begin - -- The corresponding records of task types are not controlled by design. - -- For the sake of completeness, create an empty Finalize_Address to be - -- used in task class-wide allocations. - - if Is_Task then - null; - - -- Nothing to do if the type is not controlled or it already has a - -- TSS entry for Finalize_Address. Skip class-wide subtypes which do not - -- come from source. These are usually generated for completeness and - -- do not need the Finalize_Address primitive. - - elsif not Needs_Finalization (Typ) - or else Is_Abstract_Type (Typ) - or else Present (TSS (Typ, TSS_Finalize_Address)) - or else - (Is_Class_Wide_Type (Typ) - and then Ekind (Root_Type (Typ)) = E_Record_Subtype - and then not Comes_From_Source (Root_Type (Typ))) - then - return; - end if; - - Proc_Id := - Make_Defining_Identifier (Loc, - Make_TSS_Name (Typ, TSS_Finalize_Address)); - - -- Generate: - - -- procedure <Typ>FD (V : System.Address) is - -- begin - -- null; -- for tasks - - -- declare -- for all other types - -- type Pnn is access all Typ; - -- for Pnn'Storage_Size use 0; - -- begin - -- [Deep_]Finalize (Pnn (V).all); - -- end; - -- end TypFD; - - if Is_Task then - Stmts := New_List (Make_Null_Statement (Loc)); - else - Stmts := Make_Finalize_Address_Stmts (Typ); - end if; - - Discard_Node ( - Make_Subprogram_Body (Loc, - Specification => - Make_Procedure_Specification (Loc, - Defining_Unit_Name => Proc_Id, - - Parameter_Specifications => New_List ( - Make_Parameter_Specification (Loc, - Defining_Identifier => - Make_Defining_Identifier (Loc, Name_V), - Parameter_Type => - New_Reference_To (RTE (RE_Address), Loc)))), - - Declarations => No_List, - - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => Stmts))); - - Set_TSS (Typ, Proc_Id); - end Make_Finalize_Address_Body; - - --------------------------------- - -- Make_Finalize_Address_Stmts -- - --------------------------------- - - function Make_Finalize_Address_Stmts (Typ : Entity_Id) return List_Id is - Loc : constant Source_Ptr := Sloc (Typ); - Ptr_Typ : constant Entity_Id := Make_Temporary (Loc, 'P'); - Decls : List_Id; - Desg_Typ : Entity_Id; - Obj_Expr : Node_Id; - - begin - if Is_Array_Type (Typ) then - if Is_Constrained (First_Subtype (Typ)) then - Desg_Typ := First_Subtype (Typ); - else - Desg_Typ := Base_Type (Typ); - end if; - - -- Class-wide types of constrained root types - - elsif Is_Class_Wide_Type (Typ) - and then Has_Discriminants (Root_Type (Typ)) - and then not - Is_Empty_Elmt_List (Discriminant_Constraint (Root_Type (Typ))) - then - declare - Parent_Typ : Entity_Id; - - begin - -- Climb the parent type chain looking for a non-constrained type - - Parent_Typ := Root_Type (Typ); - while Parent_Typ /= Etype (Parent_Typ) - and then Has_Discriminants (Parent_Typ) - and then not - Is_Empty_Elmt_List (Discriminant_Constraint (Parent_Typ)) - loop - Parent_Typ := Etype (Parent_Typ); - end loop; - - -- Handle views created for tagged types with unknown - -- discriminants. - - if Is_Underlying_Record_View (Parent_Typ) then - Parent_Typ := Underlying_Record_View (Parent_Typ); - end if; - - Desg_Typ := Class_Wide_Type (Underlying_Type (Parent_Typ)); - end; - - -- General case - - else - Desg_Typ := Typ; - end if; - - -- Generate: - -- type Ptr_Typ is access all Typ; - -- for Ptr_Typ'Storage_Size use 0; - - Decls := New_List ( - Make_Full_Type_Declaration (Loc, - Defining_Identifier => Ptr_Typ, - Type_Definition => - Make_Access_To_Object_Definition (Loc, - All_Present => True, - Subtype_Indication => New_Reference_To (Desg_Typ, Loc))), - - Make_Attribute_Definition_Clause (Loc, - Name => New_Reference_To (Ptr_Typ, Loc), - Chars => Name_Storage_Size, - Expression => Make_Integer_Literal (Loc, 0))); - - Obj_Expr := Make_Identifier (Loc, Name_V); - - -- Unconstrained arrays require special processing in order to retrieve - -- the elements. To achieve this, we have to skip the dope vector which - -- lays in front of the elements and then use a thin pointer to perform - -- the address-to-access conversion. - - if Is_Array_Type (Typ) - and then not Is_Constrained (First_Subtype (Typ)) - then - declare - Dope_Id : Entity_Id; - - begin - -- Ensure that Ptr_Typ a thin pointer, generate: - -- for Ptr_Typ'Size use System.Address'Size; - - Append_To (Decls, - Make_Attribute_Definition_Clause (Loc, - Name => New_Reference_To (Ptr_Typ, Loc), - Chars => Name_Size, - Expression => - Make_Integer_Literal (Loc, System_Address_Size))); - - -- Generate: - -- Dnn : constant Storage_Offset := - -- Desg_Typ'Descriptor_Size / Storage_Unit; - - Dope_Id := Make_Temporary (Loc, 'D'); - - Append_To (Decls, - Make_Object_Declaration (Loc, - Defining_Identifier => Dope_Id, - Constant_Present => True, - Object_Definition => - New_Reference_To (RTE (RE_Storage_Offset), Loc), - Expression => - Make_Op_Divide (Loc, - Left_Opnd => - Make_Attribute_Reference (Loc, - Prefix => New_Reference_To (Desg_Typ, Loc), - Attribute_Name => Name_Descriptor_Size), - Right_Opnd => - Make_Integer_Literal (Loc, System_Storage_Unit)))); - - -- Shift the address from the start of the dope vector to the - -- start of the elements: - -- - -- V + Dnn - -- - -- Note that this is done through a wrapper routine since RTSfind - -- cannot retrieve operations with string names of the form "+". - - Obj_Expr := - Make_Function_Call (Loc, - Name => - New_Reference_To (RTE (RE_Add_Offset_To_Address), Loc), - Parameter_Associations => New_List ( - Obj_Expr, - New_Reference_To (Dope_Id, Loc))); - end; - end if; - - -- Create the block and the finalization call - - return New_List ( - Make_Block_Statement (Loc, - Declarations => Decls, - - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => New_List ( - Make_Final_Call ( - Obj_Ref => - Make_Explicit_Dereference (Loc, - Prefix => Unchecked_Convert_To (Ptr_Typ, Obj_Expr)), - Typ => Desg_Typ))))); - end Make_Finalize_Address_Stmts; - - ------------------------------------- - -- Make_Handler_For_Ctrl_Operation -- - ------------------------------------- - - -- Generate: - - -- when E : others => - -- Raise_From_Controlled_Operation (E); - - -- or: - - -- when others => - -- raise Program_Error [finalize raised exception]; - - -- depending on whether Raise_From_Controlled_Operation is available - - function Make_Handler_For_Ctrl_Operation - (Loc : Source_Ptr) return Node_Id - is - E_Occ : Entity_Id; - -- Choice parameter (for the first case above) - - Raise_Node : Node_Id; - -- Procedure call or raise statement - - begin - -- Standard run-time, .NET/JVM targets: add choice parameter E and pass - -- it to Raise_From_Controlled_Operation so that the original exception - -- name and message can be recorded in the exception message for - -- Program_Error. - - if RTE_Available (RE_Raise_From_Controlled_Operation) then - E_Occ := Make_Defining_Identifier (Loc, Name_E); - Raise_Node := - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To - (RTE (RE_Raise_From_Controlled_Operation), Loc), - Parameter_Associations => New_List ( - New_Reference_To (E_Occ, Loc))); - - -- Restricted run-time: exception messages are not supported - - else - E_Occ := Empty; - Raise_Node := - Make_Raise_Program_Error (Loc, - Reason => PE_Finalize_Raised_Exception); - end if; - - return - Make_Implicit_Exception_Handler (Loc, - Exception_Choices => New_List (Make_Others_Choice (Loc)), - Choice_Parameter => E_Occ, - Statements => New_List (Raise_Node)); - end Make_Handler_For_Ctrl_Operation; - - -------------------- - -- Make_Init_Call -- - -------------------- - - function Make_Init_Call - (Obj_Ref : Node_Id; - Typ : Entity_Id) return Node_Id - is - Loc : constant Source_Ptr := Sloc (Obj_Ref); - Is_Conc : Boolean; - Proc : Entity_Id; - Ref : Node_Id; - Utyp : Entity_Id; - - begin - -- Deal with the type and object reference. Depending on the context, an - -- object reference may need several conversions. - - if Is_Concurrent_Type (Typ) then - Is_Conc := True; - Utyp := Corresponding_Record_Type (Typ); - Ref := Convert_Concurrent (Obj_Ref, Typ); - - elsif Is_Private_Type (Typ) - and then Present (Full_View (Typ)) - and then Is_Concurrent_Type (Underlying_Type (Typ)) - then - Is_Conc := True; - Utyp := Corresponding_Record_Type (Underlying_Type (Typ)); - Ref := Convert_Concurrent (Obj_Ref, Underlying_Type (Typ)); - - else - Is_Conc := False; - Utyp := Typ; - Ref := Obj_Ref; - end if; - - Set_Assignment_OK (Ref); - - Utyp := Underlying_Type (Base_Type (Utyp)); - - -- Deal with non-tagged derivation of private views - - if Is_Untagged_Derivation (Typ) - and then not Is_Conc - then - Utyp := Underlying_Type (Root_Type (Base_Type (Typ))); - Ref := Unchecked_Convert_To (Utyp, Ref); - - -- The following is to prevent problems with UC see 1.156 RH ??? - - Set_Assignment_OK (Ref); - end if; - - -- If the underlying_type is a subtype, then we are dealing with the - -- completion of a private type. We need to access the base type and - -- generate a conversion to it. - - if Utyp /= Base_Type (Utyp) then - pragma Assert (Is_Private_Type (Typ)); - Utyp := Base_Type (Utyp); - Ref := Unchecked_Convert_To (Utyp, Ref); - end if; - - -- Select the appropriate version of initialize - - if Has_Controlled_Component (Utyp) then - Proc := TSS (Utyp, Deep_Name_Of (Initialize_Case)); - else - Proc := Find_Prim_Op (Utyp, Name_Of (Initialize_Case)); - Check_Visibly_Controlled (Initialize_Case, Typ, Proc, Ref); - end if; - - -- The object reference may need another conversion depending on the - -- type of the formal and that of the actual. - - Ref := Convert_View (Proc, Ref); - - -- Generate: - -- [Deep_]Initialize (Ref); - - return - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (Proc, Loc), - Parameter_Associations => New_List (Ref)); - end Make_Init_Call; - - ------------------------------ - -- Make_Local_Deep_Finalize -- - ------------------------------ - - function Make_Local_Deep_Finalize - (Typ : Entity_Id; - Nam : Entity_Id) return Node_Id - is - Loc : constant Source_Ptr := Sloc (Typ); - Formals : List_Id; - - begin - Formals := New_List ( - - -- V : in out Typ - - Make_Parameter_Specification (Loc, - Defining_Identifier => Make_Defining_Identifier (Loc, Name_V), - In_Present => True, - Out_Present => True, - Parameter_Type => New_Reference_To (Typ, Loc)), - - -- F : Boolean := True - - Make_Parameter_Specification (Loc, - Defining_Identifier => Make_Defining_Identifier (Loc, Name_F), - Parameter_Type => New_Reference_To (Standard_Boolean, Loc), - Expression => New_Reference_To (Standard_True, Loc))); - - -- Add the necessary number of counters to represent the initialization - -- state of an object. - - return - Make_Subprogram_Body (Loc, - Specification => - Make_Procedure_Specification (Loc, - Defining_Unit_Name => Nam, - Parameter_Specifications => Formals), - - Declarations => No_List, - - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, - Statements => Make_Deep_Record_Body (Finalize_Case, Typ, True))); - end Make_Local_Deep_Finalize; - - ------------------------------------ - -- Make_Set_Finalize_Address_Call -- - ------------------------------------ - - function Make_Set_Finalize_Address_Call - (Loc : Source_Ptr; - Typ : Entity_Id; - Ptr_Typ : Entity_Id) return Node_Id - is - Desig_Typ : constant Entity_Id := - Available_View (Designated_Type (Ptr_Typ)); - Fin_Mas_Id : constant Entity_Id := Finalization_Master (Ptr_Typ); - Fin_Mas_Ref : Node_Id; - Utyp : Entity_Id; - - begin - -- If the context is a class-wide allocator, we use the class-wide type - -- to obtain the proper Finalize_Address routine. - - if Is_Class_Wide_Type (Desig_Typ) then - Utyp := Desig_Typ; - - else - Utyp := Typ; - - if Is_Private_Type (Utyp) and then Present (Full_View (Utyp)) then - Utyp := Full_View (Utyp); - end if; - - if Is_Concurrent_Type (Utyp) then - Utyp := Corresponding_Record_Type (Utyp); - end if; - end if; - - Utyp := Underlying_Type (Base_Type (Utyp)); - - -- Deal with non-tagged derivation of private views. If the parent is - -- now known to be protected, the finalization routine is the one - -- defined on the corresponding record of the ancestor (corresponding - -- records do not automatically inherit operations, but maybe they - -- should???) - - if Is_Untagged_Derivation (Typ) then - if Is_Protected_Type (Typ) then - Utyp := Corresponding_Record_Type (Root_Type (Base_Type (Typ))); - else - Utyp := Underlying_Type (Root_Type (Base_Type (Typ))); - - if Is_Protected_Type (Utyp) then - Utyp := Corresponding_Record_Type (Utyp); - end if; - end if; - end if; - - -- If the underlying_type is a subtype, we are dealing with the - -- completion of a private type. We need to access the base type and - -- generate a conversion to it. - - if Utyp /= Base_Type (Utyp) then - pragma Assert (Is_Private_Type (Typ)); - - Utyp := Base_Type (Utyp); - end if; - - Fin_Mas_Ref := New_Occurrence_Of (Fin_Mas_Id, Loc); - - -- If the call is from a build-in-place function, the Master parameter - -- is actually a pointer. Dereference it for the call. - - if Is_Access_Type (Etype (Fin_Mas_Id)) then - Fin_Mas_Ref := Make_Explicit_Dereference (Loc, Fin_Mas_Ref); - end if; - - -- Generate: - -- Set_Finalize_Address (<Ptr_Typ>FM, <Utyp>FD'Unrestricted_Access); - - return - Make_Procedure_Call_Statement (Loc, - Name => - New_Reference_To (RTE (RE_Set_Finalize_Address), Loc), - Parameter_Associations => New_List ( - Fin_Mas_Ref, - Make_Attribute_Reference (Loc, - Prefix => - New_Reference_To (TSS (Utyp, TSS_Finalize_Address), Loc), - Attribute_Name => Name_Unrestricted_Access))); - end Make_Set_Finalize_Address_Call; - - -------------------------- - -- Make_Transient_Block -- - -------------------------- - - function Make_Transient_Block - (Loc : Source_Ptr; - Action : Node_Id; - Par : Node_Id) return Node_Id - is - Decls : constant List_Id := New_List; - Instrs : constant List_Id := New_List (Action); - Block : Node_Id; - Insert : Node_Id; - - begin - -- Case where only secondary stack use is involved - - if VM_Target = No_VM - and then Uses_Sec_Stack (Current_Scope) - and then Nkind (Action) /= N_Simple_Return_Statement - and then Nkind (Par) /= N_Exception_Handler - then - declare - S : Entity_Id; - - begin - S := Scope (Current_Scope); - loop - -- At the outer level, no need to release the sec stack - - if S = Standard_Standard then - Set_Uses_Sec_Stack (Current_Scope, False); - exit; - - -- In a function, only release the sec stack if the function - -- does not return on the sec stack otherwise the result may - -- be lost. The caller is responsible for releasing. - - elsif Ekind (S) = E_Function then - Set_Uses_Sec_Stack (Current_Scope, False); - - if not Requires_Transient_Scope (Etype (S)) then - Set_Uses_Sec_Stack (S, True); - Check_Restriction (No_Secondary_Stack, Action); - end if; - - exit; - - -- In a loop or entry we should install a block encompassing - -- all the construct. For now just release right away. - - elsif Ekind_In (S, E_Entry, E_Loop) then - exit; - - -- In a procedure or a block, we release on exit of the - -- procedure or block. ??? memory leak can be created by - -- recursive calls. - - elsif Ekind_In (S, E_Block, E_Procedure) then - Set_Uses_Sec_Stack (S, True); - Check_Restriction (No_Secondary_Stack, Action); - Set_Uses_Sec_Stack (Current_Scope, False); - exit; - - else - S := Scope (S); - end if; - end loop; - end; - end if; - - -- Create the transient block. Set the parent now since the block itself - -- is not part of the tree. - - Block := - Make_Block_Statement (Loc, - Identifier => New_Reference_To (Current_Scope, Loc), - Declarations => Decls, - Handled_Statement_Sequence => - Make_Handled_Sequence_Of_Statements (Loc, Statements => Instrs), - Has_Created_Identifier => True); - Set_Parent (Block, Par); - - -- Insert actions stuck in the transient scopes as well as all freezing - -- nodes needed by those actions. - - Insert_Actions_In_Scope_Around (Action); - - Insert := Prev (Action); - if Present (Insert) then - Freeze_All (First_Entity (Current_Scope), Insert); - end if; - - -- When the transient scope was established, we pushed the entry for the - -- transient scope onto the scope stack, so that the scope was active - -- for the installation of finalizable entities etc. Now we must remove - -- this entry, since we have constructed a proper block. - - Pop_Scope; - - return Block; - end Make_Transient_Block; - - ------------------------ - -- Node_To_Be_Wrapped -- - ------------------------ - - function Node_To_Be_Wrapped return Node_Id is - begin - return Scope_Stack.Table (Scope_Stack.Last).Node_To_Be_Wrapped; - end Node_To_Be_Wrapped; - - ---------------------------- - -- Set_Node_To_Be_Wrapped -- - ---------------------------- - - procedure Set_Node_To_Be_Wrapped (N : Node_Id) is - begin - Scope_Stack.Table (Scope_Stack.Last).Node_To_Be_Wrapped := N; - end Set_Node_To_Be_Wrapped; - - ---------------------------------- - -- Store_After_Actions_In_Scope -- - ---------------------------------- - - procedure Store_After_Actions_In_Scope (L : List_Id) is - SE : Scope_Stack_Entry renames Scope_Stack.Table (Scope_Stack.Last); - - begin - if Present (SE.Actions_To_Be_Wrapped_After) then - Insert_List_Before_And_Analyze ( - First (SE.Actions_To_Be_Wrapped_After), L); - - else - SE.Actions_To_Be_Wrapped_After := L; - - if Is_List_Member (SE.Node_To_Be_Wrapped) then - Set_Parent (L, Parent (SE.Node_To_Be_Wrapped)); - else - Set_Parent (L, SE.Node_To_Be_Wrapped); - end if; - - Analyze_List (L); - end if; - end Store_After_Actions_In_Scope; - - ----------------------------------- - -- Store_Before_Actions_In_Scope -- - ----------------------------------- - - procedure Store_Before_Actions_In_Scope (L : List_Id) is - SE : Scope_Stack_Entry renames Scope_Stack.Table (Scope_Stack.Last); - - begin - if Present (SE.Actions_To_Be_Wrapped_Before) then - Insert_List_After_And_Analyze ( - Last (SE.Actions_To_Be_Wrapped_Before), L); - - else - SE.Actions_To_Be_Wrapped_Before := L; - - if Is_List_Member (SE.Node_To_Be_Wrapped) then - Set_Parent (L, Parent (SE.Node_To_Be_Wrapped)); - else - Set_Parent (L, SE.Node_To_Be_Wrapped); - end if; - - Analyze_List (L); - end if; - end Store_Before_Actions_In_Scope; - - -------------------------------- - -- Wrap_Transient_Declaration -- - -------------------------------- - - -- If a transient scope has been established during the processing of the - -- Expression of an Object_Declaration, it is not possible to wrap the - -- declaration into a transient block as usual case, otherwise the object - -- would be itself declared in the wrong scope. Therefore, all entities (if - -- any) defined in the transient block are moved to the proper enclosing - -- scope, furthermore, if they are controlled variables they are finalized - -- right after the declaration. The finalization list of the transient - -- scope is defined as a renaming of the enclosing one so during their - -- initialization they will be attached to the proper finalization list. - -- For instance, the following declaration : - - -- X : Typ := F (G (A), G (B)); - - -- (where G(A) and G(B) return controlled values, expanded as _v1 and _v2) - -- is expanded into : - - -- X : Typ := [ complex Expression-Action ]; - -- [Deep_]Finalize (_v1); - -- [Deep_]Finalize (_v2); - - procedure Wrap_Transient_Declaration (N : Node_Id) is - Encl_S : Entity_Id; - S : Entity_Id; - Uses_SS : Boolean; - - begin - S := Current_Scope; - Encl_S := Scope (S); - - -- Insert Actions kept in the Scope stack - - Insert_Actions_In_Scope_Around (N); - - -- If the declaration is consuming some secondary stack, mark the - -- enclosing scope appropriately. - - Uses_SS := Uses_Sec_Stack (S); - Pop_Scope; - - -- Put the local entities back in the enclosing scope, and set the - -- Is_Public flag appropriately. - - Transfer_Entities (S, Encl_S); - - -- Mark the enclosing dynamic scope so that the sec stack will be - -- released upon its exit unless this is a function that returns on - -- the sec stack in which case this will be done by the caller. - - if VM_Target = No_VM and then Uses_SS then - S := Enclosing_Dynamic_Scope (S); - - if Ekind (S) = E_Function - and then Requires_Transient_Scope (Etype (S)) - then - null; - else - Set_Uses_Sec_Stack (S); - Check_Restriction (No_Secondary_Stack, N); - end if; - end if; - end Wrap_Transient_Declaration; - - ------------------------------- - -- Wrap_Transient_Expression -- - ------------------------------- - - procedure Wrap_Transient_Expression (N : Node_Id) is - Expr : constant Node_Id := Relocate_Node (N); - Loc : constant Source_Ptr := Sloc (N); - Temp : constant Entity_Id := Make_Temporary (Loc, 'E', N); - Typ : constant Entity_Id := Etype (N); - - begin - -- Generate: - - -- Temp : Typ; - -- declare - -- M : constant Mark_Id := SS_Mark; - -- procedure Finalizer is ... (See Build_Finalizer) - - -- begin - -- Temp := <Expr>; - -- - -- at end - -- Finalizer; - -- end; - - Insert_Actions (N, New_List ( - Make_Object_Declaration (Loc, - Defining_Identifier => Temp, - Object_Definition => New_Reference_To (Typ, Loc)), - - Make_Transient_Block (Loc, - Action => - Make_Assignment_Statement (Loc, - Name => New_Reference_To (Temp, Loc), - Expression => Expr), - Par => Parent (N)))); - - Rewrite (N, New_Reference_To (Temp, Loc)); - Analyze_And_Resolve (N, Typ); - end Wrap_Transient_Expression; - - ------------------------------ - -- Wrap_Transient_Statement -- - ------------------------------ - - procedure Wrap_Transient_Statement (N : Node_Id) is - Loc : constant Source_Ptr := Sloc (N); - New_Stmt : constant Node_Id := Relocate_Node (N); - - begin - -- Generate: - -- declare - -- M : constant Mark_Id := SS_Mark; - -- procedure Finalizer is ... (See Build_Finalizer) - -- - -- begin - -- <New_Stmt>; - -- - -- at end - -- Finalizer; - -- end; - - Rewrite (N, - Make_Transient_Block (Loc, - Action => New_Stmt, - Par => Parent (N))); - - -- With the scope stack back to normal, we can call analyze on the - -- resulting block. At this point, the transient scope is being - -- treated like a perfectly normal scope, so there is nothing - -- special about it. - - -- Note: Wrap_Transient_Statement is called with the node already - -- analyzed (i.e. Analyzed (N) is True). This is important, since - -- otherwise we would get a recursive processing of the node when - -- we do this Analyze call. - - Analyze (N); - end Wrap_Transient_Statement; - -end Exp_Ch7; |