| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- B I N D E -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2022, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING3. If not, go to -- |
| -- http://www.gnu.org/licenses for a complete copy of the license. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Binderr; use Binderr; |
| with Butil; use Butil; |
| with Debug; use Debug; |
| with Fname; use Fname; |
| with Opt; use Opt; |
| with Osint; |
| with Output; use Output; |
| with Table; |
| with Types; use Types; |
| |
| with System.Case_Util; use System.Case_Util; |
| with System.HTable; |
| |
| package body Binde is |
| use Unit_Id_Tables; |
| |
| -- We now have Elab_New, a new elaboration-order algorithm. |
| -- |
| -- However, any change to elaboration order can break some programs. |
| -- Therefore, we are keeping the old algorithm in place, to be selected |
| -- by switches. |
| -- |
| -- The new algorithm has the following interesting properties: |
| -- |
| -- * The static and dynamic models use the same elaboration order. The |
| -- static model might get an error, but if it does not, it will use |
| -- the same order as the dynamic model. |
| -- |
| -- * Each SCC (see below) is elaborated together; that is, units from |
| -- different SCCs are not interspersed. |
| -- |
| -- * In particular, this implies that if an SCC contains just a spec and |
| -- the corresponding body, and nothing else, the body will be |
| -- elaborated immediately after the spec. This is expected to result |
| -- in a better elaboration order for most programs, because in this |
| -- case, a call from outside the library unit cannot get ABE. |
| -- |
| -- * Pragmas Elaborate_All (explicit and implicit) are ignored. Instead, |
| -- we behave as if every legal pragma Elaborate_All were present. That |
| -- is, if it would be legal to have "pragma Elaborate_All(Y);" on X, |
| -- then we behave as if such a pragma exists, even if it does not. |
| |
| Do_Old : constant Boolean := False; |
| Do_New : constant Boolean := True; |
| -- True to enable the old and new algorithms, respectively. Used for |
| -- debugging/experimentation. |
| |
| Doing_New : Boolean := False; |
| -- True if we are currently doing the new algorithm. Print certain |
| -- messages only when doing the "new" elab order algorithm, so we don't get |
| -- duplicates. And use different heuristics in Better_Choice_Optimistic. |
| |
| -- The following data structures are used to represent the graph that is |
| -- used to determine the elaboration order (using a topological sort). |
| |
| -- The following structures are used to record successors. If B is a |
| -- successor of A in this table, it means that A must be elaborated before |
| -- B is elaborated. For example, if Y (body) says "with X;", then Y (body) |
| -- will be a successor of X (spec), and X (spec) will be a predecessor of |
| -- Y (body). |
| -- |
| -- Note that we store the successors of each unit explicitly. We don't |
| -- store the predecessors, but we store a count of them. |
| -- |
| -- The basic algorithm is to first compute a directed graph of units (type |
| -- Unit_Node_Record, below), with successors as edges. A unit is "ready" |
| -- (to be chosen as the next to be elaborated) if it has no predecessors |
| -- that have not yet been chosen. We use heuristics to decide which of the |
| -- ready units should be elaborated next, and "choose" that one (which |
| -- means we append it to the elaboration-order table). |
| |
| type Successor_Id is new Nat; |
| -- Identification of single successor entry |
| |
| No_Successor : constant Successor_Id := 0; |
| -- Used to indicate end of list of successors |
| |
| type Elab_All_Id is new Nat; |
| -- Identification of Elab_All entry link |
| |
| No_Elab_All_Link : constant Elab_All_Id := 0; |
| -- Used to indicate end of list |
| |
| -- Succ_Reason indicates the reason for a particular elaboration link |
| |
| type Succ_Reason is |
| (Withed, |
| -- After directly with's Before, so the spec of Before must be |
| -- elaborated before After is elaborated. |
| |
| Forced, |
| -- Before and After come from a pair of lines in the forced-elaboration- |
| -- order file. |
| |
| Elab, |
| -- After directly mentions Before in a pragma Elaborate, so the body of |
| -- Before must be elaborated before After is elaborated. |
| |
| Elab_All, |
| -- After either mentions Before directly in a pragma Elaborate_All, or |
| -- mentions a third unit, X, which itself requires that Before be |
| -- elaborated before unit X is elaborated. The Elab_All_Link list traces |
| -- the dependencies in the latter case. |
| |
| Elab_All_Desirable, |
| -- This is just like Elab_All, except that the Elaborate_All was not |
| -- explicitly present in the source, but rather was created by the front |
| -- end, which decided that it was "desirable". |
| |
| Elab_Desirable, |
| -- This is just like Elab, except that the Elaborate was not explicitly |
| -- present in the source, but rather was created by the front end, which |
| -- decided that it was "desirable". |
| |
| Spec_First); |
| -- After is a body, and Before is the corresponding spec |
| |
| -- Successor_Link contains the information for one link |
| |
| type Successor_Link is record |
| Before : Unit_Id; |
| -- Predecessor unit |
| |
| After : Unit_Id; |
| -- Successor unit |
| |
| Next : Successor_Id; |
| -- Next successor on this list |
| |
| Reason : Succ_Reason; |
| -- Reason for this link |
| |
| Elab_Body : Boolean; |
| -- Set True if this link is needed for the special Elaborate_Body |
| -- processing described below. |
| |
| Reason_Unit : Unit_Id; |
| -- For Reason = Elab, or Elab_All or Elab_Desirable, records the unit |
| -- containing the pragma leading to the link. |
| |
| Elab_All_Link : Elab_All_Id; |
| -- If Reason = Elab_All or Elab_Desirable, then this points to the |
| -- first element in a list of Elab_All entries that record the with |
| -- chain resulting in this particular dependency. |
| end record; |
| |
| -- Note on handling of Elaborate_Body. Basically, if we have a pragma |
| -- Elaborate_Body in a unit, it means that the spec and body have to be |
| -- handled as a single entity from the point of view of determining an |
| -- elaboration order. What we do is to essentially remove the body from |
| -- consideration completely, and transfer all its links (other than the |
| -- spec link) to the spec. Then when the spec gets chosen, we choose the |
| -- body right afterwards. We mark the links that get moved from the body to |
| -- the spec by setting their Elab_Body flag True, so that we can understand |
| -- what is going on. |
| |
| Succ_First : constant := 1; |
| |
| package Succ is new Table.Table |
| (Table_Component_Type => Successor_Link, |
| Table_Index_Type => Successor_Id, |
| Table_Low_Bound => Succ_First, |
| Table_Initial => 500, |
| Table_Increment => 200, |
| Table_Name => "Succ"); |
| |
| -- For the case of Elaborate_All, the following table is used to record |
| -- chains of with relationships that lead to the Elab_All link. These are |
| -- used solely for diagnostic purposes |
| |
| type Elab_All_Entry is record |
| Needed_By : Unit_Name_Type; |
| -- Name of unit from which referencing unit was with'ed or otherwise |
| -- needed as a result of Elaborate_All or Elaborate_Desirable. |
| |
| Next_Elab : Elab_All_Id; |
| -- Link to next entry on chain (No_Elab_All_Link marks end of list) |
| end record; |
| |
| package Elab_All_Entries is new Table.Table |
| (Table_Component_Type => Elab_All_Entry, |
| Table_Index_Type => Elab_All_Id, |
| Table_Low_Bound => 1, |
| Table_Initial => 2000, |
| Table_Increment => 200, |
| Table_Name => "Elab_All_Entries"); |
| |
| type Unit_Id_Array_Ptr is access Unit_Id_Array; |
| |
| -- A Unit_Node_Record is built for each active unit |
| |
| type Unit_Node_Record is record |
| Successors : Successor_Id; |
| -- Pointer to list of links for successor nodes |
| |
| Num_Pred : Int; |
| -- Number of predecessors for this unit that have not yet been chosen. |
| -- Normally non-negative, but can go negative in the case of units |
| -- chosen by the diagnose error procedure (when cycles are being removed |
| -- from the graph). |
| |
| Nextnp : Unit_Id; |
| -- Forward pointer for list of units with no predecessors |
| |
| Visited : Boolean; |
| -- Used in computing transitive closure for Elaborate_All and also in |
| -- locating cycles and paths in the diagnose routines. |
| |
| Elab_Position : Nat; |
| -- Initialized to zero. Set non-zero when a unit is chosen and placed in |
| -- the elaboration order. The value represents the ordinal position in |
| -- the elaboration order. |
| |
| -- The following are for Elab_New. We compute the strongly connected |
| -- components (SCCs) of the directed graph of units. The edges are the |
| -- Successors, which do not include pragmas Elaborate_All (explicit or |
| -- implicit) in Elab_New. In addition, we assume there is a edge |
| -- pointing from a body to its corresponding spec; this edge is not |
| -- included in Successors, because of course a spec is elaborated BEFORE |
| -- its body, not after. |
| |
| SCC_Root : Unit_Id; |
| -- Each unit points to the root of its SCC, which is just an arbitrary |
| -- member of the SCC. Two units are in the same SCC if and only if their |
| -- SCC_Roots are equal. U is the root of its SCC if and only if |
| -- SCC(U)=U. |
| |
| Nodes : Unit_Id_Array_Ptr; |
| -- Present only in the root of an SCC. This is the set of units in the |
| -- SCC, in no particular order. |
| |
| SCC_Num_Pred : Int; |
| -- Present only in the root of an SCC. This is the number of predecessor |
| -- units of the SCC that are in other SCCs, and that have not yet been |
| -- chosen. |
| |
| Validate_Seen : Boolean := False; |
| -- See procedure Validate below |
| end record; |
| |
| package UNR is new Table.Table |
| (Table_Component_Type => Unit_Node_Record, |
| Table_Index_Type => Unit_Id, |
| Table_Low_Bound => First_Unit_Entry, |
| Table_Initial => 500, |
| Table_Increment => 200, |
| Table_Name => "UNR"); |
| |
| No_Pred : Unit_Id; |
| -- Head of list of items with no predecessors |
| |
| Num_Left : Int; |
| -- Number of entries not yet dealt with |
| |
| Cur_Unit : Unit_Id; |
| -- Current unit, set by Gather_Dependencies, and picked up in Build_Link to |
| -- set the Reason_Unit field of the created dependency link. |
| |
| Num_Chosen : Nat; |
| -- Number of units chosen in the elaboration order so far |
| |
| Diagnose_Elaboration_Problem_Called : Boolean := False; |
| -- True if Diagnose_Elaboration_Problem was called. Used in an assertion. |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| function Debug_Flag_Older return Boolean; |
| function Debug_Flag_Old return Boolean; |
| -- True if debug flags select the old or older algorithms. Pretty much any |
| -- change to elaboration order can break some programs. For example, |
| -- programs can depend on elaboration order even without failing |
| -- access-before-elaboration checks. A trivial example is a program that |
| -- prints text during elaboration. Therefore, we have flags to revert to |
| -- the old(er) algorithms. |
| |
| procedure Validate (Order : Unit_Id_Array; Doing_New : Boolean); |
| -- Assert that certain properties are true |
| |
| function Better_Choice_Optimistic |
| (U1 : Unit_Id; |
| U2 : Unit_Id) return Boolean; |
| -- U1 and U2 are both permitted candidates for selection as the next unit |
| -- to be elaborated. This function determines whether U1 is a better choice |
| -- than U2, i.e. should be elaborated in preference to U2, based on a set |
| -- of heuristics that establish a friendly and predictable order (see body |
| -- for details). The result is True if U1 is a better choice than U2, and |
| -- False if it is a worse choice, or there is no preference between them. |
| |
| function Better_Choice_Pessimistic |
| (U1 : Unit_Id; |
| U2 : Unit_Id) return Boolean; |
| -- This is like Better_Choice_Optimistic, and has the same interface, but |
| -- returns true if U1 is a worse choice than U2 in the sense of the -p |
| -- (pessimistic elaboration order) switch. We still have to obey Ada rules, |
| -- so it is not quite the direct inverse of Better_Choice_Optimistic. |
| |
| function Better_Choice (U1 : Unit_Id; U2 : Unit_Id) return Boolean; |
| -- Calls Better_Choice_Optimistic or Better_Choice_Pessimistic as |
| -- appropriate. Also takes care of the U2 = No_Unit_Id case. |
| |
| procedure Build_Link |
| (Before : Unit_Id; |
| After : Unit_Id; |
| R : Succ_Reason; |
| Ea_Id : Elab_All_Id := No_Elab_All_Link); |
| -- Establish a successor link, Before must be elaborated before After, and |
| -- the reason for the link is R. Ea_Id is the contents to be placed in the |
| -- Elab_All_Link of the entry. |
| |
| procedure Choose |
| (Elab_Order : in out Unit_Id_Table; |
| Chosen : Unit_Id; |
| Msg : String); |
| -- Chosen is the next entry chosen in the elaboration order. This procedure |
| -- updates all data structures appropriately. |
| |
| function Corresponding_Body (U : Unit_Id) return Unit_Id; |
| pragma Inline (Corresponding_Body); |
| -- Given a unit that is a spec for which there is a separate body, return |
| -- the unit id of the body. It is an error to call this routine with a unit |
| -- that is not a spec, or that does not have a separate body. |
| |
| function Corresponding_Spec (U : Unit_Id) return Unit_Id; |
| pragma Inline (Corresponding_Spec); |
| -- Given a unit that is a body for which there is a separate spec, return |
| -- the unit id of the spec. It is an error to call this routine with a unit |
| -- that is not a body, or that does not have a separate spec. |
| |
| procedure Diagnose_Elaboration_Problem |
| (Elab_Order : in out Unit_Id_Table); |
| pragma No_Return (Diagnose_Elaboration_Problem); |
| -- Called when no elaboration order can be found. Outputs an appropriate |
| -- diagnosis of the problem, and then abandons the bind. |
| |
| procedure Elab_All_Links |
| (Before : Unit_Id; |
| After : Unit_Id; |
| Reason : Succ_Reason; |
| Link : Elab_All_Id); |
| -- Used to compute the transitive closure of elaboration links for an |
| -- Elaborate_All pragma (Reason = Elab_All) or for an indication of |
| -- Elaborate_All_Desirable (Reason = Elab_All_Desirable). Unit After has a |
| -- pragma Elaborate_All or the front end has determined that a reference |
| -- probably requires Elaborate_All, and unit Before must be previously |
| -- elaborated. First a link is built making sure that unit Before is |
| -- elaborated before After, then a recursive call ensures that we also |
| -- build links for any units needed by Before (i.e. these units must/should |
| -- also be elaborated before After). Link is used to build a chain of |
| -- Elab_All_Entries to explain the reason for a link. The value passed is |
| -- the chain so far. |
| |
| procedure Elab_Error_Msg (S : Successor_Id); |
| -- Given a successor link, outputs an error message of the form |
| -- "$ must be elaborated before $ ..." where ... is the reason. |
| |
| procedure Force_Elab_Order; |
| -- Gather dependencies from the forced-elaboration-order file (-f switch) |
| |
| procedure Gather_Dependencies; |
| -- Compute dependencies, building the Succ and UNR tables |
| |
| procedure Init; |
| -- Initialize global data structures in this package body |
| |
| function Is_Body_Unit (U : Unit_Id) return Boolean; |
| pragma Inline (Is_Body_Unit); |
| -- Determines if given unit is a body |
| |
| function Is_Pure_Or_Preelab_Unit (U : Unit_Id) return Boolean; |
| -- Returns True if corresponding unit is Pure or Preelaborate. Includes |
| -- dealing with testing flags on spec if it is given a body. |
| |
| function Is_Waiting_Body (U : Unit_Id) return Boolean; |
| pragma Inline (Is_Waiting_Body); |
| -- Determines if U is a waiting body, defined as a body that has |
| -- not been elaborated, but whose spec has been elaborated. |
| |
| function Make_Elab_All_Entry |
| (Unam : Unit_Name_Type; |
| Link : Elab_All_Id) return Elab_All_Id; |
| -- Make an Elab_All_Entries table entry with the given Unam and Link |
| |
| function Unit_Id_Of (Uname : Unit_Name_Type) return Unit_Id; |
| -- This function uses the Info field set in the names table to obtain |
| -- the unit Id of a unit, given its name id value. |
| |
| procedure Write_Closure (Order : Unit_Id_Array); |
| -- Write the closure. This is for the -R and -Ra switches, "list closure |
| -- display". |
| |
| procedure Write_Dependencies; |
| -- Write out dependencies (called only if appropriate option is set) |
| |
| procedure Write_Elab_All_Chain (S : Successor_Id); |
| -- If the reason for the link S is Elaborate_All or Elaborate_Desirable, |
| -- then this routine will output the "needed by" explanation chain. |
| |
| procedure Write_Elab_Order (Order : Unit_Id_Array; Title : String); |
| -- Display elaboration order. This is for the -l switch. Title is a heading |
| -- to print; an empty string is passed to indicate Zero_Formatting. |
| |
| package Elab_New is |
| |
| -- Implementation of the new algorithm |
| |
| procedure Write_SCC (U : Unit_Id); |
| -- Write the unit names of the units in the SCC in which U lives |
| |
| procedure Find_Elab_Order (Elab_Order : out Unit_Id_Table); |
| |
| Elab_Cycle_Found : Boolean := False; |
| -- Set True if Find_Elab_Order found a cycle (usually an illegal pragma |
| -- Elaborate_All, explicit or implicit). |
| |
| function SCC (U : Unit_Id) return Unit_Id; |
| -- The root of the strongly connected component containing U |
| |
| function SCC_Num_Pred (U : Unit_Id) return Int; |
| -- The SCC_Num_Pred of the SCC in which U lives |
| |
| function Nodes (U : Unit_Id) return Unit_Id_Array_Ptr; |
| -- The nodes of the strongly connected component containing U |
| |
| end Elab_New; |
| |
| use Elab_New; |
| |
| package Elab_Old is |
| |
| -- Implementation of the old algorithm |
| |
| procedure Find_Elab_Order (Elab_Order : out Unit_Id_Table); |
| |
| end Elab_Old; |
| |
| -- Most of the code is shared between old and new; such code is outside |
| -- packages Elab_Old and Elab_New. |
| |
| ------------------- |
| -- Better_Choice -- |
| ------------------- |
| |
| function Better_Choice (U1 : Unit_Id; U2 : Unit_Id) return Boolean is |
| pragma Assert (U1 /= No_Unit_Id); |
| begin |
| if U2 = No_Unit_Id then |
| return True; |
| end if; |
| |
| if Pessimistic_Elab_Order then |
| return Better_Choice_Pessimistic (U1, U2); |
| else |
| return Better_Choice_Optimistic (U1, U2); |
| end if; |
| end Better_Choice; |
| |
| ------------------------------ |
| -- Better_Choice_Optimistic -- |
| ------------------------------ |
| |
| function Better_Choice_Optimistic |
| (U1 : Unit_Id; |
| U2 : Unit_Id) return Boolean |
| is |
| UT1 : Unit_Record renames Units.Table (U1); |
| UT2 : Unit_Record renames Units.Table (U2); |
| |
| begin |
| if Debug_Flag_B then |
| Write_Str ("Better_Choice_Optimistic ("); |
| Write_Unit_Name (UT1.Uname); |
| Write_Str (", "); |
| Write_Unit_Name (UT2.Uname); |
| Write_Line (")"); |
| end if; |
| |
| -- Note: the checks here are applied in sequence, and the ordering is |
| -- significant (i.e. the more important criteria are applied first). |
| |
| -- Prefer a waiting body to one that is not a waiting body |
| |
| if Is_Waiting_Body (U1) and then not Is_Waiting_Body (U2) then |
| if Debug_Flag_B then |
| Write_Line (" True: u1 is waiting body, u2 is not"); |
| end if; |
| |
| return True; |
| |
| elsif Is_Waiting_Body (U2) and then not Is_Waiting_Body (U1) then |
| if Debug_Flag_B then |
| Write_Line (" False: u2 is waiting body, u1 is not"); |
| end if; |
| |
| return False; |
| |
| -- Prefer a predefined unit to a non-predefined unit |
| |
| elsif UT1.Predefined and then not UT2.Predefined then |
| if Debug_Flag_B then |
| Write_Line (" True: u1 is predefined, u2 is not"); |
| end if; |
| |
| return True; |
| |
| elsif UT2.Predefined and then not UT1.Predefined then |
| if Debug_Flag_B then |
| Write_Line (" False: u2 is predefined, u1 is not"); |
| end if; |
| |
| return False; |
| |
| -- Prefer an internal unit to a non-internal unit |
| |
| elsif UT1.Internal and then not UT2.Internal then |
| if Debug_Flag_B then |
| Write_Line (" True: u1 is internal, u2 is not"); |
| end if; |
| return True; |
| |
| elsif UT2.Internal and then not UT1.Internal then |
| if Debug_Flag_B then |
| Write_Line (" False: u2 is internal, u1 is not"); |
| end if; |
| |
| return False; |
| |
| -- Prefer a pure or preelaborated unit to one that is not. Pure should |
| -- come before preelaborated. |
| |
| elsif Is_Pure_Or_Preelab_Unit (U1) |
| and then not |
| Is_Pure_Or_Preelab_Unit (U2) |
| then |
| if Debug_Flag_B then |
| Write_Line (" True: u1 is pure/preelab, u2 is not"); |
| end if; |
| |
| return True; |
| |
| elsif Is_Pure_Or_Preelab_Unit (U2) |
| and then not |
| Is_Pure_Or_Preelab_Unit (U1) |
| then |
| if Debug_Flag_B then |
| Write_Line (" False: u2 is pure/preelab, u1 is not"); |
| end if; |
| |
| return False; |
| |
| -- Prefer a body to a spec |
| |
| elsif Is_Body_Unit (U1) and then not Is_Body_Unit (U2) then |
| if Debug_Flag_B then |
| Write_Line (" True: u1 is body, u2 is not"); |
| end if; |
| |
| return True; |
| |
| elsif Is_Body_Unit (U2) and then not Is_Body_Unit (U1) then |
| if Debug_Flag_B then |
| Write_Line (" False: u2 is body, u1 is not"); |
| end if; |
| |
| return False; |
| |
| -- If both are waiting bodies, then prefer the one whose spec is more |
| -- recently elaborated. Consider the following: |
| |
| -- spec of A |
| -- spec of B |
| -- body of A or B? |
| |
| -- The normal waiting body preference would have placed the body of A |
| -- before the spec of B if it could. Since it could not, then it must be |
| -- the case that A depends on B. It is therefore a good idea to put the |
| -- body of B first. |
| |
| elsif Is_Waiting_Body (U1) and then Is_Waiting_Body (U2) then |
| declare |
| Result : constant Boolean := |
| UNR.Table (Corresponding_Spec (U1)).Elab_Position > |
| UNR.Table (Corresponding_Spec (U2)).Elab_Position; |
| begin |
| if Debug_Flag_B then |
| if Result then |
| Write_Line (" True: based on waiting body elab positions"); |
| else |
| Write_Line (" False: based on waiting body elab positions"); |
| end if; |
| end if; |
| |
| return Result; |
| end; |
| end if; |
| |
| -- Remaining choice rules are disabled by Debug flag -do |
| |
| if not Debug_Flag_Older then |
| |
| -- The following deal with the case of specs that have been marked |
| -- as Elaborate_Body_Desirable. We generally want to delay these |
| -- specs as long as possible, so that the bodies have a better chance |
| -- of being elaborated closer to the specs. |
| |
| -- If we have two units, one of which is a spec for which this flag |
| -- is set, and the other is not, we prefer to delay the spec for |
| -- which the flag is set. |
| |
| if not UT1.Elaborate_Body_Desirable |
| and then UT2.Elaborate_Body_Desirable |
| then |
| if Debug_Flag_B then |
| Write_Line (" True: u1 is elab body desirable, u2 is not"); |
| end if; |
| |
| return True; |
| |
| elsif not UT2.Elaborate_Body_Desirable |
| and then UT1.Elaborate_Body_Desirable |
| then |
| if Debug_Flag_B then |
| Write_Line (" False: u1 is elab body desirable, u2 is not"); |
| end if; |
| |
| return False; |
| |
| -- If we have two specs that are both marked as Elaborate_Body |
| -- desirable, we prefer the one whose body is nearer to being able |
| -- to be elaborated, based on the Num_Pred count. This helps to |
| -- ensure bodies are as close to specs as possible. |
| |
| elsif UT1.Elaborate_Body_Desirable |
| and then UT2.Elaborate_Body_Desirable |
| then |
| declare |
| Result : constant Boolean := |
| UNR.Table (Corresponding_Body (U1)).Num_Pred < |
| UNR.Table (Corresponding_Body (U2)).Num_Pred; |
| begin |
| if Debug_Flag_B then |
| if Result then |
| Write_Line (" True based on Num_Pred compare"); |
| else |
| Write_Line (" False based on Num_Pred compare"); |
| end if; |
| end if; |
| |
| return Result; |
| end; |
| end if; |
| end if; |
| |
| -- If we have two specs in the same SCC, choose the one whose body is |
| -- closer to being ready. |
| |
| if Doing_New |
| and then SCC (U1) = SCC (U2) |
| and then Units.Table (U1).Utype = Is_Spec |
| and then Units.Table (U2).Utype = Is_Spec |
| and then UNR.Table (Corresponding_Body (U1)).Num_Pred /= |
| UNR.Table (Corresponding_Body (U2)).Num_Pred |
| then |
| if UNR.Table (Corresponding_Body (U1)).Num_Pred < |
| UNR.Table (Corresponding_Body (U2)).Num_Pred |
| then |
| if Debug_Flag_B then |
| Write_Str (" True: same SCC; "); |
| Write_Int (UNR.Table (Corresponding_Body (U1)).Num_Pred); |
| Write_Str (" < "); |
| Write_Int (UNR.Table (Corresponding_Body (U2)).Num_Pred); |
| Write_Eol; |
| end if; |
| |
| return True; |
| else |
| if Debug_Flag_B then |
| Write_Str (" False: same SCC; "); |
| Write_Int (UNR.Table (Corresponding_Body (U1)).Num_Pred); |
| Write_Str (" > "); |
| Write_Int (UNR.Table (Corresponding_Body (U2)).Num_Pred); |
| Write_Eol; |
| end if; |
| |
| return False; |
| end if; |
| end if; |
| |
| -- If we fall through, it means that no preference rule applies, so we |
| -- use alphabetical order to at least give a deterministic result. |
| |
| if Debug_Flag_B then |
| Write_Line (" choose on alpha order"); |
| end if; |
| |
| return Uname_Less (UT1.Uname, UT2.Uname); |
| end Better_Choice_Optimistic; |
| |
| ------------------------------- |
| -- Better_Choice_Pessimistic -- |
| ------------------------------- |
| |
| function Better_Choice_Pessimistic |
| (U1 : Unit_Id; |
| U2 : Unit_Id) return Boolean |
| is |
| UT1 : Unit_Record renames Units.Table (U1); |
| UT2 : Unit_Record renames Units.Table (U2); |
| |
| begin |
| if Debug_Flag_B then |
| Write_Str ("Better_Choice_Pessimistic ("); |
| Write_Unit_Name (UT1.Uname); |
| Write_Str (", "); |
| Write_Unit_Name (UT2.Uname); |
| Write_Line (")"); |
| end if; |
| |
| -- Note: the checks here are applied in sequence, and the ordering is |
| -- significant (i.e. the more important criteria are applied first). |
| |
| -- If either unit is predefined or internal, then we use the normal |
| -- Better_Choice_Optimistic rule, since we don't want to disturb the |
| -- elaboration rules of the language with -p; same treatment for |
| -- Pure/Preelab. |
| |
| -- Prefer a predefined unit to a non-predefined unit |
| |
| if UT1.Predefined and then not UT2.Predefined then |
| if Debug_Flag_B then |
| Write_Line (" True: u1 is predefined, u2 is not"); |
| end if; |
| |
| return True; |
| |
| elsif UT2.Predefined and then not UT1.Predefined then |
| if Debug_Flag_B then |
| Write_Line (" False: u2 is predefined, u1 is not"); |
| end if; |
| |
| return False; |
| |
| -- Prefer an internal unit to a non-internal unit |
| |
| elsif UT1.Internal and then not UT2.Internal then |
| if Debug_Flag_B then |
| Write_Line (" True: u1 is internal, u2 is not"); |
| end if; |
| |
| return True; |
| |
| elsif UT2.Internal and then not UT1.Internal then |
| if Debug_Flag_B then |
| Write_Line (" False: u2 is internal, u1 is not"); |
| end if; |
| |
| return False; |
| |
| -- Prefer a pure or preelaborated unit to one that is not |
| |
| elsif Is_Pure_Or_Preelab_Unit (U1) |
| and then not |
| Is_Pure_Or_Preelab_Unit (U2) |
| then |
| if Debug_Flag_B then |
| Write_Line (" True: u1 is pure/preelab, u2 is not"); |
| end if; |
| |
| return True; |
| |
| elsif Is_Pure_Or_Preelab_Unit (U2) |
| and then not |
| Is_Pure_Or_Preelab_Unit (U1) |
| then |
| if Debug_Flag_B then |
| Write_Line (" False: u2 is pure/preelab, u1 is not"); |
| end if; |
| |
| return False; |
| |
| -- Prefer anything else to a waiting body. We want to make bodies wait |
| -- as long as possible, till we are forced to choose them. |
| |
| elsif Is_Waiting_Body (U1) and then not Is_Waiting_Body (U2) then |
| if Debug_Flag_B then |
| Write_Line (" False: u1 is waiting body, u2 is not"); |
| end if; |
| |
| return False; |
| |
| elsif Is_Waiting_Body (U2) and then not Is_Waiting_Body (U1) then |
| if Debug_Flag_B then |
| Write_Line (" True: u2 is waiting body, u1 is not"); |
| end if; |
| |
| return True; |
| |
| -- Prefer a spec to a body (this is mandatory) |
| |
| elsif Is_Body_Unit (U1) and then not Is_Body_Unit (U2) then |
| if Debug_Flag_B then |
| Write_Line (" False: u1 is body, u2 is not"); |
| end if; |
| |
| return False; |
| |
| elsif Is_Body_Unit (U2) and then not Is_Body_Unit (U1) then |
| if Debug_Flag_B then |
| Write_Line (" True: u2 is body, u1 is not"); |
| end if; |
| |
| return True; |
| |
| -- If both are waiting bodies, then prefer the one whose spec is less |
| -- recently elaborated. Consider the following: |
| |
| -- spec of A |
| -- spec of B |
| -- body of A or B? |
| |
| -- The normal waiting body preference would have placed the body of A |
| -- before the spec of B if it could. Since it could not, then it must be |
| -- the case that A depends on B. It is therefore a good idea to put the |
| -- body of B last so that if there is an elaboration order problem, we |
| -- will find it (that's what pessimistic order is about). |
| |
| elsif Is_Waiting_Body (U1) and then Is_Waiting_Body (U2) then |
| declare |
| Result : constant Boolean := |
| UNR.Table (Corresponding_Spec (U1)).Elab_Position < |
| UNR.Table (Corresponding_Spec (U2)).Elab_Position; |
| begin |
| if Debug_Flag_B then |
| if Result then |
| Write_Line (" True: based on waiting body elab positions"); |
| else |
| Write_Line (" False: based on waiting body elab positions"); |
| end if; |
| end if; |
| |
| return Result; |
| end; |
| end if; |
| |
| -- Remaining choice rules are disabled by Debug flag -do |
| |
| if not Debug_Flag_Older then |
| |
| -- The following deal with the case of specs that have been marked as |
| -- Elaborate_Body_Desirable. In the normal case, we generally want to |
| -- delay the elaboration of these specs as long as possible, so that |
| -- bodies have better chance of being elaborated closer to the specs. |
| -- Better_Choice_Pessimistic as usual wants to do the opposite and |
| -- elaborate such specs as early as possible. |
| |
| -- If we have two units, one of which is a spec for which this flag |
| -- is set, and the other is not, we normally prefer to delay the spec |
| -- for which the flag is set, so again Better_Choice_Pessimistic does |
| -- the opposite. |
| |
| if not UT1.Elaborate_Body_Desirable |
| and then UT2.Elaborate_Body_Desirable |
| then |
| if Debug_Flag_B then |
| Write_Line (" False: u1 is elab body desirable, u2 is not"); |
| end if; |
| |
| return False; |
| |
| elsif not UT2.Elaborate_Body_Desirable |
| and then UT1.Elaborate_Body_Desirable |
| then |
| if Debug_Flag_B then |
| Write_Line (" True: u1 is elab body desirable, u2 is not"); |
| end if; |
| |
| return True; |
| |
| -- If we have two specs that are both marked as Elaborate_Body |
| -- desirable, we normally prefer the one whose body is nearer to |
| -- being able to be elaborated, based on the Num_Pred count. This |
| -- helps to ensure bodies are as close to specs as possible. As |
| -- usual, Better_Choice_Pessimistic does the opposite. |
| |
| elsif UT1.Elaborate_Body_Desirable |
| and then UT2.Elaborate_Body_Desirable |
| then |
| declare |
| Result : constant Boolean := |
| UNR.Table (Corresponding_Body (U1)).Num_Pred >= |
| UNR.Table (Corresponding_Body (U2)).Num_Pred; |
| begin |
| if Debug_Flag_B then |
| if Result then |
| Write_Line (" True based on Num_Pred compare"); |
| else |
| Write_Line (" False based on Num_Pred compare"); |
| end if; |
| end if; |
| |
| return Result; |
| end; |
| end if; |
| end if; |
| |
| -- If we fall through, it means that no preference rule applies, so we |
| -- use alphabetical order to at least give a deterministic result. Since |
| -- Better_Choice_Pessimistic is in the business of stirring up the |
| -- order, we will use reverse alphabetical ordering. |
| |
| if Debug_Flag_B then |
| Write_Line (" choose on reverse alpha order"); |
| end if; |
| |
| return Uname_Less (UT2.Uname, UT1.Uname); |
| end Better_Choice_Pessimistic; |
| |
| ---------------- |
| -- Build_Link -- |
| ---------------- |
| |
| procedure Build_Link |
| (Before : Unit_Id; |
| After : Unit_Id; |
| R : Succ_Reason; |
| Ea_Id : Elab_All_Id := No_Elab_All_Link) |
| is |
| Cspec : Unit_Id; |
| |
| begin |
| Succ.Append |
| ((Before => Before, |
| After => No_Unit_Id, -- filled in below |
| Next => UNR.Table (Before).Successors, |
| Reason => R, |
| Elab_Body => False, -- set correctly below |
| Reason_Unit => Cur_Unit, |
| Elab_All_Link => Ea_Id)); |
| UNR.Table (Before).Successors := Succ.Last; |
| |
| -- Deal with special Elab_Body case. If the After of this link is |
| -- a body whose spec has Elaborate_All set, and this is not the link |
| -- directly from the body to the spec, then we make the After of the |
| -- link reference its spec instead, marking the link appropriately. |
| |
| if Units.Table (After).Utype = Is_Body then |
| Cspec := Corresponding_Spec (After); |
| |
| if Units.Table (Cspec).Elaborate_Body |
| and then Cspec /= Before |
| then |
| Succ.Table (Succ.Last).After := Cspec; |
| Succ.Table (Succ.Last).Elab_Body := True; |
| UNR.Table (Cspec).Num_Pred := UNR.Table (Cspec).Num_Pred + 1; |
| return; |
| end if; |
| end if; |
| |
| -- Fall through on normal case |
| |
| Succ.Table (Succ.Last).After := After; |
| Succ.Table (Succ.Last).Elab_Body := False; |
| UNR.Table (After).Num_Pred := UNR.Table (After).Num_Pred + 1; |
| end Build_Link; |
| |
| ------------ |
| -- Choose -- |
| ------------ |
| |
| procedure Choose |
| (Elab_Order : in out Unit_Id_Table; |
| Chosen : Unit_Id; |
| Msg : String) |
| is |
| pragma Assert (Chosen /= No_Unit_Id); |
| S : Successor_Id; |
| U : Unit_Id; |
| |
| begin |
| if Debug_Flag_C then |
| Write_Str ("Choosing Unit "); |
| Write_Unit_Name (Units.Table (Chosen).Uname); |
| Write_Str (Msg); |
| end if; |
| |
| -- We shouldn't be choosing something with unelaborated predecessors, |
| -- and we shouldn't call this twice on the same unit. But that's not |
| -- true when this is called from Diagnose_Elaboration_Problem. |
| |
| if Errors_Detected = 0 then |
| pragma Assert (UNR.Table (Chosen).Num_Pred = 0); |
| pragma Assert (UNR.Table (Chosen).Elab_Position = 0); |
| pragma Assert (not Doing_New or else SCC_Num_Pred (Chosen) = 0); |
| null; |
| end if; |
| |
| -- Add to elaboration order. Note that units having no elaboration code |
| -- are not treated specially yet. The special casing of this is in |
| -- Bindgen, where Gen_Elab_Calls skips over them. Meanwhile we need them |
| -- here, because the object file list is also driven by the contents of |
| -- the Elab_Order table. |
| |
| Append (Elab_Order, Chosen); |
| |
| -- Remove from No_Pred list. This is a little inefficient and may be we |
| -- should doubly link the list, but it will do for now. |
| |
| if No_Pred = Chosen then |
| No_Pred := UNR.Table (Chosen).Nextnp; |
| else |
| U := No_Pred; |
| while U /= No_Unit_Id loop |
| if UNR.Table (U).Nextnp = Chosen then |
| UNR.Table (U).Nextnp := UNR.Table (Chosen).Nextnp; |
| goto Done_Removal; |
| end if; |
| |
| U := UNR.Table (U).Nextnp; |
| end loop; |
| |
| -- Here if we didn't find it on the No_Pred list. This can happen |
| -- only in calls from the Diagnose_Elaboration_Problem routine, |
| -- where cycles are being removed arbitrarily from the graph. |
| |
| pragma Assert (Errors_Detected > 0); |
| <<Done_Removal>> null; |
| end if; |
| |
| -- For all successors, decrement the number of predecessors, and if it |
| -- becomes zero, then add to no-predecessor list. |
| |
| S := UNR.Table (Chosen).Successors; |
| pragma Annotate (CodePeer, Modified, S); |
| |
| while S /= No_Successor loop |
| U := Succ.Table (S).After; |
| UNR.Table (U).Num_Pred := UNR.Table (U).Num_Pred - 1; |
| |
| if Debug_Flag_N then |
| Write_Str (" decrementing Num_Pred for unit "); |
| Write_Unit_Name (Units.Table (U).Uname); |
| Write_Str (" new value = "); |
| Write_Int (UNR.Table (U).Num_Pred); |
| Write_Eol; |
| end if; |
| |
| if UNR.Table (U).Num_Pred = 0 then |
| UNR.Table (U).Nextnp := No_Pred; |
| No_Pred := U; |
| end if; |
| |
| if Doing_New and then SCC (U) /= SCC (Chosen) then |
| UNR.Table (SCC (U)).SCC_Num_Pred := |
| UNR.Table (SCC (U)).SCC_Num_Pred - 1; |
| |
| if Debug_Flag_N then |
| Write_Str (" decrementing SCC_Num_Pred for unit "); |
| Write_Unit_Name (Units.Table (U).Uname); |
| Write_Str (" new value = "); |
| Write_Int (SCC_Num_Pred (U)); |
| Write_Eol; |
| end if; |
| end if; |
| |
| S := Succ.Table (S).Next; |
| end loop; |
| |
| -- All done, adjust number of units left count and set elaboration pos |
| |
| Num_Left := Num_Left - 1; |
| Num_Chosen := Num_Chosen + 1; |
| |
| pragma Assert |
| (Errors_Detected > 0 or else Num_Chosen = Last (Elab_Order)); |
| pragma Assert (Units.Last = UNR.Last); |
| pragma Assert (Num_Chosen + Num_Left = Int (UNR.Last)); |
| |
| if Debug_Flag_C then |
| Write_Str (" "); |
| Write_Int (Int (Num_Chosen)); |
| Write_Str ("+"); |
| Write_Int (Num_Left); |
| Write_Str ("="); |
| Write_Int (Int (UNR.Last)); |
| Write_Eol; |
| end if; |
| |
| UNR.Table (Chosen).Elab_Position := Num_Chosen; |
| |
| -- If we just chose a spec with Elaborate_Body set, then we must |
| -- immediately elaborate the body, before any other units. |
| |
| if Units.Table (Chosen).Elaborate_Body then |
| |
| -- If the unit is a spec only, then there is no body. This is a bit |
| -- odd given that Elaborate_Body is here, but it is valid in an RCI |
| -- unit, where we only have the interface in the stub bind. |
| |
| if Units.Table (Chosen).Utype = Is_Spec_Only |
| and then Units.Table (Chosen).RCI |
| then |
| null; |
| |
| -- If this unit is an interface to a stand-alone library, then we |
| -- don't want to elaborate the body -- that will happen as part of |
| -- the library. |
| |
| elsif Units.Table (Chosen).SAL_Interface then |
| null; |
| |
| else |
| Choose |
| (Elab_Order => Elab_Order, |
| Chosen => Corresponding_Body (Chosen), |
| Msg => " [Elaborate_Body]"); |
| end if; |
| end if; |
| end Choose; |
| |
| ------------------------ |
| -- Corresponding_Body -- |
| ------------------------ |
| |
| -- Currently if the body and spec are separate, then they appear as two |
| -- separate units in the same ALI file, with the body appearing first and |
| -- the spec appearing second. |
| |
| function Corresponding_Body (U : Unit_Id) return Unit_Id is |
| begin |
| pragma Assert (Units.Table (U).Utype = Is_Spec); |
| return U - 1; |
| end Corresponding_Body; |
| |
| ------------------------ |
| -- Corresponding_Spec -- |
| ------------------------ |
| |
| -- Currently if the body and spec are separate, then they appear as two |
| -- separate units in the same ALI file, with the body appearing first and |
| -- the spec appearing second. |
| |
| function Corresponding_Spec (U : Unit_Id) return Unit_Id is |
| begin |
| pragma Assert (Units.Table (U).Utype = Is_Body); |
| return U + 1; |
| end Corresponding_Spec; |
| |
| -------------------- |
| -- Debug_Flag_Old -- |
| -------------------- |
| |
| function Debug_Flag_Old return Boolean is |
| begin |
| -- If the user specified both flags, we want to use the older algorithm, |
| -- rather than some confusing mix of the two. |
| |
| return Debug_Flag_P and not Debug_Flag_O; |
| end Debug_Flag_Old; |
| |
| ---------------------- |
| -- Debug_Flag_Older -- |
| ---------------------- |
| |
| function Debug_Flag_Older return Boolean is |
| begin |
| return Debug_Flag_O; |
| end Debug_Flag_Older; |
| |
| ---------------------------------- |
| -- Diagnose_Elaboration_Problem -- |
| ---------------------------------- |
| |
| procedure Diagnose_Elaboration_Problem |
| (Elab_Order : in out Unit_Id_Table) |
| is |
| function Find_Path |
| (Ufrom : Unit_Id; |
| Uto : Unit_Id; |
| ML : Nat) return Boolean; |
| -- Recursive routine used to find a path from node Ufrom to node Uto. |
| -- If a path exists, returns True and outputs an appropriate set of |
| -- error messages giving the path. Also calls Choose for each of the |
| -- nodes so that they get removed from the remaining set. There are |
| -- two cases of calls, either Ufrom = Uto for an attempt to find a |
| -- cycle, or Ufrom is a spec and Uto the corresponding body for the |
| -- case of an unsatisfiable Elaborate_Body pragma. ML is the minimum |
| -- acceptable length for a path. |
| |
| --------------- |
| -- Find_Path -- |
| --------------- |
| |
| function Find_Path |
| (Ufrom : Unit_Id; |
| Uto : Unit_Id; |
| ML : Nat) return Boolean |
| is |
| function Find_Link (U : Unit_Id; PL : Nat) return Boolean; |
| -- This is the inner recursive routine, it determines if a path |
| -- exists from U to Uto, and if so returns True and outputs the |
| -- appropriate set of error messages. PL is the path length |
| |
| --------------- |
| -- Find_Link -- |
| --------------- |
| |
| function Find_Link (U : Unit_Id; PL : Nat) return Boolean is |
| S : Successor_Id; |
| |
| begin |
| -- Recursion ends if we are at terminating node and the path is |
| -- sufficiently long, generate error message and return True. |
| |
| if U = Uto and then PL >= ML then |
| Choose (Elab_Order, U, " [Find_Link: base]"); |
| return True; |
| |
| -- All done if already visited |
| |
| elsif UNR.Table (U).Visited then |
| return False; |
| |
| -- Otherwise mark as visited and look at all successors |
| |
| else |
| UNR.Table (U).Visited := True; |
| |
| S := UNR.Table (U).Successors; |
| while S /= No_Successor loop |
| if Find_Link (Succ.Table (S).After, PL + 1) then |
| Elab_Error_Msg (S); |
| Choose (Elab_Order, U, " [Find_Link: recursive]"); |
| return True; |
| end if; |
| |
| S := Succ.Table (S).Next; |
| end loop; |
| |
| -- Falling through means this does not lead to a path |
| |
| return False; |
| end if; |
| end Find_Link; |
| |
| -- Start of processing for Find_Path |
| |
| begin |
| -- Initialize all non-chosen nodes to not visited yet |
| |
| for U in Units.First .. Units.Last loop |
| UNR.Table (U).Visited := UNR.Table (U).Elab_Position /= 0; |
| end loop; |
| |
| -- Now try to find the path |
| |
| return Find_Link (Ufrom, 0); |
| end Find_Path; |
| |
| -- Start of processing for Diagnose_Elaboration_Problem |
| |
| begin |
| Diagnose_Elaboration_Problem_Called := True; |
| Set_Standard_Error; |
| |
| -- Output state of things if debug flag N set |
| |
| if Debug_Flag_N then |
| declare |
| NP : Int; |
| |
| begin |
| Write_Eol; |
| Write_Eol; |
| Write_Line ("Diagnose_Elaboration_Problem called"); |
| Write_Line ("List of remaining unchosen units and predecessors"); |
| |
| for U in Units.First .. Units.Last loop |
| if UNR.Table (U).Elab_Position = 0 then |
| NP := UNR.Table (U).Num_Pred; |
| Write_Eol; |
| Write_Str (" Unchosen unit: #"); |
| Write_Int (Int (U)); |
| Write_Str (" "); |
| Write_Unit_Name (Units.Table (U).Uname); |
| Write_Str (" (Num_Pred = "); |
| Write_Int (NP); |
| Write_Line (")"); |
| |
| if NP = 0 then |
| if Units.Table (U).Elaborate_Body then |
| Write_Line |
| (" (not chosen because of Elaborate_Body)"); |
| else |
| Write_Line (" ****************** why not chosen?"); |
| end if; |
| end if; |
| |
| -- Search links list to find unchosen predecessors |
| |
| for S in Succ.First .. Succ.Last loop |
| declare |
| SL : Successor_Link renames Succ.Table (S); |
| |
| begin |
| if SL.After = U |
| and then UNR.Table (SL.Before).Elab_Position = 0 |
| then |
| Write_Str (" unchosen predecessor: #"); |
| Write_Int (Int (SL.Before)); |
| Write_Str (" "); |
| Write_Unit_Name (Units.Table (SL.Before).Uname); |
| Write_Eol; |
| NP := NP - 1; |
| end if; |
| end; |
| end loop; |
| |
| if NP /= 0 then |
| Write_Line (" **************** Num_Pred value wrong!"); |
| end if; |
| end if; |
| end loop; |
| end; |
| end if; |
| |
| -- Output the header for the error, and manually increment the error |
| -- count. We are using Error_Msg_Output rather than Error_Msg here for |
| -- two reasons: |
| |
| -- This is really only one error, not one for each line |
| -- We want this output on standard output since it is voluminous |
| |
| -- But we do need to deal with the error count manually in this case |
| |
| Errors_Detected := Errors_Detected + 1; |
| Error_Msg_Output ("elaboration circularity detected", Info => False); |
| |
| -- Try to find cycles starting with any of the remaining nodes that have |
| -- not yet been chosen. There must be at least one (there is some reason |
| -- we are being called). |
| |
| for U in Units.First .. Units.Last loop |
| if UNR.Table (U).Elab_Position = 0 then |
| if Find_Path (U, U, 1) then |
| raise Unrecoverable_Error; |
| end if; |
| end if; |
| end loop; |
| |
| -- We should never get here, since we were called for some reason, and |
| -- we should have found and eliminated at least one bad path. |
| |
| raise Program_Error; |
| end Diagnose_Elaboration_Problem; |
| |
| -------------------- |
| -- Elab_All_Links -- |
| -------------------- |
| |
| procedure Elab_All_Links |
| (Before : Unit_Id; |
| After : Unit_Id; |
| Reason : Succ_Reason; |
| Link : Elab_All_Id) |
| is |
| begin |
| if UNR.Table (Before).Visited then |
| return; |
| end if; |
| |
| -- Build the direct link for Before |
| |
| UNR.Table (Before).Visited := True; |
| Build_Link (Before, After, Reason, Link); |
| |
| -- Process all units with'ed by Before recursively |
| |
| for W in Units.Table (Before).First_With .. |
| Units.Table (Before).Last_With |
| loop |
| -- Skip if this with is an interface to a stand-alone library. Skip |
| -- also if no ALI file for this WITH, happens for language defined |
| -- generics while bootstrapping the compiler (see body of routine |
| -- Lib.Writ.Write_With_Lines). Finally, skip if it is a limited with |
| -- clause, which does not impose an elaboration link. |
| |
| if not Withs.Table (W).SAL_Interface |
| and then Withs.Table (W).Afile /= No_File |
| and then not Withs.Table (W).Limited_With |
| then |
| declare |
| Info : constant Int := |
| Get_Name_Table_Int (Withs.Table (W).Uname); |
| |
| begin |
| -- If the unit is unknown, for some unknown reason, fail |
| -- graciously explaining that the unit is unknown. Without |
| -- this check, gnatbind will crash in Unit_Id_Of. |
| |
| if Info = 0 or else Unit_Id (Info) = No_Unit_Id then |
| declare |
| Withed : String := |
| Get_Name_String (Withs.Table (W).Uname); |
| Last_Withed : Natural := Withed'Last; |
| Withing : String := |
| Get_Name_String |
| (Units.Table (Before).Uname); |
| Last_Withing : Natural := Withing'Last; |
| Spec_Body : String := " (Spec)"; |
| |
| begin |
| To_Mixed (Withed); |
| To_Mixed (Withing); |
| |
| if Last_Withed > 2 |
| and then Withed (Last_Withed - 1) = '%' |
| then |
| Last_Withed := Last_Withed - 2; |
| end if; |
| |
| if Last_Withing > 2 |
| and then Withing (Last_Withing - 1) = '%' |
| then |
| Last_Withing := Last_Withing - 2; |
| end if; |
| |
| if Units.Table (Before).Utype = Is_Body |
| or else Units.Table (Before).Utype = Is_Body_Only |
| then |
| Spec_Body := " (Body)"; |
| end if; |
| |
| Osint.Fail |
| ("could not find unit " |
| & Withed (Withed'First .. Last_Withed) & " needed by " |
| & Withing (Withing'First .. Last_Withing) & Spec_Body); |
| end; |
| end if; |
| |
| Elab_All_Links |
| (Unit_Id_Of (Withs.Table (W).Uname), |
| After, |
| Reason, |
| Make_Elab_All_Entry (Withs.Table (W).Uname, Link)); |
| end; |
| end if; |
| end loop; |
| |
| -- Process corresponding body, if there is one |
| |
| if Units.Table (Before).Utype = Is_Spec then |
| Elab_All_Links |
| (Corresponding_Body (Before), |
| After, Reason, |
| Make_Elab_All_Entry |
| (Units.Table (Corresponding_Body (Before)).Uname, Link)); |
| end if; |
| end Elab_All_Links; |
| |
| -------------------- |
| -- Elab_Error_Msg -- |
| -------------------- |
| |
| procedure Elab_Error_Msg (S : Successor_Id) is |
| SL : Successor_Link renames Succ.Table (S); |
| |
| begin |
| -- Nothing to do if internal unit involved and no -da flag |
| |
| if not Debug_Flag_A |
| and then |
| (Is_Internal_File_Name (Units.Table (SL.Before).Sfile) |
| or else |
| Is_Internal_File_Name (Units.Table (SL.After).Sfile)) |
| then |
| return; |
| end if; |
| |
| -- Here we want to generate output |
| |
| Error_Msg_Unit_1 := Units.Table (SL.Before).Uname; |
| |
| if SL.Elab_Body then |
| Error_Msg_Unit_2 := Units.Table (Corresponding_Body (SL.After)).Uname; |
| else |
| Error_Msg_Unit_2 := Units.Table (SL.After).Uname; |
| end if; |
| |
| Error_Msg_Output (" $ must be elaborated before $", Info => True); |
| |
| Error_Msg_Unit_1 := Units.Table (SL.Reason_Unit).Uname; |
| |
| case SL.Reason is |
| when Withed => |
| Error_Msg_Output |
| (" reason: with clause", |
| Info => True); |
| |
| when Forced => |
| Error_Msg_Output |
| (" reason: forced by -f switch", |
| Info => True); |
| |
| when Elab => |
| Error_Msg_Output |
| (" reason: pragma Elaborate in unit $", |
| Info => True); |
| |
| when Elab_All => |
| Error_Msg_Output |
| (" reason: pragma Elaborate_All in unit $", |
| Info => True); |
| |
| when Elab_All_Desirable => |
| Error_Msg_Output |
| (" reason: implicit Elaborate_All in unit $", |
| Info => True); |
| |
| Error_Msg_Output |
| (" recompile $ with -gnatel for full details", |
| Info => True); |
| |
| when Elab_Desirable => |
| Error_Msg_Output |
| (" reason: implicit Elaborate in unit $", |
| Info => True); |
| |
| Error_Msg_Output |
| (" recompile $ with -gnatel for full details", |
| Info => True); |
| |
| when Spec_First => |
| Error_Msg_Output |
| (" reason: spec always elaborated before body", |
| Info => True); |
| end case; |
| |
| Write_Elab_All_Chain (S); |
| |
| if SL.Elab_Body then |
| Error_Msg_Unit_1 := Units.Table (SL.Before).Uname; |
| Error_Msg_Unit_2 := Units.Table (SL.After).Uname; |
| Error_Msg_Output |
| (" $ must therefore be elaborated before $", True); |
| |
| Error_Msg_Unit_1 := Units.Table (SL.After).Uname; |
| Error_Msg_Output |
| (" (because $ has a pragma Elaborate_Body)", True); |
| end if; |
| |
| if not Zero_Formatting then |
| Write_Eol; |
| end if; |
| end Elab_Error_Msg; |
| |
| --------------------- |
| -- Find_Elab_Order -- |
| --------------------- |
| |
| procedure Find_Elab_Order |
| (Elab_Order : out Unit_Id_Table; |
| First_Main_Lib_File : File_Name_Type) |
| is |
| function Num_Spec_Body_Pairs (Order : Unit_Id_Array) return Nat; |
| -- Number of cases where the body of a unit immediately follows the |
| -- corresponding spec. Such cases are good, because calls to that unit |
| -- from outside can't get ABE. |
| |
| ------------------------- |
| -- Num_Spec_Body_Pairs -- |
| ------------------------- |
| |
| function Num_Spec_Body_Pairs (Order : Unit_Id_Array) return Nat is |
| Result : Nat := 0; |
| |
| begin |
| for J in Order'First + 1 .. Order'Last loop |
| if Units.Table (Order (J - 1)).Utype = Is_Spec |
| and then Units.Table (Order (J)).Utype = Is_Body |
| and then Corresponding_Spec (Order (J)) = Order (J - 1) |
| then |
| Result := Result + 1; |
| end if; |
| end loop; |
| |
| return Result; |
| end Num_Spec_Body_Pairs; |
| |
| -- Local variables |
| |
| Old_Elab_Order : Unit_Id_Table; |
| |
| -- Start of processing for Find_Elab_Order |
| |
| begin |
| -- Output warning if -p used with no -gnatE units |
| |
| if Pessimistic_Elab_Order |
| and not Dynamic_Elaboration_Checks_Specified |
| then |
| Error_Msg ("?use of -p switch questionable"); |
| Error_Msg ("?since all units compiled with static elaboration model"); |
| end if; |
| |
| if Do_New and not Debug_Flag_Old and not Debug_Flag_Older then |
| if Debug_Flag_V then |
| Write_Line ("Doing new..."); |
| end if; |
| |
| Doing_New := True; |
| Init; |
| Elab_New.Find_Elab_Order (Elab_Order); |
| end if; |
| |
| -- Elab_New does not support the pessimistic order, so if that was |
| -- requested, use the old results. Use Elab_Old if -dp or -do was |
| -- selected. Elab_New does not yet give proper error messages for |
| -- illegal Elaborate_Alls, so if there is one, run Elab_Old. |
| |
| if Do_Old |
| or Pessimistic_Elab_Order |
| or Debug_Flag_Old |
| or Debug_Flag_Older |
| or Elab_Cycle_Found |
| then |
| if Debug_Flag_V then |
| Write_Line ("Doing old..."); |
| end if; |
| |
| Doing_New := False; |
| Init; |
| Elab_Old.Find_Elab_Order (Old_Elab_Order); |
| end if; |
| |
| pragma Assert (Elab_Cycle_Found <= -- implies |
| Diagnose_Elaboration_Problem_Called); |
| |
| declare |
| Old_Order : Unit_Id_Array renames |
| Old_Elab_Order.Table (1 .. Last (Old_Elab_Order)); |
| begin |
| if Do_Old and Do_New then |
| declare |
| New_Order : Unit_Id_Array renames |
| Elab_Order.Table (1 .. Last (Elab_Order)); |
| Old_Pairs : constant Nat := Num_Spec_Body_Pairs (Old_Order); |
| New_Pairs : constant Nat := Num_Spec_Body_Pairs (New_Order); |
| |
| begin |
| Write_Line (Get_Name_String (First_Main_Lib_File)); |
| |
| pragma Assert (Old_Order'Length = New_Order'Length); |
| pragma Debug (Validate (Old_Order, Doing_New => False)); |
| pragma Debug (Validate (New_Order, Doing_New => True)); |
| |
| -- Misc debug printouts that can be used for experimentation by |
| -- changing the 'if's below. |
| |
| if True then |
| if New_Order = Old_Order then |
| Write_Line ("Elab_New: same order."); |
| else |
| Write_Line ("Elab_New: diff order."); |
| end if; |
| end if; |
| |
| if New_Order /= Old_Order and then False then |
| Write_Line ("Elaboration orders differ:"); |
| Write_Elab_Order |
| (Old_Order, Title => "OLD ELABORATION ORDER"); |
| Write_Elab_Order |
| (New_Order, Title => "NEW ELABORATION ORDER"); |
| end if; |
| |
| if True then |
| Write_Str ("Pairs: "); |
| Write_Int (Old_Pairs); |
| |
| if Old_Pairs = New_Pairs then |
| Write_Str (" = "); |
| elsif Old_Pairs < New_Pairs then |
| Write_Str (" < "); |
| else |
| Write_Str (" > "); |
| end if; |
| |
| Write_Int (New_Pairs); |
| Write_Eol; |
| end if; |
| |
| if Old_Pairs /= New_Pairs and then False then |
| Write_Str ("Pairs: "); |
| Write_Int (Old_Pairs); |
| |
| if Old_Pairs < New_Pairs then |
| Write_Str (" < "); |
| else |
| Write_Str (" > "); |
| end if; |
| |
| Write_Int (New_Pairs); |
| Write_Eol; |
| |
| if Old_Pairs /= New_Pairs and then Debug_Flag_V then |
| Write_Elab_Order |
| (Old_Order, Title => "OLD ELABORATION ORDER"); |
| Write_Elab_Order |
| (New_Order, Title => "NEW ELABORATION ORDER"); |
| pragma Assert (New_Pairs >= Old_Pairs); |
| end if; |
| end if; |
| end; |
| end if; |
| |
| -- The Elab_New algorithm doesn't implement the -p switch, so if that |
| -- was used, use the results from the old algorithm. Likewise if the |
| -- user has requested the old algorithm. |
| |
| if Pessimistic_Elab_Order or Debug_Flag_Old or Debug_Flag_Older then |
| pragma Assert |
| (Last (Elab_Order) = 0 |
| or else Last (Elab_Order) = Old_Order'Last); |
| |
| Init (Elab_Order); |
| Append_All (Elab_Order, Old_Order); |
| end if; |
| |
| -- Now set the Elab_Positions in the Units table. It is important to |
| -- do this late, in case we're running both Elab_New and Elab_Old. |
| |
| declare |
| New_Order : Unit_Id_Array renames |
| Elab_Order.Table (1 .. Last (Elab_Order)); |
| Units_Array : Units.Table_Type renames |
| Units.Table (Units.First .. Units.Last); |
| begin |
| for J in New_Order'Range loop |
| pragma Assert |
| (UNR.Table (New_Order (J)).Elab_Position = J); |
| Units_Array (New_Order (J)).Elab_Position := J; |
| end loop; |
| |
| if Errors_Detected = 0 then |
| |
| -- Display elaboration order if -l was specified |
| |
| if Elab_Order_Output then |
| if Zero_Formatting then |
| Write_Elab_Order (New_Order, Title => ""); |
| else |
| Write_Elab_Order |
| (New_Order, Title => "ELABORATION ORDER"); |
| end if; |
| end if; |
| |
| -- Display list of sources in the closure (except predefined |
| -- sources) if -R was used. Include predefined sources if -Ra |
| -- was used. |
| |
| if List_Closure then |
| Write_Closure (New_Order); |
| end if; |
| end if; |
| end; |
| end; |
| end Find_Elab_Order; |
| |
| ---------------------- |
| -- Force_Elab_Order -- |
| ---------------------- |
| |
| procedure Force_Elab_Order is |
| subtype Header_Num is Unit_Name_Type'Base range 0 .. 2**16 - 1; |
| |
| function Hash (N : Unit_Name_Type) return Header_Num; |
| |
| package Name_Map is new System.HTable.Simple_HTable |
| (Header_Num => Header_Num, |
| Element => Logical_Line_Number, |
| No_Element => No_Line_Number, |
| Key => Unit_Name_Type, |
| Hash => Hash, |
| Equal => "="); |
| -- Name_Map contains an entry for each file name seen, mapped to the |
| -- line number where we saw it first. This is used to give an error for |
| -- duplicates. |
| |
| ---------- |
| -- Hash -- |
| ---------- |
| |
| function Hash (N : Unit_Name_Type) return Header_Num is |
| -- Name_Ids are already widely dispersed; no need for any actual |
| -- hashing. Just subtract to make it zero based, and "mod" to |
| -- bring it in range. |
| begin |
| return (N - Unit_Name_Type'First) mod (Header_Num'Last + 1); |
| end Hash; |
| |
| -- Local variables |
| |
| Cur_Line_Number : Logical_Line_Number; |
| Error : Boolean := False; |
| Iter : Forced_Units_Iterator; |
| Prev_Unit : Unit_Id := No_Unit_Id; |
| Uname : Unit_Name_Type; |
| |
| -- Start of processing for Force_Elab_Order |
| |
| begin |
| Iter := Iterate_Forced_Units; |
| while Has_Next (Iter) loop |
| Next (Iter, Uname, Cur_Line_Number); |
| |
| declare |
| Dup : constant Logical_Line_Number := Name_Map.Get (Uname); |
| begin |
| if Dup = No_Line_Number then |
| Name_Map.Set (Uname, Cur_Line_Number); |
| |
| -- We don't need to give the "not present" message in the case |
| -- of "duplicate unit", because we would have already given the |
| -- "not present" message on the first occurrence. |
| |
| if Get_Name_Table_Int (Uname) = 0 |
| or else Unit_Id (Get_Name_Table_Int (Uname)) = No_Unit_Id |
| then |
| Error := True; |
| if Doing_New then |
| Write_Line |
| ("""" & Get_Name_String (Uname) |
| & """: not present; ignored"); |
| end if; |
| end if; |
| |
| else |
| Error := True; |
| if Doing_New then |
| Error_Msg_Nat_1 := Nat (Cur_Line_Number); |
| Error_Msg_Unit_1 := Uname; |
| Error_Msg_Nat_2 := Nat (Dup); |
| Error_Msg |
| (Force_Elab_Order_File.all |
| & ":#: duplicate unit name $ from line #"); |
| end if; |
| end if; |
| end; |
| |
| if not Error then |
| declare |
| Cur_Unit : constant Unit_Id := Unit_Id_Of (Uname); |
| begin |
| if Is_Internal_File_Name (Units.Table (Cur_Unit).Sfile) then |
| if Doing_New then |
| Write_Line |
| ("""" & Get_Name_String (Uname) |
| & """: predefined unit ignored"); |
| end if; |
| |
| else |
| if Prev_Unit /= No_Unit_Id then |
| if Doing_New then |
| Write_Unit_Name (Units.Table (Prev_Unit).Uname); |
| Write_Str (" <-- "); |
| Write_Unit_Name (Units.Table (Cur_Unit).Uname); |
| Write_Eol; |
| end if; |
| |
| Build_Link |
| (Before => Prev_Unit, |
| After => Cur_Unit, |
| R => Forced); |
| end if; |
| |
| Prev_Unit := Cur_Unit; |
| end if; |
| end; |
| end if; |
| end loop; |
| end Force_Elab_Order; |
| |
| ------------------------- |
| -- Gather_Dependencies -- |
| ------------------------- |
| |
| procedure Gather_Dependencies is |
| Withed_Unit : Unit_Id; |
| |
| begin |
| -- Loop through all units |
| |
| for U in Units.First .. Units.Last loop |
| Cur_Unit := U; |
| |
| -- If this is not an interface to a stand-alone library and there is |
| -- a body and a spec, then spec must be elaborated first. Note that |
| -- the corresponding spec immediately follows the body. |
| |
| if not Units.Table (U).SAL_Interface |
| and then Units.Table (U).Utype = Is_Body |
| then |
| Build_Link (Corresponding_Spec (U), U, Spec_First); |
| end if; |
| |
| -- If this unit is not an interface to a stand-alone library, process |
| -- WITH references for this unit ignoring interfaces to stand-alone |
| -- libraries. |
| |
| if not Units.Table (U).SAL_Interface then |
| for W in Units.Table (U).First_With .. |
| Units.Table (U).Last_With |
| loop |
| if Withs.Table (W).Sfile /= No_File |
| and then (not Withs.Table (W).SAL_Interface) |
| then |
| -- Check for special case of withing a unit that does not |
| -- exist any more. If the unit was completely missing we |
| -- would already have detected this, but a nasty case arises |
| -- when we have a subprogram body with no spec, and some |
| -- obsolete unit with's a previous (now disappeared) spec. |
| |
| if Get_Name_Table_Int (Withs.Table (W).Uname) = 0 then |
| if Doing_New then |
| Error_Msg_File_1 := Units.Table (U).Sfile; |
| Error_Msg_Unit_1 := Withs.Table (W).Uname; |
| Error_Msg ("{ depends on $ which no longer exists"); |
| end if; |
| |
| goto Next_With; |
| end if; |
| |
| Withed_Unit := Unit_Id_Of (Withs.Table (W).Uname); |
| |
| -- Pragma Elaborate_All case, for this we use the recursive |
| -- Elab_All_Links procedure to establish the links. |
| |
| -- Elab_New ignores Elaborate_All and Elab_All_Desirable, |
| -- except for error messages. |
| |
| if Withs.Table (W).Elaborate_All and then not Doing_New then |
| |
| -- Reset flags used to stop multiple visits to a given |
| -- node. |
| |
| for Uref in UNR.First .. UNR.Last loop |
| UNR.Table (Uref).Visited := False; |
| end loop; |
| |
| -- Now establish all the links we need |
| |
| Elab_All_Links |
| (Withed_Unit, U, Elab_All, |
| Make_Elab_All_Entry |
| (Withs.Table (W).Uname, No_Elab_All_Link)); |
| |
| -- Elaborate_All_Desirable case, for this we establish the |
| -- same links as above, but with a different reason. |
| |
| elsif Withs.Table (W).Elab_All_Desirable |
| and then not Doing_New |
| then |
| -- Reset flags used to stop multiple visits to a given |
| -- node. |
| |
| for Uref in UNR.First .. UNR.Last loop |
| UNR.Table (Uref).Visited := False; |
| end loop; |
| |
| -- Now establish all the links we need |
| |
| Elab_All_Links |
| (Withed_Unit, U, Elab_All_Desirable, |
| Make_Elab_All_Entry |
| (Withs.Table (W).Uname, No_Elab_All_Link)); |
| |
| -- Pragma Elaborate case. We must build a link for the |
| -- withed unit itself, and also the corresponding body if |
| -- there is one. |
| |
| -- However, skip this processing if there is no ALI file for |
| -- the WITH entry, because this means it is a generic (even |
| -- when we fix the generics so that an ALI file is present, |
| -- we probably still will have no ALI file for unchecked and |
| -- other special cases). |
| |
| elsif Withs.Table (W).Elaborate |
| and then Withs.Table (W).Afile /= No_File |
| then |
| Build_Link (Withed_Unit, U, Withed); |
| |
| if Units.Table (Withed_Unit).Utype = Is_Spec then |
| Build_Link |
| (Corresponding_Body (Withed_Unit), U, Elab); |
| end if; |
| |
| -- Elaborate_Desirable case, for this we establish the same |
| -- links as above, but with a different reason. |
| |
| elsif Withs.Table (W).Elab_Desirable then |
| Build_Link (Withed_Unit, U, Withed); |
| |
| if Units.Table (Withed_Unit).Utype = Is_Spec then |
| Build_Link |
| (Corresponding_Body (Withed_Unit), |
| U, Elab_Desirable); |
| end if; |
| |
| -- A limited_with does not establish an elaboration |
| -- dependence (that's the whole point). |
| |
| elsif Withs.Table (W).Limited_With then |
| null; |
| |
| -- Case of normal WITH with no elaboration pragmas, just |
| -- build the single link to the directly referenced unit |
| |
| else |
| Build_Link (Withed_Unit, U, Withed); |
| end if; |
| end if; |
| |
| <<Next_With>> |
| null; |
| end loop; |
| end if; |
| end loop; |
| |
| -- If -f<elab_order> switch was given, take into account dependences |
| -- specified in the file <elab_order>. |
| |
| if Force_Elab_Order_File /= null then |
| Force_Elab_Order; |
| end if; |
| |
| -- Output elaboration dependencies if option is set |
| |
| if Elab_Dependency_Output or Debug_Flag_E then |
| if Doing_New then |
| Write_Dependencies; |
| end if; |
| end if; |
| end Gather_Dependencies; |
| |
| ---------- |
| -- Init -- |
| ---------- |
| |
| procedure Init is |
| begin |
| Num_Chosen := 0; |
| Num_Left := Int (Units.Last - Units.First + 1); |
| Succ.Init; |
| Elab_All_Entries.Init; |
| UNR.Init; |
| |
| -- Initialize unit table for elaboration control |
| |
| for U in Units.First .. Units.Last loop |
| UNR.Append |
| ((Successors => No_Successor, |
| Num_Pred => 0, |
| Nextnp => No_Unit_Id, |
| Visited => False, |
| Elab_Position => 0, |
| SCC_Root => No_Unit_Id, |
| Nodes => null, |
| SCC_Num_Pred => 0, |
| Validate_Seen => False)); |
| end loop; |
| end Init; |
| |
| ------------------ |
| -- Is_Body_Unit -- |
| ------------------ |
| |
| function Is_Body_Unit (U : Unit_Id) return Boolean is |
| begin |
| return |
| Units.Table (U).Utype = Is_Body |
| or else Units.Table (U).Utype = Is_Body_Only; |
| end Is_Body_Unit; |
| |
| ----------------------------- |
| -- Is_Pure_Or_Preelab_Unit -- |
| ----------------------------- |
| |
| function Is_Pure_Or_Preelab_Unit (U : Unit_Id) return Boolean is |
| begin |
| -- If we have a body with separate spec, test flags on the spec |
| |
| if Units.Table (U).Utype = Is_Body then |
| return |
| Units.Table (Corresponding_Spec (U)).Preelab |
| or else Units.Table (Corresponding_Spec (U)).Pure; |
| |
| -- Otherwise we have a spec or body acting as spec, test flags on unit |
| |
| else |
| return Units.Table (U).Preelab or else Units.Table (U).Pure; |
| end if; |
| end Is_Pure_Or_Preelab_Unit; |
| |
| --------------------- |
| -- Is_Waiting_Body -- |
| --------------------- |
| |
| function Is_Waiting_Body (U : Unit_Id) return Boolean is |
| begin |
| return |
| Units.Table (U).Utype = Is_Body |
| and then UNR.Table (Corresponding_Spec (U)).Elab_Position /= 0; |
| end Is_Waiting_Body; |
| |
| ------------------------- |
| -- Make_Elab_All_Entry -- |
| ------------------------- |
| |
| function Make_Elab_All_Entry |
| (Unam : Unit_Name_Type; |
| Link : Elab_All_Id) return Elab_All_Id |
| is |
| begin |
| Elab_All_Entries.Append ((Needed_By => Unam, Next_Elab => Link)); |
| return Elab_All_Entries.Last; |
| end Make_Elab_All_Entry; |
| |
| ---------------- |
| -- Unit_Id_Of -- |
| ---------------- |
| |
| function Unit_Id_Of (Uname : Unit_Name_Type) return Unit_Id is |
| Info : constant Int := Get_Name_Table_Int (Uname); |
| |
| begin |
| pragma Assert (Info /= 0 and then Unit_Id (Info) /= No_Unit_Id); |
| return Unit_Id (Info); |
| end Unit_Id_Of; |
| |
| -------------- |
| -- Validate -- |
| -------------- |
| |
| procedure Validate (Order : Unit_Id_Array; Doing_New : Boolean) is |
| Cur_SCC : Unit_Id := No_Unit_Id; |
| OK : Boolean := True; |
| Msg : String := "Old: "; |
| |
| begin |
| if Doing_New then |
| Msg := "New: "; |
| end if; |
| |
| -- For each unit, assert that its successors are elaborated after it |
| |
| for J in Order'Range loop |
| declare |
| U : constant Unit_Id := Order (J); |
| S : Successor_Id := UNR.Table (U).Successors; |
| |
| begin |
| while S /= No_Successor loop |
| if UNR.Table (Succ.Table (S).After).Elab_Position <= |
| UNR.Table (U).Elab_Position |
| then |
| OK := False; |
| Write_Line (Msg & " elab order failed"); |
| end if; |
| |
| S := Succ.Table (S).Next; |
| end loop; |
| end; |
| end loop; |
| |
| -- An SCC of size 2 units necessarily consists of a spec and the |
| -- corresponding body. Assert that the body is elaborated immediately |
| -- after the spec, with nothing in between. (We only have SCCs in the |
| -- new algorithm.) |
| |
| if Doing_New then |
| for J in Order'Range loop |
| declare |
| U : constant Unit_Id := Order (J); |
| |
| begin |
| if Nodes (U)'Length = 2 then |
| if Units.Table (U).Utype = Is_Spec then |
| if Order (J + 1) /= Corresponding_Body (U) then |
| OK := False; |
| Write_Line (Msg & "Bad spec with SCC of size 2:"); |
| Write_SCC (SCC (U)); |
| end if; |
| end if; |
| |
| if Units.Table (U).Utype = Is_Body then |
| if Order (J - 1) /= Corresponding_Spec (U) then |
| OK := False; |
| Write_Line (Msg & "Bad body with SCC of size 2:"); |
| Write_SCC (SCC (U)); |
| end if; |
| end if; |
| end if; |
| end; |
| end loop; |
| |
| -- Assert that all units of an SCC are elaborated together, with no |
| -- units from other SCCs in between. The above spec/body case is a |
| -- special case of this general rule. |
| |
| for J in Order'Range loop |
| declare |
| U : constant Unit_Id := Order (J); |
| |
| begin |
| if SCC (U) /= Cur_SCC then |
| Cur_SCC := SCC (U); |
| if UNR.Table (Cur_SCC).Validate_Seen then |
| OK := False; |
| Write_Line (Msg & "SCC not elaborated together:"); |
| Write_SCC (Cur_SCC); |
| end if; |
| |
| UNR.Table (Cur_SCC).Validate_Seen := True; |
| end if; |
| end; |
| end loop; |
| end if; |
| |
| pragma Assert (OK); |
| end Validate; |
| |
| ------------------- |
| -- Write_Closure -- |
| ------------------- |
| |
| procedure Write_Closure (Order : Unit_Id_Array) is |
| package Closure_Sources is new Table.Table |
| (Table_Component_Type => File_Name_Type, |
| Table_Index_Type => Natural, |
| Table_Low_Bound => 1, |
| Table_Initial => 10, |
| Table_Increment => 100, |
| Table_Name => "Gnatbind.Closure_Sources"); |
| -- Table to record the sources in the closure, to avoid duplications |
| |
| function Put_In_Sources (S : File_Name_Type) return Boolean; |
| -- Check if S is already in table Sources and put in Sources if it is |
| -- not. Return False if the source is already in Sources, and True if |
| -- it is added. |
| |
| -------------------- |
| -- Put_In_Sources -- |
| -------------------- |
| |
| function Put_In_Sources (S : File_Name_Type) return Boolean is |
| begin |
| for J in 1 .. Closure_Sources.Last loop |
| if Closure_Sources.Table (J) = S then |
| return False; |
| end if; |
| end loop; |
| |
| Closure_Sources.Append (S); |
| return True; |
| end Put_In_Sources; |
| |
| -- Local variables |
| |
| Source : File_Name_Type; |
| |
| -- Start of processing for Write_Closure |
| |
| begin |
| Closure_Sources.Init; |
| |
| if not Zero_Formatting then |
| Write_Eol; |
| Write_Line ("REFERENCED SOURCES"); |
| end if; |
| |
| for J in reverse Order'Range loop |
| Source := Units.Table (Order (J)).Sfile; |
| |
| -- Do not include same source more than once |
| |
| if Put_In_Sources (Source) |
| |
| -- Do not include run-time units unless -Ra switch set |
| |
| and then (List_Closure_All |
| or else not Is_Internal_File_Name (Source)) |
| then |
| if not Zero_Formatting then |
| Write_Str (" "); |
| end if; |
| |
| Write_Line (Get_Name_String (Source)); |
| end if; |
| end loop; |
| |
| -- Subunits do not appear in the elaboration table because they are |
| -- subsumed by their parent units, but we need to list them for other |
| -- tools. For now they are listed after other files, rather than right |
| -- after their parent, since there is no easy link between the |
| -- elaboration table and the ALIs table. As subunits may appear |
| -- repeatedly in the list, if the parent unit appears in the context of |
| -- several units in the closure, duplicates are suppressed. |
| |
| for J in Sdep.First .. Sdep.Last loop |
| Source := Sdep.Table (J).Sfile; |
| |
| if Sdep.Table (J).Subunit_Name /= No_Name |
| and then Put_In_Sources (Source) |
| and then not Is_Internal_File_Name (Source) |
| then |
| if not Zero_Formatting then |
| Write_Str (" "); |
| end if; |
| |
| Write_Line (Get_Name_String (Source)); |
| end if; |
| end loop; |
| |
| if not Zero_Formatting then |
| Write_Eol; |
| end if; |
| end Write_Closure; |
| |
| ------------------------ |
| -- Write_Dependencies -- |
| ------------------------ |
| |
| procedure Write_Dependencies is |
| begin |
| if not Zero_Formatting then |
| Write_Eol; |
| Write_Line (" ELABORATION ORDER DEPENDENCIES"); |
| Write_Eol; |
| end if; |
| |
| Info_Prefix_Suppress := True; |
| |
| for S in Succ_First .. Succ.Last loop |
| Elab_Error_Msg (S); |
| end loop; |
| |
| Info_Prefix_Suppress := False; |
| |
| if not Zero_Formatting then |
| Write_Eol; |
| end if; |
| end Write_Dependencies; |
| |
| -------------------------- |
| -- Write_Elab_All_Chain -- |
| -------------------------- |
| |
| procedure Write_Elab_All_Chain (S : Successor_Id) is |
| ST : constant Successor_Link := Succ.Table (S); |
| After : constant Unit_Name_Type := Units.Table (ST.After).Uname; |
| |
| L : Elab_All_Id; |
| Nam : Unit_Name_Type; |
| |
| First_Name : Boolean := True; |
| |
| begin |
| if ST.Reason in Elab_All .. Elab_All_Desirable then |
| L := ST.Elab_All_Link; |
| pragma Annotate (CodePeer, Modified, L); |
| |
| while L /= No_Elab_All_Link loop |
| Nam := Elab_All_Entries.Table (L).Needed_By; |
| Error_Msg_Unit_1 := Nam; |
| Error_Msg_Output (" $", Info => True); |
| |
| Get_Name_String (Nam); |
| |
| if Name_Buffer (Name_Len) = 'b' then |
| if First_Name then |
| Error_Msg_Output |
| (" must be elaborated along with its spec:", |
| Info => True); |
| |
| else |
| Error_Msg_Output |
| (" which must be elaborated along with its " |
| & "spec:", |
| Info => True); |
| end if; |
| |
| else |
| if First_Name then |
| Error_Msg_Output |
| (" is withed by:", |
| Info => True); |
| |
| else |
| Error_Msg_Output |
| (" which is withed by:", |
| Info => True); |
| end if; |
| end if; |
| |
| First_Name := False; |
| |
| L := Elab_All_Entries.Table (L).Next_Elab; |
| end loop; |
| |
| Error_Msg_Unit_1 := After; |
| Error_Msg_Output (" $", Info => True); |
| end if; |
| end Write_Elab_All_Chain; |
| |
| ---------------------- |
| -- Write_Elab_Order -- |
| ---------------------- |
| |
| procedure Write_Elab_Order |
| (Order : Unit_Id_Array; Title : String) |
| is |
| begin |
| if Title /= "" then |
| Write_Eol; |
| Write_Line (Title); |
| end if; |
| |
| for J in Order'Range loop |
| if not Units.Table (Order (J)).SAL_Interface then |
| if not Zero_Formatting then |
| Write_Str (" "); |
| end if; |
| |
| Write_Unit_Name (Units.Table (Order (J)).Uname); |
| Write_Eol; |
| end if; |
| end loop; |
| |
| if Title /= "" then |
| Write_Eol; |
| end if; |
| end Write_Elab_Order; |
| |
| -------------- |
| -- Elab_New -- |
| -------------- |
| |
| package body Elab_New is |
| |
| generic |
| type Node is (<>); |
| First_Node : Node; |
| Last_Node : Node; |
| type Node_Array is array (Pos range <>) of Node; |
| with function Successors (N : Node) return Node_Array; |
| with procedure Create_SCC (Root : Node; Nodes : Node_Array); |
| |
| procedure Compute_Strongly_Connected_Components; |
| -- Compute SCCs for a directed graph. The nodes in the graph are all |
| -- values of type Node in the range First_Node .. Last_Node. |
| -- Successors(N) returns the nodes pointed to by the edges emanating |
| -- from N. Create_SCC is a callback that is called once for each SCC, |
| -- passing in the Root node for that SCC (which is an arbitrary node in |
| -- the SCC used as a representative of that SCC), and the set of Nodes |
| -- in that SCC. |
| -- |
| -- This is generic, in case we want to use it elsewhere; then we could |
| -- move this into a separate library unit. Unfortunately, it's not as |
| -- generic as one might like. Ideally, we would have "type Node is |
| -- private;", and pass in iterators to iterate over all nodes, and over |
| -- the successors of a given node. However, that leads to using advanced |
| -- features of Ada that are not allowed in the compiler and binder for |
| -- bootstrapping reasons. It also leads to trampolines, which are not |
| -- allowed in the compiler and binder. Restricting Node to be discrete |
| -- allows us to iterate over all nodes with a 'for' loop, and allows us |
| -- to attach temporary information to nodes by having an array indexed |
| -- by Node. |
| |
| procedure Compute_Unit_SCCs; |
| -- Use the above generic procedure to compute the SCCs for the graph of |
| -- units. Store in each Unit_Node_Record the SCC_Root and Nodes |
| -- components. Also initialize the SCC_Num_Pred components. |
| |
| procedure Find_Elab_All_Errors; |
| -- Generate an error for illegal Elaborate_All pragmas (explicit or |
| -- implicit). A pragma Elaborate_All (Y) on unit X is legal if and only |
| -- if X and Y are in different SCCs. |
| |
| ------------------------------------------- |
| -- Compute_Strongly_Connected_Components -- |
| ------------------------------------------- |
| |
| procedure Compute_Strongly_Connected_Components is |
| |
| -- This uses Tarjan's algorithm for finding SCCs. Comments here are |
| -- intended to tell what it does, but if you want to know how it |
| -- works, you have to look it up. Please do not modify this code |
| -- without reading up on Tarjan's algorithm. |
| |
| subtype Node_Index is Nat; |
| No_Index : constant Node_Index := 0; |
| |
| Num_Nodes : constant Nat := |
| Node'Pos (Last_Node) - Node'Pos (First_Node) + 1; |
| Stack : Node_Array (1 .. Num_Nodes); |
| Top : Node_Index := 0; |
| -- Stack of nodes, pushed when first visited. All nodes of an SCC are |
| -- popped at once when the SCC is found. |
| |
| subtype Valid_Node is Node range First_Node .. Last_Node; |
| Node_Indices : array (Valid_Node) of Node_Index := |
| (others => No_Index); |
| -- Each node has an "index", which is the sequential number in the |
| -- order in which they are visited in the recursive walk. No_Index |
| -- means "not yet visited"; we want to avoid walking any node more |
| -- than once. |
| |
| Index : Node_Index := 1; |
| -- Next value to be assigned to a node index |
| |
| Low_Links : array (Valid_Node) of Node_Index; |
| -- Low_Links (N) is the smallest index of nodes reachable from N |
| |
| On_Stack : array (Valid_Node) of Boolean := (others => False); |
| -- True if the node is currently on the stack |
| |
| procedure Walk (N : Valid_Node); |
| -- Recursive depth-first graph walk, with the node index used to |
| -- avoid visiting a node more than once. |
| |
| ---------- |
| -- Walk -- |
| ---------- |
| |
| procedure Walk (N : Valid_Node) is |
| Stack_Position_Of_N : constant Pos := Top + 1; |
| S : constant Node_Array := Successors (N); |
| |
| begin |
| -- Assign the index and low link, increment Index for next call to |
| -- Walk. |
| |
| Node_Indices (N) := Index; |
| Low_Links (N) := Index; |
| Index := Index + 1; |
| |
| -- Push it on the stack: |
| |
| Top := Stack_Position_Of_N; |
| Stack (Top) := N; |
| On_Stack (N) := True; |
| |
| -- Walk not-yet-visited subnodes, and update low link for visited |
| -- ones as appropriate. |
| |
| for J in S'Range loop |
| if Node_Indices (S (J)) = No_Index then |
| Walk (S (J)); |
| Low_Links (N) := |
| Node_Index'Min (Low_Links (N), Low_Links (S (J))); |
| elsif On_Stack (S (J)) then |
| Low_Links (N) := |
| Node_Index'Min (Low_Links (N), Node_Indices (S (J))); |
| end if; |
| end loop; |
| |
| -- If the index is (still) equal to the low link, we've found an |
| -- SCC. Pop the whole SCC off the stack, and call Create_SCC. |
| |
| if Low_Links (N) = Node_Indices (N) then |
| declare |
| SCC : Node_Array renames |
| Stack (Stack_Position_Of_N .. Top); |
| pragma Assert (SCC'Length >= 1); |
| pragma Assert (SCC (SCC'First) = N); |
| |
| begin |
| for J in SCC'Range loop |
| On_Stack (SCC (J)) := False; |
| end loop; |
| |
| Create_SCC (Root => N, Nodes => SCC); |
| pragma Assert (Top - SCC'Length = Stack_Position_Of_N - 1); |
| Top := Stack_Position_Of_N - 1; -- pop all |
| end; |
| end if; |
| end Walk; |
| |
| -- Start of processing for Compute_Strongly_Connected_Components |
| |
| begin |
| -- Walk all the nodes that have not yet been walked |
| |
| for N in Valid_Node loop |
| if Node_Indices (N) = No_Index then |
| Walk (N); |
| end if; |
| end loop; |
| end Compute_Strongly_Connected_Components; |
| |
| ----------------------- |
| -- Compute_Unit_SCCs -- |
| ----------------------- |
| |
| procedure Compute_Unit_SCCs is |
| function Successors (U : Unit_Id) return Unit_Id_Array; |
| -- Return all the units that must be elaborated after U. In addition, |
| -- if U is a body, include the corresponding spec; this ensures that |
| -- a spec/body pair are always in the same SCC. |
| |
| procedure Create_SCC (Root : Unit_Id; Nodes : Unit_Id_Array); |
| -- Set Nodes of the Root, and set SCC_Root of all the Nodes |
| |
| procedure Init_SCC_Num_Pred (U : Unit_Id); |
| -- Initialize the SCC_Num_Pred fields, so that the root of each SCC |
| -- has a count of the number of successors of all the units in the |
| -- SCC, but only for successors outside the SCC. |
| |
| procedure Compute_SCCs is new Compute_Strongly_Connected_Components |
| (Node => Unit_Id, |
| First_Node => Units.First, |
| Last_Node => Units.Last, |
| Node_Array => Unit_Id_Array, |
| Successors => Successors, |
| Create_SCC => Create_SCC); |
| |
| ---------------- |
| -- Create_SCC -- |
| ---------------- |
| |
| procedure Create_SCC (Root : Unit_Id; Nodes : Unit_Id_Array) is |
| begin |
| if Debug_Flag_V then |
| Write_Str ("Root = "); |
| Write_Int (Int (Root)); |
| Write_Str (" "); |
| Write_Unit_Name (Units.Table (Root).Uname); |
| Write_Str (" -- "); |
| Write_Int (Nodes'Length); |
| Write_Line (" units:"); |
| |
| for J in Nodes'Range loop |
| Write_Str (" "); |
| Write_Int (Int (Nodes (J))); |
| Write_Str (" "); |
| Write_Unit_Name (Units.Table (Nodes (J)).Uname); |
| Write_Eol; |
| end loop; |
| end if; |
| |
| pragma Assert (Nodes (Nodes'First) = Root); |
| pragma Assert (UNR.Table (Root).Nodes = null); |
| UNR.Table (Root).Nodes := new Unit_Id_Array'(Nodes); |
| |
| for J in Nodes'Range loop |
| pragma Assert (SCC (Nodes (J)) = No_Unit_Id); |
| UNR.Table (Nodes (J)).SCC_Root := Root; |
| end loop; |
| end Create_SCC; |
| |
| ---------------- |
| -- Successors -- |
| ---------------- |
| |
| function Successors (U : Unit_Id) return Unit_Id_Array is |
| S : Successor_Id := UNR.Table (U).Successors; |
| Tab : Unit_Id_Table; |
| |
| begin |
| -- Pretend that a spec is a successor of its body (even though it |
| -- isn't), just so both get included. |
| |
| if Units.Table (U).Utype = Is_Body then |
| Append (Tab, Corresponding_Spec (U)); |
| end if; |
| |
| -- Now include the real successors |
| |
| while S /= No_Successor loop |
| pragma Assert (Succ.Table (S).Before = U); |
| Append (Tab, Succ.Table (S).After); |
| S := Succ.Table (S).Next; |
| end loop; |
| |
| declare |
| Result : constant Unit_Id_Array := Tab.Table (1 .. Last (Tab)); |
| |
| begin |
| Free (Tab); |
| return Result; |
| end; |
| end Successors; |
| |
| ----------------------- |
| -- Init_SCC_Num_Pred -- |
| ----------------------- |
| |
| procedure Init_SCC_Num_Pred (U : Unit_Id) is |
| begin |
| if UNR.Table (U).Visited then |
| return; |
| end if; |
| |
| UNR.Table (U).Visited := True; |
| |
| declare |
| S : Successor_Id := UNR.Table (U).Successors; |
| |
| begin |
| while S /= No_Successor loop |
| pragma Assert (Succ.Table (S).Before = U); |
| Init_SCC_Num_Pred (Succ.Table (S).After); |
| |
| if SCC (U) /= SCC (Succ.Table (S).After) then |
| UNR.Table (SCC (Succ.Table (S).After)).SCC_Num_Pred := |
| UNR.Table (SCC (Succ.Table (S).After)).SCC_Num_Pred + 1; |
| end if; |
| |
| S := Succ.Table (S).Next; |
| end loop; |
| end; |
| end Init_SCC_Num_Pred; |
| |
| -- Start of processing for Compute_Unit_SCCs |
| |
| begin |
| Compute_SCCs; |
| |
| for Uref in UNR.First .. UNR.Last loop |
| pragma Assert (not UNR.Table (Uref).Visited); |
| null; |
| end loop; |
| |
| for Uref in UNR.First .. UNR.Last loop |
| Init_SCC_Num_Pred (Uref); |
| end loop; |
| |
| -- Assert that SCC_Root of all units has been set to a valid unit, |
| -- and that SCC_Num_Pred has not been modified in non-root units. |
| |
| for Uref in UNR.First .. UNR.Last loop |
| pragma Assert (UNR.Table (Uref).SCC_Root /= No_Unit_Id); |
| pragma Assert (UNR.Table (Uref).SCC_Root in UNR.First .. UNR.Last); |
| |
| if SCC (Uref) /= Uref then |
| pragma Assert (UNR.Table (Uref).SCC_Num_Pred = 0); |
| null; |
| end if; |
| end loop; |
| end Compute_Unit_SCCs; |
| |
| -------------------------- |
| -- Find_Elab_All_Errors -- |
| -------------------------- |
| |
| procedure Find_Elab_All_Errors is |
| Withed_Unit : Unit_Id; |
| |
| begin |
| for U in Units.First .. Units.Last loop |
| |
| -- If this unit is not an interface to a stand-alone library, |
| -- process WITH references for this unit ignoring interfaces to |
| -- stand-alone libraries. |
| |
| if not Units.Table (U).SAL_Interface then |
| for W in Units.Table (U).First_With .. |
| Units.Table (U).Last_With |
| loop |
| if Withs.Table (W).Sfile /= No_File |
| and then (not Withs.Table (W).SAL_Interface) |
| then |
| -- Check for special case of withing a unit that does not |
| -- exist any more. |
| |
| if Get_Name_Table_Int (Withs.Table (W).Uname) = 0 then |
| goto Next_With; |
| end if; |
| |
| Withed_Unit := Unit_Id_Of (Withs.Table (W).Uname); |
| |
| -- If it's Elaborate_All or Elab_All_Desirable, check |
| -- that the withER and withEE are not in the same SCC. |
| |
| if Withs.Table (W).Elaborate_All |
| or else Withs.Table (W).Elab_All_Desirable |
| then |
| if SCC (U) = SCC (Withed_Unit) then |
| Elab_Cycle_Found := True; |
| |
| -- We could probably give better error messages |
| -- than Elab_Old here, but for now, to avoid |
| -- disruption, we don't give any error here. |
| -- Instead, we set the Elab_Cycle_Found flag above, |
| -- and then run the Elab_Old algorithm to issue the |
| -- error message. Ideally, we would like to print |
| -- multiple errors rather than stopping after the |
| -- first cycle. |
| |
| if False then |
| Error_Msg_Output |
| ("illegal pragma Elaborate_All", |
| Info => False); |
| end if; |
| end if; |
| end if; |
| end if; |
| |
| <<Next_With>> |
| null; |
| end loop; |
| end if; |
| end loop; |
| end Find_Elab_All_Errors; |
| |
| --------------------- |
| -- Find_Elab_Order -- |
| --------------------- |
| |
| procedure Find_Elab_Order (Elab_Order : out Unit_Id_Table) is |
| Best_So_Far : Unit_Id; |
| U : Unit_Id; |
| |
| begin |
| -- Gather dependencies and output them if option set |
| |
| Gather_Dependencies; |
| |
| Compute_Unit_SCCs; |
| |
| -- Initialize the no-predecessor list |
| |
| No_Pred := No_Unit_Id; |
| for U in UNR.First .. UNR.Last loop |
| if UNR.Table (U).Num_Pred = 0 then |
| UNR.Table (U).Nextnp := No_Pred; |
| No_Pred := U; |
| end if; |
| end loop; |
| |
| -- OK, now we determine the elaboration order proper. All we do is to |
| -- select the best choice from the no-predecessor list until all the |
| -- nodes have been chosen. |
| |
| Outer : loop |
| if Debug_Flag_N then |
| Write_Line ("Outer loop"); |
| end if; |
| |
| -- If there are no nodes with predecessors, then either we are |
| -- done, as indicated by Num_Left being set to zero, or we have a |
| -- circularity. In the latter case, diagnose the circularity, |
| -- removing it from the graph and |
| -- continue. Diagnose_Elaboration_Problem always raises an |
| -- exception, so the loop never goes around more than once. |
| |
| Get_No_Pred : while No_Pred = No_Unit_Id loop |
| exit Outer when Num_Left < 1; |
| Diagnose_Elaboration_Problem (Elab_Order); |
| end loop Get_No_Pred; |
| |
| U := No_Pred; |
| Best_So_Far := No_Unit_Id; |
| |
| -- Loop to choose best entry in No_Pred list |
| |
| No_Pred_Search : loop |
| if Debug_Flag_N then |
| Write_Str (" considering choice of "); |
| Write_Unit_Name (Units.Table (U).Uname); |
| Write_Eol; |
| |
| if Units.Table (U).Elaborate_Body then |
| Write_Str |
| (" Elaborate_Body = True, Num_Pred for body = "); |
| Write_Int |
| (UNR.Table (Corresponding_Body (U)).Num_Pred); |
| else |
| Write_Str |
| (" Elaborate_Body = False"); |
| end if; |
| |
| Write_Eol; |
| end if; |
| |
| -- Don't even consider units whose SCC is not ready. This |
| -- ensures that all units of an SCC will be elaborated |
| -- together, with no other units in between. |
| |
| if SCC_Num_Pred (U) = 0 |
| and then Better_Choice (U, Best_So_Far) |
| then |
| if Debug_Flag_N then |
| Write_Line (" tentatively chosen (best so far)"); |
| end if; |
| |
| Best_So_Far := U; |
| else |
| if Debug_Flag_N then |
| Write_Line (" SCC not ready"); |
| end if; |
| end if; |
| |
| U := UNR.Table (U).Nextnp; |
| exit No_Pred_Search when U = No_Unit_Id; |
| end loop No_Pred_Search; |
| |
| -- If there are no units on the No_Pred list whose SCC is ready, |
| -- there must be a cycle. Defer to Elab_Old to print an error |
| -- message. |
| |
| if Best_So_Far = No_Unit_Id then |
| Elab_Cycle_Found := True; |
| return; |
| end if; |
| |
| -- Choose the best candidate found |
| |
| Choose (Elab_Order, Best_So_Far, " [Best_So_Far]"); |
| |
| -- If it's a spec with a body, and the body is not yet chosen, |
| -- choose the body if possible. The case where the body is |
| -- already chosen is Elaborate_Body; the above call to Choose |
| -- the spec will also Choose the body. |
| |
| if Units.Table (Best_So_Far).Utype = Is_Spec |
| and then UNR.Table |
| (Corresponding_Body (Best_So_Far)).Elab_Position = 0 |
| then |
| declare |
| Choose_The_Body : constant Boolean := |
| UNR.Table (Corresponding_Body |
| (Best_So_Far)).Num_Pred = 0; |
| |
| begin |
| if Debug_Flag_B then |
| Write_Str ("Can we choose the body?... "); |
| |
| if Choose_The_Body then |
| Write_Line ("Yes!"); |
| else |
| Write_Line ("No."); |
| end if; |
| end if; |
| |
| if Choose_The_Body then |
| Choose |
| (Elab_Order => Elab_Order, |
| Chosen => Corresponding_Body (Best_So_Far), |
| Msg => " [body]"); |
| end if; |
| end; |
| end if; |
| |
| -- Finally, choose all the rest of the units in the same SCC as |
| -- Best_So_Far. If it hasn't been chosen (Elab_Position = 0), and |
| -- it's ready to be chosen (Num_Pred = 0), then we can choose it. |
| |
| loop |
| declare |
| Chose_One_Or_More : Boolean := False; |
| SCC : Unit_Id_Array renames Nodes (Best_So_Far).all; |
| |
| begin |
| for J in SCC'Range loop |
| if UNR.Table (SCC (J)).Elab_Position = 0 |
| and then UNR.Table (SCC (J)).Num_Pred = 0 |
| then |
| Chose_One_Or_More := True; |
| Choose (Elab_Order, SCC (J), " [same SCC]"); |
| end if; |
| end loop; |
| |
| exit when not Chose_One_Or_More; |
| end; |
| end loop; |
| end loop Outer; |
| |
| Find_Elab_All_Errors; |
| end Find_Elab_Order; |
| |
| ----------- |
| -- Nodes -- |
| ----------- |
| |
| function Nodes (U : Unit_Id) return Unit_Id_Array_Ptr is |
| begin |
| return UNR.Table (SCC (U)).Nodes; |
| end Nodes; |
| |
| --------- |
| -- SCC -- |
| --------- |
| |
| function SCC (U : Unit_Id) return Unit_Id is |
| begin |
| return UNR.Table (U).SCC_Root; |
| end SCC; |
| |
| ------------------ |
| -- SCC_Num_Pred -- |
| ------------------ |
| |
| function SCC_Num_Pred (U : Unit_Id) return Int is |
| begin |
| return UNR.Table (SCC (U)).SCC_Num_Pred; |
| end SCC_Num_Pred; |
| |
| --------------- |
| -- Write_SCC -- |
| --------------- |
| |
| procedure Write_SCC (U : Unit_Id) is |
| pragma Assert (SCC (U) = U); |
| begin |
| for J in Nodes (U)'Range loop |
| Write_Int (UNR.Table (Nodes (U) (J)).Elab_Position); |
| Write_Str (". "); |
| Write_Unit_Name (Units.Table (Nodes (U) (J)).Uname); |
| Write_Eol; |
| end loop; |
| |
| Write_Eol; |
| end Write_SCC; |
| |
| end Elab_New; |
| |
| -------------- |
| -- Elab_Old -- |
| -------------- |
| |
| package body Elab_Old is |
| |
| --------------------- |
| -- Find_Elab_Order -- |
| --------------------- |
| |
| procedure Find_Elab_Order (Elab_Order : out Unit_Id_Table) is |
| Best_So_Far : Unit_Id; |
| U : Unit_Id; |
| |
| begin |
| -- Gather dependencies and output them if option set |
| |
| Gather_Dependencies; |
| |
| -- Initialize the no-predecessor list |
| |
| No_Pred := No_Unit_Id; |
| for U in UNR.First .. UNR.Last loop |
| if UNR.Table (U).Num_Pred = 0 then |
| UNR.Table (U).Nextnp := No_Pred; |
| No_Pred := U; |
| end if; |
| end loop; |
| |
| -- OK, now we determine the elaboration order proper. All we do is to |
| -- select the best choice from the no-predecessor list until all the |
| -- nodes have been chosen. |
| |
| Outer : loop |
| |
| -- If there are no nodes with predecessors, then either we are |
| -- done, as indicated by Num_Left being set to zero, or we have a |
| -- circularity. In the latter case, diagnose the circularity, |
| -- removing it from the graph and continue. |
| -- Diagnose_Elaboration_Problem always raises an exception, so the |
| -- loop never goes around more than once. |
| |
| Get_No_Pred : while No_Pred = No_Unit_Id loop |
| exit Outer when Num_Left < 1; |
| Diagnose_Elaboration_Problem (Elab_Order); |
| end loop Get_No_Pred; |
| |
| U := No_Pred; |
| Best_So_Far := No_Unit_Id; |
| |
| -- Loop to choose best entry in No_Pred list |
| |
| No_Pred_Search : loop |
| if Debug_Flag_N then |
| Write_Str (" considering choice of "); |
| Write_Unit_Name (Units.Table (U).Uname); |
| Write_Eol; |
| |
| if Units.Table (U).Elaborate_Body then |
| Write_Str |
| (" Elaborate_Body = True, Num_Pred for body = "); |
| Write_Int |
| (UNR.Table (Corresponding_Body (U)).Num_Pred); |
| else |
| Write_Str |
| (" Elaborate_Body = False"); |
| end if; |
| |
| Write_Eol; |
| end if; |
| |
| -- This is a candididate to be considered for choice |
| |
| if Better_Choice (U, Best_So_Far) then |
| if Debug_Flag_N then |
| Write_Line (" tentatively chosen (best so far)"); |
| end if; |
| |
| Best_So_Far := U; |
| end if; |
| |
| U := UNR.Table (U).Nextnp; |
| exit No_Pred_Search when U = No_Unit_Id; |
| end loop No_Pred_Search; |
| |
| -- Choose the best candidate found |
| |
| Choose (Elab_Order, Best_So_Far, " [Elab_Old Best_So_Far]"); |
| end loop Outer; |
| end Find_Elab_Order; |
| |
| end Elab_Old; |
| |
| end Binde; |