| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- L I B . L O A D -- |
| -- -- |
| -- 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 Atree; use Atree; |
| with Debug; use Debug; |
| with Einfo; use Einfo; |
| with Einfo.Entities; use Einfo.Entities; |
| with Errout; use Errout; |
| with Fname; use Fname; |
| with Fname.UF; use Fname.UF; |
| with Nlists; use Nlists; |
| with Nmake; use Nmake; |
| with Opt; use Opt; |
| with Osint; use Osint; |
| with Osint.C; use Osint.C; |
| with Output; use Output; |
| with Par; |
| with Restrict; use Restrict; |
| with Scn; use Scn; |
| with Sinfo; use Sinfo; |
| with Sinfo.Nodes; use Sinfo.Nodes; |
| with Sinfo.Utils; use Sinfo.Utils; |
| with Sinput; use Sinput; |
| with Sinput.L; use Sinput.L; |
| with Stand; use Stand; |
| with Tbuild; use Tbuild; |
| with Uname; use Uname; |
| |
| package body Lib.Load is |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| function From_Limited_With_Chain return Boolean; |
| -- Check whether a possible circular dependence includes units that |
| -- have been loaded through limited_with clauses, in which case there |
| -- is no real circularity. |
| |
| function Spec_Is_Irrelevant |
| (Spec_Unit : Unit_Number_Type; |
| Body_Unit : Unit_Number_Type) return Boolean; |
| -- The Spec_Unit and Body_Unit parameters are the unit numbers of the |
| -- spec file that corresponds to the main unit which is a body. This |
| -- function determines if the spec file is irrelevant and will be |
| -- overridden by the body as described in RM 10.1.4(4). See description |
| -- in "Special Handling of Subprogram Bodies" for further details. |
| |
| procedure Write_Dependency_Chain; |
| -- This procedure is used to generate error message info lines that |
| -- trace the current dependency chain when a load error occurs. |
| |
| ------------------------------ |
| -- Change_Main_Unit_To_Spec -- |
| ------------------------------ |
| |
| procedure Change_Main_Unit_To_Spec is |
| U : Unit_Record renames Units.Table (Main_Unit); |
| N : File_Name_Type; |
| X : Source_File_Index; |
| |
| begin |
| -- Get name of unit body |
| |
| Get_Name_String (U.Unit_File_Name); |
| |
| -- Note: for the following we should really generalize and consult the |
| -- file name pattern data, but for now we just deal with the common |
| -- naming cases, which is good enough in practice. |
| |
| -- Change .adb to .ads |
| |
| if Name_Len >= 5 |
| and then Name_Buffer (Name_Len - 3 .. Name_Len) = ".adb" |
| then |
| Name_Buffer (Name_Len) := 's'; |
| |
| -- Change .2.ada to .1.ada (Rational convention) |
| |
| elsif Name_Len >= 7 |
| and then Name_Buffer (Name_Len - 5 .. Name_Len) = ".2.ada" |
| then |
| Name_Buffer (Name_Len - 4) := '1'; |
| |
| -- Change .ada to _.ada (DEC convention) |
| |
| elsif Name_Len >= 5 |
| and then Name_Buffer (Name_Len - 3 .. Name_Len) = ".ada" |
| then |
| Name_Buffer (Name_Len - 3 .. Name_Len + 1) := "_.ada"; |
| Name_Len := Name_Len + 1; |
| |
| -- No match, don't make the change |
| |
| else |
| return; |
| end if; |
| |
| -- Try loading the spec |
| |
| N := Name_Find; |
| X := Load_Source_File (N); |
| |
| -- No change if we did not find the spec |
| |
| if X <= No_Source_File then |
| return; |
| end if; |
| |
| -- Otherwise modify Main_Unit entry to point to spec |
| |
| U.Unit_File_Name := N; |
| U.Source_Index := X; |
| end Change_Main_Unit_To_Spec; |
| |
| ------------------------------- |
| -- Create_Dummy_Package_Unit -- |
| ------------------------------- |
| |
| function Create_Dummy_Package_Unit |
| (With_Node : Node_Id; |
| Spec_Name : Unit_Name_Type) return Unit_Number_Type |
| is |
| Unum : Unit_Number_Type; |
| Cunit_Entity : Entity_Id; |
| Cunit : Node_Id; |
| Du_Name : Node_Or_Entity_Id; |
| End_Lab : Node_Id; |
| Fname : constant File_Name_Type := |
| Get_File_Name (Spec_Name, Subunit => False); |
| Pre_Name : constant Boolean := |
| Is_Predefined_File_Name (Fname, Renamings_Included => False); |
| Ren_Name : constant Boolean := |
| Is_Predefined_Renaming_File_Name (Fname); |
| GNAT_Name : constant Boolean := |
| Is_GNAT_File_Name (Fname); |
| Save_CS : constant Boolean := Get_Comes_From_Source_Default; |
| |
| begin |
| -- The created dummy package unit does not come from source |
| |
| Set_Comes_From_Source_Default (False); |
| |
| -- Normal package |
| |
| if Nkind (Name (With_Node)) = N_Identifier then |
| Cunit_Entity := |
| Make_Defining_Identifier (No_Location, |
| Chars => Chars (Name (With_Node))); |
| Du_Name := Cunit_Entity; |
| End_Lab := New_Occurrence_Of (Cunit_Entity, No_Location); |
| |
| -- Child package |
| |
| else |
| Cunit_Entity := |
| Make_Defining_Identifier (No_Location, |
| Chars => Chars (Selector_Name (Name (With_Node)))); |
| Du_Name := |
| Make_Defining_Program_Unit_Name (No_Location, |
| Name => Copy_Separate_Tree (Prefix (Name (With_Node))), |
| Defining_Identifier => Cunit_Entity); |
| |
| Set_Is_Child_Unit (Cunit_Entity); |
| |
| End_Lab := |
| Make_Designator (No_Location, |
| Name => Copy_Separate_Tree (Prefix (Name (With_Node))), |
| Identifier => New_Occurrence_Of (Cunit_Entity, No_Location)); |
| end if; |
| |
| Set_Scope (Cunit_Entity, Standard_Standard); |
| |
| Cunit := |
| Make_Compilation_Unit (No_Location, |
| Context_Items => Empty_List, |
| Unit => |
| Make_Package_Declaration (No_Location, |
| Specification => |
| Make_Package_Specification (No_Location, |
| Defining_Unit_Name => Du_Name, |
| Visible_Declarations => Empty_List, |
| End_Label => End_Lab)), |
| Aux_Decls_Node => |
| Make_Compilation_Unit_Aux (No_Location)); |
| |
| -- Mark the dummy package as analyzed to prevent analysis of this |
| -- (non-existent) unit in -gnatQ mode because at the moment the |
| -- structure and attributes of this dummy package does not allow |
| -- a normal analysis of this unit |
| |
| Set_Analyzed (Cunit); |
| |
| Units.Increment_Last; |
| Unum := Units.Last; |
| |
| Units.Table (Unum) := |
| (Cunit => Cunit, |
| Cunit_Entity => Cunit_Entity, |
| Dependency_Num => 0, |
| Dynamic_Elab => False, |
| Error_Location => Sloc (With_Node), |
| Expected_Unit => Spec_Name, |
| Fatal_Error => Error_Detected, |
| Generate_Code => False, |
| Has_RACW => False, |
| Filler => False, |
| Ident_String => Empty, |
| |
| Is_Predefined_Renaming => Ren_Name, |
| Is_Predefined_Unit => Pre_Name or Ren_Name, |
| Is_Internal_Unit => Pre_Name or Ren_Name or GNAT_Name, |
| Filler2 => False, |
| |
| Loading => False, |
| Main_Priority => Default_Main_Priority, |
| Main_CPU => Default_Main_CPU, |
| Primary_Stack_Count => 0, |
| Sec_Stack_Count => 0, |
| Munit_Index => 0, |
| No_Elab_Code_All => False, |
| Serial_Number => 0, |
| Source_Index => No_Source_File, |
| Unit_File_Name => Fname, |
| Unit_Name => Spec_Name, |
| Version => 0, |
| OA_Setting => 'O'); |
| |
| Init_Unit_Name (Unum, Spec_Name); |
| |
| Set_Comes_From_Source_Default (Save_CS); |
| Set_Error_Posted (Cunit_Entity); |
| Set_Error_Posted (Cunit); |
| return Unum; |
| end Create_Dummy_Package_Unit; |
| |
| ----------------------------- |
| -- From_Limited_With_Chain -- |
| ----------------------------- |
| |
| function From_Limited_With_Chain return Boolean is |
| Curr_Num : constant Unit_Number_Type := |
| Load_Stack.Table (Load_Stack.Last).Unit_Number; |
| |
| begin |
| -- True if the current load operation is through a limited_with clause |
| -- and we are not within a loop of regular with_clauses. |
| |
| for U in reverse Load_Stack.First .. Load_Stack.Last - 1 loop |
| if Load_Stack.Table (U).Unit_Number = Curr_Num then |
| return False; |
| |
| elsif Present (Load_Stack.Table (U).With_Node) |
| and then Limited_Present (Load_Stack.Table (U).With_Node) |
| then |
| return True; |
| end if; |
| end loop; |
| |
| return False; |
| end From_Limited_With_Chain; |
| |
| ---------------- |
| -- Initialize -- |
| ---------------- |
| |
| procedure Initialize is |
| begin |
| Units.Init; |
| Load_Stack.Init; |
| end Initialize; |
| |
| ------------------------ |
| -- Initialize_Version -- |
| ------------------------ |
| |
| procedure Initialize_Version (U : Unit_Number_Type) is |
| begin |
| Units.Table (U).Version := Source_Checksum (Source_Index (U)); |
| end Initialize_Version; |
| |
| ---------------------- |
| -- Load_Main_Source -- |
| ---------------------- |
| |
| procedure Load_Main_Source is |
| Fname : constant File_Name_Type := Next_Main_Source; |
| Pre_Name : constant Boolean := |
| Is_Predefined_File_Name (Fname, Renamings_Included => False); |
| Ren_Name : constant Boolean := |
| Is_Predefined_Renaming_File_Name (Fname); |
| GNAT_Name : constant Boolean := |
| Is_GNAT_File_Name (Fname); |
| Version : Word := 0; |
| |
| begin |
| Load_Stack.Increment_Last; |
| Load_Stack.Table (Load_Stack.Last) := (Main_Unit, Empty); |
| |
| -- Initialize unit table entry for Main_Unit. Note that we don't know |
| -- the unit name yet, that gets filled in when the parser parses the |
| -- main unit, at which time a check is made that it matches the main |
| -- file name, and then the Unit_Name field is set. The Cunit and |
| -- Cunit_Entity fields also get filled in later by the parser. |
| |
| Units.Increment_Last; |
| |
| Units.Table (Main_Unit).Unit_File_Name := Fname; |
| |
| if Fname /= No_File then |
| Main_Source_File := Load_Source_File (Fname); |
| Current_Error_Source_File := Main_Source_File; |
| |
| if Main_Source_File > No_Source_File then |
| Version := Source_Checksum (Main_Source_File); |
| |
| else |
| -- To avoid emitting a source location (since there is no file), |
| -- we write a custom error message instead of using the machinery |
| -- in errout.adb. |
| |
| Set_Standard_Error; |
| |
| if Main_Source_File = No_Access_To_Source_File then |
| Write_Str |
| ("no read access for file """ & Get_Name_String (Fname) |
| & """"); |
| else |
| Write_Str |
| ("file """ & Get_Name_String (Fname) & """ not found"); |
| end if; |
| |
| Write_Eol; |
| Set_Standard_Output; |
| end if; |
| |
| Units.Table (Main_Unit) := |
| (Cunit => Empty, |
| Cunit_Entity => Empty, |
| Dependency_Num => 0, |
| Dynamic_Elab => False, |
| Error_Location => No_Location, |
| Expected_Unit => No_Unit_Name, |
| Fatal_Error => None, |
| Generate_Code => True, |
| Has_RACW => False, |
| Filler => False, |
| Ident_String => Empty, |
| |
| Is_Predefined_Renaming => Ren_Name, |
| Is_Predefined_Unit => Pre_Name or Ren_Name, |
| Is_Internal_Unit => Pre_Name or Ren_Name or GNAT_Name, |
| Filler2 => False, |
| |
| Loading => True, |
| Main_Priority => Default_Main_Priority, |
| Main_CPU => Default_Main_CPU, |
| Primary_Stack_Count => 0, |
| Sec_Stack_Count => 0, |
| |
| Munit_Index => 0, |
| No_Elab_Code_All => False, |
| Serial_Number => 0, |
| Source_Index => Main_Source_File, |
| Unit_File_Name => Fname, |
| Unit_Name => No_Unit_Name, |
| Version => Version, |
| OA_Setting => 'O'); |
| end if; |
| end Load_Main_Source; |
| |
| --------------- |
| -- Load_Unit -- |
| --------------- |
| |
| function Load_Unit |
| (Load_Name : Unit_Name_Type; |
| Required : Boolean; |
| Error_Node : Node_Id; |
| Subunit : Boolean; |
| Corr_Body : Unit_Number_Type := No_Unit; |
| Renamings : Boolean := False; |
| With_Node : Node_Id := Empty; |
| PMES : Boolean := False) return Unit_Number_Type |
| is |
| Calling_Unit : Unit_Number_Type; |
| Uname_Actual : Unit_Name_Type; |
| Unum : Unit_Number_Type; |
| Unump : Unit_Number_Type; |
| Fname : File_Name_Type; |
| Pre_Name : Boolean; |
| Ren_Name : Boolean; |
| GNAT_Name : Boolean; |
| Src_Ind : Source_File_Index; |
| Save_PMES : constant Boolean := Parsing_Main_Extended_Source; |
| |
| Save_Cunit_Restrictions : constant Save_Cunit_Boolean_Restrictions := |
| Cunit_Boolean_Restrictions_Save; |
| -- Save current restrictions for restore at end |
| |
| begin |
| Parsing_Main_Extended_Source := PMES; |
| |
| -- Initialize restrictions to config restrictions for unit to load if |
| -- it is part of the main extended source, otherwise reset them. |
| |
| -- Note: it's a bit odd but PMES is False for subunits, which is why |
| -- we have the OR here. |
| |
| if PMES or Subunit then |
| Restore_Config_Cunit_Boolean_Restrictions; |
| else |
| Reset_Cunit_Boolean_Restrictions; |
| end if; |
| |
| -- If renamings are allowed and we have a child unit name, then we |
| -- must first load the parent to deal with finding the real name. |
| -- Retain the with_clause that names the child, so that if it is |
| -- limited, the parent is loaded under the same condition. |
| |
| if Renamings and then Is_Child_Name (Load_Name) then |
| Unump := |
| Load_Unit |
| (Load_Name => Get_Parent_Spec_Name (Load_Name), |
| Required => Required, |
| Subunit => False, |
| Renamings => True, |
| Error_Node => Error_Node, |
| With_Node => With_Node); |
| |
| if Unump = No_Unit then |
| Unum := No_Unit; |
| goto Done; |
| end if; |
| |
| -- If parent is a renaming, then we use the renamed package as |
| -- the actual parent for the subsequent load operation. |
| |
| if Nkind (Unit (Cunit (Unump))) = N_Package_Renaming_Declaration then |
| Uname_Actual := |
| New_Child |
| (Load_Name, Get_Unit_Name (Name (Unit (Cunit (Unump))))); |
| |
| -- If the load is for a with_clause, for visibility purposes both |
| -- the renamed entity and renaming one must be available in the |
| -- current unit: the renamed one in order to retrieve the child |
| -- unit, and the original one because it may be used as a prefix |
| -- in the body of the current unit. We add an explicit with_clause |
| -- for the original parent so that the renaming declaration is |
| -- properly loaded and analyzed. |
| |
| if Present (With_Node) then |
| Insert_After (With_Node, |
| Make_With_Clause (Sloc (With_Node), |
| Name => Copy_Separate_Tree (Prefix (Name (With_Node))))); |
| end if; |
| |
| -- Save the renaming entity, to establish its visibility when |
| -- installing the context. The implicit with is on this entity, |
| -- not on the package it renames. This is somewhat redundant given |
| -- the with_clause just created, but it simplifies subsequent |
| -- expansion of the current with_clause. |
| |
| if Nkind (Error_Node) = N_With_Clause |
| and then Nkind (Name (Error_Node)) = N_Selected_Component |
| then |
| declare |
| Par : Node_Id := Name (Error_Node); |
| |
| begin |
| while Nkind (Par) = N_Selected_Component |
| and then Chars (Selector_Name (Par)) /= |
| Chars (Cunit_Entity (Unump)) |
| loop |
| Par := Prefix (Par); |
| end loop; |
| |
| -- Case of some intermediate parent is a renaming |
| |
| if Nkind (Par) = N_Selected_Component then |
| Set_Entity (Selector_Name (Par), Cunit_Entity (Unump)); |
| |
| -- Case where the ultimate parent is a renaming |
| |
| else |
| Set_Entity (Par, Cunit_Entity (Unump)); |
| end if; |
| end; |
| end if; |
| |
| -- If the parent is not a renaming, then get its name (this may |
| -- be different from the parent spec name obtained above because |
| -- of renamings higher up in the hierarchy). |
| |
| else |
| Uname_Actual := New_Child (Load_Name, Unit_Name (Unump)); |
| end if; |
| |
| -- Here if unit to be loaded is not a child unit |
| |
| else |
| Uname_Actual := Load_Name; |
| end if; |
| |
| Fname := Get_File_Name (Uname_Actual, Subunit); |
| Pre_Name := |
| Is_Predefined_File_Name (Fname, Renamings_Included => False); |
| Ren_Name := Is_Predefined_Renaming_File_Name (Fname); |
| GNAT_Name := Is_GNAT_File_Name (Fname); |
| |
| if Debug_Flag_L then |
| Write_Eol; |
| Write_Str ("*** Load request for unit: "); |
| Write_Unit_Name (Load_Name); |
| |
| if Required then |
| Write_Str (" (Required = True)"); |
| else |
| Write_Str (" (Required = False)"); |
| end if; |
| |
| Write_Eol; |
| |
| if Uname_Actual /= Load_Name then |
| Write_Str ("*** Actual unit loaded: "); |
| Write_Unit_Name (Uname_Actual); |
| end if; |
| end if; |
| |
| -- Capture error location if it is for the main unit. The idea is to |
| -- post errors on the main unit location, not the most recent unit. |
| -- Note: Unit_Name (Main_Unit) is not set if we are parsing gnat.adc. |
| |
| if Present (Error_Node) |
| and then Present (Unit_Name (Main_Unit)) |
| then |
| -- It seems like In_Extended_Main_Source_Unit (Error_Node) would |
| -- do the trick here, but that's wrong, it is much too early to |
| -- call this routine. We are still in the parser, and the required |
| -- semantic information is not established yet. So we base the |
| -- judgment on unit names. |
| |
| Get_External_Unit_Name_String (Unit_Name (Main_Unit)); |
| |
| declare |
| Main_Unit_Name : constant String := Name_Buffer (1 .. Name_Len); |
| |
| begin |
| Get_External_Unit_Name_String |
| (Unit_Name (Get_Source_Unit (Error_Node))); |
| |
| -- If the two names are identical, then for sure we are part |
| -- of the extended main unit |
| |
| if Main_Unit_Name = Name_Buffer (1 .. Name_Len) then |
| Load_Msg_Sloc := Sloc (Error_Node); |
| |
| -- If the load is called from a with_type clause, the error |
| -- node is correct. |
| |
| -- Otherwise, check for the subunit case, and if so, consider |
| -- we have a match if one name is a prefix of the other name. |
| |
| else |
| if Nkind (Unit (Cunit (Main_Unit))) = N_Subunit |
| or else |
| Nkind (Unit (Cunit (Get_Source_Unit (Error_Node)))) = |
| N_Subunit |
| then |
| Name_Len := Integer'Min (Name_Len, Main_Unit_Name'Length); |
| |
| if Name_Buffer (1 .. Name_Len) |
| = |
| Main_Unit_Name (1 .. Name_Len) |
| then |
| Load_Msg_Sloc := Sloc (Error_Node); |
| end if; |
| end if; |
| end if; |
| end; |
| end if; |
| |
| -- If we are generating error messages, then capture calling unit |
| |
| if Present (Error_Node) then |
| Calling_Unit := Get_Source_Unit (Error_Node); |
| else |
| Calling_Unit := No_Unit; |
| end if; |
| |
| -- See if we already have an entry for this unit |
| |
| Unum := Unit_Names.Get (Uname_Actual); |
| if Unum = No_Unit then |
| Unum := Units.Last + 1; |
| end if; |
| |
| -- Whether or not the entry was found, Unum is now the right value, |
| -- since it is one more than Units.Last (i.e. the index of the new |
| -- entry we will create) in the not found case. |
| |
| -- A special check is necessary in the unit not found case. If the unit |
| -- is not found, but the file in which it lives has already been loaded, |
| -- then we have the problem that the file does not contain the unit that |
| -- is needed. We simply treat this as a file not found condition. |
| |
| -- We skip this test in multiple unit per file mode since in this |
| -- case we can have multiple units from the same source file. |
| |
| if Unum > Units.Last and then Get_Unit_Index (Uname_Actual) = 0 then |
| for J in Units.First .. Units.Last loop |
| if Fname = Units.Table (J).Unit_File_Name then |
| if Debug_Flag_L then |
| Write_Str (" file does not contain unit, Unit_Number = "); |
| Write_Int (Int (Unum)); |
| Write_Eol; |
| Write_Eol; |
| end if; |
| |
| if Present (Error_Node) then |
| Get_Name_String (Fname); |
| |
| if Is_Predefined_File_Name (Fname) then |
| Error_Msg_Unit_1 := Uname_Actual; |
| Error_Msg |
| ("$$ is not a language defined unit", Load_Msg_Sloc); |
| else |
| Error_Msg_File_1 := Fname; |
| Error_Msg_Unit_1 := Uname_Actual; |
| Error_Msg ("file{ does not contain unit$", Load_Msg_Sloc); |
| end if; |
| |
| Write_Dependency_Chain; |
| Unum := No_Unit; |
| goto Done; |
| |
| else |
| Unum := No_Unit; |
| goto Done; |
| end if; |
| end if; |
| end loop; |
| end if; |
| |
| -- If we are proceeding with load, then make load stack entry, |
| -- and indicate the kind of with_clause responsible for the load. |
| |
| Load_Stack.Increment_Last; |
| Load_Stack.Table (Load_Stack.Last) := (Unum, With_Node); |
| |
| -- Case of entry already in table |
| |
| if Unum <= Units.Last then |
| |
| -- Here is where we check for a circular dependency, which is |
| -- an attempt to load a unit which is currently in the process |
| -- of being loaded. We do *not* care about a circular chain that |
| -- leads back to a body, because this kind of circular dependence |
| -- legitimately occurs (e.g. two package bodies that contain |
| -- inlined subprogram referenced by the other). |
| |
| -- Ada 2005 (AI-50217): We also ignore limited_with clauses, because |
| -- their purpose is precisely to create legal circular structures. |
| |
| if Loading (Unum) |
| and then (Is_Spec_Name (Units.Table (Unum).Unit_Name) |
| or else Acts_As_Spec (Units.Table (Unum).Cunit)) |
| and then (Nkind (Error_Node) /= N_With_Clause |
| or else not Limited_Present (Error_Node)) |
| and then not From_Limited_With_Chain |
| then |
| if Debug_Flag_L then |
| Write_Str (" circular dependency encountered"); |
| Write_Eol; |
| end if; |
| |
| if Present (Error_Node) then |
| Error_Msg ("circular unit dependency", Load_Msg_Sloc); |
| Write_Dependency_Chain; |
| else |
| Load_Stack.Decrement_Last; |
| end if; |
| |
| Unum := No_Unit; |
| goto Done; |
| end if; |
| |
| if Debug_Flag_L then |
| Write_Str (" unit already in file table, Unit_Number = "); |
| Write_Int (Int (Unum)); |
| Write_Eol; |
| end if; |
| |
| Load_Stack.Decrement_Last; |
| goto Done; |
| |
| -- Unit is not already in table, so try to open the file |
| |
| else |
| if Debug_Flag_L then |
| Write_Str (" attempt unit load, Unit_Number = "); |
| Write_Int (Int (Unum)); |
| Write_Eol; |
| end if; |
| |
| Src_Ind := Load_Source_File (Fname); |
| |
| -- Make a partial entry in the file table, used even in the file not |
| -- found case to print the dependency chain including the last entry |
| |
| Units.Increment_Last; |
| Init_Unit_Name (Unum, Uname_Actual); |
| |
| -- File was found |
| |
| if Src_Ind > No_Source_File then |
| Units.Table (Unum) := |
| (Cunit => Empty, |
| Cunit_Entity => Empty, |
| Dependency_Num => 0, |
| Dynamic_Elab => False, |
| Error_Location => Sloc (Error_Node), |
| Expected_Unit => Uname_Actual, |
| Fatal_Error => None, |
| Generate_Code => False, |
| Has_RACW => False, |
| Filler => False, |
| Ident_String => Empty, |
| |
| Is_Predefined_Renaming => Ren_Name, |
| Is_Predefined_Unit => Pre_Name or Ren_Name, |
| Is_Internal_Unit => Pre_Name or Ren_Name or GNAT_Name, |
| Filler2 => False, |
| |
| Loading => True, |
| Main_Priority => Default_Main_Priority, |
| Main_CPU => Default_Main_CPU, |
| Primary_Stack_Count => 0, |
| Sec_Stack_Count => 0, |
| Munit_Index => 0, |
| No_Elab_Code_All => False, |
| Serial_Number => 0, |
| Source_Index => Src_Ind, |
| Unit_File_Name => Fname, |
| Unit_Name => Uname_Actual, |
| Version => Source_Checksum (Src_Ind), |
| OA_Setting => 'O'); |
| |
| -- Parse the new unit |
| |
| declare |
| Save_Index : constant Nat := Multiple_Unit_Index; |
| Save_PMES : constant Boolean := Parsing_Main_Extended_Source; |
| |
| begin |
| Multiple_Unit_Index := Get_Unit_Index (Uname_Actual); |
| Units.Table (Unum).Munit_Index := Multiple_Unit_Index; |
| Initialize_Scanner (Unum, Source_Index (Unum)); |
| |
| if Calling_Unit = Main_Unit and then Subunit then |
| Parsing_Main_Extended_Source := True; |
| end if; |
| |
| Discard_List (Par (Configuration_Pragmas => False)); |
| |
| Parsing_Main_Extended_Source := Save_PMES; |
| |
| Multiple_Unit_Index := Save_Index; |
| Set_Loading (Unum, False); |
| end; |
| |
| -- If spec is irrelevant, then post errors and quit |
| |
| if Corr_Body /= No_Unit |
| and then Spec_Is_Irrelevant (Unum, Corr_Body) |
| then |
| Error_Msg_File_1 := Unit_File_Name (Corr_Body); |
| Error_Msg |
| ("cannot compile subprogram in file {!", Load_Msg_Sloc); |
| Error_Msg_File_1 := Unit_File_Name (Unum); |
| Error_Msg |
| ("\incorrect spec in file { must be removed first!", |
| Load_Msg_Sloc); |
| Unum := No_Unit; |
| goto Done; |
| end if; |
| |
| -- If loaded unit had an error, then caller inherits setting |
| |
| if Present (Error_Node) then |
| case Units.Table (Unum).Fatal_Error is |
| |
| -- Nothing to do if with'ed unit had no error |
| |
| when None => |
| null; |
| |
| -- If with'ed unit had a detected fatal error, propagate it |
| |
| when Error_Detected => |
| Units.Table (Calling_Unit).Fatal_Error := Error_Detected; |
| |
| -- If with'ed unit had an ignored error, then propagate it |
| -- but do not overide an existing setting. |
| |
| when Error_Ignored => |
| if Units.Table (Calling_Unit).Fatal_Error = None then |
| Units.Table (Calling_Unit).Fatal_Error := |
| Error_Ignored; |
| end if; |
| end case; |
| end if; |
| |
| -- Remove load stack entry and return the entry in the file table |
| |
| Load_Stack.Decrement_Last; |
| |
| -- All done, return unit number |
| |
| goto Done; |
| |
| -- Case of file not found |
| |
| else |
| if Debug_Flag_L then |
| if Src_Ind = No_Access_To_Source_File then |
| Write_Str (" no read access to file, load failed"); |
| else |
| Write_Str (" file was not found, load failed"); |
| end if; |
| |
| Write_Eol; |
| end if; |
| |
| -- Generate message if unit required |
| |
| if Required then |
| Get_Name_String (Fname); |
| |
| if Is_Predefined_File_Name (Fname) then |
| |
| -- This is a predefined library unit which is not present |
| -- in the run time. If a predefined unit is not available |
| -- it may very likely be the case that there is also pragma |
| -- Restriction forbidding its usage. This is typically the |
| -- case when building a configurable run time, where the |
| -- usage of certain run-time units is restricted by means |
| -- of both the corresponding pragma Restriction (such as |
| -- No_Calendar), and by not including the unit. Hence, we |
| -- check whether this predefined unit is forbidden, so that |
| -- the message about the restriction violation is generated, |
| -- if needed. |
| |
| if Present (Error_Node) then |
| Check_Restricted_Unit (Load_Name, Error_Node); |
| end if; |
| |
| Error_Msg_Unit_1 := Uname_Actual; |
| Error_Msg -- CODEFIX |
| ("$$ is not a predefined library unit", Load_Msg_Sloc); |
| |
| else |
| Error_Msg_File_1 := Fname; |
| |
| if Src_Ind = No_Access_To_Source_File then |
| Error_Msg ("no read access to file{", Load_Msg_Sloc); |
| else |
| Error_Msg ("file{ not found", Load_Msg_Sloc); |
| end if; |
| end if; |
| |
| Write_Dependency_Chain; |
| |
| -- Remove unit from stack, to avoid cascaded errors on |
| -- subsequent missing files. |
| |
| Load_Stack.Decrement_Last; |
| Remove_Unit (Unum); |
| |
| -- If unit not required, remove load stack entry and the junk |
| -- file table entry, and return No_Unit to indicate not found. |
| |
| else |
| Load_Stack.Decrement_Last; |
| Remove_Unit (Unum); |
| end if; |
| |
| Unum := No_Unit; |
| goto Done; |
| end if; |
| end if; |
| |
| -- Here to exit, with result in Unum |
| |
| <<Done>> |
| Parsing_Main_Extended_Source := Save_PMES; |
| Cunit_Boolean_Restrictions_Restore (Save_Cunit_Restrictions); |
| return Unum; |
| end Load_Unit; |
| |
| -------------------------- |
| -- Make_Child_Decl_Unit -- |
| -------------------------- |
| |
| procedure Make_Child_Decl_Unit (N : Node_Id) is |
| Unit_Decl : constant Node_Id := Library_Unit (N); |
| Unit_Num : constant Unit_Number_Type := Get_Cunit_Unit_Number (N); |
| |
| begin |
| Units.Increment_Last; |
| Units.Table (Units.Last) := Units.Table (Unit_Num); |
| Units.Table (Units.Last).Cunit := Unit_Decl; |
| Units.Table (Units.Last).Cunit_Entity := |
| Defining_Identifier |
| (Defining_Unit_Name (Specification (Unit (Unit_Decl)))); |
| Init_Unit_Name (Units.Last, Get_Spec_Name (Unit_Name (Unit_Num))); |
| |
| -- The library unit created for of a child subprogram unit plays no |
| -- role in code generation and binding, so label it accordingly. |
| |
| Units.Table (Units.Last).Generate_Code := False; |
| Set_Has_No_Elaboration_Code (Unit_Decl); |
| end Make_Child_Decl_Unit; |
| |
| ------------------------ |
| -- Make_Instance_Unit -- |
| ------------------------ |
| |
| -- If the unit is an instance, it appears as a package declaration, but |
| -- contains both declaration and body of the instance. The body becomes |
| -- the main unit of the compilation, and the declaration is inserted |
| -- at the end of the unit table. The main unit now has the name of a |
| -- body, which is constructed from the name of the original spec, |
| -- and is attached to the compilation node of the original unit. The |
| -- declaration has been attached to a new compilation unit node, and |
| -- code will have to be generated for it. |
| |
| procedure Make_Instance_Unit (N : Node_Id; In_Main : Boolean) is |
| Sind : constant Source_File_Index := Source_Index (Main_Unit); |
| |
| begin |
| Units.Increment_Last; |
| |
| if In_Main then |
| Units.Table (Units.Last) := Units.Table (Main_Unit); |
| Units.Table (Units.Last).Cunit := Library_Unit (N); |
| Init_Unit_Name (Units.Last, Unit_Name (Main_Unit)); |
| |
| Units.Table (Main_Unit).Cunit := N; |
| Units.Table (Main_Unit).Version := Source_Checksum (Sind); |
| Init_Unit_Name (Main_Unit, |
| Get_Body_Name |
| (Unit_Name (Get_Cunit_Unit_Number (Library_Unit (N))))); |
| |
| else |
| -- Duplicate information from instance unit, for the body. The unit |
| -- node N has been rewritten as a body, but it was placed in the |
| -- units table when first loaded as a declaration. |
| |
| Units.Table (Units.Last) := Units.Table (Get_Cunit_Unit_Number (N)); |
| Units.Table (Units.Last).Cunit := Library_Unit (N); |
| end if; |
| end Make_Instance_Unit; |
| |
| ------------------------ |
| -- Spec_Is_Irrelevant -- |
| ------------------------ |
| |
| function Spec_Is_Irrelevant |
| (Spec_Unit : Unit_Number_Type; |
| Body_Unit : Unit_Number_Type) return Boolean |
| is |
| Sunit : constant Node_Id := Cunit (Spec_Unit); |
| Bunit : constant Node_Id := Cunit (Body_Unit); |
| |
| begin |
| -- The spec is irrelevant if the body is a subprogram body, and the spec |
| -- is other than a subprogram spec or generic subprogram spec. Note that |
| -- the names must be the same, we don't need to check that, because we |
| -- already know that from the fact that the file names are the same. |
| |
| return |
| Nkind (Unit (Bunit)) = N_Subprogram_Body |
| and then Nkind (Unit (Sunit)) /= N_Subprogram_Declaration |
| and then Nkind (Unit (Sunit)) /= N_Generic_Subprogram_Declaration; |
| end Spec_Is_Irrelevant; |
| |
| -------------------- |
| -- Version_Update -- |
| -------------------- |
| |
| procedure Version_Update (U : Node_Id; From : Node_Id) is |
| Unum : constant Unit_Number_Type := Get_Cunit_Unit_Number (U); |
| Fnum : constant Unit_Number_Type := Get_Cunit_Unit_Number (From); |
| begin |
| if Source_Index (Fnum) > No_Source_File then |
| Units.Table (Unum).Version := |
| Units.Table (Unum).Version |
| xor |
| Source_Checksum (Source_Index (Fnum)); |
| end if; |
| end Version_Update; |
| |
| ---------------------------- |
| -- Write_Dependency_Chain -- |
| ---------------------------- |
| |
| procedure Write_Dependency_Chain is |
| begin |
| -- The dependency chain is only written if it is at least two entries |
| -- deep, otherwise it is trivial (the main unit depending on a unit |
| -- that it obviously directly depends on). |
| |
| if Load_Stack.Last - 1 > Load_Stack.First then |
| for U in Load_Stack.First .. Load_Stack.Last - 1 loop |
| Error_Msg_Unit_1 := |
| Unit_Name (Load_Stack.Table (U).Unit_Number); |
| Error_Msg_Unit_2 := |
| Unit_Name (Load_Stack.Table (U + 1).Unit_Number); |
| Error_Msg ("$ depends on $!", Load_Msg_Sloc); |
| end loop; |
| end if; |
| end Write_Dependency_Chain; |
| |
| end Lib.Load; |