| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- E X P _ C H 7 -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2025, 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 Einfo.Entities; use Einfo.Entities; |
| with Einfo.Utils; use Einfo.Utils; |
| 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_Prag; use Exp_Prag; |
| with Exp_Tss; use Exp_Tss; |
| with Exp_Util; use Exp_Util; |
| with Freeze; use Freeze; |
| with GNAT_CUDA; use GNAT_CUDA; |
| with Inline; use Inline; |
| 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 Sinfo.Nodes; use Sinfo.Nodes; |
| with Sinfo.Utils; use Sinfo.Utils; |
| with Sem; use Sem; |
| with Sem_Aux; use Sem_Aux; |
| with Sem_Ch6; use Sem_Ch6; |
| 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 Tbuild; use Tbuild; |
| with Ttypes; use Ttypes; |
| with Uintp; use Uintp; |
| |
| package body Exp_Ch7 is |
| |
| ----------------------------- |
| -- Finalization Management -- |
| ----------------------------- |
| |
| -- This paragraph describes 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, the compiler |
| -- generates Deep_Initialize, Deep_Adjust and Deep_Finalize that are in |
| -- charge of calling the former procedures on the controlled components. |
| |
| -- Initialize calls: they are generated for either declarations or dynamic |
| -- allocations of controlled objects with no initial value. They are always |
| -- followed by an attachment to some finalization chain. For the dynamic |
| -- dynamic allocation case, this is the collection attached to the access |
| -- type definition; otherwise, this is the master of the current scope. |
| |
| -- Adjust calls: they are generated on two occasions: (1) for declarations |
| -- or dynamic allocations of controlled objects with an initial value (with |
| -- the exception of function calls), (2) after an assignment. In the first |
| -- case they are followed by an attachment to the finalization chain, in |
| -- the second case they are not. |
| |
| -- Finalization calls: they are generated on three occasions: (1) on scope |
| -- exit, (2) assignments, (3) unchecked deallocations. In case (3) objects |
| -- have to be detached from the finalization chain, in case (2) they must |
| -- not and in case (1) this is optional as we are exiting the scope anyway. |
| |
| -- There are two kinds of finalization chain to which objects are attached, |
| -- depending on the way they are created. For objects (statically) declared |
| -- in a scope, the finalization chain is that of the master of the scope, |
| -- which is embodied in a Finalization_Master object. As per RM 7.6.1(11/3) |
| -- the finalization of the master (on scope exit) performs the finalization |
| -- of objects attached to its chain in the reverse order of their creation. |
| |
| -- For dynamically allocated objects, the finalization chain is that of the |
| -- finalization collection of the access type through which the objects are |
| -- allocated, which is embodied in a Finalization_Collection object. As per |
| -- RM 7.6.1(11.1/3), the finalization of the collection performs the |
| -- finalization of objects attached to its chain in an arbitrary order. |
| |
| -- A Finalization_Collection object is implemented as a controlled object |
| -- and its finalization is therefore driven by the finalization master of |
| -- the scope where it is declared. As per RM 7.6.1(11.2/3), for a named |
| -- access type, the Finalization_Collection object is declared in the list |
| -- of actions of its freeze node. |
| |
| -- ??? For an anonymous access type, the implementation deviates from the |
| -- RM 7.6.1 clause as follows: all the anonymous access types with the same |
| -- designated type that are (implicitly) declared in a library unit share a |
| -- single Finalization_Collection object declared in the outermost scope of |
| -- the library unit, except if the designated type is declared in a dynamic |
| -- scope nested in the unit; in this case no Finalization_Collection object |
| -- is created. As a result, in the first case, objects allocated through |
| -- the anonymous access types are finalized when the library unit goes out |
| -- of scope, while in the second case, they are not finalized at all. |
| |
| -- Here is a simple example of the expansion of a controlled block: |
| |
| -- declare |
| -- X : Ctrl; |
| -- Y : Ctrl := Init; |
| |
| -- type Rec is record |
| -- C : Ctrl; |
| -- end record; |
| |
| -- W : Rec; |
| -- Z : Rec := Init; |
| |
| -- begin |
| -- X := Y; |
| -- W := Z; |
| -- end; |
| -- |
| -- is expanded into: |
| -- |
| -- declare |
| -- Mnn : System.Finalization_Primitives.Finalization_Master; |
| |
| -- XMN : aliased System.Finalization_Primitives.Master_Node; |
| -- X : Ctrl; |
| -- Bnn : begin |
| -- Abort_Defer; |
| -- Initialize (X); |
| -- System.Finalization_Primitives.Attach_To_Master |
| -- (X'address, |
| -- CtrlFD'unrestricted_access, |
| -- XMN'unrestricted_access, |
| -- Mnn); |
| -- at end |
| -- Abort_Undefer; |
| -- end Bnn; |
| |
| -- YMN : aliased System.Finalization_Primitives.Master_Node; |
| -- Y : Ctrl := Init; |
| -- System.Finalization_Primitives.Attach_To_Master |
| -- (Y'address, |
| -- CtrlFD'unrestricted_access, |
| -- YMN'unrestricted_access, |
| -- Mnn); |
| |
| -- type Rec is record |
| -- C : Ctrl; |
| -- end record; |
| |
| -- WMN : aliased System.Finalization_Primitives.Master_Node; |
| -- W : Rec; |
| -- Bnn : begin |
| -- Abort_Defer; |
| -- Bnn : begin |
| -- Deep_Initialize (W); |
| -- System.Finalization_Primitives.Attach_To_Master |
| -- (W'address, |
| -- Rec_FD'unrestricted_access, |
| -- WMN'unrestricted_access, |
| -- Mnn); |
| -- exception |
| -- when others => |
| -- Deep_Finalize (W); |
| -- end Bnn; |
| -- at end |
| -- Abort_Undefer; |
| -- end Bnn; |
| |
| -- ZMN : aliaed System.Finalization_Primitives.Master_Node; |
| -- Z : Rec := Init; |
| -- System.Finalization_Primitives.Attach_To_Master |
| -- (Z'address, |
| -- Rec_FD'unrestricted_access, |
| -- ZMN'unrestricted_access, |
| -- Mnn); |
| |
| -- procedure _Finalizer is |
| -- Ann : constant Boolean := Ada.Exceptions.Triggered_By_Abort; |
| -- Rnn : boolean := False; |
| -- begin |
| -- Abort_Defer; |
| -- Bnn : begin |
| -- System.Finalization_Primitives.Finalize_Master (Mnn); |
| -- exceptions |
| -- when others => |
| -- Rnn := True; |
| -- end Bnn; |
| -- Abort_Undefer; |
| -- if Rnn and then not Ann then |
| -- [program_error "finalize raised exception"] |
| -- end if; |
| -- end _Finalizer; |
| |
| -- begin |
| -- _Assign (X, Y); |
| -- Deep_Finalize (W); |
| -- W := Z; |
| -- Deep_Adjust (W); |
| -- end; |
| -- at end |
| -- _Finalizer; |
| |
| -- In the case of a block containing a single controlled object, the master |
| -- degenerates into a single master node: |
| |
| -- declare |
| -- X : Ctrl := Init; |
| |
| -- begin |
| -- null; |
| -- end; |
| |
| -- is expanded into: |
| |
| -- declare |
| -- XMN : aliased System.Finalization_Primitives.Master_Node; |
| -- X : Ctrl := Init; |
| -- System.Finalization_Primitives.Attach_To_Node |
| -- (X'address, |
| -- CtrlFD'unrestricted_access, |
| -- XMN'unrestricted_access); |
| |
| -- procedure _Finalizer is |
| -- Ann : constant Boolean := Ada.Exceptions.Triggered_By_Abort; |
| -- Rnn : boolean := False; |
| -- begin |
| -- Abort_Defer; |
| -- Bnn : begin |
| -- System.Finalization_Primitives.Finalize_Object (XMN); |
| -- exceptions |
| -- when others => |
| -- Rnn := True; |
| -- end Bnn; |
| -- Abort_Undefer; |
| -- if Rnn and then not Ann then |
| -- [program_error "finalize raised exception"] |
| -- end if; |
| -- end _Finalizer; |
| |
| -- begin |
| -- null; |
| -- end; |
| -- at end |
| -- _Finalizer; |
| |
| -- Here is the version with a dynamically allocated object: |
| |
| -- declare |
| -- X : P_Ctrl := new Ctrl; |
| |
| -- begin |
| -- null; |
| -- end; |
| -- |
| -- is expanded into: |
| |
| -- declare |
| -- Cnn : System.Finalization_Primitives.Finalization_Collection_Ptr := |
| -- P_CtrlFC'unrestricted_access; |
| -- [...] |
| -- Pnn : constant P_Ctrl := new Ctrl[...][...]; |
| -- Bnn : begin |
| -- Abort_Defer; |
| -- Initialize (Pnn.all); |
| -- System.Finalization_Primitives.Attach_To_Collection |
| -- (Pnn.all'address, |
| -- CtrlFD'unrestricted_access, |
| -- Cnn.all); |
| -- at end |
| -- Abort_Undefer; |
| -- end Bnn; |
| -- X : P_Ctrl := Pnn; |
| |
| -- The implementation uses two different strategies for the finalization |
| -- of (statically) declared objects and of dynamically allocated objects. |
| |
| -- For (statically) declared objects, the attachment to the finalization |
| -- chain of the current scope and the call to the finalization procedure |
| -- are generated during a post-processing phase of the expansion. These |
| -- objects are first spotted in declarative parts and statement lists by |
| -- Requires_Cleanup_Actions; then Build_Finalizer is called on the parent |
| -- node to generate both the attachment and the finalization actions. |
| |
| -- This post processing is fully transparent for the rest of the expansion |
| -- activities, in other words those have nothing to do or to care about. |
| -- However this default processing may not be sufficient in specific cases, |
| -- e.g. for the return object of an extended return statement in a function |
| -- whose result type is controlled: in this case, the return object must be |
| -- finalized only if the function returns abnormally. In order to deal with |
| -- these cases, it is possible to directly generate detachment actions (for |
| -- the return object case) or finalization actions (for transient objects) |
| -- during the rest of expansion activities. |
| |
| -- These direct actions must be signalled to the post-processing machinery |
| -- and this is achieved through the handling of Master_Node objects, which |
| -- are the items actually chained in the finalization chains of masters. |
| -- With the default processing, they are created by Build_Finalizer for the |
| -- controlled objects spotted by Requires_Cleanup_Actions. But when direct |
| -- actions are carried out, they are generated by these actions and later |
| -- recognized by Requires_Cleanup_Actions and picked up by Build_Finalizer. |
| |
| -- For dynamically allocated objects, there is no post-processing phase and |
| -- the attachment to the finalization chain of the access type, as well the |
| -- the detachment from this chain for unchecked deallocation, are generated |
| -- directly by the compiler during the expansion of allocators and calls to |
| -- instances of the Unchecked_Deallocation procedure. |
| |
| -------------------------- |
| -- Relaxed Finalization -- |
| -------------------------- |
| |
| -- This paragraph describes the differences between the implementation of |
| -- finalization as specified by the Ada RM (called "strict" and documented |
| -- in the previous paragraph) and that of finalization as specified by the |
| -- GNAT RM (called "relaxed") for a second category of controlled objects. |
| |
| -- For objects (statically) declared in a scope, the default implementation |
| -- documented in the previous paragraph is used for the scope as a whole as |
| -- soon as one controlled object with strict finalization is present in it, |
| -- including one transient controlled object. Otherwise, that is to say, if |
| -- all the controlled objects in the scope have relaxed finalization, then |
| -- no Finalization_Master is built for this scope, and all the objects are |
| -- finalized explicitly in the reverse order of their creation: |
| |
| -- declare |
| -- X : Ctrl := Init; |
| -- Y : Ctrl := Init; |
| |
| -- begin |
| -- null; |
| -- end; |
| |
| -- is expanded into: |
| |
| -- declare |
| -- XMN : aliased System.Finalization_Primitives.Master_Node; |
| -- X : Ctrl := Init; |
| -- System.Finalization_Primitives.Attach_To_Node |
| -- (X'address, |
| -- CtrlFD'unrestricted_access, |
| -- XMN'unrestricted_access); |
| -- YMN : aliased System.Finalization_Primitives.Master_Node; |
| -- Y : Ctrl := Init; |
| -- System.Finalization_Primitives.Attach_To_Node |
| -- (Y'address, |
| -- CtrlFD'unrestricted_access, |
| -- YMN'unrestricted_access); |
| |
| -- procedure _Finalizer is |
| -- begin |
| -- Abort_Defer; |
| -- System.Finalization_Primitives.Finalize_Object (YMN); |
| -- System.Finalization_Primitives.Finalize_Object (XMN); |
| -- Abort_Undefer; |
| -- end _Finalizer; |
| |
| -- begin |
| -- null; |
| -- end; |
| -- at end |
| -- _Finalizer; |
| |
| -- Dynamically allocated objects with relaxed finalization need not be |
| -- finalized and, therefore, are not attached to any finalization chain. |
| |
| 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); |
| |
| function Allows_Finalization_Collection (Typ : Entity_Id) return Boolean; |
| -- Determine whether access type Typ may have a finalization collection |
| |
| 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; |
| Additional_Cleanup : List_Id) return List_Id; |
| -- Create the cleanup calls for an asynchronous call block, task master, |
| -- protected subprogram body, task allocation block or task body, or |
| -- additional cleanup actions parked on a transient block. If the context |
| -- does not contain the above constructs, the routine returns an empty |
| -- list. |
| |
| procedure Build_Finalizer_Call (N : Node_Id; Fin_Id : Entity_Id); |
| -- N is a construct that contains a handled sequence of statements, Fin_Id |
| -- is the entity of a finalizer. Create an At_End handler that 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_Controlled_Component set and store them using the TSS mechanism. |
| |
| -------------------------------- |
| -- Transient Scope Management -- |
| -------------------------------- |
| |
| -- A transient scope is needed when certain temporary objects are created |
| -- by the compiler. These temporary objects are allocated on the secondary |
| -- stack and/or need finalization, and the transient scope is responsible |
| -- for finalizing the objects and reclaiming the memory of the secondary |
| -- stack at the appropriate time. They are generally objects allocated to |
| -- store the result of a function returning an unconstrained or controlled |
| -- value. Expressions needing to be wrapped in a transient scope may appear |
| -- in three different contexts, which lead to different kinds of transient |
| -- scope expansion: |
| |
| -- 1. In a simple statement (procedure call, assignment, ...). In this |
| -- case the statement is wrapped into a transient block, which takes |
| -- care of the finalization actions as well as the secondary stack |
| -- deallocation, See Wrap_Transient_Statement for details. |
| |
| -- 2. In an expression of a control structure (test in a If statement, |
| -- expression in a Case statement, ...). In this case the expression |
| -- is replaced by a temporary and the enclosing statement is wrapped |
| -- into a transient block, which takes care of the finalization actions |
| -- and the secondary stack deallocation. See Wrap_Transient_Expression |
| -- for details. |
| |
| -- 3. In an expression of an object declaration. No wrapping is possible |
| -- here, so the finalization actions performed on the normal path, if |
| -- any, are done right after the declaration, and those performed on |
| -- the exceptional path, as well as the secondary stack deallocation, |
| -- are deferred to the enclosing scope. See Wrap_Transient_Declaration |
| -- for details. |
| |
| -- A transient scope is created by calling Establish_Transient_Scope on the |
| -- node that needs to be serviced by it (the serviced node can subsequently |
| -- be retrieved by invoking Node_To_Be_Wrapped when the current scope is a |
| -- transient scope). Once this has been done, the normal processing of the |
| -- Insert_Actions procedures is blocked and the procedures are redirected |
| -- to the Store_xxx_Actions_In_Scope procedures and Store_Actions_In_Scope |
| -- is ultimately invoked to store the pending actions. |
| |
| -- A transient scope is finalized by calling one of the Wrap_Transient_xxx |
| -- procedures depending on the context as explained above. They ultimately |
| -- invoke Insert_Actions_In_Scope_Around as per the following picture: |
| |
| -- Wrap_Transient_Expression Wrap_Transient_Statement |
| -- | | |
| -- V V |
| -- Make_Transient_Block |
| -- | |
| -- Wrap_Transient_Declaration | |
| -- | | |
| -- V V |
| -- Insert_Actions_In_Scope_Around |
| |
| procedure Insert_Actions_In_Scope_Around |
| (N : Node_Id; |
| Clean : Boolean; |
| Manage_SS : Boolean); |
| -- 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. If Clean is |
| -- true, insert any cleanup actions kept in the scope stack and generate |
| -- required finalization actions for the before-actions and after-actions. |
| -- If Manage_SS is true, insert calls to mark/release the secondary stack. |
| |
| 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 Store_Actions_In_Scope (AK : Scope_Action_Kind; L : List_Id); |
| -- Shared processing for the Store_xxx_Actions_In_Scope routines: attach |
| -- the list L of actions to the list of actions stored in the top of the |
| -- scope stack specified by AK. |
| |
| procedure Store_New_Actions_In_Scope (AK : Scope_Action_Kind; L : List_Id); |
| -- Same as above for the case where the list of actions stored in the top |
| -- of the scope stack specified by AK is empty. |
| |
| ------------------------------------------- |
| -- Unnesting procedures for CCG and LLVM -- |
| ------------------------------------------- |
| |
| -- Expansion generates subprograms for controlled types management that |
| -- may appear in declarative lists in package declarations and bodies. |
| -- These subprograms appear within generated blocks that contain local |
| -- declarations and a call to finalization procedures. To ensure that |
| -- such subprograms get activation records when needed, we transform the |
| -- block into a procedure body, followed by a call to it in the same |
| -- declarative list. |
| |
| procedure Check_Unnesting_Elaboration_Code (N : Node_Id); |
| -- The statement part of a package body that is a compilation unit may |
| -- contain blocks that declare local subprograms. In Subprogram_Unnesting_ |
| -- Mode such subprograms must be handled as nested inside the (implicit) |
| -- elaboration procedure that executes that statement part. To handle |
| -- properly uplevel references we construct that subprogram explicitly, |
| -- to contain blocks and inner subprograms, the statement part becomes |
| -- a call to this subprogram. This is only done if blocks are present |
| -- in the statement list of the body. (It would be nice to unify this |
| -- procedure with Check_Unnesting_In_Decls_Or_Stmts, if possible, since |
| -- they're doing very similar work, but are structured differently. ???) |
| |
| procedure Check_Unnesting_In_Decls_Or_Stmts (Decls_Or_Stmts : List_Id); |
| -- Similarly, the declarations or statements in library-level packages may |
| -- have created blocks with nested subprograms. Such a block must be |
| -- transformed into a procedure followed by a call to it, so that unnesting |
| -- can handle uplevel references within these nested subprograms (typically |
| -- subprograms that handle finalization actions). This also applies to |
| -- nested packages, including instantiations, in which case it must |
| -- recursively process inner bodies. |
| |
| procedure Check_Unnesting_In_Handlers (N : Node_Id); |
| -- Similarly, check for blocks with nested subprograms occurring within |
| -- a set of exception handlers associated with a package body N. |
| |
| procedure Unnest_Block (Decl : Node_Id); |
| -- Blocks that contain nested subprograms with up-level references need to |
| -- create activation records for them. We do this by rewriting the block as |
| -- a procedure, followed by a call to it in the same declarative list, to |
| -- replicate the semantics of the original block. |
| -- |
| -- A common source for such block is a transient block created for a |
| -- construct (declaration, assignment, etc.) that involves controlled |
| -- actions or secondary-stack management, in which case the nested |
| -- subprogram is a finalizer. |
| |
| procedure Unnest_If_Statement (If_Stmt : Node_Id); |
| -- The separate statement lists associated with an if-statement (then part, |
| -- elsif parts, else part) may require unnesting if they directly contain |
| -- a subprogram body that references up-level objects. Each statement list |
| -- is traversed to locate such subprogram bodies, and if a part's statement |
| -- list contains a body, then the list is replaced with a new procedure |
| -- containing the part's statements followed by a call to the procedure. |
| -- Furthermore, any nested blocks, loops, or if statements will also be |
| -- traversed to determine the need for further unnesting transformations. |
| |
| procedure Unnest_Statement_List (Stmts : in out List_Id); |
| -- A list of statements that directly contains a subprogram at its outer |
| -- level, that may reference objects declared in that same statement list, |
| -- is rewritten as a procedure containing the statement list Stmts (which |
| -- includes any such objects as well as the nested subprogram), followed by |
| -- a call to the new procedure, and Stmts becomes the list containing the |
| -- procedure and the call. This ensures that Unnest_Subprogram will later |
| -- properly handle up-level references from the nested subprogram to |
| -- objects declared earlier in statement list, by creating an activation |
| -- record and passing it to the nested subprogram. This procedure also |
| -- resets the Scope of objects declared in the statement list, as well as |
| -- the Scope of the nested subprogram, to refer to the new procedure. |
| -- Also, the new procedure is marked Has_Nested_Subprogram, so this should |
| -- only be called when known that the statement list contains a subprogram. |
| |
| procedure Unnest_Loop (Loop_Stmt : Node_Id); |
| -- Top-level Loops that contain nested subprograms with up-level references |
| -- need to have activation records. We do this by rewriting the loop as a |
| -- procedure containing the loop, followed by a call to the procedure in |
| -- the same library-level declarative list, to replicate the semantics of |
| -- the original loop. Such loops can occur due to aggregate expansions and |
| -- other constructs. |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| 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 Contains_Subprogram (Blk : Entity_Id) return Boolean; |
| -- Check recursively whether a loop or block contains a subprogram that |
| -- may need an activation record. |
| |
| function Convert_View |
| (Proc : Entity_Id; |
| Arg : Node_Id; |
| Typ : Entity_Id) return Node_Id; |
| -- Proc is one of the Initialize/Adjust/Finalize operations, Arg is the one |
| -- argument being passed to it, and Typ is its expected type. This function |
| -- will, if necessary, generate a conversion between the partial and full |
| -- views of Arg to match the type of the formal of Proc, or else force a |
| -- conversion to the class-wide type in the case where the operation is |
| -- abstract. |
| |
| function Finalize_Address_For_Node (Node : Entity_Id) return Entity_Id |
| renames Einfo.Entities.Finalization_Master_Node; |
| -- Return the Finalize_Address primitive for the object that has been |
| -- attached to a finalization Master_Node. |
| |
| function Make_Call |
| (Loc : Source_Ptr; |
| Proc_Id : Entity_Id; |
| Param : Node_Id; |
| Skip_Self : Boolean := False) return Node_Id; |
| -- Subsidiary to Make_Adjust_Call and Make_Final_Call. Given the entity of |
| -- routine [Deep_]Adjust or [Deep_]Finalize and an object parameter, create |
| -- an adjust or finalization call. When flag Skip_Self is set, the related |
| -- action has an effect on the components only (if any). |
| |
| function Make_Deep_Proc |
| (Prim : Final_Primitives; |
| Typ : Entity_Id; |
| Stmts : List_Id) return Entity_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; |
| |
| procedure Set_Finalize_Address_For_Node (Node, Fin_Id : Entity_Id) |
| renames Einfo.Entities.Set_Finalization_Master_Node; |
| -- Set the Finalize_Address primitive for the object that has been |
| -- attached to a finalization Master_Node. |
| |
| function Shift_Address_For_Descriptor |
| (Addr : Node_Id; |
| Typ : Entity_Id; |
| Op_Nam : Name_Id) return Node_Id |
| with Pre => Is_Array_Type (Typ) |
| and then not Is_Constrained (Typ) |
| and then Op_Nam in Name_Op_Add | Name_Op_Subtract; |
| -- Add to Addr, or subtract from Addr, the size of the descriptor of Typ |
| |
| ---------------------------------- |
| -- Attach_Object_To_Master_Node -- |
| ---------------------------------- |
| |
| procedure Attach_Object_To_Master_Node |
| (Obj_Decl : Node_Id; |
| Master_Node : Entity_Id) |
| is |
| Loc : constant Source_Ptr := Sloc (Obj_Decl); |
| Obj_Id : constant Entity_Id := Defining_Entity (Obj_Decl); |
| Func_Id : constant Entity_Id := |
| (if Is_Return_Object (Obj_Id) |
| then Return_Applies_To (Scope (Obj_Id)) |
| else Empty); |
| |
| function Build_BIP_Cleanup_Stmts |
| (Func_Id : Entity_Id; |
| Obj_Addr : Node_Id) return Node_Id; |
| -- Func_Id denotes a build-in-place function. Generate the following |
| -- cleanup code: |
| -- |
| -- if BIPallocform > Secondary_Stack'Pos |
| -- and then BIPcollection /= null |
| -- then |
| -- declare |
| -- type Ptr_Typ is access Fun_Typ; |
| -- for Ptr_Typ'Storage_Pool use BIPstoragepool.all; |
| -- |
| -- begin |
| -- Free (Ptr_Typ (Obj_Addr)); |
| -- end; |
| -- end if; |
| -- |
| -- Fun_Typ is the return type of the Func_Id. |
| |
| ----------------------------- |
| -- Build_BIP_Cleanup_Stmts -- |
| ----------------------------- |
| |
| function Build_BIP_Cleanup_Stmts |
| (Func_Id : Entity_Id; |
| Obj_Addr : Node_Id) return Node_Id |
| is |
| Alloc_Id : constant Entity_Id := |
| Build_In_Place_Formal (Func_Id, BIP_Alloc_Form); |
| Decls : constant List_Id := New_List; |
| Fin_Coll_Id : constant Entity_Id := |
| Build_In_Place_Formal (Func_Id, BIP_Collection); |
| Func_Typ : constant Entity_Id := Etype (Func_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 BIPstoragepool.all; |
| |
| -- This formal is not added on ZFP as those targets do not |
| -- support pools. |
| |
| if RTE_Available (RE_Root_Storage_Pool_Ptr) then |
| Pool_Id := Make_Temporary (Loc, 'P'); |
| |
| Append_To (Decls, |
| Make_Object_Renaming_Declaration (Loc, |
| Defining_Identifier => Pool_Id, |
| Subtype_Mark => |
| New_Occurrence_Of (RTE (RE_Root_Storage_Pool), Loc), |
| Name => |
| Make_Explicit_Dereference (Loc, |
| New_Occurrence_Of |
| (Build_In_Place_Formal |
| (Func_Id, BIP_Storage_Pool), Loc)))); |
| |
| if Debug_Generated_Code then |
| Set_Debug_Info_Needed (Pool_Id); |
| end if; |
| |
| else |
| Pool_Id := Empty; |
| end if; |
| |
| -- Create an access type which uses the storage pool of the caller |
| |
| -- Generate: |
| -- type Ptr_Typ is access Func_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_Occurrence_Of (Func_Typ, Loc)))); |
| |
| -- Perform minor decoration in order to set the collection and the |
| -- storage pool attributes. |
| |
| Mutate_Ekind (Ptr_Typ, E_Access_Type); |
| Set_Finalization_Collection (Ptr_Typ, Fin_Coll_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, Obj_Addr)); |
| |
| 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 (Obj_Addr)); |
| -- 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 BIPallocform > Secondary_Stack'Pos |
| -- and then BIPcollection /= null |
| -- then |
| |
| Cond := |
| Make_And_Then (Loc, |
| Left_Opnd => |
| Make_Op_Gt (Loc, |
| Left_Opnd => New_Occurrence_Of (Alloc_Id, Loc), |
| Right_Opnd => |
| Make_Integer_Literal (Loc, |
| UI_From_Int (BIP_Allocation_Form'Pos (Secondary_Stack)))), |
| Right_Opnd => |
| Make_Op_Ne (Loc, |
| Left_Opnd => New_Occurrence_Of (Fin_Coll_Id, Loc), |
| Right_Opnd => Make_Null (Loc))); |
| |
| -- 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; |
| |
| -- Local variables |
| |
| Fin_Id : Entity_Id; |
| Master_Node_Attach : Node_Id; |
| Master_Node_Ins : Node_Id; |
| Obj_Ref : Node_Id; |
| Obj_Typ : Entity_Id; |
| |
| -- Start of processing for Attach_Object_To_Master_Node |
| |
| begin |
| -- Finalize_Address is not generated in CodePeer mode because the |
| -- body contains address arithmetic. So we don't want to generate |
| -- the attach in this case. |
| |
| if CodePeer_Mode then |
| return; |
| end if; |
| |
| -- When the object is initialized by an aggregate, the attachment must |
| -- occur after the last aggregate assignment takes place; only then is |
| -- the object considered initialized. Likewise if it is initialized by |
| -- a build-in-place call: we must attach only after the call. |
| |
| if Ekind (Obj_Id) in E_Constant | E_Variable then |
| if Present (Last_Aggregate_Assignment (Obj_Id)) then |
| Master_Node_Ins := Last_Aggregate_Assignment (Obj_Id); |
| elsif Present (BIP_Initialization_Call (Obj_Id)) then |
| Master_Node_Ins := BIP_Initialization_Call (Obj_Id); |
| else |
| Master_Node_Ins := Obj_Decl; |
| end if; |
| |
| else |
| Master_Node_Ins := Obj_Decl; |
| end if; |
| |
| -- Handle the object type and the reference to the object |
| |
| Obj_Ref := New_Occurrence_Of (Obj_Id, Loc); |
| Obj_Typ := Etype (Obj_Id); |
| if not Is_Class_Wide_Type (Obj_Typ) then |
| Obj_Typ := Base_Type (Obj_Typ); |
| end if; |
| |
| if Is_Access_Type (Obj_Typ) then |
| Obj_Ref := Make_Explicit_Dereference (Loc, Obj_Ref); |
| Obj_Typ := Available_View (Designated_Type (Obj_Typ)); |
| end if; |
| |
| -- If we are dealing with a return object of a build-in-place function |
| -- and its allocation has been done in the function, we additionally |
| -- need to detach it from the caller's finalization collection in order |
| -- to prevent double finalization. |
| |
| if Present (Func_Id) |
| and then Is_Build_In_Place_Function (Func_Id) |
| and then Needs_BIP_Collection (Func_Id) |
| then |
| declare |
| Ptr_Typ : constant Node_Id := Make_Temporary (Loc, 'P'); |
| Param : constant Entity_Id := |
| Make_Defining_Identifier (Loc, Name_V); |
| |
| Fin_Body : Node_Id; |
| Fin_Stmts : List_Id; |
| |
| begin |
| Fin_Stmts := Make_Finalize_Address_Stmts (Obj_Typ); |
| |
| Append_To (Fin_Stmts, |
| Build_BIP_Cleanup_Stmts |
| (Func_Id, New_Occurrence_Of (Param, Loc))); |
| |
| Fin_Id := |
| Make_Defining_Identifier (Loc, |
| Make_TSS_Name_Local |
| (Obj_Typ, TSS_Finalize_Address)); |
| |
| Fin_Body := |
| Make_Subprogram_Body (Loc, |
| Specification => |
| Make_Procedure_Specification (Loc, |
| Defining_Unit_Name => Fin_Id, |
| Parameter_Specifications => New_List ( |
| Make_Parameter_Specification (Loc, |
| Defining_Identifier => Param, |
| Parameter_Type => |
| New_Occurrence_Of (RTE (RE_Address), Loc)))), |
| |
| Declarations => 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_Occurrence_Of (Obj_Typ, Loc)))), |
| |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => Fin_Stmts)); |
| |
| Insert_After_And_Analyze |
| (Master_Node_Ins, Fin_Body, Suppress => All_Checks); |
| |
| Master_Node_Ins := Fin_Body; |
| end; |
| |
| else |
| Fin_Id := Finalize_Address (Obj_Typ); |
| |
| if No (Fin_Id) and then Ekind (Obj_Typ) = E_Class_Wide_Subtype then |
| Fin_Id := TSS (Obj_Typ, TSS_Finalize_Address); |
| end if; |
| end if; |
| |
| -- Now build the attachment call that will initialize the object's |
| -- Master_Node using the object's address and finalization procedure. |
| |
| Master_Node_Attach := |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (RTE (RE_Attach_Object_To_Node), Loc), |
| Parameter_Associations => New_List ( |
| Make_Address_For_Finalize (Loc, Obj_Ref, Obj_Typ), |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of (Fin_Id, Loc), |
| Attribute_Name => Name_Unrestricted_Access), |
| New_Occurrence_Of (Master_Node, Loc))); |
| |
| Set_Finalize_Address_For_Node (Master_Node, Fin_Id); |
| |
| -- Propagate the Ghost policy from the procedure to the node |
| |
| Set_Is_Ignored_Ghost_Entity |
| (Master_Node, Is_Ignored_Ghost_Entity (Fin_Id)); |
| |
| Insert_After_And_Analyze |
| (Master_Node_Ins, Master_Node_Attach, Suppress => All_Checks); |
| end Attach_Object_To_Master_Node; |
| |
| ------------------------------------ |
| -- Allows_Finalization_Collection -- |
| ------------------------------------ |
| |
| function Allows_Finalization_Collection (Typ : Entity_Id) return Boolean is |
| 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; |
| |
| -- Local variables |
| |
| Desig_Typ : constant Entity_Id := Designated_Type (Typ); |
| Ptr_Typ : constant Entity_Id := |
| Root_Type_Of_Full_View (Base_Type (Typ)); |
| |
| -- Start of processing for Allows_Finalization_Collection |
| |
| begin |
| -- Certain run-time configurations and targets do not provide support |
| -- for controlled types and therefore do not need collections. |
| |
| if Restriction_Active (No_Finalization) then |
| return False; |
| |
| -- Do not consider C and C++ types since it is assumed that the non-Ada |
| -- side will handle their cleanup. |
| |
| elsif Convention (Desig_Typ) = Convention_C |
| or else Convention (Desig_Typ) = Convention_CPP |
| then |
| return False; |
| |
| -- Do not consider controlled types with relaxed finalization |
| |
| elsif Has_Relaxed_Finalization (Desig_Typ) then |
| return False; |
| |
| -- Do not consider an access type that returns 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 False; |
| |
| -- Do not consider an access type that can never allocate an object |
| |
| elsif No_Pool_Assigned (Ptr_Typ) then |
| return False; |
| |
| -- Do not consider an access type coming from an Unchecked_Deallocation |
| -- instance. Even though the designated type may be controlled, the |
| -- access type will never participate in any allocations. |
| |
| elsif In_Deallocation_Instance (Ptr_Typ) then |
| return False; |
| |
| -- Do not consider a non-library access type when No_Nested_Finalization |
| -- is in effect, because finalization collections are controlled objects |
| -- and, if created, will violate the restriction. |
| |
| elsif Restriction_Active (No_Nested_Finalization) |
| and then not Is_Library_Level_Entity (Ptr_Typ) |
| then |
| return False; |
| |
| -- Do not consider an access type subject to pragma No_Heap_Finalization |
| -- because objects allocated through such a type are not to be finalized |
| -- when the access type goes out of scope. |
| |
| elsif No_Heap_Finalization (Ptr_Typ) then |
| return False; |
| |
| -- Do not create finalization collections in GNATprove mode because this |
| -- causes unwanted extra expansion. Compilation in this mode must always |
| -- keep the tree as close as possible to the original sources. |
| |
| elsif GNATprove_Mode then |
| return False; |
| |
| -- Otherwise the access type may use a finalization collection |
| |
| else |
| return True; |
| end if; |
| end Allows_Finalization_Collection; |
| |
| -------------------------------- |
| -- Build_Anonymous_Collection -- |
| -------------------------------- |
| |
| procedure Build_Anonymous_Collection (Ptr_Typ : Entity_Id) is |
| function Create_Anonymous_Collection |
| (Desig_Typ : Entity_Id; |
| Unit_Id : Entity_Id; |
| Unit_Decl : Node_Id) return Entity_Id; |
| -- Create a new anonymous collection for access type Ptr_Typ with |
| -- designated type Desig_Typ. The declaration of the collection and |
| -- its initialization are inserted in the declarative part of unit |
| -- Unit_Decl. Unit_Id is the entity of Unit_Decl. |
| |
| function Current_Anonymous_Collection |
| (Desig_Typ : Entity_Id; |
| Unit_Id : Entity_Id) return Entity_Id; |
| -- Find an anonymous collection declared in unit Unit_Id which services |
| -- designated type Desig_Typ. If there is none, return Empty. |
| |
| --------------------------------- |
| -- Create_Anonymous_Collection -- |
| --------------------------------- |
| |
| function Create_Anonymous_Collection |
| (Desig_Typ : Entity_Id; |
| Unit_Id : Entity_Id; |
| Unit_Decl : Node_Id) return Entity_Id |
| is |
| Loc : constant Source_Ptr := Sloc (Unit_Id); |
| |
| All_FCs : Elist_Id; |
| Decls : List_Id; |
| FC_Decl : Node_Id; |
| FC_Id : Entity_Id; |
| Unit_Spec : Node_Id; |
| |
| begin |
| -- Generate: |
| -- <FC_Id> : Finalization_Collection; |
| |
| FC_Id := Make_Temporary (Loc, 'A'); |
| |
| FC_Decl := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => FC_Id, |
| Object_Definition => |
| New_Occurrence_Of (RTE (RE_Finalization_Collection), Loc)); |
| |
| -- Find the declarative list of the unit |
| |
| if Nkind (Unit_Decl) = N_Package_Declaration then |
| Unit_Spec := Specification (Unit_Decl); |
| Decls := Visible_Declarations (Unit_Spec); |
| |
| if No (Decls) then |
| Decls := New_List; |
| Set_Visible_Declarations (Unit_Spec, Decls); |
| end if; |
| |
| -- Package body or subprogram case |
| |
| -- ??? A subprogram spec or body that acts as a compilation unit may |
| -- contain a formal parameter of an anonymous access-to-controlled |
| -- type initialized by an allocator. |
| |
| -- procedure Comp_Unit_Proc (Param : access Ctrl := new Ctrl); |
| |
| -- There is no suitable place to create the collection because the |
| -- subprogram is not in a declarative list. |
| |
| else |
| Decls := Declarations (Unit_Decl); |
| |
| if No (Decls) then |
| Decls := New_List; |
| Set_Declarations (Unit_Decl, Decls); |
| end if; |
| end if; |
| |
| Prepend_To (Decls, FC_Decl); |
| |
| -- Use the scope of the unit when analyzing the declaration of the |
| -- collection and its initialization actions. |
| |
| Push_Scope (Unit_Id); |
| Analyze (FC_Decl); |
| Pop_Scope; |
| |
| -- Mark the collection as servicing this specific designated type |
| |
| Set_Anonymous_Designated_Type (FC_Id, Desig_Typ); |
| |
| -- Include it in the list of existing anonymous collections which |
| -- appear in this unit. This effectively creates a mapping between |
| -- collections and designated types, which in turn allows for the |
| -- reuse of collections on a per-unit basis. |
| |
| All_FCs := Anonymous_Collections (Unit_Id); |
| |
| if No (All_FCs) then |
| All_FCs := New_Elmt_List; |
| Set_Anonymous_Collections (Unit_Id, All_FCs); |
| end if; |
| |
| Prepend_Elmt (FC_Id, All_FCs); |
| |
| return FC_Id; |
| end Create_Anonymous_Collection; |
| |
| ---------------------------------- |
| -- Current_Anonymous_Collection -- |
| ---------------------------------- |
| |
| function Current_Anonymous_Collection |
| (Desig_Typ : Entity_Id; |
| Unit_Id : Entity_Id) return Entity_Id |
| is |
| All_FCs : constant Elist_Id := Anonymous_Collections (Unit_Id); |
| FC_Elmt : Elmt_Id; |
| FC_Id : Entity_Id; |
| |
| begin |
| -- Inspect the list of anonymous collections declared within the unit |
| -- looking for an existing collection which services the designated |
| -- type. |
| |
| if Present (All_FCs) then |
| FC_Elmt := First_Elmt (All_FCs); |
| while Present (FC_Elmt) loop |
| FC_Id := Node (FC_Elmt); |
| |
| -- The current collection services the same designated type. |
| -- As a result, the collection can be reused and associated |
| -- with another anonymous access-to-controlled type. |
| |
| if Anonymous_Designated_Type (FC_Id) = Desig_Typ then |
| return FC_Id; |
| end if; |
| |
| Next_Elmt (FC_Elmt); |
| end loop; |
| end if; |
| |
| return Empty; |
| end Current_Anonymous_Collection; |
| |
| -- Local variables |
| |
| Desig_Typ : Entity_Id; |
| FC_Id : Entity_Id; |
| Priv_View : Entity_Id; |
| Scop : Entity_Id; |
| Unit_Decl : Node_Id; |
| Unit_Id : Entity_Id; |
| |
| -- Start of processing for Build_Anonymous_Collection |
| |
| begin |
| -- Nothing to do if the circumstances do not allow for a finalization |
| -- collection. |
| |
| if not Allows_Finalization_Collection (Ptr_Typ) then |
| return; |
| end if; |
| |
| Unit_Decl := Unit (Cunit (Current_Sem_Unit)); |
| Unit_Id := Unique_Defining_Entity (Unit_Decl); |
| |
| -- The compilation unit is a package instantiation. In this case the |
| -- anonymous collection is associated with the package spec, as both |
| -- the spec and body appear at the same level. |
| |
| if Nkind (Unit_Decl) = N_Package_Body |
| and then Nkind (Original_Node (Unit_Decl)) = N_Package_Instantiation |
| then |
| Unit_Id := Corresponding_Spec (Unit_Decl); |
| Unit_Decl := Unit_Declaration_Node (Unit_Id); |
| end if; |
| |
| -- Use the initial declaration of the designated type when it denotes |
| -- the full view of an incomplete or private type. This ensures that |
| -- types with one and two views are treated the same. |
| |
| Desig_Typ := Directly_Designated_Type (Ptr_Typ); |
| Priv_View := Incomplete_Or_Partial_View (Desig_Typ); |
| |
| if Present (Priv_View) then |
| Desig_Typ := Priv_View; |
| end if; |
| |
| -- For a designated type not declared at library level, we cannot create |
| -- a finalization collection attached to an outer unit since this would |
| -- generate dangling references to the dynamic scope through access-to- |
| -- procedure values designating the local Finalize_Address primitive. |
| |
| Scop := Enclosing_Dynamic_Scope (Desig_Typ); |
| if Scop /= Standard_Standard |
| and then Scope_Depth (Scop) > Scope_Depth (Unit_Id) |
| then |
| return; |
| end if; |
| |
| -- For the access result type of a function that is a library unit, |
| -- we cannot create a finalization collection attached to the unit as |
| -- this would cause premature finalization of objects created through |
| -- the access result type, which may be returned from the function. |
| |
| if Is_Local_Anonymous_Access (Ptr_Typ) |
| and then Ekind (Unit_Id) = E_Function |
| and then Parent (Ptr_Typ) = |
| Result_Definition (Subprogram_Specification (Unit_Id)) |
| then |
| return; |
| end if; |
| |
| -- Determine whether the current semantic unit already has an anonymous |
| -- collection which services the designated type. |
| |
| FC_Id := Current_Anonymous_Collection (Desig_Typ, Unit_Id); |
| |
| -- If this is not the case, create a new collection |
| |
| if No (FC_Id) then |
| FC_Id := Create_Anonymous_Collection (Desig_Typ, Unit_Id, Unit_Decl); |
| end if; |
| |
| Set_Finalization_Collection (Ptr_Typ, FC_Id); |
| end Build_Anonymous_Collection; |
| |
| ---------------------------- |
| -- 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_Inherently_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 (unless CodePeer_Mode) |
| |
| if not CodePeer_Mode 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; |
| Additional_Cleanup : List_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_Subp_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 undefer abort. |
| |
| elsif Is_Protected_Subp_Body then |
| if Abort_Allowed then |
| Append_To (Stmts, Build_Runtime_Call (Loc, RE_Abort_Undefer)); |
| end if; |
| |
| -- 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_Occurrence_Of |
| (RTE (RE_Expunge_Unactivated_Tasks), Loc), |
| Parameter_Associations => New_List ( |
| New_Occurrence_Of (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_Occurrence_Of (RTE (RE_Enqueued), Loc), |
| Parameter_Associations => New_List ( |
| New_Occurrence_Of (Cancel_Param, Loc))), |
| |
| Then_Statements => New_List ( |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of |
| (RTE (RE_Cancel_Protected_Entry_Call), Loc), |
| Parameter_Associations => New_List ( |
| New_Occurrence_Of (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_Occurrence_Of (RTE (RE_Cancel_Async_Delay), Loc), |
| Parameter_Associations => New_List ( |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of (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_Occurrence_Of (RTE (RE_Cancel_Task_Entry_Call), Loc), |
| Parameter_Associations => New_List ( |
| New_Occurrence_Of (Cancel_Param, Loc)))); |
| end if; |
| end; |
| end if; |
| |
| Append_List_To (Stmts, Additional_Cleanup); |
| 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_Occurrence_Of |
| (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_Occurrence_Of (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_Occurrence_Of (Data.Raised_Id, Data.Loc)), |
| |
| Then_Statements => New_List ( |
| Make_Assignment_Statement (Data.Loc, |
| Name => New_Occurrence_Of (Data.Raised_Id, Data.Loc), |
| Expression => New_Occurrence_Of (Standard_True, Data.Loc)), |
| |
| Make_Procedure_Call_Statement (Data.Loc, |
| Name => |
| New_Occurrence_Of (Proc_To_Call, Data.Loc), |
| Parameter_Associations => Actuals)))); |
| |
| else |
| -- Generate: |
| |
| -- Raised_Id := True; |
| |
| Stmts := New_List ( |
| Make_Assignment_Statement (Data.Loc, |
| Name => New_Occurrence_Of (Data.Raised_Id, Data.Loc), |
| Expression => New_Occurrence_Of (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_Collection -- |
| ----------------------------------- |
| |
| procedure Build_Finalization_Collection |
| (Typ : Entity_Id; |
| For_Lib_Level : Boolean := False; |
| For_Private : Boolean := False; |
| Context_Scope : Entity_Id := Empty; |
| Insertion_Node : Node_Id := Empty) |
| is |
| Ptr_Typ : constant Entity_Id := Root_Type_Of_Full_View (Base_Type (Typ)); |
| -- Finalization collections built for named access types are associated |
| -- with the full view (if applicable) as a consequence of freezing. The |
| -- full view criteria does not apply to anonymous access types because |
| -- those cannot have a private and a full view. |
| |
| -- Start of processing for Build_Finalization_Collection |
| |
| begin |
| -- Nothing to do if the circumstances do not allow for a finalization |
| -- collection. |
| |
| if not Allows_Finalization_Collection (Typ) then |
| return; |
| |
| -- Various machinery such as freezing may have already created a |
| -- finalization collection. |
| |
| elsif Present (Finalization_Collection (Ptr_Typ)) then |
| return; |
| end if; |
| |
| declare |
| Actions : constant List_Id := New_List; |
| Loc : constant Source_Ptr := Sloc (Ptr_Typ); |
| |
| Fin_Coll_Id : Entity_Id; |
| Pool_Id : Entity_Id; |
| |
| begin |
| -- Source access types use fixed names since the collection will be |
| -- 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_Coll_Id := |
| Make_Defining_Identifier (Loc, |
| Chars => New_External_Name (Chars (Ptr_Typ), "FC")); |
| |
| -- Internally generated access types use temporaries as their names |
| -- due to possible collision with identical names coming from other |
| -- packages. |
| |
| else |
| Fin_Coll_Id := Make_Temporary (Loc, 'F'); |
| end if; |
| |
| Set_Finalization_Collection (Ptr_Typ, Fin_Coll_Id); |
| |
| -- Generate: |
| -- <Ptr_Typ>FC : aliased Finalization_Collection; |
| |
| Append_To (Actions, |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Fin_Coll_Id, |
| Aliased_Present => True, |
| Object_Definition => |
| New_Occurrence_Of (RTE (RE_Finalization_Collection), Loc))); |
| |
| if Debug_Generated_Code then |
| Set_Debug_Info_Needed (Fin_Coll_Id); |
| end if; |
| |
| -- Set the associated pool and primitive Finalize_Address of the new |
| -- finalization collection. |
| |
| -- The access type has a user-defined storage pool, use it |
| |
| if Present (Associated_Storage_Pool (Ptr_Typ)) then |
| Pool_Id := Associated_Storage_Pool (Ptr_Typ); |
| |
| -- Otherwise the default choice is the global storage pool |
| |
| else |
| Pool_Id := RTE (RE_Global_Pool_Object); |
| Set_Associated_Storage_Pool (Ptr_Typ, Pool_Id); |
| end if; |
| |
| -- A finalization collection created for an access designating a type |
| -- with private components is inserted before a context-dependent |
| -- node. |
| |
| if For_Private then |
| |
| -- At this point both the scope of the context and the insertion |
| -- mode must be known. |
| |
| pragma Assert (Present (Context_Scope)); |
| pragma Assert (Present (Insertion_Node)); |
| |
| Push_Scope (Context_Scope); |
| |
| -- Treat use clauses as declarations and insert directly in front |
| -- of them. |
| |
| if Nkind (Insertion_Node) in |
| N_Use_Package_Clause | N_Use_Type_Clause |
| then |
| Insert_List_Before_And_Analyze (Insertion_Node, Actions); |
| else |
| Insert_Actions (Insertion_Node, Actions); |
| end if; |
| |
| Pop_Scope; |
| |
| -- The finalization collection belongs to an access type related |
| -- to a build-in-place function call used to initialize a library |
| -- level object. The collection must be inserted in front of the |
| -- access type declaration denoted by Insertion_Node. |
| |
| elsif For_Lib_Level then |
| pragma Assert (Present (Insertion_Node)); |
| Insert_Actions (Insertion_Node, Actions); |
| |
| -- Otherwise the finalization collection and its initialization |
| -- become a part of the freeze node. |
| |
| else |
| Append_Freeze_Actions (Ptr_Typ, Actions); |
| end if; |
| |
| Analyze_List (Actions); |
| |
| -- When the type the finalization collection is being generated for |
| -- was created to store a 'Old object, then mark it as such so its |
| -- finalization can be delayed until after postconditions have been |
| -- checked. |
| |
| if Stores_Attribute_Old_Prefix (Ptr_Typ) then |
| Set_Stores_Attribute_Old_Prefix (Fin_Coll_Id); |
| end if; |
| end; |
| end Build_Finalization_Collection; |
| |
| --------------------- |
| -- Build_Finalizer -- |
| --------------------- |
| |
| procedure Build_Finalizer |
| (N : Node_Id; |
| Clean_Stmts : List_Id; |
| Mark_Id : Entity_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)); |
| |
| 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. |
| |
| Count : Nat := 0; |
| -- Holds the number of controlled objects encountered so far |
| |
| 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 |
| |
| Finalization_Master : Entity_Id; |
| -- The Finalization Master object |
| |
| Finalizer_Stmts : List_Id := No_List; |
| -- The statement list of the finalizer body |
| |
| Has_Strict_Ctrl_Objs : Boolean := False; |
| -- A general flag which indicates whether N has at least one controlled |
| -- object with strict semantics for finalization. |
| |
| 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) |
| |
| 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; |
| 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. |
| |
| function Has_Ctrl_Objs return Boolean is (Count > 0); |
| -- Return true if N contains a least one controlled object |
| |
| function New_Finalizer_Name |
| (Spec_Id : Node_Id; For_Spec : Boolean) 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]. |
| |
| procedure Process_Declarations |
| (Decls : List_Id; |
| Preprocess : 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 and set Count accordingly. |
| |
| procedure Process_Object_Declaration |
| (Decl : Node_Id; |
| Is_Protected : Boolean := False); |
| -- Generate all the machinery associated with the finalization of a |
| -- single object. 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 |
| Constraints : List_Id; |
| Master_Decl : Node_Id; |
| Master_Name : Name_Id; |
| |
| begin |
| pragma Assert (Present (Decls)); |
| |
| -- If the context contains controlled objects with strict semantics |
| -- for finalization, then we create the finalization master, unless |
| -- there is a single such object: in this common case, we'll directly |
| -- finalize the object. |
| |
| if Has_Strict_Ctrl_Objs then |
| if Count > 1 then |
| if For_Package_Spec then |
| Master_Name := |
| New_External_Name (Name_uMaster, Suffix => "_spec"); |
| elsif For_Package_Body then |
| Master_Name := |
| New_External_Name (Name_uMaster, Suffix => "_body"); |
| else |
| Master_Name := New_Internal_Name ('M'); |
| end if; |
| |
| Finalization_Master := |
| Make_Defining_Identifier (Loc, Master_Name); |
| |
| -- The master is statically parameterized by the context |
| |
| Constraints := New_List; |
| Append_To (Constraints, |
| New_Occurrence_Of (Boolean_Literals (Exceptions_OK), Loc)); |
| Append_To (Constraints, |
| New_Occurrence_Of |
| (Boolean_Literals (Exception_Extra_Info), Loc)); |
| Append_To (Constraints, |
| New_Occurrence_Of (Boolean_Literals (For_Package), Loc)); |
| |
| Master_Decl := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Finalization_Master, |
| Object_Definition => |
| Make_Subtype_Indication (Loc, |
| Subtype_Mark => |
| New_Occurrence_Of |
| (RTE (RE_Finalization_Master), Loc), |
| Constraint => |
| Make_Index_Or_Discriminant_Constraint (Loc, |
| Constraints => Constraints))); |
| |
| Prepend_To (Decls, Master_Decl); |
| Analyze (Master_Decl, Suppress => All_Checks); |
| end if; |
| |
| if Exceptions_OK then |
| Finalizer_Decls := New_List; |
| Build_Object_Declarations |
| (Finalizer_Data, Finalizer_Decls, Loc, For_Package); |
| end if; |
| end if; |
| |
| -- If the context requires additional cleanup, the finalization |
| -- machinery is added after the cleanup code. |
| |
| if Acts_As_Clean then |
| Finalizer_Stmts := Clean_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_Call : Node_Id; |
| Fin_Spec : Node_Id; |
| |
| 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 (Spec_Id, For_Package_Spec)); |
| 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)); |
| Set_Is_Finalizer (Fin_Id); |
| |
| -- 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); |
| |
| -- Inlining the finalizer produces a substantial speedup at -O2. |
| -- It is inlined by default at -O3. Either way, it is called |
| -- exactly twice (once on the normal path, and once for |
| -- exceptions/abort), so this won't bloat the code too much. |
| |
| Set_Is_Inlined (Fin_Id); |
| end if; |
| |
| if Debug_Generated_Code then |
| Set_Debug_Info_Needed (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)); |
| |
| if For_Package then |
| Set_Is_Exported (Fin_Id); |
| Set_Interface_Name (Fin_Id, |
| Make_String_Literal (Loc, |
| Strval => Get_Name_String (Chars (Fin_Id)))); |
| end if; |
| |
| -- Step 3: Creation of the finalizer body |
| |
| -- Add the library-level tagged type unregistration machinery before |
| -- the finalization 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 finalization circuitry. |
| |
| 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; |
| |
| -- If there are no controlled objects to be finalized, generate; |
| |
| -- procedure Fin_Id is |
| -- begin |
| -- Abort_Defer; -- Added if abort is allowed |
| -- <call to Prev_At_End> -- Added if exists |
| -- <tag unregistration> -- Added if Has_Tagged_Types |
| -- <cleanup statements> -- Added if Acts_As_Clean |
| -- <stack release> -- Added if Mark_Id exists |
| -- Abort_Undefer; -- Added if abort is allowed |
| -- end Fin_Id; |
| |
| -- If there are strict controlled objects to be finalized, generate: |
| |
| -- procedure Fin_Id is |
| -- Abort : constant Boolean := Triggered_By_Abort; |
| -- E : Exception_Occurrence; |
| -- Raised : Boolean := False; |
| -- begin |
| -- Abort_Defer; -- Added if abort is allowed |
| -- <call to Prev_At_End> -- Added if exists |
| -- <tag unregistration> -- Added if Has_Tagged_Types |
| -- <cleanup statements> -- Added if Acts_As_Clean |
| -- <finalization statements> |
| -- <stack release> -- Added if Mark_Id exists |
| -- Abort_Undefer; -- Added if abort is allowed |
| -- <exception propagation> |
| -- end Fin_Id; |
| |
| -- If there are only controlled objects with relaxed semantics for |
| -- finalization, only the <finalization statements> are generated. |
| |
| if Has_Strict_Ctrl_Objs and then Count > 1 then |
| Fin_Call := |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (RTE (RE_Finalize_Master), Loc), |
| Parameter_Associations => |
| New_List (New_Occurrence_Of (Finalization_Master, Loc))); |
| |
| -- 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_Call := |
| 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)))); |
| end if; |
| |
| Append_To (Finalizer_Stmts, Fin_Call); |
| end if; |
| |
| -- Release the secondary stack |
| |
| if Present (Mark_Id) then |
| declare |
| Release : Node_Id := Build_SS_Release_Call (Loc, Mark_Id); |
| |
| begin |
| -- If the context is a build-in-place function, the secondary |
| -- stack must be released, unless the build-in-place function |
| -- itself is returning on the secondary stack. Generate: |
| -- |
| -- if BIP_Alloc_Form /= Secondary_Stack then |
| -- SS_Release (Mark_Id); |
| -- end if; |
| -- |
| -- Note that if the function returns on the secondary stack, |
| -- then the responsibility of reclaiming the space is always |
| -- left to the caller (recursively if needed). |
| |
| if Nkind (N) = N_Subprogram_Body then |
| declare |
| Spec_Id : constant Entity_Id := |
| Unique_Defining_Entity (N); |
| BIP_SS : constant Boolean := |
| Is_Build_In_Place_Function (Spec_Id) |
| and then Needs_BIP_Alloc_Form (Spec_Id); |
| begin |
| if BIP_SS then |
| Release := |
| Make_If_Statement (Loc, |
| Condition => |
| Make_Op_Ne (Loc, |
| Left_Opnd => |
| New_Occurrence_Of |
| (Build_In_Place_Formal |
| (Spec_Id, BIP_Alloc_Form), Loc), |
| Right_Opnd => |
| Make_Integer_Literal (Loc, |
| UI_From_Int |
| (BIP_Allocation_Form'Pos |
| (Secondary_Stack)))), |
| |
| Then_Statements => New_List (Release)); |
| end if; |
| end; |
| end if; |
| |
| Append_To (Finalizer_Stmts, Release); |
| end; |
| end if; |
| |
| -- Protect the statements with abort defer/undefer. This is only when |
| -- aborts are allowed and the cleanup statements require deferral or |
| -- there are controlled objects to be finalized. Note that the abort |
| -- defer/undefer pair does not require an extra block because the |
| -- finalization exception is caught in its corresponding finalization |
| -- block. As a result, the call to Abort_Defer always takes place. |
| |
| if Abort_Allowed and then (Defer_Abort or Has_Ctrl_Objs) then |
| Prepend_To (Finalizer_Stmts, |
| Build_Runtime_Call (Loc, RE_Abort_Defer)); |
| |
| Append_To (Finalizer_Stmts, |
| Build_Runtime_Call (Loc, RE_Abort_Undefer)); |
| 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 |
| -- cleanup, secondary stack release, and abort undeferral. Generate: |
| |
| -- if Raised and then not Abort then |
| -- Raise_From_Controlled_Operation (E); |
| -- end if; |
| |
| if Has_Strict_Ctrl_Objs and Exceptions_OK and not For_Package then |
| Append_To (Finalizer_Stmts, |
| Build_Raise_Statement (Finalizer_Data)); |
| end if; |
| |
| -- Create the body of the finalizer |
| |
| Body_Id := Make_Defining_Identifier (Loc, Chars (Fin_Id)); |
| |
| if Debug_Generated_Code then |
| Set_Debug_Info_Needed (Body_Id); |
| end if; |
| |
| 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, |
| Statements => Finalizer_Stmts)); |
| |
| -- Step 4: Spec and body insertion, analysis |
| |
| if For_Package then |
| |
| -- If a package spec has private declarations, both the finalizer |
| -- spec and body are inserted at the end of this list. |
| |
| if For_Package_Spec and then Present (Priv_Decls) then |
| Append_To (Priv_Decls, Fin_Spec); |
| Append_To (Priv_Decls, Fin_Body); |
| |
| -- Otherwise, and for a package body, 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; |
| |
| -- Non-package case |
| |
| else |
| -- Insert the spec for the finalizer. The At_End handler must be |
| -- able to call the body which resides in a nested structure. |
| |
| -- 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 (Decls)); |
| |
| Append_To (Decls, Fin_Spec); |
| |
| -- When the finalizer acts solely as a cleanup routine, the body |
| -- is inserted right after the spec. |
| |
| if Acts_As_Clean and not Has_Ctrl_Objs then |
| Insert_After (Fin_Spec, Fin_Body); |
| |
| -- In other cases the body is inserted after the last statement |
| |
| 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)); |
| Mutate_Ekind (Fin_Id, E_Procedure); |
| Freeze_Extra_Formals (Fin_Id); |
| Set_Is_Frozen (Fin_Id); |
| |
| Append_To (Stmts, Fin_Body); |
| end if; |
| end if; |
| |
| Analyze (Fin_Spec, Suppress => All_Checks); |
| Analyze (Fin_Body, Suppress => All_Checks); |
| |
| -- Never consider that the finalizer procedure is enabled Ghost, even |
| -- when the corresponding unit is Ghost, as this would lead to an |
| -- an external name with a ___ghost_ prefix that the binder cannot |
| -- generate, as it has no knowledge of the Ghost status of units. |
| |
| Set_Is_Checked_Ghost_Entity (Fin_Id, False); |
| end Create_Finalizer; |
| |
| ------------------------ |
| -- New_Finalizer_Name -- |
| ------------------------ |
| |
| function New_Finalizer_Name |
| (Spec_Id : Node_Id; For_Spec : Boolean) 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 ("__"); |
| Get_Name_String_And_Append (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_Spec then |
| Add_Str_To_Name_Buffer ("spec"); |
| else |
| Add_Str_To_Name_Buffer ("body"); |
| end if; |
| |
| return Name_Find; |
| end New_Finalizer_Name; |
| |
| -------------------------- |
| -- Process_Declarations -- |
| -------------------------- |
| |
| procedure Process_Declarations |
| (Decls : List_Id; |
| Preprocess : Boolean := False) |
| is |
| procedure Process_Package_Body (Decl : Node_Id); |
| -- Process an N_Package_Body node |
| |
| procedure Processing_Actions |
| (Decl : Node_Id; |
| Is_Protected : Boolean := False; |
| Strict : Boolean := False); |
| -- Depending on the mode of operation of Process_Declarations, either |
| -- increment the controlled object count or process the declaration. |
| -- The Flag Is_Protected is set when the declaration denotes a simple |
| -- protected object. The flag Strict is true when the declaration is |
| -- for a controlled object with strict semantics for finalization. |
| |
| -------------------------- |
| -- Process_Package_Body -- |
| -------------------------- |
| |
| procedure Process_Package_Body (Decl : Node_Id) is |
| begin |
| -- Do not inspect an ignored Ghost package body because all |
| -- code found within will not appear in the final tree. |
| |
| if Is_Ignored_Ghost_Entity (Defining_Entity (Decl)) then |
| null; |
| |
| elsif Ekind (Corresponding_Spec (Decl)) /= E_Generic_Package then |
| Process_Declarations (Declarations (Decl), Preprocess); |
| end if; |
| end Process_Package_Body; |
| |
| ------------------------ |
| -- Processing_Actions -- |
| ------------------------ |
| |
| procedure Processing_Actions |
| (Decl : Node_Id; |
| Is_Protected : Boolean := False; |
| Strict : Boolean := False) |
| is |
| begin |
| -- Library-level tagged type |
| |
| if Nkind (Decl) = N_Full_Type_Declaration then |
| if Preprocess then |
| Has_Tagged_Types := True; |
| |
| -- Unregister tagged type, unless No_Tagged_Type_Registration |
| -- is active. |
| |
| elsif not Restriction_Active (No_Tagged_Type_Registration) then |
| Process_Tagged_Type_Declaration (Decl); |
| end if; |
| |
| -- Controlled object declaration |
| |
| else |
| if Preprocess then |
| Count := Count + 1; |
| if Strict then |
| Has_Strict_Ctrl_Objs := True; |
| end if; |
| |
| else |
| Process_Object_Declaration (Decl, Is_Protected); |
| end if; |
| end if; |
| end Processing_Actions; |
| |
| -- Local variables |
| |
| Decl : Node_Id; |
| Obj_Id : Entity_Id; |
| Obj_Typ : Entity_Id; |
| Pack_Id : Entity_Id; |
| Prev : Node_Id; |
| Spec : Node_Id; |
| Typ : Entity_Id; |
| |
| -- Start of processing for Process_Declarations |
| |
| begin |
| if Is_Empty_List (Decls) then |
| return; |
| end if; |
| |
| -- Process all declarations in reverse order and be prepared for them |
| -- to be moved during the processing. |
| |
| Decl := Last_Non_Pragma (Decls); |
| while Present (Decl) loop |
| Prev := Prev_Non_Pragma (Decl); |
| |
| -- Library-level tagged types |
| |
| if Nkind (Decl) = N_Full_Type_Declaration then |
| Typ := Defining_Identifier (Decl); |
| |
| -- Ignored Ghost types do not need any cleanup actions because |
| -- they will not appear in the final tree. |
| |
| if Is_Ignored_Ghost_Entity (Typ) then |
| null; |
| |
| elsif 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 not Is_Abstract_Type (Typ) |
| and then not No_Run_Time_Mode |
| and then not Restriction_Active (No_Tagged_Type_Registration) |
| and then RTE_Available (RE_Register_Tag) |
| then |
| Processing_Actions (Decl); |
| end if; |
| |
| -- Regular object declarations |
| |
| elsif Nkind (Decl) = N_Object_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; |
| |
| -- Finalization of transient objects is treated separately in |
| -- order to handle sensitive cases. These include: |
| |
| -- * Conditional expressions |
| -- * Expressions with actions |
| -- * Transient scopes |
| |
| elsif Is_Finalized_Transient (Obj_Id) then |
| null; |
| |
| -- Finalization of specific objects is also treated separately |
| |
| elsif Is_Ignored_For_Finalization (Obj_Id) then |
| null; |
| |
| -- Ignored Ghost objects do not need any cleanup actions |
| -- because they will not appear in the final tree. |
| |
| elsif Is_Ignored_Ghost_Entity (Obj_Id) then |
| null; |
| |
| -- Conversely, if one of the above cases created a Master_Node, |
| -- finalization actions are required for the associated object. |
| |
| elsif Ekind (Obj_Id) = E_Variable |
| and then Is_RTE (Obj_Typ, RE_Master_Node) |
| then |
| Processing_Actions |
| (Decl, Strict => not Is_Independent (Obj_Id)); |
| |
| -- The object is of the form: |
| -- Obj : [constant] Typ [:= Expr]; |
| |
| -- Do not process the incomplete view of a deferred constant. |
| -- Note that an object initialized by means of a BIP function |
| -- call may appear as a deferred constant after expansion |
| -- activities. These kinds of objects must be finalized. |
| |
| 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 No (BIP_Initialization_Call (Obj_Id))) |
| then |
| Processing_Actions |
| (Decl, Strict => not Has_Relaxed_Finalization (Obj_Typ)); |
| |
| -- The object is an access-to-controlled that must be finalized |
| |
| elsif Is_Access_Type (Obj_Typ) |
| and then Is_Finalizable_Access (Decl) |
| then |
| Processing_Actions |
| (Decl, |
| Strict => not Has_Relaxed_Finalization |
| (Available_View (Designated_Type (Obj_Typ)))); |
| |
| -- Simple protected objects which use the type System.Tasking. |
| -- Protected_Objects.Protection to manage their locks should |
| -- be treated as controlled since they require manual cleanup. |
| -- but not for restricted run-time libraries (Ravenscar), see |
| -- also Cleanup_Protected_Object. |
| |
| -- 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 Has_Simple_Protected_Object (Obj_Typ) |
| and then not Restricted_Profile |
| then |
| Processing_Actions |
| (Decl, Is_Protected => True, Strict => True); |
| end if; |
| |
| -- Inspect the freeze node of an access-to-controlled type and |
| -- look for a delayed finalization collection. 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 collection would be ultimately |
| -- left out and never finalized. This is also needed for freeze |
| -- actions of designated types themselves, since in some cases the |
| -- finalization collection is associated with a designated type's |
| -- freeze node rather than that of the access type (see handling |
| -- for freeze actions in Build_Finalization_Collection). |
| |
| elsif Nkind (Decl) = N_Freeze_Entity |
| and then Present (Actions (Decl)) |
| then |
| Typ := Entity (Decl); |
| |
| -- Freeze nodes for ignored Ghost types do not need cleanup |
| -- actions because they will never appear in the final tree. |
| |
| if Is_Ignored_Ghost_Entity (Typ) then |
| null; |
| |
| elsif (Is_Access_Object_Type (Typ) |
| and then Needs_Finalization |
| (Available_View (Designated_Type (Typ)))) |
| or else (Is_Type (Typ) and then Needs_Finalization (Typ)) |
| then |
| -- Freeze nodes are considered to be identical to packages |
| -- and blocks in terms of nesting. The difference is that |
| -- a finalization collection created inside the freeze node |
| -- is at the same nesting level as the node itself. |
| |
| Process_Declarations (Actions (Decl), Preprocess); |
| end if; |
| |
| -- Nested package declarations, avoid generics |
| |
| elsif Nkind (Decl) = N_Package_Declaration then |
| Pack_Id := Defining_Entity (Decl); |
| Spec := Specification (Decl); |
| |
| -- Do not inspect an ignored Ghost package because all code |
| -- found within will not appear in the final tree. |
| |
| if Is_Ignored_Ghost_Entity (Pack_Id) then |
| null; |
| |
| elsif Ekind (Pack_Id) /= E_Generic_Package then |
| Process_Declarations |
| (Private_Declarations (Spec), Preprocess); |
| Process_Declarations |
| (Visible_Declarations (Spec), Preprocess); |
| end if; |
| |
| -- Nested package bodies, avoid generics |
| |
| elsif Nkind (Decl) = N_Package_Body then |
| Process_Package_Body (Decl); |
| |
| elsif Nkind (Decl) = N_Package_Body_Stub |
| and then Present (Stub_Subunit (Decl)) |
| then |
| Process_Package_Body |
| (Proper_Body (Unit (Stub_Subunit (Decl)))); |
| end if; |
| |
| Decl := Prev; |
| end loop; |
| end Process_Declarations; |
| |
| -------------------------------- |
| -- Process_Object_Declaration -- |
| -------------------------------- |
| |
| procedure Process_Object_Declaration |
| (Decl : Node_Id; |
| Is_Protected : Boolean := False) |
| is |
| Obj_Id : constant Entity_Id := Defining_Identifier (Decl); |
| Loc : constant Source_Ptr := Sloc (Decl); |
| |
| Fin_Call : Node_Id; |
| Fin_Id : Entity_Id; |
| Master_Node_Attach : Node_Id; |
| Master_Node_Decl : Node_Id; |
| Master_Node_Id : Entity_Id; |
| Master_Node_Ins : Node_Id; |
| Master_Node_Loc : Source_Ptr; |
| Obj_Ref : Node_Id; |
| Obj_Typ : Entity_Id; |
| |
| -- Start of processing for Process_Object_Declaration |
| |
| begin |
| -- Handle the object type and the reference to the object. Note |
| -- that objects having simple protected components or of a CW type |
| -- must retain their original type for the processing below to work. |
| |
| Obj_Ref := New_Occurrence_Of (Obj_Id, Loc); |
| Obj_Typ := Etype (Obj_Id); |
| if not Is_Protected and then not Is_Class_Wide_Type (Obj_Typ) then |
| Obj_Typ := Base_Type (Obj_Typ); |
| end if; |
| |
| if Is_Access_Type (Obj_Typ) then |
| Obj_Ref := Make_Explicit_Dereference (Loc, Obj_Ref); |
| Obj_Typ := Available_View (Designated_Type (Obj_Typ)); |
| end if; |
| |
| -- If the object is a Master_Node, then nothing to do, unless there |
| -- is no or a single controlled object with strict semantics, in |
| -- which case we move its declaration, call marker (if any) and |
| -- initialization call, and also mark it to avoid double processing. |
| |
| if Is_RTE (Obj_Typ, RE_Master_Node) then |
| Master_Node_Id := Obj_Id; |
| |
| if not Has_Strict_Ctrl_Objs or else Count = 1 then |
| if Nkind (Next (Decl)) = N_Call_Marker then |
| Prepend_To (Decls, Remove_Next (Next (Decl))); |
| end if; |
| Prepend_To (Decls, Remove_Next (Decl)); |
| Remove (Decl); |
| Prepend_To (Decls, Decl); |
| Set_Is_Ignored_For_Finalization (Obj_Id); |
| end if; |
| |
| -- Create the declaration of the Master_Node for the object and |
| -- insert it before the declaration of the object itself, unless |
| -- there is no or a single controlled object with strict semantics, |
| -- because it will effectively play the role of a degenerated master |
| -- and therefore needs to be inserted at the same place the master |
| -- would have been. |
| |
| else pragma Assert (No (Finalization_Master_Node (Obj_Id))); |
| -- In the latter case, use the Sloc the master would have had |
| |
| if not Has_Strict_Ctrl_Objs or else Count = 1 then |
| Master_Node_Loc := Sloc (N); |
| else |
| Master_Node_Loc := Loc; |
| end if; |
| |
| Master_Node_Id := |
| Make_Defining_Identifier (Master_Node_Loc, |
| Chars => New_External_Name (Chars (Obj_Id), Suffix => "MN")); |
| |
| Master_Node_Decl := |
| Make_Master_Node_Declaration (Master_Node_Loc, |
| Master_Node_Id, Obj_Id); |
| |
| Push_Scope (Scope (Obj_Id)); |
| |
| -- Avoid generating duplicate names for master nodes |
| |
| if Ekind (Obj_Id) = E_Loop_Parameter |
| and then |
| Present (Current_Entity_In_Scope (Chars (Master_Node_Id))) |
| then |
| Set_Chars (Master_Node_Id, |
| New_External_Name (Chars (Obj_Id), |
| Suffix => "MN", |
| Suffix_Index => -1)); |
| end if; |
| |
| if not Has_Strict_Ctrl_Objs or else Count = 1 then |
| Prepend_To (Decls, Master_Node_Decl); |
| else |
| Insert_Before (Decl, Master_Node_Decl); |
| end if; |
| |
| Analyze (Master_Node_Decl); |
| Pop_Scope; |
| |
| -- Mark the Master_Node to avoid double processing |
| |
| Set_Is_Ignored_For_Finalization (Master_Node_Id); |
| end if; |
| |
| -- Attach the Master_Node after all initialization has been done. The |
| -- place of insertion depends on the context. |
| |
| if Ekind (Obj_Id) in E_Constant | E_Variable then |
| |
| -- The object has delayed freezing. The Master_Node insertion |
| -- point is after the freeze node. |
| |
| if Has_Delayed_Freeze (Obj_Id) then |
| Master_Node_Ins := Freeze_Node (Obj_Id); |
| |
| -- The object is initialized by an aggregate. The Master_Node |
| -- insertion point is after the last aggregate assignment. |
| |
| elsif Present (Last_Aggregate_Assignment (Obj_Id)) then |
| Master_Node_Ins := Last_Aggregate_Assignment (Obj_Id); |
| |
| -- The object is initialized by a build-in-place function call. |
| -- The Master_Node insertion point is after the function call. |
| |
| elsif Present (BIP_Initialization_Call (Obj_Id)) then |
| Master_Node_Ins := BIP_Initialization_Call (Obj_Id); |
| |
| -- In other cases the Master_Node is inserted after the last call |
| -- to either [Deep_]Initialize or the type-specific init proc. |
| |
| else |
| Master_Node_Ins := Find_Last_Init (Decl); |
| end if; |
| |
| -- In all other cases the Master_Node is inserted after the last call |
| -- to either [Deep_]Initialize or the type-specific init proc. |
| |
| else |
| Master_Node_Ins := Find_Last_Init (Decl); |
| end if; |
| |
| -- If the Initialize function is null or trivial, the call will have |
| -- been replaced with a null statement and we place the attachment |
| -- of the Master_Node after the declaration of the object itself. |
| |
| if No (Master_Node_Ins) then |
| Master_Node_Ins := Decl; |
| end if; |
| |
| -- Processing for simple protected objects. Such objects require |
| -- manual finalization of their lock managers. Generate: |
| |
| -- procedure obj_typ_nnFD (v : system__address) is |
| -- type Ptr_Typ is access all Obj_Typ; |
| -- Rnn : Obj_Typ renames Ptr_Typ!(v).all; |
| -- begin |
| -- $system__tasking__protected_objects__finalize_protection |
| -- (Obj_TypV!(Rnn)._object); |
| -- exception |
| -- when others => |
| -- null; |
| -- end obj_typ_nnFD; |
| |
| if Is_Protected |
| or else (Has_Simple_Protected_Object (Obj_Typ) |
| and then No (Finalize_Address (Obj_Typ))) |
| then |
| declare |
| Param : constant Entity_Id := |
| Make_Defining_Identifier (Loc, Name_V); |
| Ptr_Typ : constant Entity_Id := Make_Temporary (Loc, 'P'); |
| Ren_Id : constant Entity_Id := Make_Temporary (Loc, 'R'); |
| Ren_Ref : constant Node_Id := New_Occurrence_Of (Ren_Id, Loc); |
| |
| Fin_Body : Node_Id; |
| Fin_Call : Node_Id; |
| Fin_Stmts : List_Id := No_List; |
| HSS : Node_Id; |
| |
| begin |
| Set_Etype (Ren_Ref, Obj_Typ); |
| |
| if Is_Simple_Protected_Type (Obj_Typ) then |
| Fin_Call := Cleanup_Protected_Object (Decl, Ren_Ref); |
| |
| if Present (Fin_Call) then |
| Fin_Stmts := New_List (Fin_Call); |
| end if; |
| |
| elsif Is_Array_Type (Obj_Typ) then |
| Fin_Stmts := Cleanup_Array (Decl, Ren_Ref, Obj_Typ); |
| |
| else |
| Fin_Stmts := Cleanup_Record (Decl, Ren_Ref, Obj_Typ); |
| end if; |
| |
| if No (Fin_Stmts) then |
| return; |
| end if; |
| |
| HSS := |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => Fin_Stmts); |
| |
| if Exceptions_OK then |
| Set_Exception_Handlers (HSS, New_List ( |
| Make_Exception_Handler (Loc, |
| Exception_Choices => New_List ( |
| Make_Others_Choice (Loc)), |
| Statements => New_List ( |
| Make_Null_Statement (Loc))))); |
| end if; |
| |
| Fin_Id := |
| Make_Defining_Identifier (Loc, |
| Make_TSS_Name_Local (Obj_Typ, TSS_Finalize_Address)); |
| |
| Fin_Body := |
| Make_Subprogram_Body (Loc, |
| Specification => |
| Make_Procedure_Specification (Loc, |
| Defining_Unit_Name => Fin_Id, |
| |
| Parameter_Specifications => New_List ( |
| Make_Parameter_Specification (Loc, |
| Defining_Identifier => Param, |
| Parameter_Type => |
| New_Occurrence_Of (RTE (RE_Address), Loc)))), |
| |
| Declarations => 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_Occurrence_Of (Obj_Typ, Loc))), |
| |
| Make_Object_Renaming_Declaration (Loc, |
| Defining_Identifier => Ren_Id, |
| Subtype_Mark => |
| New_Occurrence_Of (Obj_Typ, Loc), |
| Name => |
| Make_Explicit_Dereference (Loc, |
| Prefix => |
| Unchecked_Convert_To |
| (Ptr_Typ, New_Occurrence_Of (Param, Loc))))), |
| |
| Handled_Statement_Sequence => HSS); |
| |
| Push_Scope (Scope (Obj_Id)); |
| Insert_After_And_Analyze |
| (Master_Node_Ins, Fin_Body, Suppress => All_Checks); |
| Pop_Scope; |
| |
| Master_Node_Ins := Fin_Body; |
| end; |
| |
| -- If the object's subtype is an array that has a constrained first |
| -- subtype and is not this first subtype, we need to build a special |
| -- Finalize_Address primitive for the object's subtype because the |
| -- Finalize_Address primitive of the base type has been tailored to |
| -- the first subtype (see Make_Finalize_Address_Stmts). Generate: |
| |
| -- procedure obj_typ_nnFD (v : system__address) is |
| -- type Ptr_Typ is access all Obj_Typ; |
| -- begin |
| -- obj_typBDF (Ptr_Typ!(v).all, f => true); |
| -- end obj_typ_nnFD; |
| |
| elsif Is_Array_Type (Etype (Obj_Id)) |
| and then Is_Constrained (First_Subtype (Etype (Obj_Id))) |
| and then Etype (Obj_Id) /= First_Subtype (Etype (Obj_Id)) |
| then |
| declare |
| Ptr_Typ : constant Node_Id := Make_Temporary (Loc, 'P'); |
| Param : constant Entity_Id := |
| Make_Defining_Identifier (Loc, Name_V); |
| |
| Fin_Body : Node_Id; |
| |
| begin |
| Obj_Typ := Etype (Obj_Id); |
| |
| Fin_Id := |
| Make_Defining_Identifier (Loc, |
| Make_TSS_Name_Local |
| (Obj_Typ, TSS_Finalize_Address)); |
| |
| Fin_Body := |
| Make_Subprogram_Body (Loc, |
| Specification => |
| Make_Procedure_Specification (Loc, |
| Defining_Unit_Name => Fin_Id, |
| Parameter_Specifications => New_List ( |
| Make_Parameter_Specification (Loc, |
| Defining_Identifier => Param, |
| Parameter_Type => |
| New_Occurrence_Of (RTE (RE_Address), Loc)))), |
| |
| Declarations => 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_Occurrence_Of (Obj_Typ, Loc)))), |
| |
| 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, |
| Make_Identifier (Loc, Name_V))), |
| Typ => Obj_Typ)))); |
| |
| Push_Scope (Scope (Obj_Id)); |
| Insert_After_And_Analyze |
| (Master_Node_Ins, Fin_Body, Suppress => All_Checks); |
| Pop_Scope; |
| |
| Master_Node_Ins := Fin_Body; |
| end; |
| |
| else |
| Fin_Id := Finalize_Address (Obj_Typ); |
| |
| if No (Fin_Id) and then Ekind (Obj_Typ) = E_Class_Wide_Subtype then |
| Fin_Id := TSS (Obj_Typ, TSS_Finalize_Address); |
| end if; |
| end if; |
| |
| -- Now build the attachment call that will initialize the object's |
| -- Master_Node using the object's address and type's finalization |
| -- procedure and then attach the Master_Node to the master, unless |
| -- there is no or a single controlled object with strict semantics. |
| |
| if not Has_Strict_Ctrl_Objs or else Count = 1 then |
| -- Finalize_Address is not generated in CodePeer mode because the |
| -- body contains address arithmetic. So we don't want to generate |
| -- the attach in this case. Ditto if the object is a Master_Node. |
| |
| if CodePeer_Mode or else Obj_Id = Master_Node_Id then |
| Master_Node_Attach := Make_Null_Statement (Loc); |
| |
| else |
| Master_Node_Attach := |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (RTE (RE_Attach_Object_To_Node), Loc), |
| Parameter_Associations => New_List ( |
| Make_Address_For_Finalize (Loc, Obj_Ref, Obj_Typ), |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Fin_Id, Loc), |
| Attribute_Name => Name_Unrestricted_Access), |
| New_Occurrence_Of (Master_Node_Id, Loc))); |
| |
| Set_Finalize_Address_For_Node (Master_Node_Id, Fin_Id); |
| end if; |
| |
| -- We also generate the direct finalization call here |
| |
| Fin_Call := Make_Finalize_Call_For_Node (Loc, Master_Node_Id); |
| |
| -- 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 Has_Strict_Ctrl_Objs |
| and then Exceptions_OK |
| and then not CodePeer_Mode |
| then |
| Fin_Call := |
| Make_Block_Statement (Loc, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List (Fin_Call), |
| |
| Exception_Handlers => New_List ( |
| Build_Exception_Handler |
| (Finalizer_Data, For_Package)))); |
| end if; |
| |
| Append_To (Finalizer_Stmts, Fin_Call); |
| |
| else |
| -- If the object is a Master_Node, we just need to chain it |
| |
| if Obj_Id = Master_Node_Id then |
| Master_Node_Attach := |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (RTE (RE_Chain_Node_To_Master), Loc), |
| Parameter_Associations => New_List ( |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Obj_Id, Loc), |
| Attribute_Name => Name_Unrestricted_Access), |
| New_Occurrence_Of (Finalization_Master, Loc))); |
| |
| -- Finalize_Address is not generated in CodePeer mode because the |
| -- body contains address arithmetic. So we don't want to generate |
| -- the attach in this case. |
| |
| elsif CodePeer_Mode then |
| Master_Node_Attach := Make_Null_Statement (Loc); |
| |
| else |
| Master_Node_Attach := |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (RTE (RE_Attach_Object_To_Master), Loc), |
| Parameter_Associations => New_List ( |
| Make_Address_For_Finalize (Loc, Obj_Ref, Obj_Typ), |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Fin_Id, Loc), |
| Attribute_Name => Name_Unrestricted_Access), |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| New_Occurrence_Of (Master_Node_Id, Loc), |
| Attribute_Name => Name_Unrestricted_Access), |
| New_Occurrence_Of (Finalization_Master, Loc))); |
| end if; |
| end if; |
| |
| Insert_After_And_Analyze |
| (Master_Node_Ins, Master_Node_Attach, Suppress => All_Checks); |
| 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_Occurrence_Of (RTE (RE_Unregister_Tag), Loc), |
| Parameter_Associations => New_List ( |
| New_Occurrence_Of (DT_Ptr, Loc)))); |
| end Process_Tagged_Type_Declaration; |
| |
| -- Start of processing for Build_Finalizer |
| |
| begin |
| Fin_Id := Empty; |
| |
| -- Do not perform this expansion in SPARK mode because it is not |
| -- necessary. |
| |
| if GNATprove_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; |
| |
| -- We do not need to process nested packages since they are handled by |
| -- the finalizer of the enclosing scope, including at library level. |
| -- And we do not build two finalizers for an instance without body that |
| -- is a library unit (see Analyze_Package_Instantiation). |
| |
| if For_Package |
| and then (not Is_Compilation_Unit (Spec_Id) |
| 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 |
| -- For package specs and bodies, we are invoked from the Standard |
| -- scope, so we need to push the specs onto the scope stack first. |
| |
| Push_Scope (Spec_Id); |
| |
| -- 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); |
| |
| -- From all the possible contexts, only package specifications may |
| -- have private declarations. |
| |
| if For_Package_Spec then |
| Process_Declarations (Priv_Decls, Preprocess => 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 else Has_Ctrl_Objs or else 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 else 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); |
| Process_Declarations (Stmts, Preprocess => 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); |
| 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 else Has_Ctrl_Objs or else Has_Tagged_Types then |
| Build_Components; |
| end if; |
| |
| if Has_Ctrl_Objs or else Has_Tagged_Types then |
| Process_Declarations (Stmts); |
| Process_Declarations (Decls); |
| end if; |
| end if; |
| |
| -- Step 3: Finalizer creation |
| |
| if Acts_As_Clean or else Has_Ctrl_Objs or else Has_Tagged_Types then |
| Create_Finalizer; |
| end if; |
| |
| -- Pop the scope that was pushed above for package specs and bodies |
| |
| if For_Package then |
| Pop_Scope; |
| end if; |
| end Build_Finalizer; |
| |
| -------------------------- |
| -- Build_Finalizer_Call -- |
| -------------------------- |
| |
| procedure Build_Finalizer_Call (N : Node_Id; Fin_Id : Entity_Id) is |
| begin |
| -- Do not perform this expansion in SPARK mode because we do not create |
| -- finalizers in the first place. |
| |
| if GNATprove_Mode then |
| return; |
| end if; |
| |
| -- If the construct to be cleaned up is a protected subprogram body, the |
| -- finalizer call needs to be associated with the block that 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; |
| |
| declare |
| Loc : constant Source_Ptr := Sloc (N); |
| |
| Is_Protected_Subp_Body : constant Boolean := |
| Nkind (N) = N_Subprogram_Body |
| and then Is_Protected_Subprogram_Body (N); |
| -- True if N is the protected version of a subprogram that belongs to |
| -- a protected type. |
| |
| HSS : constant Node_Id := |
| (if Is_Protected_Subp_Body |
| then Handled_Statement_Sequence |
| (Last (Statements (Handled_Statement_Sequence (N)))) |
| else Handled_Statement_Sequence (N)); |
| |
| -- We attach the At_End_Proc to the HSS if this is an accept |
| -- statement or extended return statement. Also in the case of |
| -- a protected subprogram, because if Service_Entries raises an |
| -- exception, we do not lock the PO, so we also do not want to |
| -- unlock it. |
| |
| Use_HSS : constant Boolean := |
| Nkind (N) in N_Accept_Statement | N_Extended_Return_Statement |
| or else Is_Protected_Subp_Body; |
| |
| At_End_Proc_Bearer : constant Node_Id := (if Use_HSS then HSS else N); |
| begin |
| pragma Assert (No (At_End_Proc (At_End_Proc_Bearer))); |
| Set_At_End_Proc (At_End_Proc_Bearer, New_Occurrence_Of (Fin_Id, Loc)); |
| -- Attach reference to finalizer to tree, for LLVM use |
| Set_Parent (At_End_Proc (At_End_Proc_Bearer), At_End_Proc_Bearer); |
| Analyze (At_End_Proc (At_End_Proc_Bearer)); |
| Expand_At_End_Handler (At_End_Proc_Bearer, Empty); |
| end; |
| 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 |
| Decl : Node_Id; |
| |
| Dummy : Entity_Id; |
| -- This variable captures an unused dummy internal entity, see the |
| -- comment associated with its use. |
| |
| 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. |
| |
| -- This is not needed for library-level finalizers as they are called by |
| -- the environment task and cannot be aborted. |
| |
| if not For_Package then |
| if Abort_Allowed then |
| Data.Abort_Id := Make_Temporary (Loc, 'A'); |
| |
| -- 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_Occurrence_Of (Standard_Boolean, Loc), |
| Expression => |
| New_Occurrence_Of (RTE (RE_Triggered_By_Abort), Loc))); |
| |
| -- Abort is not required |
| |
| else |
| -- Generate a dummy entity to ensure that the internal symbols are |
| -- in sync when a unit is compiled with and without aborts. |
| |
| Dummy := Make_Temporary (Loc, 'A'); |
| Data.Abort_Id := Empty; |
| end if; |
| |
| -- Library-level finalizers |
| |
| else |
| Data.Abort_Id := Empty; |
| end if; |
| |
| if Exception_Extra_Info then |
| Data.E_Id := Make_Temporary (Loc, 'E'); |
| |
| -- Generate: |
| -- E_Id : Exception_Occurrence; |
| |
| Decl := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Data.E_Id, |
| Object_Definition => |
| New_Occurrence_Of (RTE (RE_Exception_Occurrence), Loc)); |
| Set_No_Initialization (Decl); |
| |
| Append_To (Decls, 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_Occurrence_Of (Standard_Boolean, Loc), |
| Expression => New_Occurrence_Of (Standard_False, Loc))); |
| |
| if Debug_Generated_Code then |
| Set_Debug_Info_Needed (Data.Raised_Id); |
| end if; |
| 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 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_Occurrence_Of |
| (RTE (RE_Raise_From_Controlled_Operation), Data.Loc), |
| Parameter_Associations => |
| New_List (New_Occurrence_Of (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_Occurrence_Of (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_Occurrence_Of (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 |
| if Has_Controlled_Component (Typ) then |
| Set_TSS |
| (Typ, |
| Make_Deep_Proc |
| (Prim => Initialize_Case, |
| Typ => Typ, |
| Stmts => Make_Deep_Record_Body (Initialize_Case, Typ))); |
| |
| if not Is_Inherently_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; |
| 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 (unless CodePeer_Mode) |
| |
| if not CodePeer_Mode 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_Occurrence_Of (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); |
| Stmts : constant List_Id := New_List; |
| U_Typ : constant Entity_Id := Underlying_Type (Typ); |
| |
| Comp : Entity_Id; |
| Tsk : Node_Id; |
| |
| 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 (U_Typ); |
| while Present (Comp) loop |
| if Chars (Comp) /= Name_uParent |
| and then (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_Occurrence_Of (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_Occurrence_Of (RTE (RE_Free_Task), Loc), |
| Parameter_Associations => New_List (Concurrent_Ref (Ref))); |
| end if; |
| end Cleanup_Task; |
| |
| -------------------------------------- |
| -- Check_Unnesting_Elaboration_Code -- |
| -------------------------------------- |
| |
| procedure Check_Unnesting_Elaboration_Code (N : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| Block_Elab_Proc : Entity_Id := Empty; |
| |
| procedure Set_Block_Elab_Proc; |
| -- Create a defining identifier for a procedure that will replace |
| -- a block with nested subprograms (unless it has already been created, |
| -- in which case this is a no-op). |
| |
| procedure Set_Block_Elab_Proc is |
| begin |
| if No (Block_Elab_Proc) then |
| Block_Elab_Proc := Make_Temporary (Loc, 'I'); |
| end if; |
| end Set_Block_Elab_Proc; |
| |
| procedure Reset_Scopes_To_Block_Elab_Proc (L : List_Id); |
| -- Find entities in the elaboration code of a library package body that |
| -- contain or represent a subprogram body. A body can appear within a |
| -- block or a loop or can appear by itself if generated for an object |
| -- declaration that involves controlled actions. The first such entity |
| -- forces creation of a new procedure entity (via Set_Block_Elab_Proc) |
| -- that will be used to reset the scopes of all entities that become |
| -- local to the new elaboration procedure. This is needed for subsequent |
| -- unnesting actions, which depend on proper setting of the Scope links |
| -- to determine the nesting level of each subprogram. |
| |
| -------------------------------------- |
| -- Reset_Scopes_To_Block_Elab_Proc -- |
| -------------------------------------- |
| Maybe_Reset_Scopes_For_Decl : constant Elist_Id := New_Elmt_List; |
| |
| procedure Reset_Scopes_To_Block_Elab_Proc (L : List_Id) is |
| Id : Entity_Id; |
| Stat : Node_Id; |
| Node : Node_Id; |
| |
| begin |
| Stat := First (L); |
| while Present (Stat) loop |
| case Nkind (Stat) is |
| when N_Block_Statement => |
| if Present (Identifier (Stat)) then |
| Id := Entity (Identifier (Stat)); |
| |
| -- The Scope of this block needs to be reset to the new |
| -- procedure if the block contains nested subprograms. |
| |
| if Present (Id) and then Contains_Subprogram (Id) then |
| Set_Block_Elab_Proc; |
| Set_Scope (Id, Block_Elab_Proc); |
| end if; |
| end if; |
| |
| when N_Loop_Statement => |
| Id := Entity (Identifier (Stat)); |
| |
| if Present (Id) and then Contains_Subprogram (Id) then |
| if Scope (Id) = Current_Scope then |
| Set_Block_Elab_Proc; |
| Set_Scope (Id, Block_Elab_Proc); |
| end if; |
| end if; |
| |
| -- We traverse the loop's statements as well, which may |
| -- include other block (etc.) statements that need to have |
| -- their Scope set to Block_Elab_Proc. (Is this really the |
| -- case, or do such nested blocks refer to the loop scope |
| -- rather than the loop's enclosing scope???.) |
| |
| Reset_Scopes_To_Block_Elab_Proc (Statements (Stat)); |
| |
| when N_If_Statement => |
| Reset_Scopes_To_Block_Elab_Proc (Then_Statements (Stat)); |
| Reset_Scopes_To_Block_Elab_Proc (Else_Statements (Stat)); |
| |
| Node := First (Elsif_Parts (Stat)); |
| while Present (Node) loop |
| Reset_Scopes_To_Block_Elab_Proc (Then_Statements (Node)); |
| Next (Node); |
| end loop; |
| |
| when N_Case_Statement => |
| Node := First (Alternatives (Stat)); |
| while Present (Node) loop |
| Reset_Scopes_To_Block_Elab_Proc (Statements (Node)); |
| Next (Node); |
| end loop; |
| |
| -- Reset the Scope of a subprogram and object declaration |
| -- occurring at the top level |
| |
| when N_Subprogram_Body => |
| Id := Defining_Entity (Stat); |
| |
| Set_Block_Elab_Proc; |
| Set_Scope (Id, Block_Elab_Proc); |
| |
| when N_Object_Declaration |
| | N_Object_Renaming_Declaration |
| => |
| Id := Defining_Entity (Stat); |
| if No (Block_Elab_Proc) then |
| Append_Elmt (Id, Maybe_Reset_Scopes_For_Decl); |
| else |
| Set_Scope (Id, Block_Elab_Proc); |
| end if; |
| |
| when others => |
| null; |
| end case; |
| |
| Next (Stat); |
| end loop; |
| |
| -- If we are creating an Elab procedure, move all the gathered |
| -- declarations in its scope. |
| |
| if Present (Block_Elab_Proc) then |
| while not Is_Empty_Elmt_List (Maybe_Reset_Scopes_For_Decl) loop |
| Set_Scope |
| (Elists.Node |
| (Last_Elmt (Maybe_Reset_Scopes_For_Decl)), Block_Elab_Proc); |
| Remove_Last_Elmt (Maybe_Reset_Scopes_For_Decl); |
| end loop; |
| end if; |
| end Reset_Scopes_To_Block_Elab_Proc; |
| |
| -- Local variables |
| |
| H_Seq : constant Node_Id := Handled_Statement_Sequence (N); |
| Elab_Body : Node_Id; |
| Elab_Call : Node_Id; |
| |
| -- Start of processing for Check_Unnesting_Elaboration_Code |
| |
| begin |
| if Present (H_Seq) then |
| Reset_Scopes_To_Block_Elab_Proc (Statements (H_Seq)); |
| |
| -- There may be subprograms declared in the exception handlers |
| -- of the current body. |
| |
| if Present (Exception_Handlers (H_Seq)) then |
| declare |
| Handler : Node_Id := First (Exception_Handlers (H_Seq)); |
| begin |
| while Present (Handler) loop |
| Reset_Scopes_To_Block_Elab_Proc (Statements (Handler)); |
| |
| Next (Handler); |
| end loop; |
| end; |
| end if; |
| |
| if Present (Block_Elab_Proc) then |
| Elab_Body := |
| Make_Subprogram_Body (Loc, |
| Specification => |
| Make_Procedure_Specification (Loc, |
| Defining_Unit_Name => Block_Elab_Proc), |
| Declarations => New_List, |
| Handled_Statement_Sequence => |
| Relocate_Node (Handled_Statement_Sequence (N))); |
| |
| Elab_Call := |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Block_Elab_Proc, Loc)); |
| |
| Append_To (Declarations (N), Elab_Body); |
| Analyze (Elab_Body); |
| Set_Has_Nested_Subprogram (Block_Elab_Proc); |
| |
| Set_Handled_Statement_Sequence (N, |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List (Elab_Call))); |
| |
| Analyze (Elab_Call); |
| |
| -- Could we reset the scopes of entities associated with the new |
| -- procedure here via a loop over entities rather than doing it in |
| -- the recursive Reset_Scopes_To_Elab_Proc procedure??? |
| end if; |
| end if; |
| end Check_Unnesting_Elaboration_Code; |
| |
| --------------------------------------- |
| -- Check_Unnesting_In_Decls_Or_Stmts -- |
| --------------------------------------- |
| |
| procedure Check_Unnesting_In_Decls_Or_Stmts (Decls_Or_Stmts : List_Id) is |
| Decl_Or_Stmt : Node_Id; |
| |
| begin |
| if Unnest_Subprogram_Mode |
| and then Present (Decls_Or_Stmts) |
| then |
| Decl_Or_Stmt := First (Decls_Or_Stmts); |
| while Present (Decl_Or_Stmt) loop |
| if Nkind (Decl_Or_Stmt) = N_Block_Statement |
| and then Contains_Subprogram (Entity (Identifier (Decl_Or_Stmt))) |
| then |
| Unnest_Block (Decl_Or_Stmt); |
| |
| -- If-statements may contain subprogram bodies at the outer level |
| -- of their statement lists, and the subprograms may make up-level |
| -- references (such as to objects declared in the same statement |
| -- list). Unlike block and loop cases, however, we don't have an |
| -- entity on which to test the Contains_Subprogram flag, so |
| -- Unnest_If_Statement must traverse the statement lists to |
| -- determine whether there are nested subprograms present. |
| |
| elsif Nkind (Decl_Or_Stmt) = N_If_Statement then |
| Unnest_If_Statement (Decl_Or_Stmt); |
| |
| elsif Nkind (Decl_Or_Stmt) = N_Loop_Statement then |
| declare |
| Id : constant Entity_Id := |
| Entity (Identifier (Decl_Or_Stmt)); |
| |
| begin |
| -- When a top-level loop within declarations of a library |
| -- package spec or body contains nested subprograms, we wrap |
| -- it in a procedure to handle possible up-level references |
| -- to entities associated with the loop (such as loop |
| -- parameters). |
| |
| if Present (Id) and then Contains_Subprogram (Id) then |
| Unnest_Loop (Decl_Or_Stmt); |
| end if; |
| end; |
| |
| elsif Nkind (Decl_Or_Stmt) = N_Package_Declaration then |
| Check_Unnesting_In_Decls_Or_Stmts |
| (Visible_Declarations (Specification (Decl_Or_Stmt))); |
| Check_Unnesting_In_Decls_Or_Stmts |
| (Private_Declarations (Specification (Decl_Or_Stmt))); |
| |
| elsif Nkind (Decl_Or_Stmt) = N_Package_Body then |
| Check_Unnesting_In_Decls_Or_Stmts (Declarations (Decl_Or_Stmt)); |
| if Present (Statements |
| (Handled_Statement_Sequence (Decl_Or_Stmt))) |
| then |
| Check_Unnesting_In_Decls_Or_Stmts (Statements |
| (Handled_Statement_Sequence (Decl_Or_Stmt))); |
| Check_Unnesting_In_Handlers (Decl_Or_Stmt); |
| end if; |
| end if; |
| |
| Next (Decl_Or_Stmt); |
| end loop; |
| end if; |
| end Check_Unnesting_In_Decls_Or_Stmts; |
| |
| --------------------------------- |
| -- Check_Unnesting_In_Handlers -- |
| --------------------------------- |
| |
| procedure Check_Unnesting_In_Handlers (N : Node_Id) is |
| Stmt_Seq : constant Node_Id := Handled_Statement_Sequence (N); |
| |
| begin |
| if Present (Stmt_Seq) |
| and then Present (Exception_Handlers (Stmt_Seq)) |
| then |
| declare |
| Handler : Node_Id := First (Exception_Handlers (Stmt_Seq)); |
| begin |
| while Present (Handler) loop |
| if Present (Statements (Handler)) then |
| Check_Unnesting_In_Decls_Or_Stmts (Statements (Handler)); |
| end if; |
| |
| Next (Handler); |
| end loop; |
| end; |
| end if; |
| end Check_Unnesting_In_Handlers; |
| |
| ------------------------------ |
| -- 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 No (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_Controlled_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; |
| |
| -------------------------- |
| -- Contains_Subprogram -- |
| -------------------------- |
| |
| function Contains_Subprogram (Blk : Entity_Id) return Boolean is |
| E : Entity_Id; |
| |
| begin |
| E := First_Entity (Blk); |
| |
| -- The compiler may generate loops with a declare block containing |
| -- nested procedures used for finalization. Recursively search for |
| -- subprograms in such constructs. |
| |
| if Ekind (Blk) = E_Loop |
| and then Parent_Kind (Blk) = N_Loop_Statement |
| then |
| declare |
| Stmt : Node_Id := First (Statements (Parent (Blk))); |
| begin |
| while Present (Stmt) loop |
| if Nkind (Stmt) = N_Block_Statement then |
| declare |
| Id : constant Entity_Id := |
| Entity (Identifier (Stmt)); |
| begin |
| if Contains_Subprogram (Id) then |
| return True; |
| end if; |
| end; |
| end if; |
| Next (Stmt); |
| end loop; |
| end; |
| end if; |
| |
| while Present (E) loop |
| if Is_Subprogram (E) then |
| return True; |
| |
| elsif Ekind (E) in E_Block | E_Loop |
| and then Contains_Subprogram (E) |
| then |
| return True; |
| end if; |
| |
| Next_Entity (E); |
| end loop; |
| |
| return False; |
| end Contains_Subprogram; |
| |
| ------------------ |
| -- Convert_View -- |
| ------------------ |
| |
| function Convert_View |
| (Proc : Entity_Id; |
| Arg : Node_Id; |
| Typ : Entity_Id) return Node_Id |
| is |
| Ftyp : constant Entity_Id := Etype (First_Formal (Proc)); |
| |
| Atyp : Entity_Id; |
| |
| begin |
| if Nkind (Arg) in N_Type_Conversion | N_Unchecked_Type_Conversion then |
| Atyp := Entity (Subtype_Mark (Arg)); |
| elsif Present (Etype (Arg)) then |
| Atyp := Etype (Arg); |
| else |
| Atyp := Typ; |
| end if; |
| |
| if Is_Abstract_Subprogram (Proc) and then Is_Tagged_Type (Ftyp) then |
| return Unchecked_Convert_To (Class_Wide_Type (Ftyp), Arg); |
| |
| elsif Present (Atyp) |
| and then Atyp /= Ftyp |
| and then (Is_Private_Type (Ftyp) |
| or else Is_Private_Type (Atyp) |
| or else Is_Private_Type (Base_Type (Atyp))) |
| and then Implementation_Base_Type (Atyp) = |
| Implementation_Base_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 (Arg) in 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; |
| |
| -- Otherwise, introduce a conversion when the designated object |
| -- has a type derived from the formal of the controlled routine. |
| |
| elsif Is_Private_Type (Ftyp) |
| and then Present (Atyp) |
| and then Is_Derived_Type (Underlying_Type (Base_Type (Atyp))) |
| then |
| return Unchecked_Convert_To (Ftyp, Arg); |
| |
| else |
| return Arg; |
| end if; |
| end Convert_View; |
| |
| ------------------------------- |
| -- 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 scope stack in order to enclose |
| -- all transient variables generated. |
| |
| procedure Establish_Transient_Scope |
| (N : Node_Id; |
| Manage_Sec_Stack : Boolean) |
| is |
| function Is_Package_Or_Subprogram (Id : Entity_Id) return Boolean; |
| -- Determine whether arbitrary Id denotes a package or subprogram [body] |
| |
| function Find_Enclosing_Transient_Scope return Int; |
| -- Examine the scope stack looking for the nearest enclosing transient |
| -- scope within the innermost enclosing package or subprogram. Return |
| -- its index in the table or else -1 if no such scope exists. |
| |
| function Find_Transient_Context (N : Node_Id) return Node_Id; |
| -- Locate a suitable context for arbitrary node N which may need to be |
| -- serviced by a transient scope. Return Empty if no suitable context |
| -- is available. |
| |
| procedure Delegate_Sec_Stack_Management; |
| -- Move the management of the secondary stack to the nearest enclosing |
| -- suitable scope. |
| |
| procedure Create_Transient_Scope (Context : Node_Id); |
| -- Place a new scope on the scope stack in order to service construct |
| -- Context. Context is the node found by Find_Transient_Context. The |
| -- new scope may also manage the secondary stack. |
| |
| ---------------------------- |
| -- Create_Transient_Scope -- |
| ---------------------------- |
| |
| procedure Create_Transient_Scope (Context : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (N); |
| |
| Iter_Loop : Entity_Id; |
| Trans_Scop : constant Entity_Id := |
| New_Internal_Entity (E_Block, Current_Scope, Loc, 'B'); |
| |
| begin |
| Set_Etype (Trans_Scop, Standard_Void_Type); |
| |
| -- Push a new scope, and set its Node_To_Be_Wrapped and Is_Transient |
| -- fields. |
| |
| Push_Scope (Trans_Scop); |
| Scope_Stack.Table (Scope_Stack.Last).Node_To_Be_Wrapped := Context; |
| Scope_Stack.Table (Scope_Stack.Last).Is_Transient := True; |
| |
| -- The transient scope must also manage the secondary stack |
| |
| if Manage_Sec_Stack then |
| Set_Uses_Sec_Stack (Trans_Scop); |
| Check_Restriction (No_Secondary_Stack, N); |
| |
| -- The expansion of iterator loops generates references to objects |
| -- in order to extract elements from a container: |
| |
| -- Ref : Reference_Type_Ptr := Reference (Container, Cursor); |
| -- Obj : <object type> renames Ref.all.Element.all; |
| |
| -- These references are controlled and returned on the secondary |
| -- stack. A new reference is created at each iteration of the loop |
| -- and as a result it must be finalized and the space occupied by |
| -- it on the secondary stack reclaimed at the end of the current |
| -- iteration. |
| |
| -- When the context that requires a transient scope is a call to |
| -- routine Reference, the node to be wrapped is the source object: |
| |
| -- for Obj of Container loop |
| |
| -- Routine Wrap_Transient_Declaration however does not generate |
| -- a physical block as wrapping a declaration will kill it too |
| -- early. To handle this peculiar case, mark the related iterator |
| -- loop as requiring the secondary stack. This signals the |
| -- finalization machinery to manage the secondary stack (see |
| -- routine Process_Statements_For_Controlled_Objects). |
| |
| Iter_Loop := Find_Enclosing_Iterator_Loop (Trans_Scop); |
| |
| if Present (Iter_Loop) then |
| Set_Uses_Sec_Stack (Iter_Loop); |
| end if; |
| end if; |
| |
| if Debug_Flag_W then |
| Write_Str (" <Transient>"); |
| Write_Eol; |
| end if; |
| end Create_Transient_Scope; |
| |
| ----------------------------------- |
| -- Delegate_Sec_Stack_Management -- |
| ----------------------------------- |
| |
| procedure Delegate_Sec_Stack_Management is |
| begin |
| for Index in reverse Scope_Stack.First .. Scope_Stack.Last loop |
| declare |
| Scope : Scope_Stack_Entry renames Scope_Stack.Table (Index); |
| begin |
| -- Prevent the search from going too far or within the scope |
| -- space of another unit. |
| |
| if Scope.Entity = Standard_Standard then |
| return; |
| |
| -- No transient scope should be encountered during the |
| -- traversal because Establish_Transient_Scope should have |
| -- already handled this case. |
| |
| elsif Scope.Is_Transient then |
| raise Program_Error; |
| |
| -- The construct that requires secondary stack management is |
| -- always enclosed by a package or subprogram scope. |
| |
| elsif Is_Package_Or_Subprogram (Scope.Entity) then |
| Set_Uses_Sec_Stack (Scope.Entity); |
| Check_Restriction (No_Secondary_Stack, N); |
| |
| return; |
| end if; |
| end; |
| end loop; |
| |
| -- At this point no suitable scope was found. This should never occur |
| -- because a construct is always enclosed by a compilation unit which |
| -- has a scope. |
| |
| pragma Assert (False); |
| end Delegate_Sec_Stack_Management; |
| |
| ------------------------------------ |
| -- Find_Enclosing_Transient_Scope -- |
| ------------------------------------ |
| |
| function Find_Enclosing_Transient_Scope return Int is |
| begin |
| for Index in reverse Scope_Stack.First .. Scope_Stack.Last loop |
| declare |
| Scope : Scope_Stack_Entry renames Scope_Stack.Table (Index); |
| begin |
| -- Prevent the search from going too far or within the scope |
| -- space of another unit. |
| |
| if Scope.Entity = Standard_Standard |
| or else Is_Package_Or_Subprogram (Scope.Entity) |
| then |
| exit; |
| |
| elsif Scope.Is_Transient then |
| return Index; |
| end if; |
| end; |
| end loop; |
| |
| return -1; |
| end Find_Enclosing_Transient_Scope; |
| |
| ---------------------------- |
| -- Find_Transient_Context -- |
| ---------------------------- |
| |
| function Find_Transient_Context (N : Node_Id) return Node_Id is |
| Curr : Node_Id := N; |
| Prev : Node_Id := Empty; |
| |
| begin |
| while Present (Curr) loop |
| case Nkind (Curr) is |
| |
| -- Declarations |
| |
| -- Declarations act as a boundary for a transient scope even if |
| -- they are not wrapped, see Wrap_Transient_Declaration. |
| |
| when N_Object_Declaration |
| | N_Object_Renaming_Declaration |
| | N_Subtype_Declaration |
| => |
| return Curr; |
| |
| -- Statements |
| |
| -- Statements and statement-like constructs act as a boundary |
| -- for a transient scope. |
| |
| when N_Accept_Alternative |
| | N_Attribute_Definition_Clause |
| | N_Case_Statement |
| | N_Case_Statement_Alternative |
| | 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 |
| => |
| pragma Assert (Present (Prev)); |
| return Prev; |
| |
| when N_Assignment_Statement => |
| return Curr; |
| |
| when N_Entry_Call_Statement |
| | N_Procedure_Call_Statement |
| => |
| -- When an entry or procedure call acts as the alternative |
| -- of a conditional or timed entry call, the proper context |
| -- is that of the alternative. |
| |
| if Nkind (Parent (Curr)) = N_Entry_Call_Alternative |
| and then Nkind (Parent (Parent (Curr))) in |
| N_Conditional_Entry_Call | N_Timed_Entry_Call |
| then |
| return Parent (Parent (Curr)); |
| |
| -- General case for entry or procedure calls |
| |
| else |
| return Curr; |
| end if; |
| |
| when N_Pragma => |
| |
| -- Pragma Check is not a valid transient context in |
| -- GNATprove mode because the pragma must remain unchanged. |
| |
| if GNATprove_Mode |
| and then Get_Pragma_Id (Curr) = Pragma_Check |
| then |
| return Empty; |
| |
| -- General case for pragmas |
| |
| else |
| return Curr; |
| end if; |
| |
| when N_Raise_Statement => |
| return Curr; |
| |
| when N_Simple_Return_Statement => |
| declare |
| Fun_Id : constant Entity_Id := |
| Return_Applies_To (Return_Statement_Entity (Curr)); |
| |
| begin |
| -- A transient context that must manage the secondary |
| -- stack cannot be a return statement of a function that |
| -- itself requires secondary stack management, because |
| -- the function's result would be reclaimed too early. |
| -- And returns of thunks never require transient scopes. |
| |
| if (Manage_Sec_Stack |
| and then Needs_Secondary_Stack (Etype (Fun_Id))) |
| or else Is_Thunk (Fun_Id) |
| then |
| return Empty; |
| |
| -- General case for return statements |
| |
| else |
| return Curr; |
| end if; |
| end; |
| |
| -- Special |
| |
| when N_Attribute_Reference => |
| if Is_Procedure_Attribute_Name (Attribute_Name (Curr)) then |
| return Curr; |
| end if; |
| |
| -- An Ada 2012 iterator specification is not a valid context |
| -- because Analyze_Iterator_Specification already employs |
| -- special processing for it. |
| |
| when N_Iterator_Specification => |
| return Empty; |
| |
| when N_Loop_Parameter_Specification => |
| |
| -- An iteration scheme is not a valid context because |
| -- routine Analyze_Iteration_Scheme already employs |
| -- special processing. |
| |
| if Nkind (Parent (Curr)) = N_Iteration_Scheme then |
| return Empty; |
| else |
| return Parent (Curr); |
| end if; |
| |
| -- Termination |
| |
| -- The following nodes represent "dummy contexts" which do not |
| -- need to be wrapped. |
| |
| when N_Component_Declaration |
| | N_Discriminant_Specification |
| | N_Parameter_Specification |
| => |
| return Empty; |
| |
| -- If the traversal leaves a scope without having been able to |
| -- find a construct to wrap, something is going wrong, but this |
| -- can happen in error situations that are not detected yet |
| -- (such as a dynamic string in a pragma Export). |
| |
| when N_Block_Statement |
| | N_Entry_Body |
| | N_Package_Body |
| | N_Package_Declaration |
| | N_Protected_Body |
| | N_Subprogram_Body |
| | N_Task_Body |
| => |
| return Empty; |
| |
| -- Default |
| |
| when others => |
| null; |
| end case; |
| |
| Prev := Curr; |
| Curr := Parent (Curr); |
| end loop; |
| |
| return Empty; |
| end Find_Transient_Context; |
| |
| ------------------------------ |
| -- Is_Package_Or_Subprogram -- |
| ------------------------------ |
| |
| function Is_Package_Or_Subprogram (Id : Entity_Id) return Boolean is |
| begin |
| return Ekind (Id) in E_Entry |
| | E_Entry_Family |
| | E_Function |
| | E_Package |
| | E_Procedure |
| | E_Subprogram_Body; |
| end Is_Package_Or_Subprogram; |
| |
| -- Local variables |
| |
| Trans_Idx : constant Int := Find_Enclosing_Transient_Scope; |
| Context : Node_Id; |
| |
| -- Start of processing for Establish_Transient_Scope |
| |
| begin |
| -- Do not create a new transient scope if there is already an enclosing |
| -- transient scope within the innermost enclosing package or subprogram. |
| |
| if Trans_Idx >= 0 then |
| |
| -- If the transient scope was requested for purposes of managing the |
| -- secondary stack, then the existing scope must perform this task, |
| -- unless the node to be wrapped is a return statement of a function |
| -- that requires secondary stack management, because the function's |
| -- result would be reclaimed too early (see Find_Transient_Context). |
| |
| if Manage_Sec_Stack then |
| declare |
| SE : Scope_Stack_Entry renames Scope_Stack.Table (Trans_Idx); |
| |
| begin |
| if Nkind (SE.Node_To_Be_Wrapped) /= N_Simple_Return_Statement |
| or else not |
| Needs_Secondary_Stack |
| (Etype |
| (Return_Applies_To |
| (Return_Statement_Entity (SE.Node_To_Be_Wrapped)))) |
| then |
| Set_Uses_Sec_Stack (SE.Entity); |
| end if; |
| end; |
| end if; |
| |
| return; |
| end if; |
| |
| -- Find the construct that must be serviced by a new transient scope, if |
| -- it exists. |
| |
| Context := Find_Transient_Context (N); |
| |
| if Present (Context) then |
| if Nkind (Context) = N_Assignment_Statement then |
| |
| -- An assignment statement with suppressed controlled semantics |
| -- does not need a transient scope because finalization is not |
| -- desirable at this point. Note that No_Ctrl_Actions is also |
| -- set for non-controlled assignments to suppress dispatching |
| -- _assign. |
| |
| if No_Ctrl_Actions (Context) |
| and then Needs_Finalization (Etype (Name (Context))) |
| then |
| -- When a controlled component is initialized by a function |
| -- call, the result on the secondary stack is always assigned |
| -- to the component. Signal the nearest suitable scope that it |
| -- is safe to manage the secondary stack. |
| |
| if Manage_Sec_Stack and then Within_Init_Proc then |
| Delegate_Sec_Stack_Management; |
| end if; |
| |
| -- Otherwise the assignment is a normal transient context and thus |
| -- requires a transient scope. |
| |
| else |
| Create_Transient_Scope (Context); |
| end if; |
| |
| -- General case |
| |
| else |
| Create_Transient_Scope (Context); |
| end if; |
| end if; |
| end Establish_Transient_Scope; |
| |
| ---------------------------- |
| -- Expand_Cleanup_Actions -- |
| ---------------------------- |
| |
| procedure Expand_Cleanup_Actions (N : Node_Id) is |
| pragma Assert |
| (Nkind (N) in N_Block_Statement |
| | N_Subprogram_Body |
| | N_Task_Body |
| | N_Entry_Body |
| | N_Extended_Return_Statement); |
| |
| 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_Extended_Return_Statement |
| and then Nkind (N) /= N_Entry_Body |
| and then Is_Task_Master (N); |
| Is_Protected_Subp_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; |
| |
| -- We mark the secondary stack if it is used in this construct, and |
| -- we're not returning a function result on the secondary stack, except |
| -- that a build-in-place function that might or might not return on the |
| -- secondary stack always needs a mark. A run-time test is required in |
| -- the case where the build-in-place function has a BIP_Alloc extra |
| -- parameter (see Create_Finalizer). |
| |
| Needs_Sec_Stack_Mark : constant Boolean := |
| (Uses_Sec_Stack (Scop) |
| and then |
| not Sec_Stack_Needed_For_Return (Scop)) |
| or else |
| (Is_Build_In_Place_Function (Scop) |
| and then Needs_BIP_Alloc_Form (Scop)); |
| |
| Needs_Custom_Cleanup : constant Boolean := |
| Nkind (N) = N_Block_Statement |
| and then Present (Cleanup_Actions (N)); |
| |
| Actions_Required : constant Boolean := |
| Requires_Cleanup_Actions (N, True) |
| or else Is_Asynchronous_Call |
| or else Is_Master |
| or else Is_Protected_Subp_Body |
| or else Is_Task_Allocation |
| or else Is_Task_Body |
| or else Needs_Sec_Stack_Mark |
| or else Needs_Custom_Cleanup; |
| |
| Loc : Source_Ptr; |
| Cln : List_Id; |
| |
| -- Start of processing for Expand_Cleanup_Actions |
| |
| begin |
| -- The current construct does not need any form of servicing |
| |
| if not Actions_Required then |
| return; |
| end if; |
| |
| -- If an extended return statement contains something like |
| -- |
| -- X := F (...); |
| -- |
| -- where F is a build-in-place function call returning a controlled |
| -- type, then a temporary object will be implicitly declared as part |
| -- of the statement list, and this will need cleanup. In such cases, |
| -- we transform: |
| -- |
| -- return Result : T := ... do |
| -- <statements> -- possibly with handlers |
| -- end return; |
| -- |
| -- into: |
| -- |
| -- return Result : T := ... do |
| -- declare -- no declarations |
| -- begin |
| -- <statements> -- possibly with handlers |
| -- end; -- no handlers |
| -- end return; |
| -- |
| -- So Expand_Cleanup_Actions will end up being called recursively on the |
| -- block statement. |
| |
| if Nkind (N) = N_Extended_Return_Statement then |
| declare |
| Block : constant Node_Id := |
| Make_Block_Statement (Sloc (N), |
| Declarations => Empty_List, |
| Handled_Statement_Sequence => |
| Handled_Statement_Sequence (N)); |
| begin |
| Set_Handled_Statement_Sequence (N, |
| Make_Handled_Sequence_Of_Statements (Sloc (N), |
| Statements => New_List (Block))); |
| |
| Analyze (Block); |
| end; |
| |
| -- Analysis of the block did all the work |
| |
| return; |
| end if; |
| |
| if Needs_Custom_Cleanup then |
| Cln := Cleanup_Actions (N); |
| else |
| Cln := No_List; |
| end if; |
| |
| if No (Declarations (N)) then |
| Set_Declarations (N, New_List); |
| end if; |
| |
| declare |
| Fin_Id : Entity_Id; |
| Mark : Entity_Id := Empty; |
| |
| 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; |
| |
| -- 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; |
| |
| -- If secondary stack is in use, generate: |
| -- |
| -- Mnn : constant Mark_Id := SS_Mark; |
| |
| if Needs_Sec_Stack_Mark then |
| Set_Uses_Sec_Stack (Scop, False); -- avoid duplicate SS marks |
| Mark := Make_Temporary (Loc, 'M'); |
| |
| declare |
| Mark_Call : constant Node_Id := Build_SS_Mark_Call (Loc, Mark); |
| begin |
| Prepend_To (Declarations (N), Mark_Call); |
| Analyze (Mark_Call); |
| end; |
| end if; |
| |
| -- 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, Cln), |
| Mark_Id => Mark, |
| 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; |
| 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 |
| Id : constant Entity_Id := Defining_Entity (N); |
| Spec_Id : constant Entity_Id := Corresponding_Spec (N); |
| |
| Fin_Id : Entity_Id; |
| |
| begin |
| -- This is done only for non-generic packages |
| |
| if Ekind (Spec_Id) = E_Package then |
| -- Build dispatch tables of library-level tagged types for bodies |
| -- that are not compilation units (see Analyze_Compilation_Unit), |
| -- except for instances because they have no N_Compilation_Unit. |
| |
| if Tagged_Type_Expansion |
| and then Is_Library_Level_Entity (Spec_Id) |
| and then (not Is_Compilation_Unit (Spec_Id) |
| or else Is_Generic_Instance (Spec_Id)) |
| then |
| Build_Static_Dispatch_Tables (N); |
| end if; |
| |
| Push_Scope (Spec_Id); |
| |
| Expand_CUDA_Package (N); |
| |
| Build_Task_Activation_Call (N); |
| |
| -- Verify the run-time semantics of pragma Initial_Condition at the |
| -- end of the body statements. |
| |
| Expand_Pragma_Initial_Condition (Spec_Id, N); |
| |
| -- If this is a library-level package and unnesting is enabled, |
| -- check for the presence of blocks with nested subprograms occurring |
| -- in elaboration code, and generate procedures to encapsulate the |
| -- blocks in case the nested subprograms make up-level references. |
| |
| if Unnest_Subprogram_Mode |
| and then |
| Is_Library_Level_Entity (Current_Scope) |
| then |
| Check_Unnesting_Elaboration_Code (N); |
| Check_Unnesting_In_Decls_Or_Stmts (Declarations (N)); |
| Check_Unnesting_In_Handlers (N); |
| end if; |
| |
| Pop_Scope; |
| end if; |
| |
| Set_Elaboration_Flag (N, Spec_Id); |
| Set_In_Package_Body (Spec_Id, False); |
| |
| -- Set to encode entity names in package body before gigi is called |
| |
| Qualify_Entity_Names (N); |
| |
| if Ekind (Spec_Id) /= E_Generic_Package |
| and then not Delay_Cleanups (Id) |
| then |
| Build_Finalizer |
| (N => N, |
| Clean_Stmts => No_List, |
| Mark_Id => Empty, |
| Defer_Abort => False, |
| Fin_Id => Fin_Id); |
| |
| if Present (Fin_Id) then |
| Set_Finalizer (Defining_Entity (N), Fin_Id); |
| 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); |
| |
| -- Generate RACW subprogram bodies |
| |
| if Has_RACW (Id) then |
| 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; |
| |
| -- Generate task activation call as last step of elaboration |
| |
| if Present (Activation_Chain_Entity (N)) then |
| Build_Task_Activation_Call (N); |
| end if; |
| |
| -- Verify the run-time semantics of pragma Initial_Condition at the |
| -- end of the private declarations when the package lacks a body. |
| |
| Expand_Pragma_Initial_Condition (Id, N); |
| |
| Pop_Scope; |
| end if; |
| |
| -- Build dispatch tables of library-level tagged types for instances |
| -- that are not compilation units (see Analyze_Compilation_Unit). |
| |
| if Tagged_Type_Expansion |
| and then Is_Library_Level_Entity (Id) |
| and then Is_Generic_Instance (Id) |
| and then not Is_Compilation_Unit (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 |
| and then not Delay_Cleanups (Id) |
| then |
| Build_Finalizer |
| (N => N, |
| Clean_Stmts => No_List, |
| Mark_Id => Empty, |
| Defer_Abort => False, |
| Fin_Id => Fin_Id); |
| |
| if Present (Fin_Id) then |
| Set_Finalizer (Id, Fin_Id); |
| end if; |
| end if; |
| |
| -- If this is a library-level package and unnesting is enabled, |
| -- check for the presence of blocks with nested subprograms occurring |
| -- in elaboration code, and generate procedures to encapsulate the |
| -- blocks in case the nested subprograms make up-level references. |
| |
| if Unnest_Subprogram_Mode |
| and then Is_Library_Level_Entity (Current_Scope) |
| then |
| Check_Unnesting_In_Decls_Or_Stmts (Visible_Declarations (Spec)); |
| Check_Unnesting_In_Decls_Or_Stmts (Private_Declarations (Spec)); |
| end if; |
| end Expand_N_Package_Declaration; |
| |
| --------------------------------- |
| -- 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; |
| Clean : Boolean; |
| Manage_SS : Boolean) |
| is |
| Act_Before : constant List_Id := |
| Scope_Stack.Table (Scope_Stack.Last).Actions_To_Be_Wrapped (Before); |
| Act_After : constant List_Id := |
| Scope_Stack.Table (Scope_Stack.Last).Actions_To_Be_Wrapped (After); |
| Act_Cleanup : constant List_Id := |
| Scope_Stack.Table (Scope_Stack.Last).Actions_To_Be_Wrapped (Cleanup); |
| -- Note: We used to use renamings of Scope_Stack.Table (Scope_Stack. |
| -- Last), but this was incorrect as Process_Transients_In_Scope may |
| -- introduce new scopes and cause a reallocation of Scope_Stack.Table. |
| |
| procedure Process_Transients_In_Scope |
| (First_Object : Node_Id; |
| Last_Object : Node_Id; |
| Related_Node : Node_Id); |
| -- Find all transient objects in the list First_Object .. Last_Object |
| -- and generate finalization actions for them. Related_Node denotes the |
| -- node which created all transient objects. |
| |
| --------------------------------- |
| -- Process_Transients_In_Scope -- |
| --------------------------------- |
| |
| procedure Process_Transients_In_Scope |
| (First_Object : Node_Id; |
| Last_Object : Node_Id; |
| Related_Node : Node_Id) |
| is |
| function Is_Subprogram_Call (N : Node_Id) return Traverse_Result; |
| -- Return Abandon if arbitrary node denotes a subprogram call |
| |
| function Has_Subprogram_Call is |
| new Traverse_Func (Is_Subprogram_Call); |
| |
| procedure Process_Transient_In_Scope |
| (Obj_Decl : Node_Id; |
| Insert_Nod : Node_Id; |
| Must_Export : Boolean); |
| -- Generate finalization actions for a single transient object |
| -- denoted by object declaration Obj_Decl. |
| |
| ------------------------ |
| -- Is_Subprogram_Call -- |
| ------------------------ |
| |
| function Is_Subprogram_Call (N : Node_Id) return Traverse_Result is |
| begin |
| -- A regular procedure or function call |
| |
| if Nkind (N) in N_Subprogram_Call then |
| return Abandon; |
| |
| -- Special cases |
| |
| -- Heavy expansion may relocate function calls outside the related |
| -- node. Inspect the original node to detect the initial placement |
| -- of the call. |
| |
| elsif Is_Rewrite_Substitution (N) then |
| return Has_Subprogram_Call (Original_Node (N)); |
| |
| -- Generalized indexing always involves a function call |
| |
| elsif Nkind (N) = N_Indexed_Component |
| and then Present (Generalized_Indexing (N)) |
| then |
| return Abandon; |
| |
| -- Keep searching |
| |
| else |
| return OK; |
| end if; |
| end Is_Subprogram_Call; |
| |
| -------------------------------- |
| -- Process_Transient_In_Scope -- |
| -------------------------------- |
| |
| procedure Process_Transient_In_Scope |
| (Obj_Decl : Node_Id; |
| Insert_Nod : Node_Id; |
| Must_Export : Boolean) |
| is |
| Loc : constant Source_Ptr := Sloc (Obj_Decl); |
| Obj_Id : constant Entity_Id := Defining_Entity (Obj_Decl); |
| |
| Master_Node_Id : Entity_Id; |
| Master_Node_Decl : Node_Id; |
| Obj_Ref : Node_Id; |
| Obj_Typ : Entity_Id; |
| |
| begin |
| -- Ignored Ghost objects do not need any cleanup actions because |
| -- they will not appear in the final tree. |
| |
| if Is_Ignored_Ghost_Entity (Obj_Id) then |
| return; |
| end if; |
| |
| -- If the object needs to be exported to the outer finalizer, |
| -- create the declaration of the Master_Node for the object, |
| -- which will later be picked up by Build_Finalizer. |
| |
| if Must_Export then |
| Master_Node_Id := Make_Temporary (Loc, 'N'); |
| Master_Node_Decl := |
| Make_Master_Node_Declaration (Loc, Master_Node_Id, Obj_Id); |
| Insert_Before_And_Analyze (Obj_Decl, Master_Node_Decl); |
| |
| -- Generate the attachment of the object to the Master_Node |
| |
| Attach_Object_To_Master_Node (Obj_Decl, Master_Node_Id); |
| |
| -- Then add the finalization call for the object |
| |
| Insert_After_And_Analyze (Insert_Nod, |
| Make_Finalize_Call_For_Node (Loc, Master_Node_Id)); |
| |
| -- Otherwise generate a direct finalization call for the object |
| |
| else |
| -- Handle the object type and the reference to the object |
| |
| Obj_Ref := New_Occurrence_Of (Obj_Id, Loc); |
| Obj_Typ := Base_Type (Etype (Obj_Id)); |
| |
| if Is_Access_Type (Obj_Typ) then |
| Obj_Ref := Make_Explicit_Dereference (Loc, Obj_Ref); |
| Obj_Typ := Available_View (Designated_Type (Obj_Typ)); |
| end if; |
| |
| Insert_After_And_Analyze (Insert_Nod, |
| Make_Final_Call (Obj_Ref => Obj_Ref, Typ => Obj_Typ)); |
| end if; |
| |
| -- Mark the transient object to avoid double finalization |
| |
| Set_Is_Finalized_Transient (Obj_Id); |
| end Process_Transient_In_Scope; |
| |
| -- Local variables |
| |
| Insert_Nod : Node_Id; |
| -- Insertion node for the finalization actions |
| |
| Must_Export : Boolean; |
| -- Flag denoting whether the context requires transient object |
| -- export to the outer finalizer. |
| |
| Obj_Decl : Node_Id; |
| |
| -- Start of processing for Process_Transients_In_Scope |
| |
| begin |
| -- The expansion performed by this routine is as follows: |
| |
| -- Ctrl_Trans_Obj_1MN : Master_Node; |
| -- Ctrl_Trans_Obj_1 : ...; |
| -- . . . |
| -- Ctrl_Trans_Obj_NMN : Master_Node; |
| -- Ctrl_Trans_Obj_N : ...; |
| |
| -- Finalize_Object (Ctrl_Trans_Obj_NMN); |
| -- . . . |
| -- Finalize_Object (Ctrl_Trans_Obj_1MN); |
| |
| -- Recognize a scenario where the transient context is an object |
| -- declaration initialized by a build-in-place function call: |
| |
| -- Obj : ... := BIP_Function_Call (Ctrl_Func_Call); |
| |
| -- The rough expansion of the above is: |
| |
| -- Temp : ... := Ctrl_Func_Call; |
| -- Obj : ...; |
| -- Res : ... := BIP_Func_Call (..., Obj, ...); |
| |
| -- The finalization of any transient object must happen after the |
| -- build-in-place function call is executed. |
| |
| if Nkind (N) = N_Object_Declaration |
| and then Present (BIP_Initialization_Call (Defining_Identifier (N))) |
| then |
| Must_Export := True; |
| Insert_Nod := BIP_Initialization_Call (Defining_Identifier (N)); |
| |
| -- Search the context for at least one subprogram call. If found, the |
| -- machinery exports all transient objects to the enclosing finalizer |
| -- due to the possibility of abnormal call termination. |
| |
| else |
| Must_Export := Has_Subprogram_Call (N) = Abandon; |
| Insert_Nod := Last_Object; |
| end if; |
| |
| Insert_List_After_And_Analyze (Insert_Nod, Act_Cleanup); |
| |
| -- Examine all the objects in the list First_Object .. Last_Object |
| -- but skip the node to be wrapped because it is not transient as |
| -- far as this scope is concerned. |
| |
| Obj_Decl := First_Object; |
| while Present (Obj_Decl) loop |
| if Obj_Decl /= Related_Node |
| and then Nkind (Obj_Decl) = N_Object_Declaration |
| and then Analyzed (Obj_Decl) |
| and then Is_Finalizable_Transient (Obj_Decl, N) |
| then |
| Process_Transient_In_Scope (Obj_Decl, Insert_Nod, Must_Export); |
| end if; |
| |
| exit when Obj_Decl = Last_Object; |
| |
| Next (Obj_Decl); |
| end loop; |
| end Process_Transients_In_Scope; |
| |
| -- Local variables |
| |
| Loc : constant Source_Ptr := Sloc (N); |
| Node_To_Wrap : constant Node_Id := Node_To_Be_Wrapped; |
| First_Obj : Node_Id; |
| Last_Obj : Node_Id; |
| Mark_Id : Entity_Id; |
| Marker : Node_Id; |
| Target : Node_Id; |
| |
| -- Start of processing for Insert_Actions_In_Scope_Around |
| |
| begin |
| -- Nothing to do if the scope does not manage the secondary stack or |
| -- does not contain meaningful actions for insertion. |
| |
| if not Manage_SS |
| and then No (Act_Before) |
| and then No (Act_After) |
| and then No (Act_Cleanup) |
| then |
| return; |
| end if; |
| |
| -- 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; |
| |
| -- 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 |
| |
| -- Declarations are inserted before the target |
| |
| if Present (Act_Before) then |
| First_Obj := First (Act_Before); |
| Insert_List_Before (Target, Act_Before); |
| else |
| First_Obj := Target; |
| end if; |
| |
| -- Set a marker on the next statement |
| |
| Marker := Next (Target); |
| |
| -- Finalization calls are inserted after the target |
| |
| if Is_Non_Empty_List (Act_After) then |
| Last_Obj := Last (Act_After); |
| Insert_List_After (Target, Act_After); |
| else |
| Last_Obj := Target; |
| end if; |
| |
| -- Mark and release the secondary stack when the context warrants it |
| |
| if Manage_SS then |
| Mark_Id := Make_Temporary (Loc, 'M'); |
| |
| -- Generate: |
| -- Mnn : constant Mark_Id := SS_Mark; |
| |
| Insert_Before_And_Analyze |
| (First_Obj, Build_SS_Mark_Call (Loc, Mark_Id)); |
| |
| -- Generate: |
| -- SS_Release (Mnn); |
| |
| Insert_After_And_Analyze |
| (Last_Obj, Build_SS_Release_Call (Loc, Mark_Id)); |
| end if; |
| |
| -- If we are handling cleanups, check for transient objects associated |
| -- with Target and generate the required finalization actions for them. |
| |
| if Clean then |
| Process_Transients_In_Scope |
| (First_Object => First_Obj, |
| Last_Object => Last_Obj, |
| Related_Node => Target); |
| end if; |
| |
| -- If the target is the declaration of an object, park the generated |
| -- statements if need be. |
| |
| if Nkind (Target) = N_Object_Declaration |
| and then Next (Target) /= Marker |
| and then Needs_Initialization_Statements (Target) |
| then |
| Move_To_Initialization_Statements (Target, Marker); |
| end if; |
| |
| -- Reset the action lists |
| |
| Scope_Stack.Table |
| (Scope_Stack.Last).Actions_To_Be_Wrapped (Before) := No_List; |
| Scope_Stack.Table |
| (Scope_Stack.Last).Actions_To_Be_Wrapped (After) := No_List; |
| |
| if Clean then |
| Scope_Stack.Table |
| (Scope_Stack.Last).Actions_To_Be_Wrapped (Cleanup) := No_List; |
| end if; |
| 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_Address_For_Finalize -- |
| ------------------------------- |
| |
| function Make_Address_For_Finalize |
| (Loc : Source_Ptr; |
| Obj_Ref : Node_Id; |
| Obj_Typ : Entity_Id) return Node_Id |
| is |
| Utyp : constant Entity_Id := Underlying_Type (Obj_Typ); |
| |
| Obj_Addr : Node_Id; |
| |
| begin |
| Obj_Addr := |
| Make_Attribute_Reference (Loc, |
| Prefix => Obj_Ref, |
| Attribute_Name => Name_Address); |
| |
| -- If the type of a constrained array has an unconstrained first |
| -- subtype, its Finalize_Address primitive expects the address of |
| -- an object with a dope vector (see Make_Finalize_Address_Stmts). |
| -- This is achieved by setting Is_Constr_Array_Subt_With_Bounds, |
| -- but the address of the object is still that of its elements, |
| -- so we need to shift it back to skip the dope vector. |
| |
| if Is_Array_Type (Utyp) |
| and then not Is_Constrained (First_Subtype (Utyp)) |
| then |
| Obj_Addr := |
| Shift_Address_For_Descriptor |
| (Obj_Addr, First_Subtype (Utyp), Name_Op_Subtract); |
| end if; |
| |
| return Obj_Addr; |
| end Make_Address_For_Finalize; |
| |
| ----------------------- |
| -- Make_Adjust_Call -- |
| ----------------------- |
| |
| function Make_Adjust_Call |
| (Obj_Ref : Node_Id; |
| Typ : Entity_Id; |
| Skip_Self : Boolean := False) return Node_Id |
| is |
| Loc : constant Source_Ptr := Sloc (Obj_Ref); |
| Adj_Id : Entity_Id := Empty; |
| Ref : Node_Id; |
| Utyp : Entity_Id; |
| |
| begin |
| Ref := Obj_Ref; |
| |
| -- 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 untagged derivation of private views |
| |
| if Present (Utyp) |
| and then Is_Untagged_Derivation (Typ) |
| and then Is_Implicit_Full_View (Utyp) |
| 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 Present (Utyp) and then Utyp /= Base_Type (Utyp) then |
| pragma Assert (Is_Private_Type (Typ)); |
| |
| Utyp := Base_Type (Utyp); |
| Ref := Unchecked_Convert_To (Utyp, Ref); |
| end if; |
| |
| -- The underlying type may not be present due to a missing full view. In |
| -- this case freezing did not take place and there is no [Deep_]Adjust |
| -- primitive to call. |
| |
| if No (Utyp) then |
| return Empty; |
| |
| elsif Skip_Self then |
| if Has_Controlled_Component (Utyp) then |
| if Is_Tagged_Type (Utyp) then |
| Adj_Id := Find_Optional_Prim_Op (Utyp, TSS_Deep_Adjust); |
| else |
| Adj_Id := TSS (Utyp, TSS_Deep_Adjust); |
| end if; |
| 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_Optional_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 |
| Adj_Id := Find_Controlled_Prim_Op (Utyp, Name_Adjust); |
| |
| -- Tagged types |
| |
| elsif Is_Tagged_Type (Utyp) then |
| Adj_Id := Find_Optional_Prim_Op (Utyp, TSS_Deep_Adjust); |
| |
| else |
| raise Program_Error; |
| end if; |
| |
| if Present (Adj_Id) then |
| -- 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, Typ); |
| end if; |
| |
| return |
| Make_Call (Loc, |
| Proc_Id => Adj_Id, |
| Param => Ref, |
| Skip_Self => Skip_Self); |
| else |
| return Empty; |
| end if; |
| end Make_Adjust_Call; |
| |
| --------------- |
| -- Make_Call -- |
| --------------- |
| |
| function Make_Call |
| (Loc : Source_Ptr; |
| Proc_Id : Entity_Id; |
| Param : Node_Id; |
| Skip_Self : Boolean := False) return Node_Id |
| is |
| Params : constant List_Id := New_List (Param); |
| |
| begin |
| -- Do not apply the controlled action to the object itself by signaling |
| -- the related routine to avoid self. |
| |
| if Skip_Self then |
| Append_To (Params, New_Occurrence_Of (Standard_False, Loc)); |
| end if; |
| |
| return |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (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_Occurrence; |
| -- 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); |
| |
| procedure Build_Indexes; |
| -- Generate the indexes used in the dimension loops |
| |
| ------------------- |
| -- Build_Indexes -- |
| ------------------- |
| |
| procedure Build_Indexes 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_Indexes; |
| |
| -- Local variables |
| |
| Final_Decls : List_Id := No_List; |
| Final_Data : Finalization_Exception_Data; |
| Block : Node_Id; |
| Call : Node_Id; |
| Comp_Ref : Node_Id; |
| Core_Loop : Node_Id; |
| Dim : Int; |
| J : Entity_Id; |
| Loop_Id : Entity_Id; |
| Stmts : List_Id; |
| |
| -- Start of processing for Build_Adjust_Or_Finalize_Statements |
| |
| begin |
| Final_Decls := New_List; |
| |
| Build_Indexes; |
| Build_Object_Declarations (Final_Data, Final_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; |
| |
| if Present (Call) then |
| |
| -- Generate the block which houses the adjust or finalize call: |
| |
| -- begin |
| -- <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 (Final_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, 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 |
| -- Raise_From_Controlled_Operation (E); |
| -- end if; |
| -- end; |
| |
| Stmts := New_List (Core_Loop); |
| |
| if Exceptions_OK then |
| Append_To (Stmts, Build_Raise_Statement (Final_Data)); |
| end if; |
| |
| Block := |
| Make_Block_Statement (Loc, |
| Declarations => Final_Decls, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => Stmts)); |
| |
| -- Otherwise previous errors or a missing full view may prevent the |
| -- proper freezing of the component type. If this is the case, there |
| -- is no [Deep_]Adjust or [Deep_]Finalize primitive to call. |
| |
| else |
| Block := Make_Null_Statement (Loc); |
| end if; |
| |
| return New_List (Block); |
| 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); |
| |
| function Build_Assignment (Counter_Id : Entity_Id) return Node_Id; |
| -- Generate the following assignment: |
| -- Counter := V'Length (1) * |
| -- ... |
| -- V'Length (N) - Counter; |
| -- |
| -- Counter_Id denotes the entity of the counter. |
| |
| function Build_Finalization_Call return Node_Id; |
| -- Generate a deep finalization call for an array element |
| |
| procedure Build_Indexes; |
| -- Generate the initialization and finalization indexes used in the |
| -- dimension loops. |
| |
| function Build_Initialization_Call return Node_Id; |
| -- Generate a deep initialization call for an array element |
| |
| ---------------------- |
| -- Build_Assignment -- |
| ---------------------- |
| |
| function Build_Assignment (Counter_Id : Entity_Id) 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_Occurrence_Of (Counter_Id, Loc), |
| Expression => |
| Make_Op_Subtract (Loc, |
| Left_Opnd => Expr, |
| Right_Opnd => New_Occurrence_Of (Counter_Id, Loc))); |
| end Build_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_Indexes -- |
| ------------------- |
| |
| procedure Build_Indexes 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_Indexes; |
| |
| ------------------------------- |
| -- 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; |
| |
| -- Local variables |
| |
| Counter_Id : Entity_Id; |
| Dim : Int; |
| F : Node_Id; |
| Fin_Stmt : Node_Id; |
| Final_Block : Node_Id; |
| Final_Data : Finalization_Exception_Data; |
| Final_Decls : List_Id := No_List; |
| Final_Loop : Node_Id; |
| Init_Block : Node_Id; |
| Init_Call : Node_Id; |
| Init_Loop : Node_Id; |
| J : Node_Id; |
| Loop_Id : Node_Id; |
| Stmts : List_Id; |
| |
| -- Start of processing for Build_Initialize_Statements |
| |
| begin |
| Counter_Id := Make_Temporary (Loc, 'C'); |
| Final_Decls := New_List; |
| |
| Build_Indexes; |
| Build_Object_Declarations (Final_Data, Final_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 |
| -- 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; |
| |
| Fin_Stmt := Build_Finalization_Call; |
| |
| if Present (Fin_Stmt) then |
| 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 (Final_Data)))); |
| 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_Occurrence_Of (Counter_Id, Loc), |
| Right_Opnd => Make_Integer_Literal (Loc, 0)), |
| |
| Then_Statements => New_List ( |
| Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of (Counter_Id, Loc), |
| Expression => |
| Make_Op_Subtract (Loc, |
| Left_Opnd => New_Occurrence_Of (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 |
| -- Raise_From_Controlled_Operation (E); |
| -- end if; |
| |
| -- raise; |
| -- end; |
| |
| Stmts := New_List (Build_Assignment (Counter_Id), Final_Loop); |
| |
| if Exceptions_OK then |
| Append_To (Stmts, Build_Raise_Statement (Final_Data)); |
| Append_To (Stmts, Make_Raise_Statement (Loc)); |
| end if; |
| |
| Final_Block := |
| Make_Block_Statement (Loc, |
| Declarations => Final_Decls, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => Stmts)); |
| |
| -- Otherwise previous errors or a missing full view may prevent the |
| -- proper freezing of the component type. If this is the case, there |
| -- is no [Deep_]Finalize primitive to call. |
| |
| else |
| Final_Block := Make_Null_Statement (Loc); |
| end if; |
| |
| -- 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_Call := Build_Initialization_Call; |
| |
| -- Only create finalization block if there is a nontrivial call |
| -- to initialization or a Default_Initial_Condition check to be |
| -- performed. |
| |
| if (Present (Init_Call) |
| and then Nkind (Init_Call) /= N_Null_Statement) |
| or else |
| (Has_DIC (Comp_Typ) |
| and then not GNATprove_Mode |
| and then Present (DIC_Procedure (Comp_Typ)) |
| and then not Has_Null_Body (DIC_Procedure (Comp_Typ))) |
| then |
| declare |
| Init_Stmts : constant List_Id := New_List; |
| |
| begin |
| if Present (Init_Call) then |
| Append_To (Init_Stmts, Init_Call); |
| end if; |
| |
| if Has_DIC (Comp_Typ) |
| and then Present (DIC_Procedure (Comp_Typ)) |
| then |
| Append_To |
| (Init_Stmts, |
| Build_DIC_Call (Loc, |
| Make_Indexed_Component (Loc, |
| Prefix => Make_Identifier (Loc, Name_V), |
| Expressions => New_References_To (Index_List, Loc)), |
| Comp_Typ)); |
| end if; |
| |
| Init_Loop := |
| Make_Block_Statement (Loc, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => Init_Stmts, |
| Exception_Handlers => New_List ( |
| Make_Exception_Handler (Loc, |
| Exception_Choices => New_List ( |
| Make_Others_Choice (Loc)), |
| Statements => New_List (Final_Block))))); |
| end; |
| |
| Append_To (Statements (Handled_Statement_Sequence (Init_Loop)), |
| Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of (Counter_Id, Loc), |
| Expression => |
| Make_Op_Add (Loc, |
| Left_Opnd => New_Occurrence_Of (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; |
| |
| Init_Block := |
| Make_Block_Statement (Loc, |
| Declarations => New_List ( |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Counter_Id, |
| Object_Definition => |
| New_Occurrence_Of (Standard_Integer, Loc), |
| Expression => Make_Integer_Literal (Loc, 0))), |
| |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List (Init_Loop))); |
| |
| if Debug_Generated_Code then |
| Set_Debug_Info_Needed (Counter_Id); |
| end if; |
| |
| -- Otherwise previous errors or a missing full view may prevent the |
| -- proper freezing of the component type. If this is the case, there |
| -- is no [Deep_]Initialize primitive to call. |
| |
| else |
| Init_Block := Make_Null_Statement (Loc); |
| end if; |
| |
| return New_List (Init_Block); |
| 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_Occurrence_Of (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_Occurrence_Of (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_Occurrence_Of (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_Occurrence_Of (Standard_Boolean, Loc), |
| Expression => |
| New_Occurrence_Of (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))); |
| |
| -- If there are no calls to component initialization, indicate that |
| -- the procedure is trivial, so prevent calls to it. |
| |
| if Is_Empty_List (Stmts) |
| or else Nkind (First (Stmts)) = N_Null_Statement |
| then |
| Set_Is_Trivial_Subprogram (Proc_Id); |
| end if; |
| |
| 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 |
| Loc : constant Source_Ptr := Sloc (Typ); |
| |
| 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_Occurrence (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_Occurrence; |
| -- Raised : Boolean := False; |
| -- |
| -- begin |
| -- begin |
| -- <Destructor_Proc> (V); -- If applicable |
| -- exception |
| -- when others => |
| -- if not Raised then |
| -- Raised := True; |
| -- Save_Occurrence (E, Get_Current_Excep.all.all); |
| -- end if; |
| -- end; |
| -- |
| -- if F then |
| -- begin |
| -- Finalize (V); -- If applicable |
| -- exception |
| -- when others => |
| -- if not Raised then |
| -- Raised := True; |
| -- Save_Occurrence (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_Occurrence (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_Occurrence (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 |
| -- or |
| -- Deep_Finalize (Parent_Type (V), False); -- Untagged case |
| -- 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 Nat; |
| 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 |
| Typ_Def : constant Node_Id := Type_Definition (Parent (Typ)); |
| |
| Finalizer_Data : Finalization_Exception_Data; |
| |
| 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; |
| |
| 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_Call : Node_Id; |
| |
| begin |
| -- begin |
| -- [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_Call := |
| Make_Adjust_Call ( |
| Obj_Ref => |
| Make_Selected_Component (Loc, |
| Prefix => Make_Identifier (Loc, Name_V), |
| Selector_Name => Make_Identifier (Loc, Chars (Id))), |
| Typ => Typ); |
| |
| -- Guard against a missing [Deep_]Adjust when the component |
| -- type was not properly frozen. |
| |
| if Present (Adj_Call) then |
| if Exceptions_OK then |
| Adj_Call := |
| Make_Block_Statement (Loc, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List (Adj_Call), |
| Exception_Handlers => New_List ( |
| Build_Exception_Handler (Finalizer_Data)))); |
| end if; |
| |
| Append_To (Stmts, Adj_Call); |
| end if; |
| end Process_Component_For_Adjust; |
| |
| -- Local variables |
| |
| Decl : Node_Id; |
| Decl_Id : Entity_Id; |
| Decl_Typ : Entity_Id; |
| Has_POC : Boolean; |
| Num_Comps : Nat; |
| Var_Case : Node_Id; |
| |
| -- 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; |
| |
| -- Local variables |
| |
| Bod_Stmts : List_Id := No_List; |
| Finalizer_Decls : List_Id := No_List; |
| Rec_Def : Node_Id; |
| |
| -- 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 invoked 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, |
| Skip_Self => True); |
| |
| -- Generate: |
| -- begin |
| -- 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_Controlled_Prim_Op (Typ, Name_Adjust); |
| |
| -- Generate: |
| -- if F then |
| -- begin |
| -- 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_Occurrence_Of (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_New_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_Occurrence; |
| -- 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 |
| Typ_Def : constant Node_Id := Type_Definition (Parent (Typ)); |
| |
| Counter : Nat := 0; |
| Finalizer_Data : Finalization_Exception_Data; |
| Last_POC_Call : Node_Id := Empty; |
| |
| function Process_Component_List_For_Finalize |
| (Comps : Node_Id; |
| In_Variant_Part : Boolean := False) 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. In_Variant_Part indicates whether this is a recursive |
| -- call. |
| |
| ----------------------------------------- |
| -- Process_Component_List_For_Finalize -- |
| ----------------------------------------- |
| |
| function Process_Component_List_For_Finalize |
| (Comps : Node_Id; |
| In_Variant_Part : Boolean := False) return List_Id |
| is |
| procedure Process_Component_For_Finalize |
| (Decl : Node_Id; |
| Alts : List_Id; |
| Decls : List_Id; |
| Stmts : List_Id; |
| Num_Comps : in out Nat); |
| -- 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. Num_Comps |
| -- denotes the current number of components needing finalization. |
| |
| ------------------------------------ |
| -- Process_Component_For_Finalize -- |
| ------------------------------------ |
| |
| procedure Process_Component_For_Finalize |
| (Decl : Node_Id; |
| Alts : List_Id; |
| Decls : List_Id; |
| Stmts : List_Id; |
| Num_Comps : in out Nat) |
| is |
| Id : constant Entity_Id := Defining_Identifier (Decl); |
| Typ : constant Entity_Id := Etype (Id); |
| Fin_Call : 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_Occurrence_Of (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_Call := |
| Make_Final_Call |
| (Obj_Ref => |
| Make_Selected_Component (Loc, |
| Prefix => Make_Identifier (Loc, Name_V), |
| Selector_Name => Make_Identifier (Loc, Chars (Id))), |
| Typ => Typ); |
| |
| -- Guard against a missing [Deep_]Finalize when the component |
| -- type was not properly frozen. |
| |
| if Present (Fin_Call) then |
| if Exceptions_OK then |
| Fin_Call := |
| Make_Block_Statement (Loc, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List (Fin_Call), |
| Exception_Handlers => New_List ( |
| Build_Exception_Handler (Finalizer_Data)))); |
| end if; |
| |
| Append_To (Stmts, Fin_Call); |
| end if; |
| end Process_Component_For_Finalize; |
| |
| -- Local variables |
| |
| Alts : List_Id; |
| Counter_Id : Entity_Id := Empty; |
| 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 : Nat; |
| Stmts : List_Id; |
| Var_Case : Node_Id; |
| |
| -- 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), |
| In_Variant_Part => True))); |
| |
| 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, Num_Comps); |
| end if; |
| |
| Prev_Non_Pragma (Decl); |
| end loop; |
| end if; |
| |
| if not In_Variant_Part then |
| Last_POC_Call := Last (Stmts); |
| -- In the case of a type extension, the deep-finalize call |
| -- for the _Parent component will be inserted here. |
| 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, Num_Comps); |
| 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_Occurrence_Of (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; |
| |
| -- Local variables |
| |
| Bod_Stmts : List_Id := No_List; |
| Finalizer_Decls : List_Id := No_List; |
| Rec_Def : Node_Id; |
| |
| -- 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 invoked for field _parent, a value of False |
| -- is provided for the flag: |
| |
| -- Deep_Finalize (Obj._parent, False); |
| |
| if Is_Derived_Type (Typ) then |
| declare |
| Tagd : constant Boolean := Is_Tagged_Type (Typ); |
| Par_Typ : constant Entity_Id := |
| (if Tagd |
| then Parent_Field_Type (Typ) |
| else Etype (Base_Type (Typ))); |
| Call : Node_Id; |
| Fin_Stmt : Node_Id; |
| |
| begin |
| if Needs_Finalization (Par_Typ) then |
| Call := |
| Make_Final_Call |
| (Obj_Ref => |
| (if Tagd |
| then |
| Make_Selected_Component |
| (Loc, |
| Prefix => Make_Identifier (Loc, Name_V), |
| Selector_Name => |
| Make_Identifier (Loc, Name_uParent)) |
| else |
| Convert_To |
| (Par_Typ, Make_Identifier (Loc, Name_V))), |
| Typ => Par_Typ, |
| Skip_Self => True); |
| |
| -- Generate: |
| -- begin |
| -- 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; |
| -- |
| -- in the tagged case. In the untagged case, which arises |
| -- with the Destructor aspect, generate: |
| -- |
| -- begin |
| -- Deep_Finalize (Parent_Type (V), 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; |
| |
| -- The intended component finalization order is |
| -- 1) POC components of extension |
| -- 2) _Parent component |
| -- 3) non-POC components of extension. |
| -- |
| -- With this "finalize the parent part in the middle" |
| -- ordering, we can avoid the need for making two |
| -- calls to the parent's subprogram in the way that |
| -- is necessary for Init_Procs. This does have the |
| -- peculiar (but legal) consequence that the parent's |
| -- non-POC components are finalized before the |
| -- non-POC extension components. This violates the |
| -- usual "finalize in reverse declaration order" |
| -- principle, but that's ok (see RM 7.6.1(9)). |
| -- |
| -- Last_POC_Call should be non-empty if the extension |
| -- has at least one POC. Interactions with variant |
| -- parts are incorrectly ignored. |
| |
| if Present (Last_POC_Call) then |
| Insert_After (Last_POC_Call, Fin_Stmt); |
| else |
| -- At this point, we could look for the common case |
| -- where there are no POC components anywhere in |
| -- sight (inherited or not) and, in that common case, |
| -- call Append_To instead of Prepend_To. That would |
| -- result in finalizing the parent part after, rather |
| -- than before, the extension components. That might |
| -- be more intuitive (as discussed in preceding |
| -- comment), but it is not required. |
| Prepend_New_To (Bod_Stmts, Fin_Stmt); |
| end if; |
| 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_Controlled_Prim_Op (Typ, Name_Finalize); |
| |
| -- Generate: |
| -- if F then |
| -- 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_Occurrence_Of (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_New_To (Bod_Stmts, |
| Make_If_Statement (Loc, |
| Condition => Make_Identifier (Loc, Name_F), |
| Then_Statements => New_List (Fin_Stmt))); |
| end if; |
| end; |
| |
| declare |
| ASN : constant Opt_N_Aspect_Specification_Id := |
| Get_Rep_Item (Typ, Name_Destructor, False); |
| |
| Stmt : Node_Id; |
| Proc : Entity_Id; |
| begin |
| if Present (ASN) then |
| -- Generate: |
| -- begin |
| -- <Destructor_Proc> (V); |
| |
| -- exception |
| -- when others => |
| -- if not Raised then |
| -- Raised := True; |
| -- Save_Occurrence (E, |
| -- Get_Current_Excep.all.all); |
| -- end if; |
| -- end; |
| |
| Proc := Entity (Expression (ASN)); |
| Stmt := |
| Make_Procedure_Call_Statement |
| (Loc, |
| Name => New_Occurrence_Of (Proc, Loc), |
| Parameter_Associations => |
| New_List (Make_Identifier (Loc, Name_V))); |
| if Exceptions_OK then |
| Stmt := |
| Make_Block_Statement |
| (Loc, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements |
| (Loc, |
| Statements => New_List (Stmt), |
| Exception_Handlers => |
| New_List |
| (Build_Exception_Handler |
| (Finalizer_Data)))); |
| end if; |
| |
| Prepend_New_To (Bod_Stmts, 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_Occurrence; |
| -- 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 Nat; |
| 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 => |
| if Is_Controlled (Typ) then |
| declare |
| Intlz : constant Entity_Id := |
| Find_Controlled_Prim_Op (Typ, Name_Initialize); |
| begin |
| if Present (Intlz) then |
| return |
| New_List |
| (Make_Procedure_Call_Statement |
| (Loc, |
| Name => |
| New_Occurrence_Of (Intlz, Loc), |
| Parameter_Associations => |
| New_List (Make_Identifier (Loc, Name_V)))); |
| else |
| return Empty_List; |
| end if; |
| end; |
| else |
| return Empty_List; |
| end if; |
| end case; |
| end Make_Deep_Record_Body; |
| |
| ---------------------- |
| -- Make_Final_Call -- |
| ---------------------- |
| |
| function Make_Final_Call |
| (Obj_Ref : Node_Id; |
| Typ : Entity_Id; |
| Skip_Self : Boolean := False) return Node_Id |
| is |
| Loc : constant Source_Ptr := Sloc (Obj_Ref); |
| Atyp : Entity_Id; |
| Prot_Typ : Entity_Id := Empty; |
| Fin_Id : Entity_Id := Empty; |
| Ref : Node_Id; |
| Utyp : Entity_Id; |
| |
| begin |
| Ref := Obj_Ref; |
| |
| -- Recover the proper type which contains [Deep_]Finalize |
| |
| if Is_Class_Wide_Type (Typ) then |
| Utyp := Root_Type (Typ); |
| Atyp := Utyp; |
| |
| elsif Is_Concurrent_Type (Typ) then |
| Utyp := Corresponding_Record_Type (Typ); |
| Atyp := Empty; |
| Ref := Convert_Concurrent (Ref, Typ); |
| |
| elsif Is_Private_Type (Typ) |
| and then Present (Underlying_Type (Typ)) |
| and then Is_Concurrent_Type (Underlying_Type (Typ)) |
| then |
| Utyp := Corresponding_Record_Type (Underlying_Type (Typ)); |
| Atyp := Typ; |
| Ref := Convert_Concurrent (Ref, Underlying_Type (Typ)); |
| |
| else |
| Utyp := Typ; |
| Atyp := Typ; |
| end if; |
| |
| Utyp := Underlying_Type (Base_Type (Utyp)); |
| Set_Assignment_OK (Ref); |
| |
| -- Deal with untagged 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))); |
| elsif Is_Implicit_Full_View (Utyp) then |
| 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 Present (Utyp) |
| and then 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 Present (Utyp) and then 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; |
| |
| -- Detect if Typ is a protected type or an expanded protected type and |
| -- store the relevant type within Prot_Typ for later processing. |
| |
| if Is_Protected_Type (Typ) then |
| Prot_Typ := Typ; |
| |
| elsif Ekind (Typ) = E_Record_Type |
| and then Present (Corresponding_Concurrent_Type (Typ)) |
| and then Is_Protected_Type (Corresponding_Concurrent_Type (Typ)) |
| then |
| Prot_Typ := Corresponding_Concurrent_Type (Typ); |
| end if; |
| |
| -- The underlying type may not be present due to a missing full view. In |
| -- this case freezing did not take place and there is no [Deep_]Finalize |
| -- primitive to call. |
| |
| if No (Utyp) then |
| return Empty; |
| |
| elsif Skip_Self then |
| if Has_Controlled_Component (Utyp) or else Has_Destructor (Utyp) then |
| if Is_Tagged_Type (Utyp) then |
| Fin_Id := Find_Optional_Prim_Op (Utyp, TSS_Deep_Finalize); |
| else |
| Fin_Id := TSS (Utyp, TSS_Deep_Finalize); |
| end if; |
| 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) |
| or else Has_Destructor (Utyp) |
| then |
| if Is_Tagged_Type (Utyp) then |
| Fin_Id := Find_Optional_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 |
| Fin_Id := Find_Controlled_Prim_Op (Utyp, Name_Finalize); |
| |
| -- Tagged types |
| |
| elsif Is_Tagged_Type (Utyp) then |
| Fin_Id := Find_Optional_Prim_Op (Utyp, TSS_Deep_Finalize); |
| |
| -- Protected types: these also require finalization even though they |
| -- are not marked controlled explicitly. |
| |
| elsif Present (Prot_Typ) then |
| -- Protected objects do not need to be finalized on restricted |
| -- runtimes. |
| |
| if Restricted_Profile then |
| return Empty; |
| |
| -- ??? Only handle the simple case for now. Will not support a record |
| -- or array containing protected objects. |
| |
| elsif Is_Simple_Protected_Type (Prot_Typ) then |
| Fin_Id := RTE (RE_Finalize_Protection); |
| else |
| raise Program_Error; |
| end if; |
| |
| 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, Typ); |
| end if; |
| |
| return |
| Make_Call (Loc, |
| Proc_Id => Fin_Id, |
| Param => Ref, |
| Skip_Self => Skip_Self); |
| else |
| pragma Assert (Serious_Errors_Detected > 0 |
| or else not Has_Controlled_Component (Utyp)); |
| 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 does not need finalization or already has |
| -- a TSS entry for Finalize_Address. Skip class-wide subtypes that do |
| -- not come from source, as they are usually generated for completeness |
| -- and need no Finalize_Address. |
| |
| elsif not Needs_Finalization (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; |
| |
| -- Do not generate Finalize_Address routine for CodePeer |
| |
| if CodePeer_Mode 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_Occurrence_Of (RTE (RE_Address), Loc)))), |
| |
| Declarations => No_List, |
| |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => Stmts))); |
| |
| -- If the type has relaxed semantics for finalization, the indirect |
| -- calls to Finalize_Address may be turned into direct ones and, in |
| -- this case, inlining them is generally profitable. |
| |
| if Has_Relaxed_Finalization (Typ) then |
| Set_Is_Inlined (Proc_Id); |
| end if; |
| |
| 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); |
| |
| Decls : List_Id; |
| Desig_Typ : Entity_Id; |
| Fin_Block : Node_Id; |
| Fin_Call : Node_Id; |
| Obj_Expr : Node_Id; |
| Ptr_Typ : Entity_Id; |
| |
| begin |
| -- Array types: picking the (unconstrained) base type as designated type |
| -- requires allocating the bounds alongside the data, so we only do this |
| -- when the first subtype itself was declared as unconstrained. |
| |
| if Is_Array_Type (Typ) then |
| if Is_Constrained (First_Subtype (Typ)) then |
| Desig_Typ := First_Subtype (Typ); |
| else |
| Desig_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; |
| Parent_Utyp : 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; |
| |
| Parent_Utyp := Underlying_Type (Parent_Typ); |
| |
| -- Handle views created for a synchronized private extension with |
| -- known, non-defaulted discriminants. In that case, parent_typ |
| -- will be the private extension, as it is the first "non |
| -- -constrained" type in the parent chain. Unfortunately, the |
| -- underlying type, being a protected or task type, is not the |
| -- "real" type needing finalization. Rather, the "corresponding |
| -- record type" should be the designated type here. In fact, TSS |
| -- finalizer generation is specifically skipped for the nominal |
| -- class-wide type of (the full view of) a concurrent type (see |
| -- exp_ch7.Expand_Freeze_Class_Wide_Type). If we don't designate |
| -- the underlying record (Tprot_typeVC), we will end up trying to |
| -- dispatch to prot_typeVDF from an incorrectly designated |
| -- Tprot_typeC, which is, of course, not actually a member of |
| -- prot_typeV'Class, and thus incompatible. |
| |
| if Ekind (Parent_Utyp) in Concurrent_Kind |
| and then Present (Corresponding_Record_Type (Parent_Utyp)) |
| then |
| Parent_Utyp := Corresponding_Record_Type (Parent_Utyp); |
| end if; |
| |
| Desig_Typ := Class_Wide_Type (Parent_Utyp); |
| end; |
| |
| -- General case |
| |
| else |
| Desig_Typ := Typ; |
| end if; |
| |
| -- Generate: |
| -- type Ptr_Typ is access all Typ; |
| -- for Ptr_Typ'Storage_Size use 0; |
| |
| Ptr_Typ := Make_Temporary (Loc, 'P'); |
| |
| 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_Occurrence_Of (Desig_Typ, Loc))), |
| |
| Make_Attribute_Definition_Clause (Loc, |
| Name => New_Occurrence_Of (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 (Desig_Typ) then |
| Obj_Expr := |
| Shift_Address_For_Descriptor (Obj_Expr, Desig_Typ, Name_Op_Add); |
| |
| -- Ensure that Ptr_Typ is a thin pointer; generate: |
| -- for Ptr_Typ'Size use System.Address'Size; |
| |
| Append_To (Decls, |
| Make_Attribute_Definition_Clause (Loc, |
| Name => New_Occurrence_Of (Ptr_Typ, Loc), |
| Chars => Name_Size, |
| Expression => Make_Integer_Literal (Loc, System_Address_Size))); |
| end if; |
| |
| Fin_Call := |
| Make_Final_Call ( |
| Obj_Ref => |
| Make_Explicit_Dereference (Loc, |
| Prefix => Unchecked_Convert_To (Ptr_Typ, Obj_Expr)), |
| Typ => Desig_Typ); |
| |
| if Present (Fin_Call) then |
| Fin_Block := |
| Make_Block_Statement (Loc, |
| Declarations => Decls, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List (Fin_Call))); |
| |
| -- Otherwise previous errors or a missing full view may prevent the |
| -- proper freezing of the designated type. If this is the case, there |
| -- is no [Deep_]Finalize primitive to call. |
| |
| else |
| Fin_Block := Make_Null_Statement (Loc); |
| end if; |
| |
| return New_List (Fin_Block); |
| end Make_Finalize_Address_Stmts; |
| |
| --------------------------------- |
| -- Make_Finalize_Call_For_Node -- |
| --------------------------------- |
| |
| function Make_Finalize_Call_For_Node |
| (Loc : Source_Ptr; |
| Node : Entity_Id) return Node_Id |
| is |
| Fin_Id : constant Entity_Id := Finalize_Address_For_Node (Node); |
| |
| Fin_Call : Node_Id; |
| Fin_Ref : Node_Id; |
| |
| begin |
| -- Finalize_Address is not generated in CodePeer mode because the |
| -- body contains address arithmetic. So we don't want to generate |
| -- the call in this case. |
| |
| if CodePeer_Mode then |
| return Make_Null_Statement (Loc); |
| end if; |
| |
| -- The Finalize_Address primitive may be missing when the Master_Node |
| -- is written down in the source code for testing purposes. |
| |
| if Present (Fin_Id) then |
| Fin_Ref := |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Fin_Id, Loc), |
| Attribute_Name => Name_Unrestricted_Access); |
| |
| else |
| Fin_Ref := |
| Make_Selected_Component (Loc, |
| Prefix => New_Occurrence_Of (Node, Loc), |
| Selector_Name => Make_Identifier (Loc, Name_Finalize_Address)); |
| end if; |
| |
| Fin_Call := |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (RTE (RE_Finalize_Object), Loc), |
| Parameter_Associations => New_List ( |
| New_Occurrence_Of (Node, Loc), |
| Fin_Ref)); |
| |
| -- Present Finalize_Address procedure to the back end so that it can |
| -- inline the call to the procedure made by Finalize_Object. |
| |
| if Present (Fin_Id) and then Is_Inlined (Fin_Id) then |
| Add_Inlined_Body (Fin_Id, Fin_Call); |
| end if; |
| |
| return Fin_Call; |
| end Make_Finalize_Call_For_Node; |
| |
| ------------------------------------- |
| -- 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: 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_Occurrence_Of |
| (RTE (RE_Raise_From_Controlled_Operation), Loc), |
| Parameter_Associations => New_List ( |
| New_Occurrence_Of (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 |
| Ref := Obj_Ref; |
| |
| -- 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 (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 (Ref, Underlying_Type (Typ)); |
| |
| else |
| Is_Conc := False; |
| Utyp := Typ; |
| end if; |
| |
| Utyp := Underlying_Type (Base_Type (Utyp)); |
| Set_Assignment_OK (Ref); |
| |
| -- Deal with untagged derivation of private views |
| |
| if Is_Untagged_Derivation (Typ) |
| and then not Is_Conc |
| and then Is_Implicit_Full_View (Utyp) |
| 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 Present (Utyp) and then Utyp /= Base_Type (Utyp) then |
| pragma Assert (Is_Private_Type (Typ)); |
| Utyp := Base_Type (Utyp); |
| Ref := Unchecked_Convert_To (Utyp, Ref); |
| end if; |
| |
| -- The underlying type may not be present due to a missing full view. |
| -- In this case freezing did not take place and there is no suitable |
| -- [Deep_]Initialize primitive to call. |
| -- If Typ is protected then no additional processing is needed either. |
| |
| if No (Utyp) |
| or else Is_Protected_Type (Typ) |
| then |
| return Empty; |
| end if; |
| |
| -- Select the appropriate version of initialize |
| |
| if Has_Controlled_Component (Utyp) then |
| Proc := TSS (Utyp, TSS_Deep_Initialize); |
| elsif Is_Mutably_Tagged_Type (Utyp) then |
| Proc := Find_Controlled_Prim_Op (Etype (Utyp), Name_Initialize); |
| Check_Visibly_Controlled (Initialize_Case, Etype (Typ), Proc, Ref); |
| else |
| Proc := Find_Controlled_Prim_Op (Utyp, Name_Initialize); |
| Check_Visibly_Controlled (Initialize_Case, Typ, Proc, Ref); |
| end if; |
| |
| -- If initialization procedure for an array of controlled objects is |
| -- trivial, do not generate a useless call to it. |
| -- The initialization procedure may be missing altogether in the case |
| -- of a derived container whose components have trivial initialization. |
| |
| if No (Proc) |
| or else (Is_Array_Type (Utyp) and then Is_Trivial_Subprogram (Proc)) |
| or else |
| (not Comes_From_Source (Proc) |
| and then Present (Alias (Proc)) |
| and then Is_Trivial_Subprogram (Alias (Proc))) |
| then |
| return Empty; |
| 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, Typ); |
| |
| -- Generate: |
| -- [Deep_]Initialize (Ref); |
| |
| return |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (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_Occurrence_Of (Typ, Loc)), |
| |
| -- F : Boolean := True |
| |
| Make_Parameter_Specification (Loc, |
| Defining_Identifier => Make_Defining_Identifier (Loc, Name_F), |
| Parameter_Type => New_Occurrence_Of (Standard_Boolean, Loc), |
| Expression => New_Occurrence_Of (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_Master_Node_Declaration -- |
| ---------------------------------- |
| |
| function Make_Master_Node_Declaration |
| (Loc : Source_Ptr; |
| Master_Node : Entity_Id; |
| Obj : Entity_Id) return Node_Id |
| is |
| begin |
| Set_Finalization_Master_Node (Obj, Master_Node); |
| |
| return |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Master_Node, |
| Aliased_Present => True, |
| Object_Definition => |
| New_Occurrence_Of (RTE (RE_Master_Node), Loc)); |
| end Make_Master_Node_Declaration; |
| |
| ---------------------------------------- |
| -- Make_Suppress_Object_Finalize_Call -- |
| ---------------------------------------- |
| |
| function Make_Suppress_Object_Finalize_Call |
| (Loc : Source_Ptr; |
| Obj : Entity_Id) return Node_Id |
| is |
| Obj_Decl : constant Node_Id := Declaration_Node (Obj); |
| |
| Master_Node_Decl : Node_Id; |
| Master_Node_Id : Entity_Id; |
| |
| begin |
| -- Create the declaration of the Master_Node for the object and |
| -- insert it before the declaration of the object itself. |
| |
| if Present (Finalization_Master_Node (Obj)) then |
| Master_Node_Id := Finalization_Master_Node (Obj); |
| |
| else |
| Master_Node_Id := Make_Temporary (Loc, 'N'); |
| Master_Node_Decl := |
| Make_Master_Node_Declaration (Loc, Master_Node_Id, Obj); |
| Insert_Before_And_Analyze (Obj_Decl, Master_Node_Decl); |
| |
| -- Generate the attachment of the object to the Master_Node |
| |
| Attach_Object_To_Master_Node (Obj_Decl, Master_Node_Id); |
| |
| -- Mark the object to avoid double finalization |
| |
| Set_Is_Ignored_For_Finalization (Obj); |
| end if; |
| |
| return |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (RTE (RE_Suppress_Object_Finalize_At_End), Loc), |
| Parameter_Associations => New_List ( |
| New_Occurrence_Of (Master_Node_Id, Loc))); |
| end Make_Suppress_Object_Finalize_Call; |
| |
| -------------------------- |
| -- Make_Transient_Block -- |
| -------------------------- |
| |
| function Make_Transient_Block |
| (Loc : Source_Ptr; |
| Action : Node_Id; |
| Par : Node_Id) return Node_Id |
| is |
| function Manages_Sec_Stack (Id : Entity_Id) return Boolean; |
| -- Determine whether scoping entity Id manages the secondary stack |
| |
| function Within_Loop_Statement (N : Node_Id) return Boolean; |
| -- Return True when N appears within a loop and no block is containing N |
| |
| ----------------------- |
| -- Manages_Sec_Stack -- |
| ----------------------- |
| |
| function Manages_Sec_Stack (Id : Entity_Id) return Boolean is |
| begin |
| case Ekind (Id) is |
| |
| -- An exception handler with a choice parameter utilizes a dummy |
| -- block to provide a declarative region. Such a block should not |
| -- be considered because it never manifests in the tree and can |
| -- never release the secondary stack. |
| |
| when E_Block => |
| return |
| Uses_Sec_Stack (Id) and then not Is_Exception_Handler (Id); |
| |
| when E_Entry |
| | E_Entry_Family |
| | E_Function |
| | E_Procedure |
| => |
| return Uses_Sec_Stack (Id); |
| |
| when others => |
| return False; |
| end case; |
| end Manages_Sec_Stack; |
| |
| --------------------------- |
| -- Within_Loop_Statement -- |
| --------------------------- |
| |
| function Within_Loop_Statement (N : Node_Id) return Boolean is |
| Par : Node_Id := Parent (N); |
| |
| begin |
| while Nkind (Par) not in |
| N_Handled_Sequence_Of_Statements | N_Loop_Statement | |
| N_Package_Specification | N_Proper_Body |
| loop |
| pragma Assert (Present (Par)); |
| Par := Parent (Par); |
| end loop; |
| |
| return Nkind (Par) = N_Loop_Statement; |
| end Within_Loop_Statement; |
| |
| -- Local variables |
| |
| Decls : constant List_Id := New_List; |
| Instrs : constant List_Id := New_List (Action); |
| Trans_Id : constant Entity_Id := Current_Scope; |
| |
| Block : Node_Id; |
| Insert : Node_Id; |
| Scop : Entity_Id; |
| |
| -- Start of processing for Make_Transient_Block |
| |
| begin |
| -- Even though the transient block is tasked with managing the secondary |
| -- stack, the block may forgo this functionality depending on how the |
| -- secondary stack is managed by enclosing scopes. |
| |
| if Manages_Sec_Stack (Trans_Id) then |
| |
| -- Determine whether an enclosing scope already manages the secondary |
| -- stack. |
| |
| Scop := Scope (Trans_Id); |
| while Present (Scop) loop |
| |
| -- It should not be possible to reach Standard without hitting one |
| -- of the other cases first unless Standard was manually pushed. |
| |
| if Scop = Standard_Standard then |
| exit; |
| |
| -- The transient block is within a function which returns on the |
| -- secondary stack. Take a conservative approach and assume that |
| -- the value on the secondary stack is part of the result. Note |
| -- that it is not possible to detect this dependency without flow |
| -- analysis which the compiler does not have. Letting the object |
| -- live longer than the transient block will not leak any memory |
| -- because the caller will reclaim the total storage used by the |
| -- function. |
| |
| elsif Ekind (Scop) = E_Function |
| and then Sec_Stack_Needed_For_Return (Scop) |
| then |
| Set_Uses_Sec_Stack (Trans_Id, False); |
| exit; |
| |
| -- The transient block must manage the secondary stack when the |
| -- block appears within a loop in order to reclaim the memory at |
| -- each iteration. |
| |
| elsif Ekind (Scop) = E_Loop then |
| exit; |
| |
| -- Ditto when the block appears without a block that does not |
| -- manage the secondary stack and is located within a loop. |
| |
| elsif Ekind (Scop) = E_Block |
| and then not Manages_Sec_Stack (Scop) |
| and then Present (Block_Node (Scop)) |
| and then Within_Loop_Statement (Block_Node (Scop)) |
| then |
| exit; |
| |
| -- The transient block does not need to manage the secondary stack |
| -- when there is an enclosing construct which already does that. |
| -- This optimization saves on SS_Mark and SS_Release calls but may |
| -- allow objects to live a little longer than required. |
| |
| -- The transient block must manage the secondary stack when switch |
| -- -gnatd.s (strict management) is in effect. |
| |
| elsif Manages_Sec_Stack (Scop) and then not Debug_Flag_Dot_S then |
| Set_Uses_Sec_Stack (Trans_Id, False); |
| exit; |
| |
| -- Prevent the search from going too far because transient blocks |
| -- are bounded by packages and subprogram scopes. |
| |
| elsif Ekind (Scop) in E_Entry |
| | E_Entry_Family |
| | E_Function |
| | E_Package |
| | E_Procedure |
| | E_Subprogram_Body |
| then |
| exit; |
| end if; |
| |
| Scop := Scope (Scop); |
| end loop; |
| end if; |
| |
| -- Create the transient block. Set the parent now since the block itself |
| -- is not part of the tree. The current scope is the E_Block entity that |
| -- has been pushed by Establish_Transient_Scope. |
| |
| pragma Assert (Ekind (Trans_Id) = E_Block); |
| |
| Block := |
| Make_Block_Statement (Loc, |
| Identifier => New_Occurrence_Of (Trans_Id, 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. Do not insert cleanup actions here, |
| -- they will be transferred to the newly created block. |
| |
| Insert_Actions_In_Scope_Around |
| (Action, Clean => False, Manage_SS => False); |
| |
| Insert := Prev (Action); |
| |
| if Present (Insert) then |
| Freeze_All (First_Entity (Trans_Id), Insert); |
| end if; |
| |
| -- Transfer cleanup actions to the newly created block |
| |
| declare |
| Cleanup_Actions : List_Id |
| renames Scope_Stack.Table (Scope_Stack.Last). |
| Actions_To_Be_Wrapped (Cleanup); |
| begin |
| Set_Cleanup_Actions (Block, Cleanup_Actions); |
| Cleanup_Actions := No_List; |
| end; |
| |
| -- 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; |
| |
| ---------------------------------- |
| -- Shift_Address_For_Descriptor -- |
| ---------------------------------- |
| |
| function Shift_Address_For_Descriptor |
| (Addr : Node_Id; |
| Typ : Entity_Id; |
| Op_Nam : Name_Id) return Node_Id |
| is |
| Loc : constant Source_Ptr := Sloc (Addr); |
| Dummy : constant Entity_Id := RTE (RE_Storage_Offset); |
| -- Make sure System_Storage_Elements is loaded for RTU_Entity |
| |
| begin |
| -- Generate: |
| -- Addr +/- (Typ'Descriptor_Size / Storage_Unit) |
| |
| return |
| Make_Function_Call (Loc, |
| Name => |
| Make_Expanded_Name (Loc, |
| Chars => Op_Nam, |
| Prefix => |
| New_Occurrence_Of |
| (RTU_Entity (System_Storage_Elements), Loc), |
| Selector_Name => Make_Identifier (Loc, Op_Nam)), |
| Parameter_Associations => New_List ( |
| Addr, |
| Make_Op_Divide (Loc, |
| Left_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Typ, Loc), |
| Attribute_Name => Name_Descriptor_Size), |
| Right_Opnd => |
| Make_Integer_Literal (Loc, System_Storage_Unit)))); |
| end Shift_Address_For_Descriptor; |
| |
| ---------------------------- |
| -- Store_Actions_In_Scope -- |
| ---------------------------- |
| |
| procedure Store_Actions_In_Scope (AK : Scope_Action_Kind; L : List_Id) is |
| SE : Scope_Stack_Entry renames Scope_Stack.Table (Scope_Stack.Last); |
| Actions : List_Id renames SE.Actions_To_Be_Wrapped (AK); |
| |
| begin |
| if Is_Empty_List (Actions) then |
| Store_New_Actions_In_Scope (AK, L); |
| Analyze_List (L); |
| |
| elsif AK = Before then |
| Insert_List_After_And_Analyze (Last (Actions), L); |
| |
| else |
| Insert_List_Before_And_Analyze (First (Actions), L); |
| end if; |
| end Store_Actions_In_Scope; |
| |
| ---------------------------------- |
| -- Store_After_Actions_In_Scope -- |
| ---------------------------------- |
| |
| procedure Store_After_Actions_In_Scope (L : List_Id) is |
| begin |
| Store_Actions_In_Scope (After, L); |
| end Store_After_Actions_In_Scope; |
| |
| --------------------------------------------------- |
| -- Store_After_Actions_In_Scope_Without_Analysis -- |
| --------------------------------------------------- |
| |
| procedure Store_After_Actions_In_Scope_Without_Analysis (L : List_Id) is |
| SE : Scope_Stack_Entry renames Scope_Stack.Table (Scope_Stack.Last); |
| Actions : List_Id renames SE.Actions_To_Be_Wrapped (After); |
| |
| begin |
| if Is_Empty_List (Actions) then |
| Store_New_Actions_In_Scope (After, L); |
| else |
| Insert_List_Before (First (Actions), L); |
| end if; |
| end Store_After_Actions_In_Scope_Without_Analysis; |
| |
| ----------------------------------- |
| -- Store_Before_Actions_In_Scope -- |
| ----------------------------------- |
| |
| procedure Store_Before_Actions_In_Scope (L : List_Id) is |
| begin |
| Store_Actions_In_Scope (Before, L); |
| end Store_Before_Actions_In_Scope; |
| |
| ----------------------------------- |
| -- Store_Cleanup_Actions_In_Scope -- |
| ----------------------------------- |
| |
| procedure Store_Cleanup_Actions_In_Scope (L : List_Id) is |
| begin |
| Store_Actions_In_Scope (Cleanup, L); |
| end Store_Cleanup_Actions_In_Scope; |
| |
| -------------------------------- |
| -- Store_New_Actions_In_Scope -- |
| -------------------------------- |
| |
| procedure Store_New_Actions_In_Scope (AK : Scope_Action_Kind; L : List_Id) |
| is |
| SE : Scope_Stack_Entry renames Scope_Stack.Table (Scope_Stack.Last); |
| Actions : List_Id renames SE.Actions_To_Be_Wrapped (AK); |
| |
| begin |
| pragma Assert (Is_Empty_List (Actions)); |
| |
| Actions := L; |
| |
| -- Set the Parent link to provide the context for the actions |
| |
| 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; |
| end Store_New_Actions_In_Scope; |
| |
| ------------------ |
| -- Unnest_Block -- |
| ------------------ |
| |
| procedure Unnest_Block (Decl : Node_Id) is |
| Loc : constant Source_Ptr := Sloc (Decl); |
| Ent : Entity_Id; |
| Local_Body : Node_Id; |
| Local_Call : Node_Id; |
| Local_Proc : Entity_Id; |
| Local_Scop : Entity_Id; |
| |
| begin |
| Local_Scop := Entity (Identifier (Decl)); |
| Ent := First_Entity (Local_Scop); |
| |
| Local_Proc := Make_Temporary (Loc, 'P'); |
| |
| Local_Body := |
| Make_Subprogram_Body (Loc, |
| Specification => |
| Make_Procedure_Specification (Loc, |
| Defining_Unit_Name => Local_Proc), |
| Declarations => Declarations (Decl), |
| Handled_Statement_Sequence => |
| Handled_Statement_Sequence (Decl), |
| At_End_Proc => New_Copy_Tree (At_End_Proc (Decl))); |
| |
| -- Handlers in the block may contain nested subprograms that require |
| -- unnesting. |
| |
| Check_Unnesting_In_Handlers (Local_Body); |
| |
| Rewrite (Decl, Local_Body); |
| Analyze (Decl); |
| Set_Has_Nested_Subprogram (Local_Proc); |
| |
| Local_Call := |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Local_Proc, Loc)); |
| |
| Insert_After (Decl, Local_Call); |
| Analyze (Local_Call); |
| |
| -- The new subprogram has the same scope as the original block |
| |
| Set_Scope (Local_Proc, Scope (Local_Scop)); |
| |
| -- And the entity list of the new procedure is that of the block |
| |
| Set_First_Entity (Local_Proc, Ent); |
| |
| -- Reset the scopes of all the entities to the new procedure |
| |
| while Present (Ent) loop |
| Set_Scope (Ent, Local_Proc); |
| Next_Entity (Ent); |
| end loop; |
| end Unnest_Block; |
| |
| ------------------------- |
| -- Unnest_If_Statement -- |
| ------------------------- |
| |
| procedure Unnest_If_Statement (If_Stmt : Node_Id) is |
| |
| procedure Check_Stmts_For_Subp_Unnesting (Stmts : in out List_Id); |
| -- A list of statements (that may be a list associated with a then, |
| -- elsif, or else part of an if-statement) is traversed at the top |
| -- level to determine whether it contains a subprogram body, and if so, |
| -- the statements will be replaced with a new procedure body containing |
| -- the statements followed by a call to the procedure. The individual |
| -- statements may also be blocks, loops, or other if statements that |
| -- themselves may require contain nested subprograms needing unnesting. |
| |
| procedure Check_Stmts_For_Subp_Unnesting (Stmts : in out List_Id) is |
| Subp_Found : Boolean := False; |
| |
| begin |
| if Is_Empty_List (Stmts) then |
| return; |
| end if; |
| |
| declare |
| Stmt : Node_Id := First (Stmts); |
| begin |
| while Present (Stmt) loop |
| if Nkind (Stmt) = N_Subprogram_Body then |
| Subp_Found := True; |
| exit; |
| end if; |
| |
| Next (Stmt); |
| end loop; |
| end; |
| |
| -- The statements themselves may be blocks, loops, etc. that in turn |
| -- contain nested subprograms requiring an unnesting transformation. |
| -- We perform this traversal after looking for subprogram bodies, to |
| -- avoid considering procedures created for one of those statements |
| -- (such as a block rewritten as a procedure) as a nested subprogram |
| -- of the statement list (which could result in an unneeded wrapper |
| -- procedure). |
| |
| Check_Unnesting_In_Decls_Or_Stmts (Stmts); |
| |
| -- If there was a top-level subprogram body in the statement list, |
| -- then perform an unnesting transformation on the list by replacing |
| -- the statements with a wrapper procedure body containing the |
| -- original statements followed by a call to that procedure. |
| |
| if Subp_Found then |
| Unnest_Statement_List (Stmts); |
| end if; |
| end Check_Stmts_For_Subp_Unnesting; |
| |
| -- Local variables |
| |
| Then_Stmts : List_Id := Then_Statements (If_Stmt); |
| Else_Stmts : List_Id := Else_Statements (If_Stmt); |
| |
| -- Start of processing for Unnest_If_Statement |
| |
| begin |
| Check_Stmts_For_Subp_Unnesting (Then_Stmts); |
| Set_Then_Statements (If_Stmt, Then_Stmts); |
| |
| if not Is_Empty_List (Elsif_Parts (If_Stmt)) then |
| declare |
| Elsif_Part : Node_Id := |
| First (Elsif_Parts (If_Stmt)); |
| Elsif_Stmts : List_Id; |
| begin |
| while Present (Elsif_Part) loop |
| Elsif_Stmts := Then_Statements (Elsif_Part); |
| |
| Check_Stmts_For_Subp_Unnesting (Elsif_Stmts); |
| Set_Then_Statements (Elsif_Part, Elsif_Stmts); |
| |
| Next (Elsif_Part); |
| end loop; |
| end; |
| end if; |
| |
| Check_Stmts_For_Subp_Unnesting (Else_Stmts); |
| Set_Else_Statements (If_Stmt, Else_Stmts); |
| end Unnest_If_Statement; |
| |
| ----------------- |
| -- Unnest_Loop -- |
| ----------------- |
| |
| procedure Unnest_Loop (Loop_Stmt : Node_Id) is |
| |
| procedure Fixup_Inner_Scopes (Loop_Or_Block : Node_Id); |
| -- This procedure fixes the scope for 2 identified cases of incorrect |
| -- scope information. |
| -- |
| -- 1) The loops created by the compiler for array aggregates can have |
| -- nested finalization procedure when the type of the array components |
| -- needs finalization. It has the following form: |
| |
| -- for J4b in 10 .. 12 loop |
| -- declare |
| -- procedure __finalizer; |
| -- begin |
| -- procedure __finalizer is |
| -- ... |
| -- end; |
| -- ... |
| -- obj (J4b) := ...; |
| |
| -- When the compiler creates the N_Block_Statement, it sets its scope to |
| -- the outer scope (the one containing the loop). |
| |
| -- The Unnest_Loop procedure moves the N_Loop_Statement inside a new |
| -- procedure and correctly sets the scopes for both the new procedure |
| -- and the loop entity. The inner block scope is not modified and this |
| -- leaves the Tree in an incoherent state (i.e. the inner procedure must |
| -- have its enclosing procedure in its scope ancestries). |
| |
| -- 2) The second case happens when an object declaration is created |
| -- within a loop used to initialize the 'others' components of an |
| -- aggregate that is nested within a transient scope. When the transient |
| -- scope is removed, the object scope is set to the outer scope. For |
| -- example: |
| |
| -- package pack |
| -- ... |
| -- L98s : for J90s in 2 .. 19 loop |
| -- B101s : declare |
| -- R92s : aliased some_type; |
| -- ... |
| |
| -- The loop L98s was initially wrapped in a transient scope B72s and |
| -- R92s was nested within it. Then the transient scope is removed and |
| -- the scope of R92s is set to 'pack'. And finally, when the unnester |
| -- moves the loop body in a new procedure, R92s's scope is still left |
| -- unchanged. |
| |
| -- This procedure finds the two previous patterns and fixes the scope |
| -- information. |
| |
| -- Another (better) fix would be to have the block scope set to be the |
| -- loop entity earlier (when the block is created or when the loop gets |
| -- an actual entity set). But unfortunately this proved harder to |
| -- implement ??? |
| |
| procedure Fixup_Inner_Scopes (Loop_Or_Block : Node_Id) is |
| Stmt : Node_Id; |
| Loop_Or_Block_Ent : Entity_Id; |
| Ent_To_Fix : Entity_Id; |
| Decl : Node_Id := Empty; |
| begin |
| pragma Assert (Nkind (Loop_Or_Block) in |
| N_Loop_Statement | N_Block_Statement); |
| |
| Loop_Or_Block_Ent := Entity (Identifier (Loop_Or_Block)); |
| if Nkind (Loop_Or_Block) = N_Loop_Statement then |
| Stmt := First (Statements (Loop_Or_Block)); |
| else -- N_Block_Statement |
| Stmt := First |
| (Statements (Handled_Statement_Sequence (Loop_Or_Block))); |
| Decl := First (Declarations (Loop_Or_Block)); |
| end if; |
| |
| -- Fix scopes for any object declaration found in the block |
| while Present (Decl) loop |
| if Nkind (Decl) = N_Object_Declaration then |
| Ent_To_Fix := Defining_Identifier (Decl); |
| Set_Scope (Ent_To_Fix, Loop_Or_Block_Ent); |
| end if; |
| Next (Decl); |
| end loop; |
| |
| while Present (Stmt) loop |
| if Nkind (Stmt) = N_Block_Statement |
| and then Is_Abort_Block (Stmt) |
| then |
| Ent_To_Fix := Entity (Identifier (Stmt)); |
| Set_Scope (Ent_To_Fix, Loop_Or_Block_Ent); |
| elsif Nkind (Stmt) in N_Block_Statement | N_Loop_Statement |
| then |
| Fixup_Inner_Scopes (Stmt); |
| end if; |
| Next (Stmt); |
| end loop; |
| end Fixup_Inner_Scopes; |
| |
| Loc : constant Source_Ptr := Sloc (Loop_Stmt); |
| Ent : Entity_Id; |
| Local_Body : Node_Id; |
| Local_Call : Node_Id; |
| Loop_Ent : Entity_Id; |
| Local_Proc : Entity_Id; |
| Loop_Copy : constant Node_Id := |
| Relocate_Node (Loop_Stmt); |
| begin |
| Loop_Ent := Entity (Identifier (Loop_Stmt)); |
| Ent := First_Entity (Loop_Ent); |
| |
| Local_Proc := Make_Temporary (Loc, 'P'); |
| |
| Local_Body := |
| Make_Subprogram_Body (Loc, |
| Specification => |
| Make_Procedure_Specification (Loc, |
| Defining_Unit_Name => Local_Proc), |
| Declarations => Empty_List, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List (Loop_Copy))); |
| |
| Rewrite (Loop_Stmt, Local_Body); |
| Analyze (Loop_Stmt); |
| |
| Set_Has_Nested_Subprogram (Local_Proc); |
| |
| Local_Call := |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Local_Proc, Loc)); |
| |
| Insert_After (Loop_Stmt, Local_Call); |
| Analyze (Local_Call); |
| |
| -- New procedure has the same scope as the original loop, and the scope |
| -- of the loop is the new procedure. |
| |
| Set_Scope (Local_Proc, Scope (Loop_Ent)); |
| Set_Scope (Loop_Ent, Local_Proc); |
| |
| Fixup_Inner_Scopes (Loop_Copy); |
| |
| -- The entity list of the new procedure is that of the loop |
| |
| Set_First_Entity (Local_Proc, Ent); |
| |
| -- Note that the entities associated with the loop don't need to have |
| -- their Scope fields reset, since they're still associated with the |
| -- same loop entity that now belongs to the copied loop statement. |
| end Unnest_Loop; |
| |
| --------------------------- |
| -- Unnest_Statement_List -- |
| --------------------------- |
| |
| procedure Unnest_Statement_List (Stmts : in out List_Id) is |
| Loc : constant Source_Ptr := Sloc (First (Stmts)); |
| Local_Body : Node_Id; |
| Local_Call : Node_Id; |
| Local_Proc : Entity_Id; |
| New_Stmts : constant List_Id := Empty_List; |
| |
| begin |
| Local_Proc := Make_Temporary (Loc, 'P'); |
| |
| Local_Body := |
| Make_Subprogram_Body (Loc, |
| Specification => |
| Make_Procedure_Specification (Loc, |
| Defining_Unit_Name => Local_Proc), |
| Declarations => Empty_List, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => Stmts)); |
| |
| Append_To (New_Stmts, Local_Body); |
| |
| Analyze (Local_Body); |
| |
| Set_Has_Nested_Subprogram (Local_Proc); |
| |
| Local_Call := |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Local_Proc, Loc)); |
| |
| Append_To (New_Stmts, Local_Call); |
| Analyze (Local_Call); |
| |
| -- Traverse the statements, and for any that are declarations or |
| -- subprogram bodies that have entities, set the Scope of those |
| -- entities to the new procedure's Entity_Id. |
| |
| declare |
| Stmt : Node_Id := First (Stmts); |
| |
| begin |
| while Present (Stmt) loop |
| case Nkind (Stmt) is |
| when N_Declaration |
| | N_Renaming_Declaration |
| => |
| Set_Scope (Defining_Identifier (Stmt), Local_Proc); |
| |
| when N_Subprogram_Body => |
| Set_Scope |
| (Defining_Unit_Name (Specification (Stmt)), Local_Proc); |
| |
| when others => |
| null; |
| end case; |
| |
| Next (Stmt); |
| end loop; |
| end; |
| |
| Stmts := New_Stmts; |
| end Unnest_Statement_List; |
| |
| -------------------------------- |
| -- 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 |
| Curr_S : Entity_Id; |
| Encl_S : Entity_Id; |
| |
| begin |
| Curr_S := Current_Scope; |
| Encl_S := Scope (Curr_S); |
| |
| -- Insert all actions including cleanup generated while analyzing or |
| -- expanding the transient context back into the tree. Manage the |
| -- secondary stack when the object declaration appears in a library |
| -- level package [body]. |
| |
| Insert_Actions_In_Scope_Around |
| (N => N, |
| Clean => True, |
| Manage_SS => |
| Uses_Sec_Stack (Curr_S) |
| and then Nkind (N) = N_Object_Declaration |
| and then Ekind (Encl_S) in E_Package | E_Package_Body |
| and then Is_Library_Level_Entity (Encl_S)); |
| Pop_Scope; |
| |
| -- Relocate local entities declared within the transient scope to the |
| -- enclosing scope. This action sets their Is_Public flag accordingly. |
| |
| Transfer_Entities (Curr_S, Encl_S); |
| |
| -- Mark the enclosing dynamic scope to ensure that the secondary stack |
| -- is properly released upon exiting the said scope. |
| |
| if Uses_Sec_Stack (Curr_S) then |
| Curr_S := Enclosing_Dynamic_Scope (Curr_S); |
| |
| -- Do not mark a function that returns on the secondary stack as the |
| -- reclamation is done by the caller. |
| |
| if Ekind (Curr_S) = E_Function |
| and then Needs_Secondary_Stack (Etype (Curr_S)) |
| then |
| null; |
| |
| -- Otherwise mark the enclosing dynamic scope |
| |
| else |
| Set_Uses_Sec_Stack (Curr_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 |
| Loc : constant Source_Ptr := Sloc (N); |
| Expr : Node_Id := Relocate_Node (N); |
| Typ : constant Entity_Id := Etype (N); |
| |
| Temp : constant Entity_Id := Make_Temporary (Loc, 'E', |
| Related_Node => Expr); |
| -- We link the temporary with its relocated expression to facilitate |
| -- locating the expression in the expanded code; this simplifies the |
| -- implementation of the function that searchs in the expanded code |
| -- for a function call that has been wrapped in a transient block |
| -- (see Get_Relocated_Function_Call). |
| |
| begin |
| -- Generate: |
| |
| -- Temp : Typ; |
| -- declare |
| -- M : constant Mark_Id := SS_Mark; |
| -- procedure Finalizer is ... (See Build_Finalizer) |
| |
| -- begin |
| -- Temp := <Expr>; -- general case |
| -- Temp := (if <Expr> then True else False); -- boolean case |
| |
| -- at end |
| -- Finalizer; |
| -- end; |
| |
| -- A special case is made for Boolean expressions so that the back end |
| -- knows to generate a conditional branch instruction, if running with |
| -- -fpreserve-control-flow. This ensures that a control-flow change |
| -- signaling the decision outcome occurs before the cleanup actions. |
| |
| if Opt.Suppress_Control_Flow_Optimizations |
| and then Is_Boolean_Type (Typ) |
| then |
| Expr := |
| Make_If_Expression (Loc, |
| Expressions => New_List ( |
| Expr, |
| New_Occurrence_Of (Standard_True, Loc), |
| New_Occurrence_Of (Standard_False, Loc))); |
| end if; |
| |
| Insert_Actions (N, New_List ( |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Temp, |
| Object_Definition => New_Occurrence_Of (Typ, Loc)), |
| |
| Make_Transient_Block (Loc, |
| Action => |
| Make_Assignment_Statement (Loc, |
| Name => New_Occurrence_Of (Temp, Loc), |
| Expression => Expr), |
| Par => Parent (N)))); |
| |
| if Debug_Generated_Code then |
| Set_Debug_Info_Needed (Temp); |
| end if; |
| |
| Rewrite (N, New_Occurrence_Of (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; |