------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- S E M _ E L A B -- -- -- -- B o d y -- -- -- -- Copyright (C) 1997-2005 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 2, 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 COPYING. If not, write -- -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- -- MA 02111-1307, USA. -- -- -- -- GNAT was originally developed by the GNAT team at New York University. -- -- Extensive contributions were provided by Ada Core Technologies Inc. -- -- -- ------------------------------------------------------------------------------ with Atree; use Atree; with Checks; use Checks; with Debug; use Debug; with Einfo; use Einfo; with Elists; use Elists; with Errout; use Errout; with Exp_Tss; use Exp_Tss; with Exp_Util; use Exp_Util; with Expander; use Expander; with Fname; use Fname; with Lib; use Lib; with Lib.Load; use Lib.Load; with Namet; use Namet; 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 Sem; use Sem; with Sem_Cat; use Sem_Cat; 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 Sinfo; use Sinfo; with Sinput; use Sinput; with Snames; use Snames; with Stand; use Stand; with Table; with Tbuild; use Tbuild; with Uname; use Uname; package body Sem_Elab is -- The following table records the recursive call chain for output -- in the Output routine. Each entry records the call node and the -- entity of the called routine. The number of entries in the table -- (i.e. the value of Elab_Call.Last) indicates the current depth -- of recursion and is used to identify the outer level. type Elab_Call_Entry is record Cloc : Source_Ptr; Ent : Entity_Id; end record; package Elab_Call is new Table.Table ( Table_Component_Type => Elab_Call_Entry, Table_Index_Type => Int, Table_Low_Bound => 1, Table_Initial => 50, Table_Increment => 100, Table_Name => "Elab_Call"); -- This table is initialized at the start of each outer level call. -- It holds the entities for all subprograms that have been examined -- for this particular outer level call, and is used to prevent both -- infinite recursion, and useless reanalysis of bodies already seen package Elab_Visited is new Table.Table ( Table_Component_Type => Entity_Id, Table_Index_Type => Int, Table_Low_Bound => 1, Table_Initial => 200, Table_Increment => 100, Table_Name => "Elab_Visited"); -- This table stores calls to Check_Internal_Call that are delayed -- until all generics are instantiated, and in particular that all -- generic bodies have been inserted. We need to delay, because we -- need to be able to look through the inserted bodies. type Delay_Element is record N : Node_Id; -- The parameter N from the call to Check_Internal_Call. Note that -- this node may get rewritten over the delay period by expansion -- in the call case (but not in the instantiation case). E : Entity_Id; -- The parameter E from the call to Check_Internal_Call Orig_Ent : Entity_Id; -- The parameter Orig_Ent from the call to Check_Internal_Call Curscop : Entity_Id; -- The current scope of the call. This is restored when we complete -- the delayed call, so that we do this in the right scope. From_Elab_Code : Boolean; -- Save indication of whether this call is from elaboration code Outer_Scope : Entity_Id; -- Save scope of outer level call end record; package Delay_Check is new Table.Table ( Table_Component_Type => Delay_Element, Table_Index_Type => Int, Table_Low_Bound => 1, Table_Initial => 1000, Table_Increment => 100, Table_Name => "Delay_Check"); C_Scope : Entity_Id; -- Top level scope of current scope. We need to compute this only -- once at the outer level, i.e. for a call to Check_Elab_Call from -- outside this unit. Outer_Level_Sloc : Source_Ptr; -- Save Sloc value for outer level call node for comparisons of source -- locations. A body is too late if it appears after the *outer* level -- call, not the particular call that is being analyzed. From_Elab_Code : Boolean; -- This flag shows whether the outer level call currently being examined -- is or is not in elaboration code. We are only interested in calls to -- routines in other units if this flag is True. In_Task_Activation : Boolean := False; -- This flag indicates whether we are performing elaboration checks on -- task procedures, at the point of activation. If true, we do not trace -- internal calls in these procedures, because all local bodies are known -- to be elaborated. Delaying_Elab_Checks : Boolean := True; -- This is set True till the compilation is complete, including the -- insertion of all instance bodies. Then when Check_Elab_Calls is -- called, the delay table is used to make the delayed calls and -- this flag is reset to False, so that the calls are processed ----------------------- -- Local Subprograms -- ----------------------- -- Note: Outer_Scope in all following specs represents the scope of -- interest of the outer level call. If it is set to Standard_Standard, -- then it means the outer level call was at elaboration level, and that -- thus all calls are of interest. If it was set to some other scope, -- then the original call was an inner call, and we are not interested -- in calls that go outside this scope. procedure Check_A_Call (N : Node_Id; E : Entity_Id; Outer_Scope : Entity_Id; Inter_Unit_Only : Boolean; Generate_Warnings : Boolean := True); -- This is the internal recursive routine that is called to check for -- a possible elaboration error. The argument N is a subprogram call -- or generic instantiation to be checked, and E is the entity of -- the called subprogram, or instantiated generic unit. The flag -- Outer_Scope is the outer level scope for the original call. -- Inter_Unit_Only is set if the call is only to be checked in the -- case where it is to another unit (and skipped if within a unit). -- Generate_Warnings is set to False to suppress warning messages -- about missing pragma Elaborate_All's. These messages are not -- wanted for inner calls in the dynamic model. procedure Check_Bad_Instantiation (N : Node_Id); -- N is a node for an instantiation (if called with any other node kind, -- Check_Bad_Instantiation ignores the call). This subprogram checks for -- the special case of a generic instantiation of a generic spec in the -- same declarative part as the instantiation where a body is present and -- has not yet been seen. This is an obvious error, but needs to be checked -- specially at the time of the instantiation, since it is a case where we -- cannot insert the body anywhere. If this case is detected, warnings are -- generated, and a raise of Program_Error is inserted. In addition any -- subprograms in the generic spec are stubbed, and the Bad_Instantiation -- flag is set on the instantiation node. The caller in Sem_Ch12 uses this -- flag as an indication that no attempt should be made to insert an -- instance body. procedure Check_Internal_Call (N : Node_Id; E : Entity_Id; Outer_Scope : Entity_Id; Orig_Ent : Entity_Id); -- N is a function call or procedure statement call node and E is -- the entity of the called function, which is within the current -- compilation unit (where subunits count as part of the parent). -- This call checks if this call, or any call within any accessed -- body could cause an ABE, and if so, outputs a warning. Orig_Ent -- differs from E only in the case of renamings, and points to the -- original name of the entity. This is used for error messages. -- Outer_Scope is the outer level scope for the original call. procedure Check_Internal_Call_Continue (N : Node_Id; E : Entity_Id; Outer_Scope : Entity_Id; Orig_Ent : Entity_Id); -- The processing for Check_Internal_Call is divided up into two phases, -- and this represents the second phase. The second phase is delayed if -- Delaying_Elab_Calls is set to True. In this delayed case, the first -- phase makes an entry in the Delay_Check table, which is processed -- when Check_Elab_Calls is called. N, E and Orig_Ent are as for the call -- to Check_Internal_Call. Outer_Scope is the outer level scope for -- the original call. procedure Set_Elaboration_Constraint (Call : Node_Id; Subp : Entity_Id; Scop : Entity_Id); -- The current unit U may depend semantically on some unit P which is not -- in the current context. If there is an elaboration call that reaches P, -- we need to indicate that P requires an Elaborate_All, but this is not -- effective in U's ali file, if there is no with_clause for P. In this -- case we add the Elaborate_All on the unit Q that directly or indirectly -- makes P available. This can happen in two cases: -- -- a) Q declares a subtype of a type declared in P, and the call is an -- initialization call for an object of that subtype. -- -- b) Q declares an object of some tagged type whose root type is -- declared in P, and the initialization call uses object notation on -- that object to reach a primitive operation or a classwide operation -- declared in P. -- -- If P appears in the context of U, the current processing is correct. -- Otherwise we must identify these two cases to retrieve Q and place the -- Elaborate_All_Desirable on it. function Has_Generic_Body (N : Node_Id) return Boolean; -- N is a generic package instantiation node, and this routine determines -- if this package spec does in fact have a generic body. If so, then -- True is returned, otherwise False. Note that this is not at all the -- same as checking if the unit requires a body, since it deals with -- the case of optional bodies accurately (i.e. if a body is optional, -- then it looks to see if a body is actually present). Note: this -- function can only do a fully correct job if in generating code mode -- where all bodies have to be present. If we are operating in semantics -- check only mode, then in some cases of optional bodies, a result of -- False may incorrectly be given. In practice this simply means that -- some cases of warnings for incorrect order of elaboration will only -- be given when generating code, which is not a big problem (and is -- inevitable, given the optional body semantics of Ada). procedure Insert_Elab_Check (N : Node_Id; C : Node_Id := Empty); -- Given code for an elaboration check (or unconditional raise if -- the check is not needed), inserts the code in the appropriate -- place. N is the call or instantiation node for which the check -- code is required. C is the test whose failure triggers the raise. procedure Output_Calls (N : Node_Id); -- Outputs chain of calls stored in the Elab_Call table. The caller -- has already generated the main warning message, so the warnings -- generated are all continuation messages. The argument is the -- call node at which the messages are to be placed. function Same_Elaboration_Scope (Scop1, Scop2 : Entity_Id) return Boolean; -- Given two scopes, determine whether they are the same scope from an -- elaboration point of view, i.e. packages and blocks are ignored. procedure Set_C_Scope; -- On entry C_Scope is set to some scope. On return, C_Scope is reset -- to be the enclosing compilation unit of this scope. function Spec_Entity (E : Entity_Id) return Entity_Id; -- Given a compilation unit entity, if it is a spec entity, it is -- returned unchanged. If it is a body entity, then the spec for -- the corresponding spec is returned procedure Supply_Bodies (N : Node_Id); -- Given a node, N, that is either a subprogram declaration or a package -- declaration, this procedure supplies dummy bodies for the subprogram -- or for all subprograms in the package. If the given node is not one -- of these two possibilities, then Supply_Bodies does nothing. The -- dummy body is supplied by setting the subprogram to be Imported with -- convention Stubbed. procedure Supply_Bodies (L : List_Id); -- Calls Supply_Bodies for all elements of the given list L. function Within (E1, E2 : Entity_Id) return Boolean; -- Given two scopes E1 and E2, returns True if E1 is equal to E2, or -- is one of its contained scopes, False otherwise. function Within_Elaborate_All (E : Entity_Id) return Boolean; -- Before emitting a warning on a scope E for a missing elaborate_all, -- check whether E may be in the context of a directly visible unit -- U to which the pragma applies. This prevents spurious warnings when -- the called entity is renamed within U. ------------------ -- Check_A_Call -- ------------------ procedure Check_A_Call (N : Node_Id; E : Entity_Id; Outer_Scope : Entity_Id; Inter_Unit_Only : Boolean; Generate_Warnings : Boolean := True) is Loc : constant Source_Ptr := Sloc (N); Ent : Entity_Id; Decl : Node_Id; E_Scope : Entity_Id; -- Top level scope of entity for called subprogram. This -- value includes following renamings and derivations, so -- this scope can be in a non-visible unit. This is the -- scope that is to be investigated to see whether an -- elaboration check is required. W_Scope : Entity_Id; -- Top level scope of directly called entity for subprogram. This -- differs from E_Scope in the case where renamings or derivations -- are involved, since it does not follow these links. W_Scope is -- generally in a visible unit, and it is this scope that may require -- an Elaborate_All. However, there are some cases (initialization -- calls and calls involving object notation) where W_Scope might not -- be in the context of the current unit, and there is an intermediate -- package that is, in which case the Elaborate_All has to be placed -- on this intedermediate package. These special cases are handled in -- Set_Elaboration_Constraint. Body_Acts_As_Spec : Boolean; -- Set to true if call is to body acting as spec (no separate spec) Inst_Case : constant Boolean := Nkind (N) in N_Generic_Instantiation; -- Indicates if we have instantiation case Caller_Unit_Internal : Boolean; Callee_Unit_Internal : Boolean; Inst_Caller : Source_Ptr; Inst_Callee : Source_Ptr; Unit_Caller : Unit_Number_Type; Unit_Callee : Unit_Number_Type; Cunit_SC : Boolean := False; -- Set to suppress dynamic elaboration checks where one of the -- enclosing scopes has Elaboration_Checks_Suppressed set, or else -- if a pragma Elaborate (_All) applies to that scope, in which case -- warnings on the scope are also suppressed. For the internal case, -- we ignore this flag. begin -- If the call is known to be within a local Suppress Elaboration -- pragma, nothing to check. This can happen in task bodies. if (Nkind (N) = N_Function_Call or else Nkind (N) = N_Procedure_Call_Statement) and then No_Elaboration_Check (N) then return; end if; -- Go to parent for derived subprogram, or to original subprogram -- in the case of a renaming (Alias covers both these cases) Ent := E; loop if (Suppress_Elaboration_Warnings (Ent) or else Elaboration_Checks_Suppressed (Ent)) and then (Inst_Case or else No (Alias (Ent))) then return; end if; -- Nothing to do for imported entities if Is_Imported (Ent) then return; end if; exit when Inst_Case or else No (Alias (Ent)); Ent := Alias (Ent); end loop; Decl := Unit_Declaration_Node (Ent); if Nkind (Decl) = N_Subprogram_Body then Body_Acts_As_Spec := True; elsif Nkind (Decl) = N_Subprogram_Declaration or else Nkind (Decl) = N_Subprogram_Body_Stub or else Inst_Case then Body_Acts_As_Spec := False; -- If we have none of an instantiation, subprogram body or -- subprogram declaration, then it is not a case that we want -- to check. (One case is a call to a generic formal subprogram, -- where we do not want the check in the template). else return; end if; E_Scope := Ent; loop if Elaboration_Checks_Suppressed (E_Scope) or else Suppress_Elaboration_Warnings (E_Scope) then Cunit_SC := True; end if; -- Exit when we get to compilation unit, not counting subunits exit when Is_Compilation_Unit (E_Scope) and then (Is_Child_Unit (E_Scope) or else Scope (E_Scope) = Standard_Standard); -- If we did not find a compilation unit, other than standard, -- then nothing to check (happens in some instantiation cases) if E_Scope = Standard_Standard then return; -- Otherwise move up a scope looking for compilation unit else E_Scope := Scope (E_Scope); end if; end loop; -- No checks needed for pure or preelaborated compilation units if Is_Pure (E_Scope) or else Is_Preelaborated (E_Scope) then return; end if; -- If the generic entity is within a deeper instance than we are, then -- either the instantiation to which we refer itself caused an ABE, in -- which case that will be handled separately, or else we know that the -- body we need appears as needed at the point of the instantiation. -- However, this assumption is only valid if we are in static mode. if not Dynamic_Elaboration_Checks and then Instantiation_Depth (Sloc (Ent)) > Instantiation_Depth (Sloc (N)) then return; end if; -- Do not give a warning for a package with no body if Ekind (Ent) = E_Generic_Package and then not Has_Generic_Body (N) then return; end if; -- Case of entity is not in current unit (i.e. with'ed unit case) if E_Scope /= C_Scope then -- We are only interested in such calls if the outer call was from -- elaboration code, or if we are in Dynamic_Elaboration_Checks mode. if not From_Elab_Code and then not Dynamic_Elaboration_Checks then return; end if; -- Nothing to do if some scope said that no checks were required if Cunit_SC then return; end if; -- Nothing to do for a generic instance, because in this case -- the checking was at the point of instantiation of the generic -- However, this shortcut is only applicable in static mode. if Is_Generic_Instance (Ent) and not Dynamic_Elaboration_Checks then return; end if; -- Nothing to do if subprogram with no separate spec. However, -- a call to Deep_Initialize may result in a call to a user-defined -- Initialize procedure, which imposes a body dependency. This -- happens only if the type is controlled and the Initialize -- procedure is not inherited. if Body_Acts_As_Spec then if Is_TSS (Ent, TSS_Deep_Initialize) then declare Typ : Entity_Id; Init : Entity_Id; begin Typ := Etype (Next_Formal (First_Formal (Ent))); if not Is_Controlled (Typ) then return; else Init := Find_Prim_Op (Typ, Name_Initialize); if Comes_From_Source (Init) then Ent := Init; else return; end if; end if; end; else return; end if; end if; -- Check cases of internal units Callee_Unit_Internal := Is_Internal_File_Name (Unit_File_Name (Get_Source_Unit (E_Scope))); -- Do not give a warning if the with'ed unit is internal -- and this is the generic instantiation case (this saves a -- lot of hassle dealing with the Text_IO special child units) if Callee_Unit_Internal and Inst_Case then return; end if; if C_Scope = Standard_Standard then Caller_Unit_Internal := False; else Caller_Unit_Internal := Is_Internal_File_Name (Unit_File_Name (Get_Source_Unit (C_Scope))); end if; -- Do not give a warning if the with'ed unit is internal -- and the caller is not internal (since the binder always -- elaborates internal units first). if Callee_Unit_Internal and (not Caller_Unit_Internal) then return; end if; -- For now, if debug flag -gnatdE is not set, do no checking for -- one internal unit withing another. This fixes the problem with -- the sgi build and storage errors. To be resolved later ??? if (Callee_Unit_Internal and Caller_Unit_Internal) and then not Debug_Flag_EE then return; end if; if Is_TSS (E, TSS_Deep_Initialize) then Ent := E; end if; -- If the call is in an instance, and the called entity is not -- defined in the same instance, then the elaboration issue -- focuses around the unit containing the template, it is -- this unit which requires an Elaborate_All. -- However, if we are doing dynamic elaboration, we need to -- chase the call in the usual manner. -- We do not handle the case of calling a generic formal correctly -- in the static case. See test 4703-004 to explore this gap ??? Inst_Caller := Instantiation (Get_Source_File_Index (Sloc (N))); Inst_Callee := Instantiation (Get_Source_File_Index (Sloc (Ent))); if Inst_Caller = No_Location then Unit_Caller := No_Unit; else Unit_Caller := Get_Source_Unit (N); end if; if Inst_Callee = No_Location then Unit_Callee := No_Unit; else Unit_Callee := Get_Source_Unit (Ent); end if; if Unit_Caller /= No_Unit and then Unit_Callee /= Unit_Caller and then not Dynamic_Elaboration_Checks then E_Scope := Spec_Entity (Cunit_Entity (Unit_Caller)); -- If we don't get a spec entity, just ignore call. Not -- quite clear why this check is necessary. if No (E_Scope) then return; end if; -- Otherwise step to enclosing compilation unit while not Is_Compilation_Unit (E_Scope) loop E_Scope := Scope (E_Scope); end loop; -- For the case N is not an instance, or a call within instance -- We recompute E_Scope for the error message, since we -- do NOT want to go to the unit which has the ultimate -- declaration in the case of renaming and derivation and -- we also want to go to the generic unit in the case of -- an instance, and no further. else -- Loop to carefully follow renamings and derivations -- one step outside the current unit, but not further. if not Inst_Case and then Present (Alias (Ent)) then E_Scope := Alias (Ent); else E_Scope := Ent; end if; loop while not Is_Compilation_Unit (E_Scope) loop E_Scope := Scope (E_Scope); end loop; -- If E_Scope is the same as C_Scope, it means that there -- definitely was a local renaming or derivation, and we -- are not yet out of the current unit. exit when E_Scope /= C_Scope; Ent := Alias (Ent); E_Scope := Ent; -- If no alias, there is a previous error if No (Ent) then return; end if; end loop; end if; if Within_Elaborate_All (E_Scope) then return; end if; -- Find top level scope for called entity (not following renamings -- or derivations). This is where the Elaborate_All will go if it -- is needed. We start with the called entity, except in the case -- of an initialization procedure outside the current package, where -- the init proc is in the root package, and we start from the entity -- of the name in the call. if Is_Entity_Name (Name (N)) and then Is_Init_Proc (Entity (Name (N))) and then not In_Same_Extended_Unit (N, Entity (Name (N))) then W_Scope := Scope (Entity (Name (N))); else W_Scope := E; end if; while not Is_Compilation_Unit (W_Scope) loop W_Scope := Scope (W_Scope); end loop; -- Now check if an elaborate_all (or dynamic check) is needed if not Suppress_Elaboration_Warnings (Ent) and then not Elaboration_Checks_Suppressed (Ent) and then not Suppress_Elaboration_Warnings (E_Scope) and then not Elaboration_Checks_Suppressed (E_Scope) and then Elab_Warnings and then Generate_Warnings then if Inst_Case then Error_Msg_NE ("instantiation of& may raise Program_Error?", N, Ent); else if Is_Init_Proc (Entity (Name (N))) and then Comes_From_Source (Ent) then Error_Msg_NE ("implicit call to & may raise Program_Error?", N, Ent); else Error_Msg_NE ("call to & may raise Program_Error?", N, Ent); end if; end if; Error_Msg_Qual_Level := Nat'Last; Error_Msg_NE ("\missing pragma Elaborate_All for&?", N, W_Scope); Error_Msg_Qual_Level := 0; Output_Calls (N); -- Set flag to prevent further warnings for same unit -- unless in All_Errors_Mode. if not All_Errors_Mode and not Dynamic_Elaboration_Checks then Set_Suppress_Elaboration_Warnings (W_Scope, True); end if; end if; -- Check for runtime elaboration check required if Dynamic_Elaboration_Checks then if not Elaboration_Checks_Suppressed (Ent) and then not Elaboration_Checks_Suppressed (W_Scope) and then not Elaboration_Checks_Suppressed (E_Scope) and then not Cunit_SC then -- Runtime elaboration check required. Generate check of the -- elaboration Boolean for the unit containing the entity. -- Note that for this case, we do check the real unit (the -- one from following renamings, since that is the issue!) -- Could this possibly miss a useless but required PE??? Insert_Elab_Check (N, Make_Attribute_Reference (Loc, Attribute_Name => Name_Elaborated, Prefix => New_Occurrence_Of (Spec_Entity (E_Scope), Loc))); end if; -- Case of static elaboration model else -- Do not do anything if elaboration checks suppressed. Note -- that we check Ent here, not E, since we want the real entity -- for the body to see if checks are suppressed for it, not the -- dummy entry for renamings or derivations. if Elaboration_Checks_Suppressed (Ent) or else Elaboration_Checks_Suppressed (E_Scope) or else Elaboration_Checks_Suppressed (W_Scope) then null; -- Here we need to generate an implicit elaborate all else -- Generate elaborate_all warning unless suppressed if (Elab_Warnings and Generate_Warnings and not Inst_Case) and then not Suppress_Elaboration_Warnings (Ent) and then not Suppress_Elaboration_Warnings (E_Scope) and then not Suppress_Elaboration_Warnings (W_Scope) then Error_Msg_Node_2 := W_Scope; Error_Msg_NE ("call to& in elaboration code " & "requires pragma Elaborate_All on&?", N, E); end if; -- Set indication for binder to generate Elaborate_All Set_Elaboration_Constraint (N, E, W_Scope); end if; end if; -- Case of entity is in same unit as call or instantiation elsif not Inter_Unit_Only then Check_Internal_Call (N, Ent, Outer_Scope, E); end if; end Check_A_Call; ----------------------------- -- Check_Bad_Instantiation -- ----------------------------- procedure Check_Bad_Instantiation (N : Node_Id) is Ent : Entity_Id; begin -- Nothing to do if we do not have an instantiation (happens in some -- error cases, and also in the formal package declaration case) if Nkind (N) not in N_Generic_Instantiation then return; -- Nothing to do if serious errors detected (avoid cascaded errors) elsif Serious_Errors_Detected /= 0 then return; -- Nothing to do if not in full analysis mode elsif not Full_Analysis then return; -- Nothing to do if inside a generic template elsif Inside_A_Generic then return; -- Nothing to do if a library level instantiation elsif Nkind (Parent (N)) = N_Compilation_Unit then return; -- Nothing to do if we are compiling a proper body for semantic -- purposes only. The generic body may be in another proper body. elsif Nkind (Parent (Unit_Declaration_Node (Main_Unit_Entity))) = N_Subunit then return; end if; Ent := Get_Generic_Entity (N); -- The case we are interested in is when the generic spec is in the -- current declarative part if not Same_Elaboration_Scope (Current_Scope, Scope (Ent)) or else not In_Same_Extended_Unit (N, Ent) then return; end if; -- If the generic entity is within a deeper instance than we are, then -- either the instantiation to which we refer itself caused an ABE, in -- which case that will be handled separately. Otherwise, we know that -- the body we need appears as needed at the point of the instantiation. -- If they are both at the same level but not within the same instance -- then the body of the generic will be in the earlier instance. declare D1 : constant Int := Instantiation_Depth (Sloc (Ent)); D2 : constant Int := Instantiation_Depth (Sloc (N)); begin if D1 > D2 then return; elsif D1 = D2 and then Is_Generic_Instance (Scope (Ent)) and then not In_Open_Scopes (Scope (Ent)) then return; end if; end; -- Now we can proceed, if the entity being called has a completion, -- then we are definitely OK, since we have already seen the body. if Has_Completion (Ent) then return; end if; -- If there is no body, then nothing to do if not Has_Generic_Body (N) then return; end if; -- Here we definitely have a bad instantiation Error_Msg_NE ("?cannot instantiate& before body seen", N, Ent); if Present (Instance_Spec (N)) then Supply_Bodies (Instance_Spec (N)); end if; Error_Msg_N ("\?Program_Error will be raised at run time", N); Insert_Elab_Check (N); Set_ABE_Is_Certain (N); end Check_Bad_Instantiation; --------------------- -- Check_Elab_Call -- --------------------- procedure Check_Elab_Call (N : Node_Id; Outer_Scope : Entity_Id := Empty) is Ent : Entity_Id; P : Node_Id; function Get_Called_Ent return Entity_Id; -- Retrieve called entity. If this is a call to a protected subprogram, -- entity is a selected component. The callable entity may be absent, -- in which case there is no check to perform. This happens with -- non-analyzed calls in nested generics. -------------------- -- Get_Called_Ent -- -------------------- function Get_Called_Ent return Entity_Id is Nam : Node_Id; begin Nam := Name (N); if No (Nam) then return Empty; elsif Nkind (Nam) = N_Selected_Component then return Entity (Selector_Name (Nam)); elsif not Is_Entity_Name (Nam) then return Empty; else return Entity (Nam); end if; end Get_Called_Ent; -- Start of processing for Check_Elab_Call begin -- If the call does not come from the main unit, there is nothing to -- check. Elaboration call from units in the context of the main unit -- will lead to semantic dependencies when those units are compiled. if not In_Extended_Main_Code_Unit (N) then return; end if; -- For an entry call, check relevant restriction if Nkind (N) = N_Entry_Call_Statement and then not In_Subprogram_Or_Concurrent_Unit then Check_Restriction (No_Entry_Calls_In_Elaboration_Code, N); -- Nothing to do if this is not a call (happens in some error -- conditions, and in some cases where rewriting occurs). elsif Nkind (N) /= N_Function_Call and then Nkind (N) /= N_Procedure_Call_Statement then return; -- Nothing to do if this is a call already rewritten for elab checking. elsif Nkind (Parent (N)) = N_Conditional_Expression then return; -- Nothing to do if inside a generic template elsif Inside_A_Generic and then not Present (Enclosing_Generic_Body (N)) then return; end if; -- Here we have a call at elaboration time which must be checked if Debug_Flag_LL then Write_Str (" Check_Elab_Call: "); if No (Name (N)) or else not Is_Entity_Name (Name (N)) then Write_Str ("<> "); else Write_Name (Chars (Entity (Name (N)))); end if; Write_Str (" call at "); Write_Location (Sloc (N)); Write_Eol; end if; -- Climb up the tree to make sure we are not inside a -- default expression of a parameter specification or -- a record component, since in both these cases, we -- will be doing the actual call later, not now, and it -- is at the time of the actual call (statically speaking) -- that we must do our static check, not at the time of -- its initial analysis). However, we have to check calls -- within component definitions (e.g., a function call -- that determines an array component bound), so we -- terminate the loop in that case. P := Parent (N); while Present (P) loop if Nkind (P) = N_Parameter_Specification or else Nkind (P) = N_Component_Declaration then return; -- The call occurs within the constraint of a component, -- so it must be checked. elsif Nkind (P) = N_Component_Definition then exit; else P := Parent (P); end if; end loop; -- Stuff that happens only at the outer level if No (Outer_Scope) then Elab_Visited.Set_Last (0); -- Nothing to do if current scope is Standard (this is a bit -- odd, but it happens in the case of generic instantiations). C_Scope := Current_Scope; if C_Scope = Standard_Standard then return; end if; -- First case, we are in elaboration code From_Elab_Code := not In_Subprogram_Or_Concurrent_Unit; if From_Elab_Code then -- Complain if call that comes from source in preelaborated -- unit and we are not inside a subprogram (i.e. we are in -- elab code) if Comes_From_Source (N) and then In_Preelaborated_Unit and then not In_Inlined_Body then Error_Msg_N ("non-static call not allowed in preelaborated unit", N); return; end if; -- Second case, we are inside a subprogram or concurrent unit -- i.e, we are not in elaboration code. else -- In this case, the issue is whether we are inside the -- declarative part of the unit in which we live, or inside -- its statements. In the latter case, there is no issue of -- ABE calls at this level (a call from outside to the unit -- in which we live might cause an ABE, but that will be -- detected when we analyze that outer level call, as it -- recurses into the called unit). -- Climb up the tree, doing this test, and also testing -- for being inside a default expression, which, as -- discussed above, is not checked at this stage. declare P : Node_Id; L : List_Id; begin P := N; loop -- If we find a parentless subtree, it seems safe to -- assume that we are not in a declarative part and -- that no checking is required. if No (P) then return; end if; if Is_List_Member (P) then L := List_Containing (P); P := Parent (L); else L := No_List; P := Parent (P); end if; exit when Nkind (P) = N_Subunit; -- Filter out case of default expressions, where -- we do not do the check at this stage. if Nkind (P) = N_Parameter_Specification or else Nkind (P) = N_Component_Declaration then return; end if; if Nkind (P) = N_Subprogram_Body or else Nkind (P) = N_Protected_Body or else Nkind (P) = N_Task_Body or else Nkind (P) = N_Block_Statement then if L = Declarations (P) then exit; -- We are not in elaboration code, but we are doing -- dynamic elaboration checks, in this case, we still -- need to do the call, since the subprogram we are in -- could be called from another unit, also in dynamic -- elaboration check mode, at elaboration time. elsif Dynamic_Elaboration_Checks then -- This is a rather new check, going into version -- 3.14a1 for the first time (V1.80 of this unit), -- so we provide a debug flag to enable it. That -- way we have an easy work around for regressions -- that are caused by this new check. This debug -- flag can be removed later. if Debug_Flag_DD then return; end if; -- Do the check in this case exit; elsif Nkind (P) = N_Task_Body then -- The check is deferred until Check_Task_Activation -- but we need to capture local suppress pragmas -- that may inhibit checks on this call. Ent := Get_Called_Ent; if No (Ent) then return; elsif Elaboration_Checks_Suppressed (Current_Scope) or else Elaboration_Checks_Suppressed (Ent) or else Elaboration_Checks_Suppressed (Scope (Ent)) then Set_No_Elaboration_Check (N); end if; return; -- Static model, call is not in elaboration code, we -- never need to worry, because in the static model -- the top level caller always takes care of things. else return; end if; end if; end loop; end; end if; end if; Ent := Get_Called_Ent; if No (Ent) then return; end if; -- Nothing to do if this is a recursive call (i.e. a call to -- an entity that is already in the Elab_Call stack) for J in 1 .. Elab_Visited.Last loop if Ent = Elab_Visited.Table (J) then return; end if; end loop; -- See if we need to analyze this call. We analyze it if either of -- the following conditions is met: -- It is an inner level call (since in this case it was triggered -- by an outer level call from elaboration code), but only if the -- call is within the scope of the original outer level call. -- It is an outer level call from elaboration code, or the called -- entity is in the same elaboration scope. -- And in these cases, we will check both inter-unit calls and -- intra-unit (within a single unit) calls. C_Scope := Current_Scope; -- If not outer level call, then we follow it if it is within -- the original scope of the outer call. if Present (Outer_Scope) and then Within (Scope (Ent), Outer_Scope) then Set_C_Scope; Check_A_Call (N, Ent, Outer_Scope, Inter_Unit_Only => False); elsif Elaboration_Checks_Suppressed (Current_Scope) then null; elsif From_Elab_Code then Set_C_Scope; Check_A_Call (N, Ent, Standard_Standard, Inter_Unit_Only => False); elsif Same_Elaboration_Scope (C_Scope, Scope (Ent)) then Set_C_Scope; Check_A_Call (N, Ent, Scope (Ent), Inter_Unit_Only => False); -- If none of those cases holds, but Dynamic_Elaboration_Checks mode -- is set, then we will do the check, but only in the inter-unit case -- (this is to accommodate unguarded elaboration calls from other units -- in which this same mode is set). We don't want warnings in this case, -- it would generate warnings having nothing to do with elaboration. elsif Dynamic_Elaboration_Checks then Set_C_Scope; Check_A_Call (N, Ent, Standard_Standard, Inter_Unit_Only => True, Generate_Warnings => False); -- Otherwise nothing to do else return; end if; -- A call to an Init_Proc in elaboration code may bring additional -- dependencies, if some of the record components thereof have -- initializations that are function calls that come from source. -- We treat the current node as a call to each of these functions, -- to check their elaboration impact. if Is_Init_Proc (Ent) and then From_Elab_Code then Process_Init_Proc : declare Unit_Decl : constant Node_Id := Unit_Declaration_Node (Ent); function Process (Nod : Node_Id) return Traverse_Result; -- Find subprogram calls within body of init_proc for -- Traverse instantiation below. function Process (Nod : Node_Id) return Traverse_Result is Func : Entity_Id; begin if (Nkind (Nod) = N_Function_Call or else Nkind (Nod) = N_Procedure_Call_Statement) and then Is_Entity_Name (Name (Nod)) then Func := Entity (Name (Nod)); if Comes_From_Source (Func) then Check_A_Call (N, Func, Standard_Standard, Inter_Unit_Only => True); end if; return OK; else return OK; end if; end Process; procedure Traverse_Body is new Traverse_Proc (Process); -- Start of processing for Process_Init_Proc begin if Nkind (Unit_Decl) = N_Subprogram_Body then Traverse_Body (Handled_Statement_Sequence (Unit_Decl)); end if; end Process_Init_Proc; end if; end Check_Elab_Call; ---------------------- -- Check_Elab_Calls -- ---------------------- procedure Check_Elab_Calls is begin -- If expansion is disabled, do not generate any checks. Also -- skip checks if any subunits are missing because in either -- case we lack the full information that we need, and no object -- file will be created in any case. if not Expander_Active or else Is_Generic_Unit (Cunit_Entity (Main_Unit)) or else Subunits_Missing then return; end if; -- Skip delayed calls if we had any errors if Serious_Errors_Detected = 0 then Delaying_Elab_Checks := False; Expander_Mode_Save_And_Set (True); for J in Delay_Check.First .. Delay_Check.Last loop New_Scope (Delay_Check.Table (J).Curscop); From_Elab_Code := Delay_Check.Table (J).From_Elab_Code; Check_Internal_Call_Continue ( N => Delay_Check.Table (J).N, E => Delay_Check.Table (J).E, Outer_Scope => Delay_Check.Table (J).Outer_Scope, Orig_Ent => Delay_Check.Table (J).Orig_Ent); Pop_Scope; end loop; -- Set Delaying_Elab_Checks back on for next main compilation Expander_Mode_Restore; Delaying_Elab_Checks := True; end if; end Check_Elab_Calls; ------------------------------ -- Check_Elab_Instantiation -- ------------------------------ procedure Check_Elab_Instantiation (N : Node_Id; Outer_Scope : Entity_Id := Empty) is Ent : Entity_Id; begin -- Check for and deal with bad instantiation case. There is some -- duplicated code here, but we will worry about this later ??? Check_Bad_Instantiation (N); if ABE_Is_Certain (N) then return; end if; -- Nothing to do if we do not have an instantiation (happens in some -- error cases, and also in the formal package declaration case) if Nkind (N) not in N_Generic_Instantiation then return; end if; -- Nothing to do if inside a generic template if Inside_A_Generic then return; end if; -- Nothing to do if the instantiation is not in the main unit. if not In_Extended_Main_Code_Unit (N) then return; end if; Ent := Get_Generic_Entity (N); From_Elab_Code := not In_Subprogram_Or_Concurrent_Unit; -- See if we need to analyze this instantiation. We analyze it if -- either of the following conditions is met: -- It is an inner level instantiation (since in this case it was -- triggered by an outer level call from elaboration code), but -- only if the instantiation is within the scope of the original -- outer level call. -- It is an outer level instantiation from elaboration code, or the -- instantiated entity is in the same elaboratoin scope. -- And in these cases, we will check both the inter-unit case and -- the intra-unit (within a single unit) case. C_Scope := Current_Scope; if Present (Outer_Scope) and then Within (Scope (Ent), Outer_Scope) then Set_C_Scope; Check_A_Call (N, Ent, Outer_Scope, Inter_Unit_Only => False); elsif From_Elab_Code then Set_C_Scope; Check_A_Call (N, Ent, Standard_Standard, Inter_Unit_Only => False); elsif Same_Elaboration_Scope (C_Scope, Scope (Ent)) then Set_C_Scope; Check_A_Call (N, Ent, Scope (Ent), Inter_Unit_Only => False); -- If none of those cases holds, but Dynamic_Elaboration_Checks mode -- is set, then we will do the check, but only in the inter-unit case -- (this is to accommodate unguarded elaboration calls from other units -- in which this same mode is set). We inhibit warnings in this case, -- since this instantiation is not occurring in elaboration code. elsif Dynamic_Elaboration_Checks then Set_C_Scope; Check_A_Call (N, Ent, Standard_Standard, Inter_Unit_Only => True, Generate_Warnings => False); else return; end if; end Check_Elab_Instantiation; ------------------------- -- Check_Internal_Call -- ------------------------- procedure Check_Internal_Call (N : Node_Id; E : Entity_Id; Outer_Scope : Entity_Id; Orig_Ent : Entity_Id) is Inst_Case : constant Boolean := Nkind (N) in N_Generic_Instantiation; begin -- If not function or procedure call or instantiation, then ignore -- call (this happens in some error case and rewriting cases) if Nkind (N) /= N_Function_Call and then Nkind (N) /= N_Procedure_Call_Statement and then not Inst_Case then return; -- Nothing to do if this is a call or instantiation that has -- already been found to be a sure ABE elsif ABE_Is_Certain (N) then return; -- Nothing to do if errors already detected (avoid cascaded errors) elsif Serious_Errors_Detected /= 0 then return; -- Nothing to do if not in full analysis mode elsif not Full_Analysis then return; -- Nothing to do if within a default expression, since the call -- is not actualy being made at this time. elsif In_Default_Expression then return; -- Nothing to do for call to intrinsic subprogram elsif Is_Intrinsic_Subprogram (E) then return; -- No need to trace local calls if checking task activation, because -- other local bodies are elaborated already. elsif In_Task_Activation then return; end if; -- Delay this call if we are still delaying calls if Delaying_Elab_Checks then Delay_Check.Increment_Last; Delay_Check.Table (Delay_Check.Last) := (N => N, E => E, Orig_Ent => Orig_Ent, Curscop => Current_Scope, Outer_Scope => Outer_Scope, From_Elab_Code => From_Elab_Code); return; -- Otherwise, call phase 2 continuation right now else Check_Internal_Call_Continue (N, E, Outer_Scope, Orig_Ent); end if; end Check_Internal_Call; ---------------------------------- -- Check_Internal_Call_Continue -- ---------------------------------- procedure Check_Internal_Call_Continue (N : Node_Id; E : Entity_Id; Outer_Scope : Entity_Id; Orig_Ent : Entity_Id) is Loc : constant Source_Ptr := Sloc (N); Inst_Case : constant Boolean := Is_Generic_Unit (E); Sbody : Node_Id; Ebody : Entity_Id; function Process (N : Node_Id) return Traverse_Result; -- Function applied to each node as we traverse the body. -- Checks for call that needs checking, and if so checks -- it. Always returns OK, so entire tree is traversed. ------------- -- Process -- ------------- function Process (N : Node_Id) return Traverse_Result is begin -- If user has specified that there are no entry calls in elaboration -- code, do not trace past an accept statement, because the rendez- -- vous will happen after elaboration. if (Nkind (Original_Node (N)) = N_Accept_Statement or else Nkind (Original_Node (N)) = N_Selective_Accept) and then Restriction_Active (No_Entry_Calls_In_Elaboration_Code) then return Abandon; -- If we have a subprogram call, check it elsif Nkind (N) = N_Function_Call or else Nkind (N) = N_Procedure_Call_Statement then Check_Elab_Call (N, Outer_Scope); return OK; -- If we have a generic instantiation, check it elsif Nkind (N) in N_Generic_Instantiation then Check_Elab_Instantiation (N, Outer_Scope); return OK; -- Skip subprogram bodies that come from source (wait for -- call to analyze these). The reason for the come from -- source test is to avoid catching task bodies. -- For task bodies, we should really avoid these too, waiting -- for the task activation, but that's too much trouble to -- catch for now, so we go in unconditionally. This is not -- so terrible, it means the error backtrace is not quite -- complete, and we are too eager to scan bodies of tasks -- that are unused, but this is hardly very significant! elsif Nkind (N) = N_Subprogram_Body and then Comes_From_Source (N) then return Skip; else return OK; end if; end Process; procedure Traverse is new Atree.Traverse_Proc; -- Traverse procedure using above Process function -- Start of processing for Check_Internal_Call_Continue begin -- Save outer level call if at outer level if Elab_Call.Last = 0 then Outer_Level_Sloc := Loc; end if; Elab_Visited.Increment_Last; Elab_Visited.Table (Elab_Visited.Last) := E; -- If the call is to a function that renames a literal, no check -- is needed. if Ekind (E) = E_Enumeration_Literal then return; end if; Sbody := Unit_Declaration_Node (E); if Nkind (Sbody) /= N_Subprogram_Body and then Nkind (Sbody) /= N_Package_Body then Ebody := Corresponding_Body (Sbody); if No (Ebody) then return; else Sbody := Unit_Declaration_Node (Ebody); end if; end if; -- If the body appears after the outer level call or -- instantiation then we have an error case handled below. if Earlier_In_Extended_Unit (Outer_Level_Sloc, Sloc (Sbody)) and then not In_Task_Activation then null; -- If we have the instantiation case we are done, since we now -- know that the body of the generic appeared earlier. elsif Inst_Case then return; -- Otherwise we have a call, so we trace through the called -- body to see if it has any problems .. else pragma Assert (Nkind (Sbody) = N_Subprogram_Body); Elab_Call.Increment_Last; Elab_Call.Table (Elab_Call.Last).Cloc := Loc; Elab_Call.Table (Elab_Call.Last).Ent := E; if Debug_Flag_LL then Write_Str ("Elab_Call.Last = "); Write_Int (Int (Elab_Call.Last)); Write_Str (" Ent = "); Write_Name (Chars (E)); Write_Str (" at "); Write_Location (Sloc (N)); Write_Eol; end if; -- Now traverse declarations and statements of subprogram body. -- Note that we cannot simply Traverse (Sbody), since traverse -- does not normally visit subprogram bodies. declare Decl : Node_Id := First (Declarations (Sbody)); begin while Present (Decl) loop Traverse (Decl); Next (Decl); end loop; end; Traverse (Handled_Statement_Sequence (Sbody)); Elab_Call.Decrement_Last; return; end if; -- Here is the case of calling a subprogram where the body has -- not yet been encountered, a warning message is needed. -- If we have nothing in the call stack, then this is at the -- outer level, and the ABE is bound to occur. if Elab_Call.Last = 0 then if Inst_Case then Error_Msg_NE ("?cannot instantiate& before body seen", N, Orig_Ent); else Error_Msg_NE ("?cannot call& before body seen", N, Orig_Ent); end if; Error_Msg_N ("\?Program_Error will be raised at run time", N); Insert_Elab_Check (N); -- Call is not at outer level else -- Deal with dynamic elaboration check if not Elaboration_Checks_Suppressed (E) then Set_Elaboration_Entity_Required (E); -- Case of no elaboration entity allocated yet if No (Elaboration_Entity (E)) then -- Create object declaration for elaboration entity, and put it -- just in front of the spec of the subprogram or generic unit, -- in the same scope as this unit. declare Loce : constant Source_Ptr := Sloc (E); Ent : constant Entity_Id := Make_Defining_Identifier (Loc, Chars => New_External_Name (Chars (E), 'E')); begin Set_Elaboration_Entity (E, Ent); New_Scope (Scope (E)); Insert_Action (Declaration_Node (E), Make_Object_Declaration (Loce, Defining_Identifier => Ent, Object_Definition => New_Occurrence_Of (Standard_Boolean, Loce), Expression => New_Occurrence_Of (Standard_False, Loce))); -- Set elaboration flag at the point of the body Set_Elaboration_Flag (Sbody, E); -- Kill current value indication. This is necessary -- because the tests of this flag are inserted out of -- sequence and must not pick up bogus indications of -- the wrong constant value. Also, this is never a true -- constant, since one way or another, it gets reset. Set_Current_Value (Ent, Empty); Set_Is_True_Constant (Ent, False); Pop_Scope; end; end if; -- Generate check of the elaboration Boolean Insert_Elab_Check (N, New_Occurrence_Of (Elaboration_Entity (E), Loc)); end if; -- Generate the warning if not Suppress_Elaboration_Warnings (E) and then not Elaboration_Checks_Suppressed (E) then if Inst_Case then Error_Msg_NE ("instantiation of& may occur before body is seen?", N, Orig_Ent); else Error_Msg_NE ("call to& may occur before body is seen?", N, Orig_Ent); end if; Error_Msg_N ("\Program_Error may be raised at run time?", N); Output_Calls (N); end if; end if; -- Set flag to suppress further warnings on same subprogram -- unless in all errors mode if not All_Errors_Mode then Set_Suppress_Elaboration_Warnings (E); end if; end Check_Internal_Call_Continue; --------------------------- -- Check_Task_Activation -- --------------------------- procedure Check_Task_Activation (N : Node_Id) is Loc : constant Source_Ptr := Sloc (N); Inter_Procs : constant Elist_Id := New_Elmt_List; Intra_Procs : constant Elist_Id := New_Elmt_List; Ent : Entity_Id; P : Entity_Id; Task_Scope : Entity_Id; Cunit_SC : Boolean := False; Decl : Node_Id; Elmt : Elmt_Id; Enclosing : Entity_Id; procedure Add_Task_Proc (Typ : Entity_Id); -- Add to Task_Procs the task body procedure(s) of task types in Typ. -- For record types, this procedure recurses over component types. procedure Collect_Tasks (Decls : List_Id); -- Collect the types of the tasks that are to be activated in the given -- list of declarations, in order to perform elaboration checks on the -- corresponding task procedures which are called implicitly here. function Outer_Unit (E : Entity_Id) return Entity_Id; -- find enclosing compilation unit of Entity, ignoring subunits, or -- else enclosing subprogram. If E is not a package, there is no need -- for inter-unit elaboration checks. ------------------- -- Add_Task_Proc -- ------------------- procedure Add_Task_Proc (Typ : Entity_Id) is Comp : Entity_Id; Proc : Entity_Id := Empty; begin if Is_Task_Type (Typ) then Proc := Get_Task_Body_Procedure (Typ); elsif Is_Array_Type (Typ) and then Has_Task (Base_Type (Typ)) then Add_Task_Proc (Component_Type (Typ)); elsif Is_Record_Type (Typ) and then Has_Task (Base_Type (Typ)) then Comp := First_Component (Typ); while Present (Comp) loop Add_Task_Proc (Etype (Comp)); Comp := Next_Component (Comp); end loop; end if; -- If the task type is another unit, we will perform the usual -- elaboration check on its enclosing unit. If the type is in the -- same unit, we can trace the task body as for an internal call, -- but we only need to examine other external calls, because at -- the point the task is activated, internal subprogram bodies -- will have been elaborated already. We keep separate lists for -- each kind of task. -- Skip this test if errors have occurred, since in this case -- we can get false indications. if Serious_Errors_Detected /= 0 then return; end if; if Present (Proc) then if Outer_Unit (Scope (Proc)) = Enclosing then if No (Corresponding_Body (Unit_Declaration_Node (Proc))) and then (not Is_Generic_Instance (Scope (Proc)) or else Scope (Proc) = Scope (Defining_Identifier (Decl))) then Error_Msg_N ("task will be activated before elaboration of its body?", Decl); Error_Msg_N ("Program_Error will be raised at run-time?", Decl); elsif Present (Corresponding_Body (Unit_Declaration_Node (Proc))) then Append_Elmt (Proc, Intra_Procs); end if; else Elmt := First_Elmt (Inter_Procs); -- No need for multiple entries of the same type. while Present (Elmt) loop if Node (Elmt) = Proc then return; end if; Next_Elmt (Elmt); end loop; Append_Elmt (Proc, Inter_Procs); end if; end if; end Add_Task_Proc; ------------------- -- Collect_Tasks -- ------------------- procedure Collect_Tasks (Decls : List_Id) is begin if Present (Decls) then Decl := First (Decls); while Present (Decl) loop if Nkind (Decl) = N_Object_Declaration and then Has_Task (Etype (Defining_Identifier (Decl))) then Add_Task_Proc (Etype (Defining_Identifier (Decl))); end if; Next (Decl); end loop; end if; end Collect_Tasks; ---------------- -- Outer_Unit -- ---------------- function Outer_Unit (E : Entity_Id) return Entity_Id is Outer : Entity_Id := E; begin while Present (Outer) loop if Elaboration_Checks_Suppressed (Outer) then Cunit_SC := True; end if; exit when Is_Child_Unit (Outer) or else Scope (Outer) = Standard_Standard or else Ekind (Outer) /= E_Package; Outer := Scope (Outer); end loop; return Outer; end Outer_Unit; -- Start of processing for Check_Task_Activation begin Enclosing := Outer_Unit (Current_Scope); -- Find all tasks declared in the current unit. if Nkind (N) = N_Package_Body then P := Unit_Declaration_Node (Corresponding_Spec (N)); Collect_Tasks (Declarations (N)); Collect_Tasks (Visible_Declarations (Specification (P))); Collect_Tasks (Private_Declarations (Specification (P))); elsif Nkind (N) = N_Package_Declaration then Collect_Tasks (Visible_Declarations (Specification (N))); Collect_Tasks (Private_Declarations (Specification (N))); else Collect_Tasks (Declarations (N)); end if; -- We only perform detailed checks in all tasks are library level -- entities. If the master is a subprogram or task, activation will -- depend on the activation of the master itself. -- Should dynamic checks be added in the more general case??? if Ekind (Enclosing) /= E_Package then return; end if; -- For task types defined in other units, we want the unit containing -- the task body to be elaborated before the current one. Elmt := First_Elmt (Inter_Procs); while Present (Elmt) loop Ent := Node (Elmt); Task_Scope := Outer_Unit (Scope (Ent)); if not Is_Compilation_Unit (Task_Scope) then null; elsif Suppress_Elaboration_Warnings (Task_Scope) or else Elaboration_Checks_Suppressed (Task_Scope) then null; elsif Dynamic_Elaboration_Checks then if not Elaboration_Checks_Suppressed (Ent) and then not Cunit_SC and then not Restriction_Active (No_Entry_Calls_In_Elaboration_Code) then -- Runtime elaboration check required. generate check of the -- elaboration Boolean for the unit containing the entity. Insert_Elab_Check (N, Make_Attribute_Reference (Loc, Attribute_Name => Name_Elaborated, Prefix => New_Occurrence_Of (Spec_Entity (Task_Scope), Loc))); end if; else -- Force the binder to elaborate other unit first if not Suppress_Elaboration_Warnings (Ent) and then not Elaboration_Checks_Suppressed (Ent) and then Elab_Warnings and then not Suppress_Elaboration_Warnings (Task_Scope) and then not Elaboration_Checks_Suppressed (Task_Scope) then Error_Msg_Node_2 := Task_Scope; Error_Msg_NE ("activation of an instance of task type&" & " requires pragma Elaborate_All on &?", N, Ent); end if; Set_Elaborate_All_Desirable (Task_Scope); Set_Suppress_Elaboration_Warnings (Task_Scope); end if; Next_Elmt (Elmt); end loop; -- For tasks declared in the current unit, trace other calls within -- the task procedure bodies, which are available. In_Task_Activation := True; Elmt := First_Elmt (Intra_Procs); while Present (Elmt) loop Ent := Node (Elmt); Check_Internal_Call_Continue (N, Ent, Enclosing, Ent); Next_Elmt (Elmt); end loop; In_Task_Activation := False; end Check_Task_Activation; -------------------------------- -- Set_Elaboration_Constraint -- -------------------------------- procedure Set_Elaboration_Constraint (Call : Node_Id; Subp : Entity_Id; Scop : Entity_Id) is Elab_Unit : Entity_Id; Init_Call : constant Boolean := Chars (Subp) = Name_Initialize and then Comes_From_Source (Subp) and then Present (Parameter_Associations (Call)) and then Is_Controlled (Etype (First (Parameter_Associations (Call)))); begin -- If the unit is mentioned in a with_clause of the current -- unit, it is visible, and we can set the elaboration flag. if Is_Immediately_Visible (Scop) or else (Is_Child_Unit (Scop) and then Is_Visible_Child_Unit (Scop)) then Set_Elaborate_All_Desirable (Scop); Set_Suppress_Elaboration_Warnings (Scop, True); return; end if; -- If this is not an initialization call or a call using object notation -- we know that the unit of the called entity is in the context, and -- we can set the flag as well. The unit need not be visible if the call -- occurs within an instantiation. if Is_Init_Proc (Subp) or else Init_Call or else Nkind (Original_Node (Call)) = N_Selected_Component then null; -- detailed processing follows. else Set_Elaborate_All_Desirable (Scop); Set_Suppress_Elaboration_Warnings (Scop, True); return; end if; -- If the unit is not in the context, there must be an intermediate -- unit that is, on which we need to place to elaboration flag. if Is_Init_Proc (Subp) or else Init_Call then -- The initialization call is on an object whose type is not -- declared in the same scope as the subprogram. The type of -- the object must be a subtype of the type of operation. This -- object is the first actual in the call. declare Typ : constant Entity_Id := Etype (First (Parameter_Associations (Call))); begin Elab_Unit := Scope (Typ); while (Present (Elab_Unit)) and then not Is_Compilation_Unit (Elab_Unit) loop Elab_Unit := Scope (Elab_Unit); end loop; end; elsif Nkind (Original_Node (Call)) = N_Selected_Component then -- If original node uses selected component notation, the -- prefix is visible and determines the scope that must be -- elaborated. After rewriting, the prefix is the first actual -- in the call. Elab_Unit := Scope (Etype (First (Parameter_Associations (Call)))); else -- Using previously computed scope. If the elaboration check is -- done after analysis, the scope is not visible any longer, but -- must still be in the context. Elab_Unit := Scop; end if; Set_Elaborate_All_Desirable (Elab_Unit); Set_Suppress_Elaboration_Warnings (Elab_Unit, True); end Set_Elaboration_Constraint; ---------------------- -- Has_Generic_Body -- ---------------------- function Has_Generic_Body (N : Node_Id) return Boolean is Ent : constant Entity_Id := Get_Generic_Entity (N); Decl : constant Node_Id := Unit_Declaration_Node (Ent); Scop : Entity_Id; function Find_Body_In (E : Entity_Id; N : Node_Id) return Node_Id; -- Determine if the list of nodes headed by N and linked by Next -- contains a package body for the package spec entity E, and if -- so return the package body. If not, then returns Empty. function Load_Package_Body (Nam : Unit_Name_Type) return Node_Id; -- This procedure is called load the unit whose name is given by Nam. -- This unit is being loaded to see whether it contains an optional -- generic body. The returned value is the loaded unit, which is -- always a package body (only package bodies can contain other -- entities in the sense in which Has_Generic_Body is interested). -- We only attempt to load bodies if we are generating code. If we -- are in semantics check only mode, then it would be wrong to load -- bodies that are not required from a semantic point of view, so -- in this case we return Empty. The result is that the caller may -- incorrectly decide that a generic spec does not have a body when -- in fact it does, but the only harm in this is that some warnings -- on elaboration problems may be lost in semantic checks only mode, -- which is not big loss. We also return Empty if we go for a body -- and it is not there. function Locate_Corresponding_Body (PE : Entity_Id) return Node_Id; -- PE is the entity for a package spec. This function locates the -- corresponding package body, returning Empty if none is found. -- The package body returned is fully parsed but may not yet be -- analyzed, so only syntactic fields should be referenced. ------------------ -- Find_Body_In -- ------------------ function Find_Body_In (E : Entity_Id; N : Node_Id) return Node_Id is Nod : Node_Id; begin Nod := N; while Present (Nod) loop -- If we found the package body we are looking for, return it if Nkind (Nod) = N_Package_Body and then Chars (Defining_Unit_Name (Nod)) = Chars (E) then return Nod; -- If we found the stub for the body, go after the subunit, -- loading it if necessary. elsif Nkind (Nod) = N_Package_Body_Stub and then Chars (Defining_Identifier (Nod)) = Chars (E) then if Present (Library_Unit (Nod)) then return Unit (Library_Unit (Nod)); else return Load_Package_Body (Get_Unit_Name (Nod)); end if; -- If neither package body nor stub, keep looking on chain else Next (Nod); end if; end loop; return Empty; end Find_Body_In; ----------------------- -- Load_Package_Body -- ----------------------- function Load_Package_Body (Nam : Unit_Name_Type) return Node_Id is U : Unit_Number_Type; begin if Operating_Mode /= Generate_Code then return Empty; else U := Load_Unit (Load_Name => Nam, Required => False, Subunit => False, Error_Node => N); if U = No_Unit then return Empty; else return Unit (Cunit (U)); end if; end if; end Load_Package_Body; ------------------------------- -- Locate_Corresponding_Body -- ------------------------------- function Locate_Corresponding_Body (PE : Entity_Id) return Node_Id is Spec : constant Node_Id := Declaration_Node (PE); Decl : constant Node_Id := Parent (Spec); Scop : constant Entity_Id := Scope (PE); PBody : Node_Id; begin if Is_Library_Level_Entity (PE) then -- If package is a library unit that requires a body, we have -- no choice but to go after that body because it might contain -- an optional body for the original generic package. if Unit_Requires_Body (PE) then -- Load the body. Note that we are a little careful here to -- use Spec to get the unit number, rather than PE or Decl, -- since in the case where the package is itself a library -- level instantiation, Spec will properly reference the -- generic template, which is what we really want. return Load_Package_Body (Get_Body_Name (Unit_Name (Get_Source_Unit (Spec)))); -- But if the package is a library unit that does NOT require -- a body, then no body is permitted, so we are sure that there -- is no body for the original generic package. else return Empty; end if; -- Otherwise look and see if we are embedded in a further package elsif Is_Package (Scop) then -- If so, get the body of the enclosing package, and look in -- its package body for the package body we are looking for. PBody := Locate_Corresponding_Body (Scop); if No (PBody) then return Empty; else return Find_Body_In (PE, First (Declarations (PBody))); end if; -- If we are not embedded in a further package, then the body -- must be in the same declarative part as we are. else return Find_Body_In (PE, Next (Decl)); end if; end Locate_Corresponding_Body; -- Start of processing for Has_Generic_Body begin if Present (Corresponding_Body (Decl)) then return True; elsif Unit_Requires_Body (Ent) then return True; -- Compilation units cannot have optional bodies elsif Is_Compilation_Unit (Ent) then return False; -- Otherwise look at what scope we are in else Scop := Scope (Ent); -- Case of entity is in other than a package spec, in this case -- the body, if present, must be in the same declarative part. if not Is_Package (Scop) then declare P : Node_Id; begin P := Declaration_Node (Ent); -- Declaration node may get us a spec, so if so, go to -- the parent declaration. while not Is_List_Member (P) loop P := Parent (P); end loop; return Present (Find_Body_In (Ent, Next (P))); end; -- If the entity is in a package spec, then we have to locate -- the corresponding package body, and look there. else declare PBody : constant Node_Id := Locate_Corresponding_Body (Scop); begin if No (PBody) then return False; else return Present (Find_Body_In (Ent, (First (Declarations (PBody))))); end if; end; end if; end if; end Has_Generic_Body; ----------------------- -- Insert_Elab_Check -- ----------------------- procedure Insert_Elab_Check (N : Node_Id; C : Node_Id := Empty) is Nod : Node_Id; Loc : constant Source_Ptr := Sloc (N); begin -- If expansion is disabled, do not generate any checks. Also -- skip checks if any subunits are missing because in either -- case we lack the full information that we need, and no object -- file will be created in any case. if not Expander_Active or else Subunits_Missing then return; end if; -- If we have a generic instantiation, where Instance_Spec is set, -- then this field points to a generic instance spec that has -- been inserted before the instantiation node itself, so that -- is where we want to insert a check. if Nkind (N) in N_Generic_Instantiation and then Present (Instance_Spec (N)) then Nod := Instance_Spec (N); else Nod := N; end if; -- If we are inserting at the top level, insert in Aux_Decls if Nkind (Parent (Nod)) = N_Compilation_Unit then declare ADN : constant Node_Id := Aux_Decls_Node (Parent (Nod)); R : Node_Id; begin if No (C) then R := Make_Raise_Program_Error (Loc, Reason => PE_Access_Before_Elaboration); else R := Make_Raise_Program_Error (Loc, Condition => Make_Op_Not (Loc, C), Reason => PE_Access_Before_Elaboration); end if; if No (Declarations (ADN)) then Set_Declarations (ADN, New_List (R)); else Append_To (Declarations (ADN), R); end if; Analyze (R); end; -- Otherwise just insert before the node in question. However, if -- the context of the call has already been analyzed, an insertion -- will not work if it depends on subsequent expansion (e.g. a call in -- a branch of a short-circuit). In that case we replace the call with -- a conditional expression, or with a Raise if it is unconditional. -- Unfortunately this does not work if the call has a dynamic size, -- because gigi regards it as a dynamic-sized temporary. If such a call -- appears in a short-circuit expression, the elaboration check will be -- missed (rare enough ???). Otherwise, the code below inserts the check -- at the appropriate place before the call. Same applies in the even -- rarer case the return type has a known size but is unconstrained. else if Nkind (N) = N_Function_Call and then Analyzed (Parent (N)) and then Size_Known_At_Compile_Time (Etype (N)) and then (not Has_Discriminants (Etype (N)) or else Is_Constrained (Etype (N))) then declare Typ : constant Entity_Id := Etype (N); Chk : constant Boolean := Do_Range_Check (N); R : constant Node_Id := Make_Raise_Program_Error (Loc, Reason => PE_Access_Before_Elaboration); begin Set_Etype (R, Typ); if No (C) then Rewrite (N, R); else Rewrite (N, Make_Conditional_Expression (Loc, Expressions => New_List (C, Relocate_Node (N), R))); end if; Analyze_And_Resolve (N, Typ); -- If the original call requires a range check, so does the -- conditional expression. if Chk then Enable_Range_Check (N); else Set_Do_Range_Check (N, False); end if; end; else if No (C) then Insert_Action (Nod, Make_Raise_Program_Error (Loc, Reason => PE_Access_Before_Elaboration)); else Insert_Action (Nod, Make_Raise_Program_Error (Loc, Condition => Make_Op_Not (Loc, Right_Opnd => C), Reason => PE_Access_Before_Elaboration)); end if; end if; end if; end Insert_Elab_Check; ------------------ -- Output_Calls -- ------------------ procedure Output_Calls (N : Node_Id) is Ent : Entity_Id; function Is_Printable_Error_Name (Nm : Name_Id) return Boolean; -- An internal function, used to determine if a name, Nm, is either -- a non-internal name, or is an internal name that is printable -- by the error message circuits (i.e. it has a single upper -- case letter at the end). function Is_Printable_Error_Name (Nm : Name_Id) return Boolean is begin if not Is_Internal_Name (Nm) then return True; elsif Name_Len = 1 then return False; else Name_Len := Name_Len - 1; return not Is_Internal_Name; end if; end Is_Printable_Error_Name; -- Start of processing for Output_Calls begin for J in reverse 1 .. Elab_Call.Last loop Error_Msg_Sloc := Elab_Call.Table (J).Cloc; Ent := Elab_Call.Table (J).Ent; if Is_Generic_Unit (Ent) then Error_Msg_NE ("\?& instantiated #", N, Ent); elsif Is_Init_Proc (Ent) then Error_Msg_N ("\?initialization procedure called #", N); elsif Is_Printable_Error_Name (Chars (Ent)) then Error_Msg_NE ("\?& called #", N, Ent); else Error_Msg_N ("\? called #", N); end if; end loop; end Output_Calls; ---------------------------- -- Same_Elaboration_Scope -- ---------------------------- function Same_Elaboration_Scope (Scop1, Scop2 : Entity_Id) return Boolean is S1 : Entity_Id := Scop1; S2 : Entity_Id := Scop2; begin while S1 /= Standard_Standard and then (Ekind (S1) = E_Package or else Ekind (S1) = E_Block) loop S1 := Scope (S1); end loop; while S2 /= Standard_Standard and then (Ekind (S2) = E_Package or else Ekind (S2) = E_Protected_Type or else Ekind (S2) = E_Block) loop S2 := Scope (S2); end loop; return S1 = S2; end Same_Elaboration_Scope; ----------------- -- Set_C_Scope -- ----------------- procedure Set_C_Scope is begin while not Is_Compilation_Unit (C_Scope) loop C_Scope := Scope (C_Scope); end loop; end Set_C_Scope; ----------------- -- Spec_Entity -- ----------------- function Spec_Entity (E : Entity_Id) return Entity_Id is Decl : Node_Id; begin -- Check for case of body entity -- Why is the check for E_Void needed??? if Ekind (E) = E_Void or else Ekind (E) = E_Subprogram_Body or else Ekind (E) = E_Package_Body then Decl := E; loop Decl := Parent (Decl); exit when Nkind (Decl) in N_Proper_Body; end loop; return Corresponding_Spec (Decl); else return E; end if; end Spec_Entity; ------------------- -- Supply_Bodies -- ------------------- procedure Supply_Bodies (N : Node_Id) is begin if Nkind (N) = N_Subprogram_Declaration then declare Ent : constant Entity_Id := Defining_Unit_Name (Specification (N)); begin Set_Is_Imported (Ent); Set_Convention (Ent, Convention_Stubbed); end; elsif Nkind (N) = N_Package_Declaration then declare Spec : constant Node_Id := Specification (N); begin New_Scope (Defining_Unit_Name (Spec)); Supply_Bodies (Visible_Declarations (Spec)); Supply_Bodies (Private_Declarations (Spec)); Pop_Scope; end; end if; end Supply_Bodies; procedure Supply_Bodies (L : List_Id) is Elmt : Node_Id; begin if Present (L) then Elmt := First (L); while Present (Elmt) loop Supply_Bodies (Elmt); Next (Elmt); end loop; end if; end Supply_Bodies; ------------ -- Within -- ------------ function Within (E1, E2 : Entity_Id) return Boolean is Scop : Entity_Id; begin Scop := E1; loop if Scop = E2 then return True; elsif Scop = Standard_Standard then return False; else Scop := Scope (Scop); end if; end loop; raise Program_Error; end Within; -------------------------- -- Within_Elaborate_All -- -------------------------- function Within_Elaborate_All (E : Entity_Id) return Boolean is Item : Node_Id; Item2 : Node_Id; Elab_Id : Entity_Id; Par : Node_Id; begin Item := First (Context_Items (Cunit (Current_Sem_Unit))); while Present (Item) loop if Nkind (Item) = N_Pragma and then Get_Pragma_Id (Chars (Item)) = Pragma_Elaborate_All then if Error_Posted (Item) then -- Some previous error on the pragma itself return False; end if; Elab_Id := Entity ( Expression (First (Pragma_Argument_Associations (Item)))); Par := Parent (Unit_Declaration_Node (Elab_Id)); Item2 := First (Context_Items (Par)); while Present (Item2) loop if Nkind (Item2) = N_With_Clause and then Entity (Name (Item2)) = E then return True; end if; Next (Item2); end loop; end if; Next (Item); end loop; return False; end Within_Elaborate_All; end Sem_Elab;