blob: 2c6683d0ff66ab875028ce5ef4e3e4cd3ce51375 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- E X P _ C H 7 --
-- --
-- B o d y --
-- --
-- Copyright (C) 1992-2015, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
-- for more details. You should have received a copy of the GNU General --
-- Public License distributed with GNAT; see file COPYING3. If not, go to --
-- http://www.gnu.org/licenses for a complete copy of the license. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
-- This package contains virtually all expansion mechanisms related to
-- - controlled types
-- - transient scopes
with Atree; use Atree;
with Debug; use Debug;
with Einfo; use Einfo;
with Elists; use Elists;
with Errout; use Errout;
with Exp_Ch6; use Exp_Ch6;
with Exp_Ch9; use Exp_Ch9;
with Exp_Ch11; use Exp_Ch11;
with Exp_Dbug; use Exp_Dbug;
with Exp_Dist; use Exp_Dist;
with Exp_Disp; use Exp_Disp;
with Exp_Prag; use Exp_Prag;
with Exp_Tss; use Exp_Tss;
with Exp_Util; use Exp_Util;
with Freeze; use Freeze;
with Lib; use Lib;
with Nlists; use Nlists;
with Nmake; use Nmake;
with Opt; use Opt;
with Output; use Output;
with Restrict; use Restrict;
with Rident; use Rident;
with Rtsfind; use Rtsfind;
with Sinfo; use Sinfo;
with Sem; use Sem;
with Sem_Aux; use Sem_Aux;
with Sem_Ch3; use Sem_Ch3;
with Sem_Ch7; use Sem_Ch7;
with Sem_Ch8; use Sem_Ch8;
with Sem_Res; use Sem_Res;
with Sem_Util; use Sem_Util;
with Snames; use Snames;
with Stand; use Stand;
with Targparm; use Targparm;
with Tbuild; use Tbuild;
with Ttypes; use Ttypes;
with Uintp; use Uintp;
package body Exp_Ch7 is
--------------------------------
-- Transient Scope Management --
--------------------------------
-- A transient scope is created when temporary objects are created by the
-- compiler. These temporary objects are allocated on the secondary stack
-- and the transient scope is responsible for finalizing the object when
-- appropriate and reclaiming the memory at the right time. The temporary
-- objects are generally the objects allocated to store the result of a
-- function returning an unconstrained or a tagged value. Expressions
-- needing to be wrapped in a transient scope (functions calls returning
-- unconstrained or tagged values) may appear in 3 different contexts which
-- lead to 3 different kinds of transient scope expansion:
-- 1. In a simple statement (procedure call, assignment, ...). In this
-- case the instruction is wrapped into a transient block. See
-- Wrap_Transient_Statement for details.
-- 2. In an expression of a control structure (test in a IF statement,
-- expression in a CASE statement, ...). See Wrap_Transient_Expression
-- for details.
-- 3. In a expression of an object_declaration. No wrapping is possible
-- here, so the finalization actions, if any, are done right after the
-- declaration and the secondary stack deallocation is done in the
-- proper enclosing scope. See Wrap_Transient_Declaration for details.
-- Note about functions returning tagged types: it has been decided to
-- always allocate their result in the secondary stack, even though is not
-- absolutely mandatory when the tagged type is constrained because the
-- caller knows the size of the returned object and thus could allocate the
-- result in the primary stack. An exception to this is when the function
-- builds its result in place, as is done for functions with inherently
-- limited result types for Ada 2005. In that case, certain callers may
-- pass the address of a constrained object as the target object for the
-- function result.
-- By allocating tagged results in the secondary stack a number of
-- implementation difficulties are avoided:
-- - If it is a dispatching function call, the computation of the size of
-- the result is possible but complex from the outside.
-- - If the returned type is controlled, the assignment of the returned
-- value to the anonymous object involves an Adjust, and we have no
-- easy way to access the anonymous object created by the back end.
-- - If the returned type is class-wide, this is an unconstrained type
-- anyway.
-- Furthermore, the small loss in efficiency which is the result of this
-- decision is not such a big deal because functions returning tagged types
-- are not as common in practice compared to functions returning access to
-- a tagged type.
--------------------------------------------------
-- Transient Blocks and Finalization Management --
--------------------------------------------------
function Find_Node_To_Be_Wrapped (N : Node_Id) return Node_Id;
-- N is a node which may generate a transient scope. Loop over the parent
-- pointers of N until it find the appropriate node to wrap. If it returns
-- Empty, it means that no transient scope is needed in this context.
procedure Insert_Actions_In_Scope_Around
(N : Node_Id;
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 flag Clean
-- is set, insert any cleanup actions. If flag Manage_SS is set, insert
-- calls to mark and 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 Set_Node_To_Be_Wrapped (N : Node_Id);
-- Set the field Node_To_Be_Wrapped of the current scope
-- ??? The entire comment needs to be rewritten
-- ??? which entire comment?
procedure Store_Actions_In_Scope (AK : Scope_Action_Kind; L : List_Id);
-- Shared processing for Store_xxx_Actions_In_Scope
-----------------------------
-- Finalization Management --
-----------------------------
-- This part describe how Initialization/Adjustment/Finalization procedures
-- are generated and called. Two cases must be considered, types that are
-- Controlled (Is_Controlled flag set) and composite types that contain
-- controlled components (Has_Controlled_Component flag set). In the first
-- case the procedures to call are the user-defined primitive operations
-- Initialize/Adjust/Finalize. In the second case, GNAT generates
-- Deep_Initialize, Deep_Adjust and Deep_Finalize that are in charge
-- of calling the former procedures on the controlled components.
-- For records with Has_Controlled_Component set, a hidden "controller"
-- component is inserted. This controller component contains its own
-- finalization list on which all controlled components are attached
-- creating an indirection on the upper-level Finalization list. This
-- technique facilitates the management of objects whose number of
-- controlled components changes during execution. This controller
-- component is itself controlled and is attached to the upper-level
-- finalization chain. Its adjust primitive is in charge of calling adjust
-- on the components and adjusting the finalization pointer to match their
-- new location (see a-finali.adb).
-- It is not possible to use a similar technique for arrays that have
-- Has_Controlled_Component set. In this case, deep procedures are
-- generated that call initialize/adjust/finalize + attachment or
-- detachment on the finalization list for all component.
-- Initialize calls: they are generated for declarations or dynamic
-- allocations of Controlled objects with no initial value. They are always
-- followed by an attachment to the current Finalization Chain. For the
-- dynamic allocation case this the chain attached to the scope of the
-- access type definition otherwise, this is the chain of the current
-- scope.
-- Adjust Calls: They are generated on 2 occasions: (1) for declarations
-- or dynamic allocations of Controlled objects with an initial value.
-- (2) after an assignment. In the first case they are followed by an
-- attachment to the final chain, in the second case they are not.
-- Finalization Calls: They are generated on (1) scope exit, (2)
-- assignments, (3) unchecked deallocations. In case (3) they have to
-- be detached from the final chain, in case (2) they must not and in
-- case (1) this is not important since we are exiting the scope anyway.
-- Other details:
-- Type extensions will have a new record controller at each derivation
-- level containing controlled components. The record controller for
-- the parent/ancestor is attached to the finalization list of the
-- extension's record controller (i.e. the parent is like a component
-- of the extension).
-- For types that are both Is_Controlled and Has_Controlled_Components,
-- the record controller and the object itself are handled separately.
-- It could seem simpler to attach the object at the end of its record
-- controller but this would not tackle view conversions properly.
-- A classwide type can always potentially have controlled components
-- but the record controller of the corresponding actual type may not
-- be known at compile time so the dispatch table contains a special
-- field that allows computation of the offset of the record controller
-- dynamically. See s-finimp.Deep_Tag_Attach and a-tags.RC_Offset.
-- Here is a simple example of the expansion of a controlled block :
-- declare
-- X : Controlled;
-- Y : Controlled := Init;
--
-- type R is record
-- C : Controlled;
-- end record;
-- W : R;
-- Z : R := (C => X);
-- begin
-- X := Y;
-- W := Z;
-- end;
--
-- is expanded into
--
-- declare
-- _L : System.FI.Finalizable_Ptr;
-- procedure _Clean is
-- begin
-- Abort_Defer;
-- System.FI.Finalize_List (_L);
-- Abort_Undefer;
-- end _Clean;
-- X : Controlled;
-- begin
-- Abort_Defer;
-- Initialize (X);
-- Attach_To_Final_List (_L, Finalizable (X), 1);
-- at end: Abort_Undefer;
-- Y : Controlled := Init;
-- Adjust (Y);
-- Attach_To_Final_List (_L, Finalizable (Y), 1);
--
-- type R is record
-- C : Controlled;
-- end record;
-- W : R;
-- begin
-- Abort_Defer;
-- Deep_Initialize (W, _L, 1);
-- at end: Abort_Under;
-- Z : R := (C => X);
-- Deep_Adjust (Z, _L, 1);
-- begin
-- _Assign (X, Y);
-- Deep_Finalize (W, False);
-- <save W's final pointers>
-- W := Z;
-- <restore W's final pointers>
-- Deep_Adjust (W, _L, 0);
-- at end
-- _Clean;
-- end;
type Final_Primitives is
(Initialize_Case, Adjust_Case, Finalize_Case, Address_Case);
-- This enumeration type is defined in order to ease sharing code for
-- building finalization procedures for composite types.
Name_Of : constant array (Final_Primitives) of Name_Id :=
(Initialize_Case => Name_Initialize,
Adjust_Case => Name_Adjust,
Finalize_Case => Name_Finalize,
Address_Case => Name_Finalize_Address);
Deep_Name_Of : constant array (Final_Primitives) of TSS_Name_Type :=
(Initialize_Case => TSS_Deep_Initialize,
Adjust_Case => TSS_Deep_Adjust,
Finalize_Case => TSS_Deep_Finalize,
Address_Case => TSS_Finalize_Address);
procedure Build_Array_Deep_Procs (Typ : Entity_Id);
-- Build the deep Initialize/Adjust/Finalize for a record Typ with
-- Has_Controlled_Component set and store them using the TSS mechanism.
function Build_Cleanup_Statements
(N : Node_Id;
Additional_Cleanup : List_Id) return List_Id;
-- Create the clean up calls for an asynchronous call block, task master,
-- protected subprogram body, task allocation block or task body, 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
(N : Node_Id;
Clean_Stmts : List_Id;
Mark_Id : Entity_Id;
Top_Decls : List_Id;
Defer_Abort : Boolean;
Fin_Id : out Entity_Id);
-- N may denote an accept statement, block, entry body, package body,
-- package spec, protected body, subprogram body, or a task body. Create
-- a procedure which contains finalization calls for all controlled objects
-- declared in the declarative or statement region of N. The calls are
-- built in reverse order relative to the original declarations. In the
-- case of a task body, the routine delays the creation of the finalizer
-- until all statements have been moved to the task body procedure.
-- Clean_Stmts may contain additional context-dependent code used to abort
-- asynchronous calls or complete tasks (see Build_Cleanup_Statements).
-- Mark_Id is the secondary stack used in the current context or Empty if
-- missing. Top_Decls is the list on which the declaration of the finalizer
-- is attached in the non-package case. Defer_Abort indicates that the
-- statements passed in perform actions that require abort to be deferred,
-- such as for task termination. Fin_Id is the finalizer declaration
-- entity.
procedure Build_Finalizer_Call (N : Node_Id; Fin_Id : Entity_Id);
-- N is a construct which contains a handled sequence of statements, Fin_Id
-- is the entity of a finalizer. Create an At_End handler which covers the
-- statements of N and calls Fin_Id. If the handled statement sequence has
-- an exception handler, the statements will be wrapped in a block to avoid
-- unwanted interaction with the new At_End handler.
procedure Build_Record_Deep_Procs (Typ : Entity_Id);
-- Build the deep Initialize/Adjust/Finalize for a record Typ with
-- Has_Component_Component set and store them using the TSS mechanism.
procedure Check_Visibly_Controlled
(Prim : Final_Primitives;
Typ : Entity_Id;
E : in out Entity_Id;
Cref : in out Node_Id);
-- The controlled operation declared for a derived type may not be
-- overriding, if the controlled operations of the parent type are hidden,
-- for example when the parent is a private type whose full view is
-- controlled. For other primitive operations we modify the name of the
-- operation to indicate that it is not overriding, but this is not
-- possible for Initialize, etc. because they have to be retrievable by
-- name. Before generating the proper call to one of these operations we
-- check whether Typ is known to be controlled at the point of definition.
-- If it is not then we must retrieve the hidden operation of the parent
-- and use it instead. This is one case that might be solved more cleanly
-- once Overriding pragmas or declarations are in place.
function Convert_View
(Proc : Entity_Id;
Arg : Node_Id;
Ind : Pos := 1) return Node_Id;
-- Proc is one of the Initialize/Adjust/Finalize operations, and Arg is the
-- argument being passed to it. Ind indicates which formal of procedure
-- Proc we are trying to match. This function will, if necessary, generate
-- a conversion between the partial and full view of Arg to match the type
-- of the formal of Proc, or force a conversion to the class-wide type in
-- the case where the operation is abstract.
function Enclosing_Function (E : Entity_Id) return Entity_Id;
-- Given an arbitrary entity, traverse the scope chain looking for the
-- first enclosing function. Return Empty if no function was found.
function Make_Call
(Loc : Source_Ptr;
Proc_Id : Entity_Id;
Param : Node_Id;
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. Wnen 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 Node_Id;
-- This function generates the tree for Deep_Initialize, Deep_Adjust or
-- Deep_Finalize procedures according to the first parameter, these
-- procedures operate on the type Typ. The Stmts parameter gives the body
-- of the procedure.
function Make_Deep_Array_Body
(Prim : Final_Primitives;
Typ : Entity_Id) return List_Id;
-- This function generates the list of statements for implementing
-- Deep_Initialize, Deep_Adjust or Deep_Finalize procedures according to
-- the first parameter, these procedures operate on the array type Typ.
function Make_Deep_Record_Body
(Prim : Final_Primitives;
Typ : Entity_Id;
Is_Local : Boolean := False) return List_Id;
-- This function generates the list of statements for implementing
-- Deep_Initialize, Deep_Adjust or Deep_Finalize procedures according to
-- the first parameter, these procedures operate on the record type Typ.
-- Flag Is_Local is used in conjunction with Deep_Finalize to designate
-- whether the inner logic should be dictated by state counters.
function Make_Finalize_Address_Stmts (Typ : Entity_Id) return List_Id;
-- Subsidiary to Make_Finalize_Address_Body, Make_Deep_Array_Body and
-- Make_Deep_Record_Body. Generate the following statements:
--
-- declare
-- type Acc_Typ is access all Typ;
-- for Acc_Typ'Storage_Size use 0;
-- begin
-- [Deep_]Finalize (Acc_Typ (V).all);
-- end;
----------------------------
-- Build_Array_Deep_Procs --
----------------------------
procedure Build_Array_Deep_Procs (Typ : Entity_Id) is
begin
Set_TSS (Typ,
Make_Deep_Proc
(Prim => Initialize_Case,
Typ => Typ,
Stmts => Make_Deep_Array_Body (Initialize_Case, Typ)));
if not Is_Limited_View (Typ) then
Set_TSS (Typ,
Make_Deep_Proc
(Prim => Adjust_Case,
Typ => Typ,
Stmts => Make_Deep_Array_Body (Adjust_Case, Typ)));
end if;
-- Do not generate Deep_Finalize and Finalize_Address if finalization is
-- suppressed since these routine will not be used.
if not Restriction_Active (No_Finalization) then
Set_TSS (Typ,
Make_Deep_Proc
(Prim => Finalize_Case,
Typ => Typ,
Stmts => Make_Deep_Array_Body (Finalize_Case, Typ)));
-- Create TSS primitive Finalize_Address for non-VM targets. JVM and
-- .NET do not support address arithmetic and unchecked conversions.
if VM_Target = No_VM then
Set_TSS (Typ,
Make_Deep_Proc
(Prim => Address_Case,
Typ => Typ,
Stmts => Make_Deep_Array_Body (Address_Case, Typ)));
end if;
end if;
end Build_Array_Deep_Procs;
------------------------------
-- Build_Cleanup_Statements --
------------------------------
function Build_Cleanup_Statements
(N : Node_Id;
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_Body : constant Boolean :=
Nkind (N) = N_Subprogram_Body
and then Is_Protected_Subprogram_Body (N);
Is_Task_Allocation : constant Boolean :=
Nkind (N) = N_Block_Statement
and then Is_Task_Allocation_Block (N);
Is_Task_Body : constant Boolean :=
Nkind (Original_Node (N)) = N_Task_Body;
Loc : constant Source_Ptr := Sloc (N);
Stmts : constant List_Id := New_List;
begin
if Is_Task_Body then
if Restricted_Profile then
Append_To (Stmts,
Build_Runtime_Call (Loc, RE_Complete_Restricted_Task));
else
Append_To (Stmts, Build_Runtime_Call (Loc, RE_Complete_Task));
end if;
elsif Is_Master then
if Restriction_Active (No_Task_Hierarchy) = False then
Append_To (Stmts, Build_Runtime_Call (Loc, RE_Complete_Master));
end if;
-- Add statements to unlock the protected object parameter and to
-- undefer abort. If the context is a protected procedure and the object
-- has entries, call the entry service routine.
-- NOTE: The generated code references _object, a parameter to the
-- procedure.
elsif Is_Protected_Body then
declare
Spec : constant Node_Id := Parent (Corresponding_Spec (N));
Conc_Typ : Entity_Id;
Param : Node_Id;
Param_Typ : Entity_Id;
begin
-- Find the _object parameter representing the protected object
Param := First (Parameter_Specifications (Spec));
loop
Param_Typ := Etype (Parameter_Type (Param));
if Ekind (Param_Typ) = E_Record_Type then
Conc_Typ := Corresponding_Concurrent_Type (Param_Typ);
end if;
exit when No (Param) or else Present (Conc_Typ);
Next (Param);
end loop;
pragma Assert (Present (Param));
-- Historical note: In earlier versions of GNAT, there was code
-- at this point to generate stuff to service entry queues. It is
-- now abstracted in Build_Protected_Subprogram_Call_Cleanup.
Build_Protected_Subprogram_Call_Cleanup
(Specification (N), Conc_Typ, Loc, Stmts);
end;
-- Add a call to Expunge_Unactivated_Tasks for dynamically allocated
-- tasks. Other unactivated tasks are completed by Complete_Task or
-- Complete_Master.
-- NOTE: The generated code references _chain, a local object
elsif Is_Task_Allocation then
-- Generate:
-- Expunge_Unactivated_Tasks (_chain);
-- where _chain is the list of tasks created by the allocator but not
-- yet activated. This list will be empty unless the block completes
-- abnormally.
Append_To (Stmts,
Make_Procedure_Call_Statement (Loc,
Name =>
New_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_Master --
-------------------------------
procedure Build_Finalization_Master
(Typ : Entity_Id;
For_Anonymous : Boolean := False;
For_Private : Boolean := False;
Context_Scope : Entity_Id := Empty;
Insertion_Node : Node_Id := Empty)
is
procedure Add_Pending_Access_Type
(Typ : Entity_Id;
Ptr_Typ : Entity_Id);
-- Add access type Ptr_Typ to the pending access type list for type Typ
function In_Deallocation_Instance (E : Entity_Id) return Boolean;
-- Determine whether entity E is inside a wrapper package created for
-- an instance of Ada.Unchecked_Deallocation.
-----------------------------
-- Add_Pending_Access_Type --
-----------------------------
procedure Add_Pending_Access_Type
(Typ : Entity_Id;
Ptr_Typ : Entity_Id)
is
List : Elist_Id;
begin
if Present (Pending_Access_Types (Typ)) then
List := Pending_Access_Types (Typ);
else
List := New_Elmt_List;
Set_Pending_Access_Types (Typ, List);
end if;
Prepend_Elmt (Ptr_Typ, List);
end Add_Pending_Access_Type;
------------------------------
-- 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));
-- A finalization master created for a named access type is 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_Master
begin
-- Certain run-time configurations and targets do not provide support
-- for controlled types.
if Restriction_Active (No_Finalization) then
return;
-- Do not process C, C++, CIL and Java types since it is assumend that
-- the non-Ada side will handle their clean up.
elsif Convention (Desig_Typ) = Convention_C
or else Convention (Desig_Typ) = Convention_CIL
or else Convention (Desig_Typ) = Convention_CPP
or else Convention (Desig_Typ) = Convention_Java
then
return;
-- Various machinery such as freezing may have already created a
-- finalization master.
elsif Present (Finalization_Master (Ptr_Typ)) then
return;
-- Do not process types that return on the secondary stack
elsif Present (Associated_Storage_Pool (Ptr_Typ))
and then Is_RTE (Associated_Storage_Pool (Ptr_Typ), RE_SS_Pool)
then
return;
-- Do not process types which may never allocate an object
elsif No_Pool_Assigned (Ptr_Typ) then
return;
-- Do not process access types coming from Ada.Unchecked_Deallocation
-- instances. Even though the designated type may be controlled, the
-- access type will never participate in allocation.
elsif In_Deallocation_Instance (Ptr_Typ) then
return;
-- Ignore the general use of anonymous access types unless the context
-- requires a finalization master.
elsif Ekind (Ptr_Typ) = E_Anonymous_Access_Type
and then not For_Anonymous
then
return;
-- Do not process non-library access types when restriction No_Nested_
-- Finalization is in effect since masters are controlled objects.
elsif Restriction_Active (No_Nested_Finalization)
and then not Is_Library_Level_Entity (Ptr_Typ)
then
return;
-- For .NET/JVM targets, allow the processing of access-to-controlled
-- types where the designated type is explicitly derived from [Limited_]
-- Controlled.
elsif VM_Target /= No_VM and then not Is_Controlled (Desig_Typ) then
return;
-- Do not create finalization masters in GNATprove mode because this
-- unwanted extra expansion. A compilation in this mode keeps the tree
-- as close as possible to the original sources.
elsif GNATprove_Mode then
return;
end if;
declare
Actions : constant List_Id := New_List;
Loc : constant Source_Ptr := Sloc (Ptr_Typ);
Fin_Mas_Id : Entity_Id;
Pool_Id : Entity_Id;
begin
-- Source access types use fixed master names since the master is
-- inserted in the same source unit only once. The only exception to
-- this are instances using the same access type as generic actual.
if Comes_From_Source (Ptr_Typ) and then not Inside_A_Generic then
Fin_Mas_Id :=
Make_Defining_Identifier (Loc,
Chars => New_External_Name (Chars (Ptr_Typ), "FM"));
-- Internally generated access types use temporaries as their names
-- due to possible collision with identical names coming from other
-- packages.
else
Fin_Mas_Id := Make_Temporary (Loc, 'F');
end if;
Set_Finalization_Master (Ptr_Typ, Fin_Mas_Id);
-- Generate:
-- <Ptr_Typ>FM : aliased Finalization_Master;
Append_To (Actions,
Make_Object_Declaration (Loc,
Defining_Identifier => Fin_Mas_Id,
Aliased_Present => True,
Object_Definition =>
New_Occurrence_Of (RTE (RE_Finalization_Master), Loc)));
-- Set the associated pool and primitive Finalize_Address of the new
-- finalization master. This step is skipped on .NET/JVM because the
-- target does not support storage pools or address arithmetic.
if VM_Target = No_VM then
-- 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;
-- Generate:
-- Set_Base_Pool (<Ptr_Typ>FM, Pool_Id'Unchecked_Access);
Append_To (Actions,
Make_Procedure_Call_Statement (Loc,
Name =>
New_Occurrence_Of (RTE (RE_Set_Base_Pool), Loc),
Parameter_Associations => New_List (
New_Occurrence_Of (Fin_Mas_Id, Loc),
Make_Attribute_Reference (Loc,
Prefix => New_Occurrence_Of (Pool_Id, Loc),
Attribute_Name => Name_Unrestricted_Access))));
-- Finalize_Address is not generated in CodePeer mode because the
-- body contains address arithmetic. Skip this step.
if CodePeer_Mode then
null;
-- Associate the Finalize_Address primitive of the designated type
-- with the finalization master of the access type. The designated
-- type must be forzen as Finalize_Address is generated when the
-- freeze node is expanded.
elsif Is_Frozen (Desig_Typ)
and then Present (Finalize_Address (Desig_Typ))
-- The finalization master of an anonymous access type may need
-- to be inserted in a specific place in the tree. For instance:
-- type Comp_Typ;
-- <finalization master of "access Comp_Typ">
-- type Rec_Typ is record
-- Comp : access Comp_Typ;
-- end record;
-- <freeze node for Comp_Typ>
-- <freeze node for Rec_Typ>
-- Due to this oddity, the anonymous access type is stored for
-- later processing (see below).
and then Ekind (Ptr_Typ) /= E_Anonymous_Access_Type
then
-- Generate:
-- Set_Finalize_Address
-- (<Ptr_Typ>FM, <Desig_Typ>FD'Unrestricted_Access);
Append_To (Actions,
Make_Set_Finalize_Address_Call
(Loc => Loc,
Ptr_Typ => Ptr_Typ));
-- Otherwise the designated type is either anonymous access or a
-- Taft-amendment type and has not been frozen. Store the access
-- type for later processing (see Freeze_Type).
else
Add_Pending_Access_Type (Desig_Typ, Ptr_Typ);
end if;
end if;
-- A finalization master created for an anonymous access type or an
-- access designating a type with private components must be inserted
-- before a context-dependent node.
if For_Anonymous or 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_In (Insertion_Node, 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;
-- Otherwise the finalization master and its initialization become a
-- part of the freeze node.
else
Append_Freeze_Actions (Ptr_Typ, Actions);
end if;
end;
end Build_Finalization_Master;
---------------------
-- Build_Finalizer --
---------------------
procedure Build_Finalizer
(N : Node_Id;
Clean_Stmts : List_Id;
Mark_Id : Entity_Id;
Top_Decls : List_Id;
Defer_Abort : Boolean;
Fin_Id : out Entity_Id)
is
Acts_As_Clean : constant Boolean :=
Present (Mark_Id)
or else
(Present (Clean_Stmts)
and then Is_Non_Empty_List (Clean_Stmts));
Exceptions_OK : constant Boolean :=
not Restriction_Active (No_Exception_Propagation);
For_Package_Body : constant Boolean := Nkind (N) = N_Package_Body;
For_Package_Spec : constant Boolean := Nkind (N) = N_Package_Declaration;
For_Package : constant Boolean :=
For_Package_Body or else For_Package_Spec;
Loc : constant Source_Ptr := Sloc (N);
-- NOTE: Local variable declarations are conservative and do not create
-- structures right from the start. Entities and lists are created once
-- it has been established that N has at least one controlled object.
Components_Built : Boolean := False;
-- A flag used to avoid double initialization of entities and lists. If
-- the flag is set then the following variables have been initialized:
-- Counter_Id
-- Finalizer_Decls
-- Finalizer_Stmts
-- Jump_Alts
Counter_Id : Entity_Id := Empty;
Counter_Val : Int := 0;
-- Name and value of the state counter
Decls : List_Id := No_List;
-- Declarative region of N (if available). If N is a package declaration
-- Decls denotes the visible declarations.
Finalizer_Data : Finalization_Exception_Data;
-- Data for the exception
Finalizer_Decls : List_Id := No_List;
-- Local variable declarations. This list holds the label declarations
-- of all jump block alternatives as well as the declaration of the
-- local exception occurence and the raised flag:
-- E : Exception_Occurrence;
-- Raised : Boolean := False;
-- L<counter value> : label;
Finalizer_Insert_Nod : Node_Id := Empty;
-- Insertion point for the finalizer body. Depending on the context
-- (Nkind of N) and the individual grouping of controlled objects, this
-- node may denote a package declaration or body, package instantiation,
-- block statement or a counter update statement.
Finalizer_Stmts : List_Id := No_List;
-- The statement list of the finalizer body. It contains the following:
--
-- Abort_Defer; -- Added if abort is allowed
-- <call to Prev_At_End> -- Added if exists
-- <cleanup statements> -- Added if Acts_As_Clean
-- <jump block> -- Added if Has_Ctrl_Objs
-- <finalization statements> -- Added if Has_Ctrl_Objs
-- <stack release> -- Added if Mark_Id exists
-- Abort_Undefer; -- Added if abort is allowed
Has_Ctrl_Objs : Boolean := False;
-- A general flag which denotes whether N has at least one controlled
-- object.
Has_Tagged_Types : Boolean := False;
-- A general flag which indicates whether N has at least one library-
-- level tagged type declaration.
HSS : Node_Id := Empty;
-- The sequence of statements of N (if available)
Jump_Alts : List_Id := No_List;
-- Jump block alternatives. Depending on the value of the state counter,
-- the control flow jumps to a sequence of finalization statements. This
-- list contains the following:
--
-- when <counter value> =>
-- goto L<counter value>;
Jump_Block_Insert_Nod : Node_Id := Empty;
-- Specific point in the finalizer statements where the jump block is
-- inserted.
Last_Top_Level_Ctrl_Construct : Node_Id := Empty;
-- The last controlled construct encountered when processing the top
-- level lists of N. This can be a nested package, an instantiation or
-- an object declaration.
Prev_At_End : Entity_Id := Empty;
-- The previous at end procedure of the handled statements block of N
Priv_Decls : List_Id := No_List;
-- The private declarations of N if N is a package declaration
Spec_Id : Entity_Id := Empty;
Spec_Decls : List_Id := Top_Decls;
Stmts : List_Id := No_List;
Tagged_Type_Stmts : List_Id := No_List;
-- Contains calls to Ada.Tags.Unregister_Tag for all library-level
-- tagged types found in N.
-----------------------
-- Local subprograms --
-----------------------
procedure Build_Components;
-- Create all entites and initialize all lists used in the creation of
-- the finalizer.
procedure Create_Finalizer;
-- Create the spec and body of the finalizer and insert them in the
-- proper place in the tree depending on the context.
procedure Process_Declarations
(Decls : List_Id;
Preprocess : Boolean := False;
Top_Level : Boolean := False);
-- Inspect a list of declarations or statements which may contain
-- objects that need finalization. When flag Preprocess is set, the
-- routine will simply count the total number of controlled objects in
-- Decls. Flag Top_Level denotes whether the processing is done for
-- objects in nested package declarations or instances.
procedure Process_Object_Declaration
(Decl : Node_Id;
Has_No_Init : Boolean := False;
Is_Protected : Boolean := False);
-- Generate all the machinery associated with the finalization of a
-- single object. Flag Has_No_Init is used to denote certain contexts
-- where Decl does not have initialization call(s). Flag Is_Protected
-- is set when Decl denotes a simple protected object.
procedure Process_Tagged_Type_Declaration (Decl : Node_Id);
-- Generate all the code necessary to unregister the external tag of a
-- tagged type.
----------------------
-- Build_Components --
----------------------
procedure Build_Components is
Counter_Decl : Node_Id;
Counter_Typ : Entity_Id;
Counter_Typ_Decl : Node_Id;
begin
pragma Assert (Present (Decls));
-- This routine might be invoked several times when dealing with
-- constructs that have two lists (either two declarative regions
-- or declarations and statements). Avoid double initialization.
if Components_Built then
return;
end if;
Components_Built := True;
if Has_Ctrl_Objs then
-- Create entities for the counter, its type, the local exception
-- and the raised flag.
Counter_Id := Make_Temporary (Loc, 'C');
Counter_Typ := Make_Temporary (Loc, 'T');
Finalizer_Decls := New_List;
Build_Object_Declarations
(Finalizer_Data, Finalizer_Decls, Loc, For_Package);
-- Since the total number of controlled objects is always known,
-- build a subtype of Natural with precise bounds. This allows
-- the backend to optimize the case statement. Generate:
--
-- subtype Tnn is Natural range 0 .. Counter_Val;
Counter_Typ_Decl :=
Make_Subtype_Declaration (Loc,
Defining_Identifier => Counter_Typ,
Subtype_Indication =>
Make_Subtype_Indication (Loc,
Subtype_Mark => New_Occurrence_Of (Standard_Natural, Loc),
Constraint =>
Make_Range_Constraint (Loc,
Range_Expression =>
Make_Range (Loc,
Low_Bound =>
Make_Integer_Literal (Loc, Uint_0),
High_Bound =>
Make_Integer_Literal (Loc, Counter_Val)))));
-- Generate the declaration of the counter itself:
--
-- Counter : Integer := 0;
Counter_Decl :=
Make_Object_Declaration (Loc,
Defining_Identifier => Counter_Id,
Object_Definition => New_Occurrence_Of (Counter_Typ, Loc),
Expression => Make_Integer_Literal (Loc, 0));
-- Set the type of the counter explicitly to prevent errors when
-- examining object declarations later on.
Set_Etype (Counter_Id, Counter_Typ);
-- The counter and its type are inserted before the source
-- declarations of N.
Prepend_To (Decls, Counter_Decl);
Prepend_To (Decls, Counter_Typ_Decl);
-- The counter and its associated type must be manually analized
-- since N has already been analyzed. Use the scope of the spec
-- when inserting in a package.
if For_Package then
Push_Scope (Spec_Id);
Analyze (Counter_Typ_Decl);
Analyze (Counter_Decl);
Pop_Scope;
else
Analyze (Counter_Typ_Decl);
Analyze (Counter_Decl);
end if;
Jump_Alts := New_List;
end if;
-- If the context requires additional clean up, the finalization
-- machinery is added after the clean up code.
if Acts_As_Clean then
Finalizer_Stmts := Clean_Stmts;
Jump_Block_Insert_Nod := Last (Finalizer_Stmts);
else
Finalizer_Stmts := New_List;
end if;
if Has_Tagged_Types then
Tagged_Type_Stmts := New_List;
end if;
end Build_Components;
----------------------
-- Create_Finalizer --
----------------------
procedure Create_Finalizer is
Body_Id : Entity_Id;
Fin_Body : Node_Id;
Fin_Spec : Node_Id;
Jump_Block : Node_Id;
Label : Node_Id;
Label_Id : Entity_Id;
function New_Finalizer_Name return Name_Id;
-- Create a fully qualified name of a package spec or body finalizer.
-- The generated name is of the form: xx__yy__finalize_[spec|body].
------------------------
-- New_Finalizer_Name --
------------------------
function New_Finalizer_Name return Name_Id is
procedure New_Finalizer_Name (Id : Entity_Id);
-- Place "__<name-of-Id>" in the name buffer. If the identifier
-- has a non-standard scope, process the scope first.
------------------------
-- New_Finalizer_Name --
------------------------
procedure New_Finalizer_Name (Id : Entity_Id) is
begin
if Scope (Id) = Standard_Standard then
Get_Name_String (Chars (Id));
else
New_Finalizer_Name (Scope (Id));
Add_Str_To_Name_Buffer ("__");
Add_Str_To_Name_Buffer (Get_Name_String (Chars (Id)));
end if;
end New_Finalizer_Name;
-- Start of processing for New_Finalizer_Name
begin
-- Create the fully qualified name of the enclosing scope
New_Finalizer_Name (Spec_Id);
-- Generate:
-- __finalize_[spec|body]
Add_Str_To_Name_Buffer ("__finalize_");
if For_Package_Spec then
Add_Str_To_Name_Buffer ("spec");
else
Add_Str_To_Name_Buffer ("body");
end if;
return Name_Find;
end New_Finalizer_Name;
-- Start of processing for Create_Finalizer
begin
-- Step 1: Creation of the finalizer name
-- Packages must use a distinct name for their finalizers since the
-- binder will have to generate calls to them by name. The name is
-- of the following form:
-- xx__yy__finalize_[spec|body]
if For_Package then
Fin_Id := Make_Defining_Identifier (Loc, New_Finalizer_Name);
Set_Has_Qualified_Name (Fin_Id);
Set_Has_Fully_Qualified_Name (Fin_Id);
-- The default name is _finalizer
else
Fin_Id :=
Make_Defining_Identifier (Loc,
Chars => New_External_Name (Name_uFinalizer));
-- The visibility semantics of AT_END handlers force a strange
-- separation of spec and body for stack-related finalizers:
-- declare : Enclosing_Scope
-- procedure _finalizer;
-- begin
-- <controlled objects>
-- procedure _finalizer is
-- ...
-- at end
-- _finalizer;
-- end;
-- Both spec and body are within the same construct and scope, but
-- the body is part of the handled sequence of statements. This
-- placement confuses the elaboration mechanism on targets where
-- AT_END handlers are expanded into "when all others" handlers:
-- exception
-- when all others =>
-- _finalizer; -- appears to require elab checks
-- at end
-- _finalizer;
-- end;
-- Since the compiler guarantees that the body of a _finalizer is
-- always inserted in the same construct where the AT_END handler
-- resides, there is no need for elaboration checks.
Set_Kill_Elaboration_Checks (Fin_Id);
end if;
-- Step 2: Creation of the finalizer specification
-- Generate:
-- procedure Fin_Id;
Fin_Spec :=
Make_Subprogram_Declaration (Loc,
Specification =>
Make_Procedure_Specification (Loc,
Defining_Unit_Name => Fin_Id));
-- Step 3: Creation of the finalizer body
if Has_Ctrl_Objs then
-- Add L0, the default destination to the jump block
Label_Id := Make_Identifier (Loc, New_External_Name ('L', 0));
Set_Entity (Label_Id,
Make_Defining_Identifier (Loc, Chars (Label_Id)));
Label := Make_Label (Loc, Label_Id);
-- Generate:
-- L0 : label;
Prepend_To (Finalizer_Decls,
Make_Implicit_Label_Declaration (Loc,
Defining_Identifier => Entity (Label_Id),
Label_Construct => Label));
-- Generate:
-- when others =>
-- goto L0;
Append_To (Jump_Alts,
Make_Case_Statement_Alternative (Loc,
Discrete_Choices => New_List (Make_Others_Choice (Loc)),
Statements => New_List (
Make_Goto_Statement (Loc,
Name => New_Occurrence_Of (Entity (Label_Id), Loc)))));
-- Generate:
-- <<L0>>
Append_To (Finalizer_Stmts, Label);
-- Create the jump block which controls the finalization flow
-- depending on the value of the state counter.
Jump_Block :=
Make_Case_Statement (Loc,
Expression => Make_Identifier (Loc, Chars (Counter_Id)),
Alternatives => Jump_Alts);
if Acts_As_Clean and then Present (Jump_Block_Insert_Nod) then
Insert_After (Jump_Block_Insert_Nod, Jump_Block);
else
Prepend_To (Finalizer_Stmts, Jump_Block);
end if;
end if;
-- Add the library-level tagged type unregistration machinery before
-- the jump block circuitry. This ensures that external tags will be
-- removed even if a finalization exception occurs at some point.
if Has_Tagged_Types then
Prepend_List_To (Finalizer_Stmts, Tagged_Type_Stmts);
end if;
-- Add a call to the previous At_End handler if it exists. The call
-- must always precede the jump block.
if Present (Prev_At_End) then
Prepend_To (Finalizer_Stmts,
Make_Procedure_Call_Statement (Loc, Prev_At_End));
-- Clear the At_End handler since we have already generated the
-- proper replacement call for it.
Set_At_End_Proc (HSS, Empty);
end if;
-- Release the secondary stack mark
if Present (Mark_Id) then
Append_To (Finalizer_Stmts, Build_SS_Release_Call (Loc, Mark_Id));
end if;
-- Protect the statements with abort defer/undefer. This is only when
-- aborts are allowed and the clean up statements require deferral or
-- there are controlled objects to be finalized.
if Abort_Allowed and then (Defer_Abort or Has_Ctrl_Objs) then
Prepend_To (Finalizer_Stmts,
Make_Procedure_Call_Statement (Loc,
Name => New_Occurrence_Of (RTE (RE_Abort_Defer), Loc)));
Append_To (Finalizer_Stmts,
Make_Procedure_Call_Statement (Loc,
Name => New_Occurrence_Of (RTE (RE_Abort_Undefer), Loc)));
end if;
-- The local exception does not need to be reraised for library-level
-- finalizers. Note that this action must be carried out after object
-- clean up, secondary stack release and abort undeferral. Generate:
-- if Raised and then not Abort then
-- Raise_From_Controlled_Operation (E);
-- end if;
if Has_Ctrl_Objs and Exceptions_OK and not For_Package then
Append_To (Finalizer_Stmts,
Build_Raise_Statement (Finalizer_Data));
end if;
-- Generate:
-- procedure Fin_Id is
-- Abort : constant Boolean := Triggered_By_Abort;
-- <or>
-- Abort : constant Boolean := False; -- no abort
-- E : Exception_Occurrence; -- All added if flag
-- Raised : Boolean := False; -- Has_Ctrl_Objs is set
-- L0 : label;
-- ...
-- Lnn : label;
-- begin
-- Abort_Defer; -- Added if abort is allowed
-- <call to Prev_At_End> -- Added if exists
-- <cleanup statements> -- Added if Acts_As_Clean
-- <jump block> -- Added if Has_Ctrl_Objs
-- <finalization statements> -- Added if Has_Ctrl_Objs
-- <stack release> -- Added if Mark_Id exists
-- Abort_Undefer; -- Added if abort is allowed
-- <exception propagation> -- Added if Has_Ctrl_Objs
-- end Fin_Id;
-- Create the body of the finalizer
Body_Id := Make_Defining_Identifier (Loc, Chars (Fin_Id));
if For_Package then
Set_Has_Qualified_Name (Body_Id);
Set_Has_Fully_Qualified_Name (Body_Id);
end if;
Fin_Body :=
Make_Subprogram_Body (Loc,
Specification =>
Make_Procedure_Specification (Loc,
Defining_Unit_Name => Body_Id),
Declarations => Finalizer_Decls,
Handled_Statement_Sequence =>
Make_Handled_Sequence_Of_Statements (Loc, Finalizer_Stmts));
-- Step 4: Spec and body insertion, analysis
if For_Package then
-- If the package spec has private declarations, the finalizer
-- body must be added to the end of the list in order to have
-- visibility of all private controlled objects.
if For_Package_Spec then
if Present (Priv_Decls) then
Append_To (Priv_Decls, Fin_Spec);
Append_To (Priv_Decls, Fin_Body);
else
Append_To (Decls, Fin_Spec);
Append_To (Decls, Fin_Body);
end if;
-- For package bodies, both the finalizer spec and body are
-- inserted at the end of the package declarations.
else
Append_To (Decls, Fin_Spec);
Append_To (Decls, Fin_Body);
end if;
-- Push the name of the package
Push_Scope (Spec_Id);
Analyze (Fin_Spec);
Analyze (Fin_Body);
Pop_Scope;
-- Non-package case
else
-- Create the spec for the finalizer. The At_End handler must be
-- able to call the body which resides in a nested structure.
-- Generate:
-- declare
-- procedure Fin_Id; -- Spec
-- begin
-- <objects and possibly statements>
-- procedure Fin_Id is ... -- Body
-- <statements>
-- at end
-- Fin_Id; -- At_End handler
-- end;
pragma Assert (Present (Spec_Decls));
Append_To (Spec_Decls, Fin_Spec);
Analyze (Fin_Spec);
-- When the finalizer acts solely as a clean up routine, the body
-- is inserted right after the spec.
if Acts_As_Clean and not Has_Ctrl_Objs then
Insert_After (Fin_Spec, Fin_Body);
-- In all other cases the body is inserted after either:
--
-- 1) The counter update statement of the last controlled object
-- 2) The last top level nested controlled package
-- 3) The last top level controlled instantiation
else
-- Manually freeze the spec. This is somewhat of a hack because
-- a subprogram is frozen when its body is seen and the freeze
-- node appears right before the body. However, in this case,
-- the spec must be frozen earlier since the At_End handler
-- must be able to call it.
--
-- declare
-- procedure Fin_Id; -- Spec
-- [Fin_Id] -- Freeze node
-- begin
-- ...
-- at end
-- Fin_Id; -- At_End handler
-- end;
Ensure_Freeze_Node (Fin_Id);
Insert_After (Fin_Spec, Freeze_Node (Fin_Id));
Set_Is_Frozen (Fin_Id);
-- In the case where the last construct to contain a controlled
-- object is either a nested package, an instantiation or a
-- freeze node, the body must be inserted directly after the
-- construct.
if Nkind_In (Last_Top_Level_Ctrl_Construct,
N_Freeze_Entity,
N_Package_Declaration,
N_Package_Body)
then
Finalizer_Insert_Nod := Last_Top_Level_Ctrl_Construct;
end if;
Insert_After (Finalizer_Insert_Nod, Fin_Body);
end if;
Analyze (Fin_Body);
end if;
end Create_Finalizer;
--------------------------
-- Process_Declarations --
--------------------------
procedure Process_Declarations
(Decls : List_Id;
Preprocess : Boolean := False;
Top_Level : Boolean := False)
is
Decl : Node_Id;
Expr : Node_Id;
Obj_Id : Entity_Id;
Obj_Typ : Entity_Id;
Pack_Id : Entity_Id;
Spec : Node_Id;
Typ : Entity_Id;
Old_Counter_Val : Int;
-- This variable is used to determine whether a nested package or
-- instance contains at least one controlled object.
procedure Processing_Actions
(Has_No_Init : Boolean := False;
Is_Protected : Boolean := False);
-- Depending on the mode of operation of Process_Declarations, either
-- increment the controlled object counter, set the controlled object
-- flag and store the last top level construct or process the current
-- declaration. Flag Has_No_Init is used to propagate scenarios where
-- the current declaration may not have initialization proc(s). Flag
-- Is_Protected should be set when the current declaration denotes a
-- simple protected object.
------------------------
-- Processing_Actions --
------------------------
procedure Processing_Actions
(Has_No_Init : Boolean := False;
Is_Protected : Boolean := False)
is
begin
-- Library-level tagged type
if Nkind (Decl) = N_Full_Type_Declaration then
if Preprocess then
Has_Tagged_Types := True;
if Top_Level and then No (Last_Top_Level_Ctrl_Construct) then
Last_Top_Level_Ctrl_Construct := Decl;
end if;
else
Process_Tagged_Type_Declaration (Decl);
end if;
-- Controlled object declaration
else
if Preprocess then
Counter_Val := Counter_Val + 1;
Has_Ctrl_Objs := True;
if Top_Level and then No (Last_Top_Level_Ctrl_Construct) then
Last_Top_Level_Ctrl_Construct := Decl;
end if;
else
Process_Object_Declaration (Decl, Has_No_Init, Is_Protected);
end if;
end if;
end Processing_Actions;
-- Start of processing for Process_Declarations
begin
if No (Decls) or else Is_Empty_List (Decls) then
return;
end if;
-- Process all declarations in reverse order
Decl := Last_Non_Pragma (Decls);
while Present (Decl) loop
-- Library-level tagged types
if Nkind (Decl) = N_Full_Type_Declaration then
Typ := Defining_Identifier (Decl);
-- 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 RTE_Available (RE_Register_Tag)
and then not Is_Abstract_Type (Typ)
and then not No_Run_Time_Mode
then
Processing_Actions;
end if;
-- Regular object declarations
elsif Nkind (Decl) = N_Object_Declaration then
Obj_Id := Defining_Identifier (Decl);
Obj_Typ := Base_Type (Etype (Obj_Id));
Expr := Expression (Decl);
-- Bypass any form of processing for objects which have their
-- finalization disabled. This applies only to objects at the
-- library level.
if For_Package and then Finalize_Storage_Only (Obj_Typ) then
null;
-- Transient variables are treated separately in order to
-- minimize the size of the generated code. For details, see
-- Process_Transient_Objects.
elsif Is_Processed_Transient (Obj_Id) then
null;
-- 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;
-- The object is of the form:
-- Obj : Typ [:= Expr];
-- Do not process the incomplete view of a deferred constant.
-- Do not consider tag-to-class-wide conversions.
elsif not Is_Imported (Obj_Id)
and then Needs_Finalization (Obj_Typ)
and then not (Ekind (Obj_Id) = E_Constant
and then not Has_Completion (Obj_Id))
and then not Is_Tag_To_Class_Wide_Conversion (Obj_Id)
then
Processing_Actions;
-- The object is of the form:
-- Obj : Access_Typ := Non_BIP_Function_Call'reference;
-- Obj : Access_Typ :=
-- BIP_Function_Call (BIPalloc => 2, ...)'reference;
elsif Is_Access_Type (Obj_Typ)
and then Needs_Finalization
(Available_View (Designated_Type (Obj_Typ)))
and then Present (Expr)
and then
(Is_Secondary_Stack_BIP_Func_Call (Expr)
or else
(Is_Non_BIP_Func_Call (Expr)
and then not Is_Related_To_Func_Return (Obj_Id)))
then
Processing_Actions (Has_No_Init => True);
-- Processing for "hook" objects generated for controlled
-- transients declared inside an Expression_With_Actions.
elsif Is_Access_Type (Obj_Typ)
and then Present (Status_Flag_Or_Transient_Decl (Obj_Id))
and then Nkind (Status_Flag_Or_Transient_Decl (Obj_Id)) =
N_Object_Declaration
then
Processing_Actions (Has_No_Init => True);
-- Process intermediate results of an if expression with one
-- of the alternatives using a controlled function call.
elsif Is_Access_Type (Obj_Typ)
and then Present (Status_Flag_Or_Transient_Decl (Obj_Id))
and then Nkind (Status_Flag_Or_Transient_Decl (Obj_Id)) =
N_Defining_Identifier
and then Present (Expr)
and then Nkind (Expr) = N_Null
then
Processing_Actions (Has_No_Init => True);
-- Simple protected objects which use type System.Tasking.
-- Protected_Objects.Protection to manage their locks should
-- be treated as controlled since they require manual cleanup.
-- The only exception is illustrated in the following example:
-- package Pkg is
-- type Ctrl is new Controlled ...
-- procedure Finalize (Obj : in out Ctrl);
-- Lib_Obj : Ctrl;
-- end Pkg;
-- package body Pkg is
-- protected Prot is
-- procedure Do_Something (Obj : in out Ctrl);
-- end Prot;
-- protected body Prot is
-- procedure Do_Something (Obj : in out Ctrl) is ...
-- end Prot;
-- procedure Finalize (Obj : in out Ctrl) is
-- begin
-- Prot.Do_Something (Obj);
-- end Finalize;
-- end Pkg;
-- Since for the most part entities in package bodies depend on
-- those in package specs, Prot's lock should be cleaned up
-- first. The subsequent cleanup of the spec finalizes Lib_Obj.
-- This act however attempts to invoke Do_Something and fails
-- because the lock has disappeared.
elsif Ekind (Obj_Id) = E_Variable
and then not In_Library_Level_Package_Body (Obj_Id)
and then (Is_Simple_Protected_Type (Obj_Typ)
or else Has_Simple_Protected_Object (Obj_Typ))
then
Processing_Actions (Is_Protected => True);
end if;
-- Specific cases of object renamings
elsif Nkind (Decl) = N_Object_Renaming_Declaration then
Obj_Id := Defining_Identifier (Decl);
Obj_Typ := Base_Type (Etype (Obj_Id));
-- Bypass any form of processing for objects which have their
-- finalization disabled. This applies only to objects at the
-- library level.
if For_Package and then Finalize_Storage_Only (Obj_Typ) then
null;
-- Ignored Ghost object renamings do not need any cleanup
-- actions because they will not appear in the final tree.
elsif Is_Ignored_Ghost_Entity (Obj_Id) then
null;
-- Return object of a build-in-place function. This case is
-- recognized and marked by the expansion of an extended return
-- statement (see Expand_N_Extended_Return_Statement).
elsif Needs_Finalization (Obj_Typ)
and then Is_Return_Object (Obj_Id)
and then Present (Status_Flag_Or_Transient_Decl (Obj_Id))
then
Processing_Actions (Has_No_Init => True);
-- Detect a case where a source object has been initialized by
-- a controlled function call or another object which was later
-- rewritten as a class-wide conversion of Ada.Tags.Displace.
-- Obj1 : CW_Type := Src_Obj;
-- Obj2 : CW_Type := Function_Call (...);
-- Obj1 : CW_Type renames (... Ada.Tags.Displace (Src_Obj));
-- Tmp : ... := Function_Call (...)'reference;
-- Obj2 : CW_Type renames (... Ada.Tags.Displace (Tmp));
elsif Is_Displacement_Of_Object_Or_Function_Result (Obj_Id) then
Processing_Actions (Has_No_Init => True);
end if;
-- Inspect the freeze node of an access-to-controlled type and
-- look for a delayed finalization master. This case arises when
-- the freeze actions are inserted at a later time than the
-- expansion of the context. Since Build_Finalizer is never called
-- on a single construct twice, the master will be ultimately
-- left out and never finalized. This is also needed for freeze
-- actions of designated types themselves, since in some cases the
-- finalization master is associated with a designated type's
-- freeze node rather than that of the access type (see handling
-- for freeze actions in Build_Finalization_Master).
elsif Nkind (Decl) = N_Freeze_Entity
and then Present (Actions (Decl))
then
Typ := Entity (Decl);
-- 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_Type (Typ)
and then not Is_Access_Subprogram_Type (Typ)
and then Needs_Finalization
(Available_View (Designated_Type (Typ))))
or else (Is_Type (Typ) and then Needs_Finalization (Typ))
then
Old_Counter_Val := Counter_Val;
-- Freeze nodes are considered to be identical to packages
-- and blocks in terms of nesting. The difference is that
-- a finalization master created inside the freeze node is
-- at the same nesting level as the node itself.
Process_Declarations (Actions (Decl), Preprocess);
-- The freeze node contains a finalization master
if Preprocess
and then Top_Level
and then No (Last_Top_Level_Ctrl_Construct)
and then Counter_Val > Old_Counter_Val
then
Last_Top_Level_Ctrl_Construct := Decl;
end if;
end if;
-- Nested package declarations, avoid generics
elsif Nkind (Decl) = N_Package_Declaration then
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
Old_Counter_Val := Counter_Val;
Process_Declarations
(Private_Declarations (Spec), Preprocess);
Process_Declarations
(Visible_Declarations (Spec), Preprocess);
-- Either the visible or the private declarations contain a
-- controlled object. The nested package declaration is the
-- last such construct.
if Preprocess
and then Top_Level
and then No (Last_Top_Level_Ctrl_Construct)
and then Counter_Val > Old_Counter_Val
then
Last_Top_Level_Ctrl_Construct := Decl;
end if;
end if;
-- Nested package bodies, avoid generics
elsif Nkind (Decl) = N_Package_Body then
-- 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
Old_Counter_Val := Counter_Val;
Process_Declarations (Declarations (Decl), Preprocess);
-- The nested package body is the last construct to contain
-- a controlled object.
if Preprocess
and then Top_Level
and then No (Last_Top_Level_Ctrl_Construct)
and then Counter_Val > Old_Counter_Val
then
Last_Top_Level_Ctrl_Construct := Decl;
end if;
end if;
-- Handle a rare case caused by a controlled transient variable
-- created as part of a record init proc. The variable is wrapped
-- in a block, but the block is not associated with a transient
-- scope.
elsif Nkind (Decl) = N_Block_Statement
and then Inside_Init_Proc
then
Old_Counter_Val := Counter_Val;
if Present (Handled_Statement_Sequence (Decl)) then
Process_Declarations
(Statements (Handled_Statement_Sequence (Decl)),
Preprocess);
end if;
Process_Declarations (Declarations (Decl), Preprocess);
-- Either the declaration or statement list of the block has a
-- controlled object.
if Preprocess
and then Top_Level
and then No (Last_Top_Level_Ctrl_Construct)
and then Counter_Val > Old_Counter_Val
then
Last_Top_Level_Ctrl_Construct := Decl;
end if;
-- Handle the case where the original context has been wrapped in
-- a block to avoid interference between exception handlers and
-- At_End handlers. Treat the block as transparent and process its
-- contents.
elsif Nkind (Decl) = N_Block_Statement
and then Is_Finalization_Wrapper (Decl)
then
if Present (Handled_Statement_Sequence (Decl)) then
Process_Declarations
(Statements (Handled_Statement_Sequence (Decl)),
Preprocess);
end if;
Process_Declarations (Declarations (Decl), Preprocess);
end if;
Prev_Non_Pragma (Decl);
end loop;
end Process_Declarations;
--------------------------------
-- Process_Object_Declaration --
--------------------------------
procedure Process_Object_Declaration
(Decl : Node_Id;
Has_No_Init : Boolean := False;
Is_Protected : Boolean := False)
is
Loc : constant Source_Ptr := Sloc (Decl);
Obj_Id : constant Entity_Id := Defining_Identifier (Decl);
Init_Typ : Entity_Id;
-- The initialization type of the related object declaration. Note
-- that this is not necessarely the same type as Obj_Typ because of
-- possible type derivations.
Obj_Typ : Entity_Id;
-- The type of the related object declaration
function Build_BIP_Cleanup_Stmts (Func_Id : Entity_Id) return Node_Id;
-- Func_Id denotes a build-in-place function. Generate the following
-- cleanup code:
--
-- if BIPallocfrom > Secondary_Stack'Pos
-- and then BIPfinalizationmaster /= null
-- then
-- declare
-- type Ptr_Typ is access Obj_Typ;
-- for Ptr_Typ'Storage_Pool
-- use Base_Pool (BIPfinalizationmaster);
-- begin
-- Free (Ptr_Typ (Temp));
-- end;
-- end if;
--
-- Obj_Typ is the type of the current object, Temp is the original
-- allocation which Obj_Id renames.
procedure Find_Last_Init
(Last_Init : out Node_Id;
Body_Insert : out Node_Id);
-- Find the last initialization call related to object declaration
-- Decl. Last_Init denotes the last initialization call which follows
-- Decl. Body_Insert denotes a node where the finalizer body could be
-- potentially inserted after (if blocks are involved).
-----------------------------
-- Build_BIP_Cleanup_Stmts --
-----------------------------
function Build_BIP_Cleanup_Stmts
(Func_Id : Entity_Id) return Node_Id
is
Decls : constant List_Id := New_List;
Fin_Mas_Id : constant Entity_Id :=
Build_In_Place_Formal
(Func_Id, BIP_Finalization_Master);
Func_Typ : constant Entity_Id := Etype (Func_Id);
Temp_Id : constant Entity_Id :=
Entity (Prefix (Name (Parent (Obj_Id))));
Cond : Node_Id;
Free_Blk : Node_Id;
Free_Stmt : Node_Id;
Pool_Id : Entity_Id;
Ptr_Typ : Entity_Id;
begin
-- Generate:
-- Pool_Id renames Base_Pool (BIPfinalizationmaster.all).all;
Pool_Id := Make_Temporary (Loc, 'P');
Append_To (Decls,
Make_Object_Renaming_Declaration (Loc,
Defining_Identifier => Pool_Id,
Subtype_Mark =>
New_Occurrence_Of (RTE (RE_Root_Storage_Pool), Loc),
Name =>
Make_Explicit_Dereference (Loc,
Prefix =>
Make_Function_Call (Loc,
Name =>
New_Occurrence_Of (RTE (RE_Base_Pool), Loc),
Parameter_Associations => New_List (
Make_Explicit_Dereference (Loc,
Prefix =>
New_Occurrence_Of (Fin_Mas_Id, Loc)))))));
-- Create an access type which uses the storage pool of the
-- caller's finalization master.
-- Generate:
-- type Ptr_Typ is access 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 master and the
-- storage pool attributes.
Set_Ekind (Ptr_Typ, E_Access_Type);
Set_Finalization_Master (Ptr_Typ, Fin_Mas_Id);
Set_Associated_Storage_Pool (Ptr_Typ, Pool_Id);
-- Create an explicit free statement. Note that the free uses the
-- caller's pool expressed as a renaming.
Free_Stmt :=
Make_Free_Statement (Loc,
Expression =>
Unchecked_Convert_To (Ptr_Typ,
New_Occurrence_Of (Temp_Id, Loc)));
Set_Storage_Pool (Free_Stmt, Pool_Id);
-- Create a block to house the dummy type and the instantiation as
-- well as to perform the cleanup the temporary.
-- Generate:
-- declare
-- <Decls>
-- begin
-- Free (Ptr_Typ (Temp_Id));
-- end;
Free_Blk :=
Make_Block_Statement (Loc,
Declarations => Decls,
Handled_Statement_Sequence =>
Make_Handled_Sequence_Of_Statements (Loc,
Statements => New_List (Free_Stmt)));
-- Generate:
-- if BIPfinalizationmaster /= null then
Cond :=
Make_Op_Ne (Loc,
Left_Opnd => New_Occurrence_Of (Fin_Mas_Id, Loc),
Right_Opnd => Make_Null (Loc));
-- For constrained or tagged results escalate the condition to
-- include the allocation format. Generate:
-- if BIPallocform > Secondary_Stack'Pos
-- and then BIPfinalizationmaster /= null
-- then
if not Is_Constrained (Func_Typ)
or else Is_Tagged_Type (Func_Typ)
then
declare
Alloc : constant Entity_Id :=
Build_In_Place_Formal (Func_Id, BIP_Alloc_Form);
begin
Cond :=
Make_And_Then (Loc,
Left_Opnd =>
Make_Op_Gt (Loc,
Left_Opnd => New_Occurrence_Of (Alloc, Loc),
Right_Opnd =>
Make_Integer_Literal (Loc,
UI_From_Int
(BIP_Allocation_Form'Pos (Secondary_Stack)))),
Right_Opnd => Cond);
end;
end if;
-- Generate:
-- if <Cond> then
-- <Free_Blk>
-- end if;
return
Make_If_Statement (Loc,
Condition => Cond,
Then_Statements => New_List (Free_Blk));
end Build_BIP_Cleanup_Stmts;
--------------------
-- Find_Last_Init --
--------------------
procedure Find_Last_Init
(Last_Init : out Node_Id;
Body_Insert : out Node_Id)
is
function Find_Last_Init_In_Block (Blk : Node_Id) return Node_Id;
-- Find the last initialization call within the statements of
-- block Blk.
function Is_Init_Call (N : Node_Id) return Boolean;
-- Determine whether node N denotes one of the initialization
-- procedures of types Init_Typ or Obj_Typ.
function Next_Suitable_Statement (Stmt : Node_Id) return Node_Id;
-- Given a statement which is part of a list, return the next
-- statement while skipping over dynamic elab checks.
-----------------------------
-- Find_Last_Init_In_Block --
-----------------------------
function Find_Last_Init_In_Block (Blk : Node_Id) return Node_Id is
HSS : constant Node_Id := Handled_Statement_Sequence (Blk);
Stmt : Node_Id;
begin
-- Examine the individual statements of the block in reverse to
-- locate the last initialization call.
if Present (HSS) and then Present (Statements (HSS)) then
Stmt := Last (Statements (HSS));
while Present (Stmt) loop
-- Peek inside nested blocks in case aborts are allowed
if Nkind (Stmt) = N_Block_Statement then
return Find_Last_Init_In_Block (Stmt);
elsif Is_Init_Call (Stmt) then
return Stmt;
end if;
Prev (Stmt);
end loop;
end if;
return Empty;
end Find_Last_Init_In_Block;
------------------
-- Is_Init_Call --
------------------
function Is_Init_Call (N : Node_Id) return Boolean is
function Is_Init_Proc_Of
(Subp_Id : Entity_Id;
Typ : Entity_Id) return Boolean;
-- Determine whether subprogram Subp_Id is a valid init proc of
-- type Typ.
---------------------
-- Is_Init_Proc_Of --
---------------------
function Is_Init_Proc_Of
(Subp_Id : Entity_Id;
Typ : Entity_Id) return Boolean
is
Deep_Init : Entity_Id := Empty;
Prim_Init : Entity_Id := Empty;
Type_Init : Entity_Id := Empty;
begin
-- Obtain all possible initialization routines of the
-- related type and try to match the subprogram entity
-- against one of them.
-- Deep_Initialize
Deep_Init := TSS (Typ, TSS_Deep_Initialize);
-- Primitive Initialize
if Is_Controlled (Typ) then
Prim_Init := Find_Prim_Op (Typ, Name_Initialize);
if Present (Prim_Init) then
Prim_Init := Ultimate_Alias (Prim_Init);
end if;
end if;
-- Type initialization routine
if Has_Non_Null_Base_Init_Proc (Typ) then
Type_Init := Base_Init_Proc (Typ);
end if;
return
(Present (Deep_Init) and then Subp_Id = Deep_Init)
or else
(Present (Prim_Init) and then Subp_Id = Prim_Init)
or else
(Present (Type_Init) and then Subp_Id = Type_Init);
end Is_Init_Proc_Of;
-- Local variables
Call_Id : Entity_Id;
-- Start of processing for Is_Init_Call
begin
if Nkind (N) = N_Procedure_Call_Statement
and then Nkind (Name (N)) = N_Identifier
then
Call_Id := Entity (Name (N));
-- Consider both the type of the object declaration and its
-- related initialization type.
return
Is_Init_Proc_Of (Call_Id, Init_Typ)
or else
Is_Init_Proc_Of (Call_Id, Obj_Typ);
end if;
return False;
end Is_Init_Call;
-----------------------------
-- Next_Suitable_Statement --
-----------------------------
function Next_Suitable_Statement (Stmt : Node_Id) return Node_Id is
Result : Node_Id := Next (Stmt);
begin
-- Skip over access-before-elaboration checks
if Dynamic_Elaboration_Checks
and then Nkind (Result) = N_Raise_Program_Error
then
Result := Next (Result);
end if;
return Result;
end Next_Suitable_Statement;
-- Local variables
Call : Node_Id;
Stmt : Node_Id;
Stmt_2 : Node_Id;
Deep_Init_Found : Boolean := False;
-- A flag set when a call to [Deep_]Initialize has been found
-- Start of processing for Find_Last_Init
begin
Last_Init := Decl;
Body_Insert := Empty;
-- Object renamings and objects associated with controlled
-- function results do not require initialization.
if Has_No_Init then
return;
end if;
Stmt := Next_Suitable_Statement (Decl);
-- A limited controlled object initialized by a function call uses
-- the build-in-place machinery to obtain its value.
-- Obj : Lim_Controlled_Type := Func_Call;
-- is expanded into
-- Obj : Lim_Controlled_Type;
-- type Ptr_Typ is access Lim_Controlled_Type;
-- Temp : constant Ptr_Typ :=
-- Func_Call
-- (BIPalloc => 1,
-- BIPaccess => Obj'Unrestricted_Access)'reference;
-- In this scenario the declaration of the temporary acts as the
-- last initialization statement.
if Is_Limited_Type (Obj_Typ)
and then Has_Init_Expression (Decl)
and then No (Expression (Decl))
then
while Present (Stmt) loop
if Nkind (Stmt) = N_Object_Declaration
and then Present (Expression (Stmt))
and then Is_Object_Access_BIP_Func_Call
(Expr => Expression (Stmt),
Obj_Id => Obj_Id)
then
Last_Init := Stmt;
exit;
end if;
Next (Stmt);
end loop;
-- Nothing to do for an object with supporessed initialization.
-- Note that this check is not performed at the beginning of the
-- routine because a declaration marked with No_Initialization
-- may still be initialized by a build-in-place call (the case
-- above).
elsif No_Initialization (Decl) then
return;
-- In all other cases the initialization calls follow the related
-- object. The general structure of object initialization built by
-- routine Default_Initialize_Object is as follows:
-- [begin -- aborts allowed
-- Abort_Defer;]
-- Type_Init_Proc (Obj);
-- [begin] -- exceptions allowed
-- Deep_Initialize (Obj);
-- [exception -- exceptions allowed
-- when others =>
-- Deep_Finalize (Obj, Self => False);
-- raise;
-- end;]
-- [at end -- aborts allowed
-- Abort_Undefer;
-- end;]
-- When aborts are allowed, the initialization calls are housed
-- within a block.
elsif Nkind (Stmt) = N_Block_Statement then
Last_Init := Find_Last_Init_In_Block (Stmt);
Body_Insert := Stmt;
-- Otherwise the initialization calls follow the related object
else
Stmt_2 := Next_Suitable_Statement (Stmt);
-- Check for an optional call to Deep_Initialize which may
-- appear within a block depending on whether the object has
-- controlled components.
if Present (Stmt_2) then
if Nkind (Stmt_2) = N_Block_Statement then
Call := Find_Last_Init_In_Block (Stmt_2);
if Present (Call) then
Deep_Init_Found := True;
Last_Init := Call;
Body_Insert := Stmt_2;
end if;
elsif Is_Init_Call (Stmt_2) then
Deep_Init_Found := True;
Last_Init := Stmt_2;
Body_Insert := Last_Init;
end if;
end if;
-- If the object lacks a call to Deep_Initialize, then it must
-- have a call to its related type init proc.
if not Deep_Init_Found and then Is_Init_Call (Stmt) then
Last_Init := Stmt;
Body_Insert := Last_Init;
end if;
end if;
end Find_Last_Init;
-- Local variables
Body_Ins : Node_Id;
Count_Ins : Node_Id;
Fin_Call : Node_Id;
Fin_Stmts : List_Id;
Inc_Decl : Node_Id;
Label : Node_Id;
Label_Id : Entity_Id;
Obj_Ref : Node_Id;
-- Start of processing for Process_Object_Declaration
begin
-- 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));
loop
if Is_Access_Type (Obj_Typ) then
Obj_Typ := Directly_Designated_Type (Obj_Typ);
Obj_Ref := Make_Explicit_Dereference (Loc, Obj_Ref);
elsif Is_Concurrent_Type (Obj_Typ)
and then Present (Corresponding_Record_Type (Obj_Typ))
then
Obj_Typ := Corresponding_Record_Type (Obj_Typ);
Obj_Ref := Unchecked_Convert_To (Obj_Typ, Obj_Ref);
elsif Is_Private_Type (Obj_Typ)
and then Present (Full_View (Obj_Typ))
then
Obj_Typ := Full_View (Obj_Typ);
Obj_Ref := Unchecked_Convert_To (Obj_Typ, Obj_Ref);
elsif Obj_Typ /= Base_Type (Obj_Typ) then
Obj_Typ := Base_Type (Obj_Typ);
Obj_Ref := Unchecked_Convert_To (Obj_Typ, Obj_Ref);
else
exit;
end if;
end loop;
Set_Etype (Obj_Ref, Obj_Typ);
-- Handle the initialization type of the object declaration
Init_Typ := Obj_Typ;
loop
if Is_Private_Type (Init_Typ)
and then Present (Full_View (Init_Typ))
then
Init_Typ := Full_View (Init_Typ);
elsif Is_Untagged_Derivation (Init_Typ) then
Init_Typ := Root_Type (Init_Typ);
else
exit;
end if;
end loop;
-- Set a new value for the state counter and insert the statement
-- after the object declaration. Generate:
-- Counter := <value>;
Inc_Decl :=
Make_Assignment_Statement (Loc,
Name => New_Occurrence_Of (Counter_Id, Loc),
Expression => Make_Integer_Literal (Loc, Counter_Val));
-- Insert the counter after all initialization has been done. The
-- place of insertion depends on the context. If an object is being
-- initialized via an aggregate, then the counter must be inserted
-- after the last aggregate assignment.
if Ekind_In (Obj_Id, E_Constant, E_Variable)
and then Present (Last_Aggregate_Assignment (Obj_Id))
then
Count_Ins := Last_Aggregate_Assignment (Obj_Id);
Body_Ins := Empty;
-- In all other cases the counter is inserted after the last call to
-- either [Deep_]Initialize or the type specific init proc.
else
Find_Last_Init (Count_Ins, Body_Ins);
end if;
Insert_After (Count_Ins, Inc_Decl);
Analyze (Inc_Decl);
-- If the current declaration is the last in the list, the finalizer
-- body needs to be inserted after the set counter statement for the
-- current object declaration. This is complicated by the fact that
-- the set counter statement may appear in abort deferred block. In
-- that case, the proper insertion place is after the block.
if No (Finalizer_Insert_Nod) then
-- Insertion after an abort deffered block
if Present (Body_Ins) then
Finalizer_Insert_Nod := Body_Ins;
else
Finalizer_Insert_Nod := Inc_Decl;
end if;
end if;
-- Create the associated label with this object, generate:
-- L<counter> : label;
Label_Id :=
Make_Identifier (Loc, New_External_Name ('L', Counter_Val));
Set_Entity
(Label_Id, Make_Defining_Identifier (Loc, Chars (Label_Id)));
Label := Make_Label (Loc, Label_Id);
Prepend_To (Finalizer_Decls,
Make_Implicit_Label_Declaration (Loc,
Defining_Identifier => Entity (Label_Id),
Label_Construct => Label));
-- Create the associated jump with this object, generate:
-- when <counter> =>
-- goto L<counter>;
Prepend_To (Jump_Alts,
Make_Case_Statement_Alternative (Loc,
Discrete_Choices => New_List (
Make_Integer_Literal (Loc, Counter_Val)),
Statements => New_List (
Make_Goto_Statement (Loc,
Name => New_Occurrence_Of (Entity (Label_Id), Loc)))));
-- Insert the jump destination, generate:
-- <<L<counter>>>
Append_To (Finalizer_Stmts, Label);
-- Processing for simple protected objects. Such objects require
-- manual finalization of their lock managers.
if Is_Protected then
Fin_Stmts := No_List;
if Is_Simple_Protected_Type (Obj_Typ) then
Fin_Call := Cleanup_Protected_Object (Decl, Obj_Ref);
if Present (Fin_Call) then
Fin_Stmts := New_List (Fin_Call);
end if;
elsif Has_Simple_Protected_Object (Obj_Typ) then
if Is_Record_Type (Obj_Typ) then
Fin_Stmts := Cleanup_Record (Decl, Obj_Ref, Obj_Typ);
elsif Is_Array_Type (Obj_Typ) then
Fin_Stmts := Cleanup_Array (Decl, Obj_Ref, Obj_Typ);
end if;
end if;
-- Generate:
-- begin
-- System.Tasking.Protected_Objects.Finalize_Protection
-- (Obj._object);
-- exception
-- when others =>
-- null;
-- end;
if Present (Fin_Stmts) then
Append_To (Finalizer_Stmts,
Make_Block_Statement (Loc,
Handled_Statement_Sequence =>
Make_Handled_Sequence_Of_Statements (Loc,
Statements => Fin_Stmts,
Exception_Handlers => New_List (
Make_Exception_Handler (Loc,
Exception_Choices => New_List (
Make_Others_Choice (Loc)),
Statements => New_List (
Make_Null_Statement (Loc)))))));
end if;
-- Processing for regular controlled objects
else
-- Generate:
-- [Deep_]Finalize (Obj); -- No_Exception_Propagation
-- begin -- Exception handlers allowed
-- [Deep_]Finalize (Obj);
-- exception
-- when Id : others =>
-- if not Raised then
-- Raised := True;
-- Save_Occurrence (E, Id);
-- end if;
-- end;
Fin_Call :=
Make_Final_Call (
Obj_Ref => Obj_Ref,
Typ => Obj_Typ);
-- For CodePeer, the exception handlers normally generated here
-- generate complex flowgraphs which result in capacity problems.
-- Omitting these handlers for CodePeer is justified as follows:
-- If a handler is dead, then omitting it is surely ok
-- If a handler is live, then CodePeer should flag the
-- potentially-exception-raising construct that causes it
-- to be live. That is what we are interested in, not what
-- happens after the exception is raised.
if Exceptions_OK and not CodePeer_Mode then
Fin_Stmts := New_List (
Make_Block_Statement (Loc,
Handled_Statement_Sequence =>
Make_Handled_Sequence_Of_Statements (Loc,
Statements => New_List (Fin_Call),
Exception_Handlers => New_List (
Build_Exception_Handler
(Finalizer_Data, For_Package)))));
-- When exception handlers are prohibited, the finalization call
-- appears unprotected. Any exception raised during finalization
-- will bypass the circuitry which ensures the cleanup of all
-- remaining objects.
else
Fin_Stmts := New_List (Fin_Call);
end if;
-- If we are dealing with a return object of a build-in-place
-- function, generate the following cleanup statements:
-- if BIPallocfrom > Secondary_Stack'Pos
-- and then BIPfinalizationmaster /= null
-- then
-- declare
-- type Ptr_Typ is access Obj_Typ;
-- for Ptr_Typ'Storage_Pool use
-- Base_Pool (BIPfinalizationmaster.all).all;
-- begin
-- Free (Ptr_Typ (Temp));
-- end;
-- end if;
-- The generated code effectively detaches the temporary from the
-- caller finalization master and deallocates the object. This is
-- disabled on .NET/JVM because pools are not supported.
if VM_Target = No_VM and then Is_Return_Object (Obj_Id) then
declare
Func_Id : constant Entity_Id := Enclosing_Function (Obj_Id);
begin
if Is_Build_In_Place_Function (Func_Id)
and then Needs_BIP_Finalization_Master (Func_Id)
then
Append_To (Fin_Stmts, Build_BIP_Cleanup_Stmts (Func_Id));
end if;
end;
end if;
if Ekind_In (Obj_Id, E_Constant, E_Variable)
and then Present (Status_Flag_Or_Transient_Decl (Obj_Id))
then
-- Temporaries created for the purpose of "exporting" a
-- controlled transient out of an Expression_With_Actions (EWA)
-- need guards. The following illustrates the usage of such
-- temporaries.
-- Access_Typ : access [all] Obj_Typ;
-- Temp : Access_Typ := null;
-- <Counter> := ...;
-- do
-- Ctrl_Trans : [access [all]] Obj_Typ := ...;
-- Temp := Access_Typ (Ctrl_Trans); -- when a pointer
-- <or>
-- Temp := Ctrl_Trans'Unchecked_Access;
-- in ... end;
-- The finalization machinery does not process EWA nodes as
-- this may lead to premature finalization of expressions. Note
-- that Temp is marked as being properly initialized regardless
-- of whether the initialization of Ctrl_Trans succeeded. Since
-- a failed initialization may leave Temp with a value of null,
-- add a guard to handle this case:
-- if Obj /= null then
-- <object finalization statements>
-- end if;
if Nkind (Status_Flag_Or_Transient_Decl (Obj_Id)) =
N_Object_Declaration
then
Fin_Stmts := New_List (
Make_If_Statement (Loc,
Condition =>
Make_Op_Ne (Loc,
Left_Opnd => New_Occurrence_Of (Obj_Id, Loc),
Right_Opnd => Make_Null (Loc)),
Then_Statements => Fin_Stmts));
-- Return objects use a flag to aid in processing their
-- potential finalization when the enclosing function fails
-- to return properly. Generate:
-- if not Flag then
-- <object finalization statements>
-- end if;
else
Fin_Stmts := New_List (
Make_If_Statement (Loc,
Condition =>
Make_Op_Not (Loc,
Right_Opnd =>
New_Occurrence_Of
(Status_Flag_Or_Transient_Decl (Obj_Id), Loc)),
Then_Statements => Fin_Stmts));
end if;
end if;
end if;
Append_List_To (Finalizer_Stmts, Fin_Stmts);
-- Since the declarations are examined in reverse, the state counter
-- must be decremented in order to keep with the true position of
-- objects.
Counter_Val := Counter_Val - 1;
end Process_Object_Declaration;
-------------------------------------
-- Process_Tagged_Type_Declaration --
-------------------------------------
procedure Process_Tagged_Type_Declaration (Decl : Node_Id) is
Typ : constant Entity_Id := Defining_Identifier (Decl);
DT_Ptr : constant Entity_Id :=
Node (First_Elmt (Access_Disp_Table (Typ)));
begin
-- Generate:
-- Ada.Tags.Unregister_Tag (<Typ>P);
Append_To (Tagged_Type_Stmts,
Make_Procedure_Call_Statement (Loc,
Name =>
New_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;
-- Do not process nested packages since those are handled by the
-- enclosing scope's finalizer. Do not process non-expanded package
-- instantiations since those will be re-analyzed and re-expanded.
if For_Package
and then
(not Is_Library_Level_Entity (Spec_Id)
-- Nested packages are considered to be library level entities,
-- but do not need to be processed separately. True library level
-- packages have a scope value of 1.
or else Scope_Depth_Value (Spec_Id) /= Uint_1
or else (Is_Generic_Instance (Spec_Id)
and then Package_Instantiation (Spec_Id) /= N))
then
return;
end if;
-- Step 2: Object [pre]processing
if For_Package then
-- Preprocess the visible declarations now in order to obtain the
-- correct number of controlled object by the time the private
-- declarations are processed.
Process_Declarations (Decls, Preprocess => True, Top_Level => True);
-- From all the possible contexts, only package specifications may
-- have private declarations.
if For_Package_Spec then
Process_Declarations
(Priv_Decls, Preprocess => True, Top_Level => True);
end if;
-- The current context may lack controlled objects, but require some
-- other form of completion (task termination for instance). In such
-- cases, the finalizer must be created and carry the additional
-- statements.
if Acts_As_Clean or Has_Ctrl_Objs or Has_Tagged_Types then
Build_Components;
end if;
-- The preprocessing has determined that the context has controlled
-- objects or library-level tagged types.
if Has_Ctrl_Objs or Has_Tagged_Types then
-- Private declarations are processed first in order to preserve
-- possible dependencies between public and private objects.
if For_Package_Spec then
Process_Declarations (Priv_Decls);
end if;
Process_Declarations (Decls);
end if;
-- Non-package case
else
-- Preprocess both declarations and statements
Process_Declarations (Decls, Preprocess => True, Top_Level => True);
Process_Declarations (Stmts, Preprocess => True, Top_Level => True);
-- At this point it is known that N has controlled objects. Ensure
-- that N has a declarative list since the finalizer spec will be
-- attached to it.
if Has_Ctrl_Objs and then No (Decls) then
Set_Declarations (N, New_List);
Decls := Declarations (N);
Spec_Decls := Decls;
end if;
-- The current context may lack controlled objects, but require some
-- other form of completion (task termination for instance). In such
-- cases, the finalizer must be created and carry the additional
-- statements.
if Acts_As_Clean or Has_Ctrl_Objs or Has_Tagged_Types then
Build_Components;
end if;
if Has_Ctrl_Objs or Has_Tagged_Types then
Process_Declarations (Stmts);
Process_Declarations (Decls);
end if;
end if;
-- Step 3: Finalizer creation
if Acts_As_Clean or Has_Ctrl_Objs or Has_Tagged_Types then
Create_Finalizer;
end if;
end Build_Finalizer;
--------------------------
-- Build_Finalizer_Call --
--------------------------
procedure Build_Finalizer_Call (N : Node_Id; Fin_Id : Entity_Id) is
Is_Prot_Body : constant Boolean :=
Nkind (N) = N_Subprogram_Body
and then Is_Protected_Subprogram_Body (N);
-- Determine whether N denotes the protected version of a subprogram
-- which belongs to a protected type.
Loc : constant Source_Ptr := Sloc (N);
HSS : Node_Id;
begin
-- Do not perform this expansion in SPARK mode because we do not create
-- finalizers in the first place.
if GNATprove_Mode then
return;
end if;
-- The At_End handler should have been assimilated by the finalizer
HSS := Handled_Statement_Sequence (N);
pragma Assert (No (At_End_Proc (HSS)));
-- If the construct to be cleaned up is a protected subprogram body, the
-- finalizer call needs to be associated with the block which wraps the
-- unprotected version of the subprogram. The following illustrates this
-- scenario:
-- procedure Prot_SubpP is
-- procedure finalizer is
-- begin
-- Service_Entries (Prot_Obj);
-- Abort_Undefer;
-- end finalizer;
-- begin
-- . . .
-- begin
-- Prot_SubpN (Prot_Obj);
-- at end
-- finalizer;
-- end;
-- end Prot_SubpP;
if Is_Prot_Body then
HSS := Handled_Statement_Sequence (Last (Statements (HSS)));
-- An At_End handler and regular exception handlers cannot coexist in
-- the same statement sequence. Wrap the original statements in a block.
elsif Present (Exception_Handlers (HSS)) then
declare
End_Lab : constant Node_Id := End_Label (HSS);
Block : Node_Id;
begin
Block :=
Make_Block_Statement (Loc, Handled_Statement_Sequence => HSS);
Set_Handled_Statement_Sequence (N,
Make_Handled_Sequence_Of_Statements (Loc, New_List (Block)));
HSS := Handled_Statement_Sequence (N);
Set_End_Label (HSS, End_Lab);
end;
end if;
Set_At_End_Proc (HSS, New_Occurrence_Of (Fin_Id, Loc));
Analyze (At_End_Proc (HSS));
Expand_At_End_Handler (HSS, Empty);
end Build_Finalizer_Call;
---------------------
-- Build_Late_Proc --
---------------------
procedure Build_Late_Proc (Typ : Entity_Id; Nam : Name_Id) is
begin
for Final_Prim in Name_Of'Range loop
if Name_Of (Final_Prim) = Nam then
Set_TSS (Typ,
Make_Deep_Proc
(Prim => Final_Prim,
Typ => Typ,
Stmts => Make_Deep_Record_Body (Final_Prim, Typ)));
end if;
end loop;
end Build_Late_Proc;
-------------------------------
-- Build_Object_Declarations --
-------------------------------
procedure Build_Object_Declarations
(Data : out Finalization_Exception_Data;
Decls : List_Id;
Loc : Source_Ptr;
For_Package : Boolean := False)
is
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.
-- No need to do this for VM case, since VM version of Ada.Exceptions
-- does not include routine Raise_From_Controlled_Operation which is the
-- the sole user of flag Abort.
-- This is not needed for library-level finalizers as they are called by
-- the environment task and cannot be aborted.
if VM_Target = No_VM and then 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;
-- .NET/JVM or 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)));
end Build_Object_Declarations;
---------------------------
-- Build_Raise_Statement --
---------------------------
function Build_Raise_Statement
(Data : Finalization_Exception_Data) return Node_Id
is
Stmt : Node_Id;
Expr : Node_Id;
begin
-- Standard run-time and .NET/JVM targets use the specialized routine
-- Raise_From_Controlled_Operation.
if Exception_Extra_Info
and then RTE_Available (RE_Raise_From_Controlled_Operation)
then
Stmt :=
Make_Procedure_Call_Statement (Data.Loc,
Name =>
New_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
Set_TSS (Typ,
Make_Deep_Proc
(Prim => Initialize_Case,
Typ => Typ,
Stmts => Make_Deep_Record_Body (Initialize_Case, Typ)));
if not Is_Limited_View (Typ) then
Set_TSS (Typ,
Make_Deep_Proc
(Prim => Adjust_Case,
Typ => Typ,
Stmts => Make_Deep_Record_Body (Adjust_Case, Typ)));
end if;
-- Do not generate Deep_Finalize and Finalize_Address if finalization is
-- suppressed since these routine will not be used.
if not Restriction_Active (No_Finalization) then
Set_TSS (Typ,
Make_Deep_Proc
(Prim => Finalize_Case,
Typ => Typ,
Stmts => Make_Deep_Record_Body (Finalize_Case, Typ)));
-- Create TSS primitive Finalize_Address for non-VM targets. JVM and
-- .NET do not support address arithmetic and unchecked conversions.
if VM_Target = No_VM then
Set_TSS (Typ,
Make_Deep_Proc
(Prim => Address_Case,
Typ => Typ,
Stmts => Make_Deep_Record_Body (Address_Case, Typ)));
end if;
end if;
end Build_Record_Deep_Procs;
-------------------
-- Cleanup_Array --
-------------------
function Cleanup_Array
(N : Node_Id;
Obj : Node_Id;
Typ : Entity_Id) return List_Id
is
Loc : constant Source_Ptr := Sloc (N);
Index_List : constant List_Id := New_List;
function Free_Component return List_Id;
-- Generate the code to finalize the task or protected subcomponents
-- of a single component of the array.
function Free_One_Dimension (Dim : Int) return List_Id;
-- Generate a loop over one dimension of the array
--------------------
-- Free_Component --
--------------------
function Free_Component return List_Id is
Stmts : List_Id := New_List;
Tsk : Node_Id;
C_Typ : constant Entity_Id := Component_Type (Typ);
begin
-- Component type is known to contain tasks or protected objects
Tsk :=
Make_Indexed_Component (Loc,
Prefix => Duplicate_Subexpr_No_Checks (Obj),
Expressions => Index_List);
Set_Etype (Tsk, C_Typ);
if Is_Task_Type (C_Typ) then
Append_To (Stmts, Cleanup_Task (N, Tsk));
elsif Is_Simple_Protected_Type (C_Typ) then
Append_To (Stmts, Cleanup_Protected_Object (N, Tsk));
elsif Is_Record_Type (C_Typ) then
Stmts := Cleanup_Record (N, Tsk, C_Typ);
elsif Is_Array_Type (C_Typ) then
Stmts := Cleanup_Array (N, Tsk, C_Typ);
end if;
return Stmts;
end Free_Component;
------------------------
-- Free_One_Dimension --
------------------------
function Free_One_Dimension (Dim : Int) return List_Id is
Index : Entity_Id;
begin
if Dim > Number_Dimensions (Typ) then
return Free_Component;
-- Here we generate the required loop
else
Index := Make_Temporary (Loc, 'J');
Append (New_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);
Tsk : Node_Id;
Comp : Entity_Id;
Stmts : constant List_Id := New_List;
U_Typ : constant Entity_Id := Underlying_Type (Typ);
begin
if Has_Discriminants (U_Typ)
and then Nkind (Parent (U_Typ)) = N_Full_Type_Declaration
and then Nkind (Type_Definition (Parent (U_Typ))) = N_Record_Definition
and then
Present
(Variant_Part (Component_List (Type_Definition (Parent (U_Typ)))))
then
-- For now, do not attempt to free a component that may appear in a
-- variant, and instead issue a warning. Doing this "properly" would
-- require building a case statement and would be quite a mess. Note
-- that the RM only requires that free "work" for the case of a task
-- access value, so already we go way beyond this in that we deal
-- with the array case and non-discriminated record cases.
Error_Msg_N
("task/protected object in variant record will not be freed??", N);
return New_List (Make_Null_Statement (Loc));
end if;
Comp := First_Component (Typ);
while Present (Comp) loop
if Has_Task (Etype (Comp))
or else Has_Simple_Protected_Object (Etype (Comp))
then
Tsk :=
Make_Selected_Component (Loc,
Prefix => Duplicate_Subexpr_No_Checks (Obj),
Selector_Name => New_Occurrence_Of (Comp, Loc));
Set_Etype (Tsk, Etype (Comp));
if Is_Task_Type (Etype (Comp)) then
Append_To (Stmts, Cleanup_Task (N, Tsk));
elsif Is_Simple_Protected_Type (Etype (Comp)) then
Append_To (Stmts, Cleanup_Protected_Object (N, Tsk));
elsif Is_Record_Type (Etype (Comp)) then
-- Recurse, by generating the prefix of the argument to
-- the eventual cleanup call.
Append_List_To (Stmts, Cleanup_Record (N, Tsk, Etype (Comp)));
elsif Is_Array_Type (Etype (Comp)) then
Append_List_To (Stmts, Cleanup_Array (N, Tsk, Etype (Comp)));
end if;
end if;
Next_Component (Comp);
end loop;
return Stmts;
end Cleanup_Record;
------------------------------
-- Cleanup_Protected_Object --
------------------------------
function Cleanup_Protected_Object
(N : Node_Id;
Ref : Node_Id) return Node_Id
is
Loc : constant Source_Ptr := Sloc (N);
begin
-- For restricted run-time libraries (Ravenscar), tasks are
-- non-terminating, and protected objects can only appear at library
-- level, so we do not want finalization of protected objects.
if Restricted_Profile then
return Empty;
else
return
Make_Procedure_Call_Statement (Loc,
Name =>
New_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_Visibly_Controlled --
------------------------------
procedure Check_Visibly_Controlled
(Prim : Final_Primitives;
Typ : Entity_Id;
E : in out Entity_Id;
Cref : in out Node_Id)
is
Parent_Type : Entity_Id;
Op : Entity_Id;
begin
if Is_Derived_Type (Typ)
and then Comes_From_Source (E)
and then not Present (Overridden_Operation (E))
then
-- We know that the explicit operation on the type does not override
-- the inherited operation of the parent, and that the derivation
-- is from a private type that is not visibly controlled.
Parent_Type := Etype (Typ);
Op := Find_Prim_Op (Parent_Type, Name_Of (Prim));
if Present (Op) then
E := Op;
-- Wrap the object to be initialized into the proper
-- unchecked conversion, to be compatible with the operation
-- to be called.
if Nkind (Cref) = N_Unchecked_Type_Conversion then
Cref := Unchecked_Convert_To (Parent_Type, Expression (Cref));
else
Cref := Unchecked_Convert_To (Parent_Type, Cref);
end if;
end if;
end if;
end Check_Visibly_Controlled;
-------------------------------
-- CW_Or_Has_Controlled_Part --
-------------------------------
function CW_Or_Has_Controlled_Part (T : Entity_Id) return Boolean is
begin
return Is_Class_Wide_Type (T) or else Needs_Finalization (T);
end CW_Or_Has_Controlled_Part;
------------------
-- Convert_View --
------------------
function Convert_View
(Proc : Entity_Id;
Arg : Node_Id;
Ind : Pos := 1) return Node_Id
is
Fent : Entity_Id := First_Entity (Proc);
Ftyp : Entity_Id;
Atyp : Entity_Id;
begin
for J in 2 .. Ind loop
Next_Entity (Fent);
end loop;
Ftyp := Etype (Fent);
if Nkind_In (Arg, N_Type_Conversion, N_Unchecked_Type_Conversion) then
Atyp := Entity (Subtype_Mark (Arg));
else
Atyp := Etype (Arg);
end if;
if Is_Abstract_Subprogram (Proc) and then Is_Tagged_Type (Ftyp) then
return Unchecked_Convert_To (Class_Wide_Type (Ftyp), Arg);
elsif Ftyp /= Atyp
and then Present (Atyp)
and then (Is_Private_Type (Ftyp) or else Is_Private_Type (Atyp))
and then Base_Type (Underlying_Type (Atyp)) =
Base_Type (Underlying_Type (Ftyp))
then
return Unchecked_Convert_To (Ftyp, Arg);
-- If the argument is already a conversion, as generated by
-- Make_Init_Call, set the target type to the type of the formal
-- directly, to avoid spurious typing problems.
elsif Nkind_In (Arg, N_Unchecked_Type_Conversion, N_Type_Conversion)
and then not Is_Class_Wide_Type (Atyp)
then
Set_Subtype_Mark (Arg, New_Occurrence_Of (Ftyp, Sloc (Arg)));
Set_Etype (Arg, Ftyp);
return Arg;
-- 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;
------------------------
-- Enclosing_Function --
------------------------
function Enclosing_Function (E : Entity_Id) return Entity_Id is
Func_Id : Entity_Id;
begin
Func_Id := E;
while Present (Func_Id) and then Func_Id /= Standard_Standard loop
if Ekind (Func_Id) = E_Function then
return Func_Id;
end if;
Func_Id := Scope (Func_Id);
end loop;
return Empty;
end Enclosing_Function;
-------------------------------
-- Establish_Transient_Scope --
-------------------------------
-- This procedure is called each time a transient block has to be inserted
-- that is to say for each call to a function with unconstrained or tagged
-- result. It creates a new scope on the stack scope in order to enclose
-- all transient variables generated.
procedure Establish_Transient_Scope (N : Node_Id; Sec_Stack : Boolean) is
Loc : constant Source_Ptr := Sloc (N);
Iter_Loop : Entity_Id;
Wrap_Node : Node_Id;
begin
-- Do not create a transient scope if we are already inside one
for S in reverse Scope_Stack.First .. Scope_Stack.Last loop
if Scope_Stack.Table (S).Is_Transient then
if Sec_Stack then
Set_Uses_Sec_Stack (Scope_Stack.Table (S).Entity);
end if;
return;
-- If we encounter Standard there are no enclosing transient scopes
elsif Scope_Stack.Table (S).Entity = Standard_Standard then
exit;
end if;
end loop;
Wrap_Node := Find_Node_To_Be_Wrapped (N);
-- The context does not contain a node that requires a transient scope,
-- nothing to do.
if No (Wrap_Node) then
null;
-- If the node to wrap is an iteration_scheme, the expression is one of
-- the bounds, and the expansion will make an explicit declaration for
-- it (see Analyze_Iteration_Scheme, sem_ch5.adb), so do not apply any
-- transformations here. Same for an Ada 2012 iterator specification,
-- where a block is created for the expression that build the container.
elsif Nkind_In (Wrap_Node, N_Iteration_Scheme,
N_Iterator_Specification)
then
null;
-- In formal verification mode, if the node to wrap is a pragma check,
-- this node and enclosed expression are not expanded, so do not apply
-- any transformations here.
elsif GNATprove_Mode
and then Nkind (Wrap_Node) = N_Pragma
and then Get_Pragma_Id (Wrap_Node) = Pragma_Check
then
null;
-- Create a block entity to act as a transient scope. Note that when the
-- node to be wrapped is an expression or a statement, a real physical
-- block is constructed (see routines Wrap_Transient_Expression and
-- Wrap_Transient_Statement) and inserted into the tree.
else
Push_Scope (New_Internal_Entity (E_Block, Current_Scope, Loc, 'B'));
Set_Scope_Is_Transient;
-- The transient scope must also take care of the secondary stack
-- management.
if Sec_Stack then
Set_Uses_Sec_Stack (Current_Scope);
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 ealy.
-- 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 (Current_Scope);
if Present (Iter_Loop) then
Set_Uses_Sec_Stack (Iter_Loop);
end if;
end if;
Set_Etype (Current_Scope, Standard_Void_Type);
Set_Node_To_Be_Wrapped (Wrap_Node);
if Debug_Flag_W then
Write_Str (" <Transient>");
Write_Eol;
end if;
end if;
end Establish_Transient_Scope;
----------------------------
-- Expand_Cleanup_Actions --
----------------------------
procedure Expand_Cleanup_Actions (N : Node_Id) is
Scop : constant Entity_Id := Current_Scope;
Is_Asynchronous_Call : constant Boolean :=
Nkind (N) = N_Block_Statement
and then Is_Asynchronous_Call_Block (N);
Is_Master : constant Boolean :=
Nkind (N) /= N_Entry_Body
and then Is_Task_Master (N);
Is_Protected_Body : constant Boolean :=
Nkind (N) = N_Subprogram_Body
and then Is_Protected_Subprogram_Body (N);
Is_Task_Allocation : constant Boolean :=
Nkind (N) = N_Block_Statement
and then Is_Task_Allocation_Block (N);
Is_Task_Body : constant Boolean :=
Nkind (Original_Node (N)) = N_Task_Body;
Needs_Sec_Stack_Mark : constant Boolean :=
Uses_Sec_Stack (Scop)
and then
not Sec_Stack_Needed_For_Return (Scop)
and then VM_Target = No_VM;
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_Body
or else Is_Task_Allocation
or else Is_Task_Body
or else Needs_Sec_Stack_Mark
or else Needs_Custom_Cleanup;
HSS : Node_Id := Handled_Statement_Sequence (N);
Loc : Source_Ptr;
Cln : List_Id;
procedure Wrap_HSS_In_Block;
-- Move HSS inside a new block along with the original exception
-- handlers. Make the newly generated block the sole statement of HSS.
-----------------------
-- Wrap_HSS_In_Block --
-----------------------
procedure Wrap_HSS_In_Block is
Block : Node_Id;
End_Lab : Node_Id;
begin
-- Preserve end label to provide proper cross-reference information
End_Lab := End_Label (HSS);
Block :=
Make_Block_Statement (Loc, Handled_Statement_Sequence => HSS);
-- Signal the finalization machinery that this particular block
-- contains the original context.
Set_Is_Finalization_Wrapper (Block);
Set_Handled_Statement_Sequence (N,
Make_Handled_Sequence_Of_Statements (Loc, New_List (Block)));
HSS := Handled_Statement_Sequence (N);
Set_First_Real_Statement (HSS, Block);
Set_End_Label (HSS, End_Lab);
-- Comment needed here, see RH for 1.306 ???
if Nkind (N) = N_Subprogram_Body then
Set_Has_Nested_Block_With_Handler (Scop);
end if;
end Wrap_HSS_In_Block;
-- Start of processing for Expand_Cleanup_Actions
begin
-- The current construct does not need any form of servicing
if not Actions_Required then
return;
-- If the current node is a rewritten task body and the descriptors have
-- not been delayed (due to some nested instantiations), do not generate
-- redundant cleanup actions.
elsif Is_Task_Body
and then Nkind (N) = N_Subprogram_Body
and then not Delay_Subprogram_Descriptors (Corresponding_Spec (N))
then
return;
end if;
if Needs_Custom_Cleanup then
Cln := Cleanup_Actions (N);
else
Cln := No_List;
end if;
declare
Decls : List_Id := Declarations (N);
Fin_Id : Entity_Id;
Mark : Entity_Id := Empty;
New_Decls : List_Id;
Old_Poll : Boolean;
begin
-- If we are generating expanded code for debugging purposes, use the
-- Sloc of the point of insertion for the cleanup code. The Sloc will
-- be updated subsequently to reference the proper line in .dg files.
-- If we are not debugging generated code, use No_Location instead,
-- so that no debug information is generated for the cleanup code.
-- This makes the behavior of the NEXT command in GDB monotonic, and
-- makes the placement of breakpoints more accurate.
if Debug_Generated_Code then
Loc := Sloc (Scop);
else
Loc := No_Location;
end if;
-- Set polling off. The finalization and cleanup code is executed
-- with aborts deferred.
Old_Poll := Polling_Required;
Polling_Required := False;
-- A task activation call has already been built for a task
-- allocation block.
if not Is_Task_Allocation then
Build_Task_Activation_Call (N);
end if;
if Is_Master then
Establish_Task_Master (N);
end if;
New_Decls := New_List;
-- If secondary stack is in use, generate:
--
-- Mnn : constant Mark_Id := SS_Mark;
-- Suppress calls to SS_Mark and SS_Release if VM_Target, since the
-- secondary stack is never used on a VM.
if Needs_Sec_Stack_Mark then
Mark := Make_Temporary (Loc, 'M');
Append_To (New_Decls, Build_SS_Mark_Call (Loc, Mark));
Set_Uses_Sec_Stack (Scop, False);
end if;
-- If exception handlers are present, wrap the sequence of statements
-- in a block since it is not possible to have exception handlers and
-- an At_End handler in the same construct.
if Present (Exception_Handlers (HSS)) then
Wrap_HSS_In_Block;
-- Ensure that the First_Real_Statement field is set
elsif No (First_Real_Statement (HSS)) then
Set_First_Real_Statement (HSS, First (Statements (HSS)));
end if;
-- Do not move the Activation_Chain declaration in the context of
-- task allocation blocks. Task allocation blocks use _chain in their
-- cleanup handlers and gigi complains if it is declared in the
-- sequence of statements of the scope that declares the handler.
if Is_Task_Allocation then
declare
Chain : constant Entity_Id := Activation_Chain_Entity (N);
Decl : Node_Id;
begin
Decl := First (Decls);
while Nkind (Decl) /= N_Object_Declaration
or else Defining_Identifier (Decl) /= Chain
loop
Next (Decl);
-- A task allocation block should always include a _chain
-- declaration.
pragma Assert (Present (Decl));
end loop;
Remove (Decl);
Prepend_To (New_Decls, Decl);
end;
end if;
-- Ensure the presence of a declaration list in order to successfully
-- append all original statements to it.
if No (Decls) then
Set_Declarations (N, New_List);
Decls := Declarations (N);
end if;
-- Move the declarations into the sequence of statements in order to
-- have them protected by the At_End handler. It may seem weird to
-- put declarations in the sequence of statement but in fact nothing
-- forbids that at the tree level.
Append_List_To (Decls, Statements (HSS));
Set_Statements (HSS, Decls);
-- Reset the Sloc of the handled statement sequence to properly
-- reflect the new initial "statement" in the sequence.
Set_Sloc (HSS, Sloc (First (Decls)));
-- The declarations of finalizer spec and auxiliary variables replace
-- the old declarations that have been moved inward.
Set_Declarations (N, New_Decls);
Analyze_Declarations (New_Decls);
-- Generate finalization calls for all controlled objects appearing
-- in the statements of N. Add context specific cleanup for various
-- constructs.
Build_Finalizer
(N => N,
Clean_Stmts => Build_Cleanup_Statements (N, Cln),
Mark_Id => Mark,
Top_Decls => New_Decls,
Defer_Abort => Nkind (Original_Node (N)) = N_Task_Body
or else Is_Master,
Fin_Id => Fin_Id);
if Present (Fin_Id) then
Build_Finalizer_Call (N, Fin_Id);
end if;
-- Restore saved polling mode
Polling_Required := Old_Poll;
end;
end Expand_Cleanup_Actions;
---------------------------
-- Expand_N_Package_Body --
---------------------------
-- Add call to Activate_Tasks if body is an activator (actual processing
-- is in chapter 9).
-- Generate subprogram descriptor for elaboration routine
-- Encode entity names in package body
procedure Expand_N_Package_Body (N : Node_Id) is
Spec_Ent : constant Entity_Id := Corresponding_Spec (N);
Fin_Id : Entity_Id;
begin
-- This is done only for non-generic packages
if Ekind (Spec_Ent) = E_Package then
Push_Scope (Corresponding_Spec (N));
-- Build dispatch tables of library level tagged types
if Tagged_Type_Expansion
and then Is_Library_Level_Entity (Spec_Ent)
then
Build_Static_Dispatch_Tables (N);
end if;
Build_Task_Activation_Call (N);
-- When the package is subject to pragma Initial_Condition, the
-- assertion expression must be verified at the end of the body
-- statements.
if Present (Get_Pragma (Spec_Ent, Pragma_Initial_Condition)) then
Expand_Pragma_Initial_Condition (N);
end if;
Pop_Scope;
end if;
Set_Elaboration_Flag (N, Corresponding_Spec (N));
Set_In_Package_Body (Spec_Ent, False);
-- Set to encode entity names in package body before gigi is called
Qualify_Entity_Names (N);
if Ekind (Spec_Ent) /= E_Generic_Package then
Build_Finalizer
(N => N,
Clean_Stmts => No_List,
Mark_Id => Empty,
Top_Decls => No_List,
Defer_Abort => False,
Fin_Id => Fin_Id);
if Present (Fin_Id) then
declare
Body_Ent : Node_Id := Defining_Unit_Name (N);
begin
if Nkind (Body_Ent) = N_Defining_Program_Unit_Name then
Body_Ent := Defining_Identifier (Body_Ent);
end if;
Set_Finalizer (Body_Ent, Fin_Id);
end;
end if;
end if;
end Expand_N_Package_Body;
----------------------------------
-- Expand_N_Package_Declaration --
----------------------------------
-- Add call to Activate_Tasks if there are tasks declared and the package
-- has no body. Note that in Ada 83 this may result in premature activation
-- of some tasks, given that we cannot tell whether a body will eventually
-- appear.
procedure Expand_N_Package_Declaration (N : Node_Id) is
Id : constant Entity_Id := Defining_Entity (N);
Spec : constant Node_Id := Specification (N);
Decls : List_Id;
Fin_Id : Entity_Id;
No_Body : Boolean := False;
-- True in the case of a package declaration that is a compilation
-- unit and for which no associated body will be compiled in this
-- compilation.
begin
-- Case of a package declaration other than a compilation unit
if Nkind (Parent (N)) /= N_Compilation_Unit then
null;
-- Case of a compilation unit that does not require a body
elsif not Body_Required (Parent (N))
and then not Unit_Requires_Body (Id)
then
No_Body := True;
-- Special case of generating calling stubs for a remote call interface
-- package: even though the package declaration requires one, the body
-- won't be processed in this compilation (so any stubs for RACWs
-- declared in the package must be generated here, along with the spec).
elsif Parent (N) = Cunit (Main_Unit)
and then Is_Remote_Call_Interface (Id)
and then Distribution_Stub_Mode = Generate_Caller_Stub_Body
then
No_Body := True;
end if;
-- For a nested instance, delay processing until freeze point
if Has_Delayed_Freeze (Id)
and then Nkind (Parent (N)) /= N_Compilation_Unit
then