------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- B C H E C K -- -- -- -- 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 ALI; use ALI; with ALI.Util; use ALI.Util; with Binderr; use Binderr; with Butil; use Butil; with Casing; use Casing; with Fname; use Fname; with Gnatvsn; with Namet; use Namet; with Opt; use Opt; with Osint; with Output; use Output; with Rident; use Rident; with Types; use Types; package body Bcheck is ----------------------- -- Local Subprograms -- ----------------------- -- The following checking subprograms make up the parts of the -- configuration consistency check. See bodies for details of checks. procedure Check_Consistent_Dispatching_Policy; procedure Check_Consistent_Dynamic_Elaboration_Checking; procedure Check_Consistent_Interrupt_States; procedure Check_Consistent_Locking_Policy; procedure Check_Consistent_No_Component_Reordering; procedure Check_Consistent_Normalize_Scalars; procedure Check_Consistent_Optimize_Alignment; procedure Check_Consistent_Partition_Elaboration_Policy; procedure Check_Consistent_Queuing_Policy; procedure Check_Consistent_Restrictions; procedure Check_Consistent_Restriction_No_Default_Initialization; procedure Check_Consistent_SSO_Default; procedure Check_Consistent_Exception_Handling; procedure Consistency_Error_Msg (Msg : String); -- Produce an error or a warning message, depending on whether an -- inconsistent configuration is permitted or not. function Same_Unit (U1 : Unit_Name_Type; U2 : Name_Id) return Boolean; -- Used to compare two unit names for No_Dependence checks. U1 is in -- standard unit name format, and U2 is in literal form with periods. ------------------------------------- -- Check_Configuration_Consistency -- ------------------------------------- procedure Check_Configuration_Consistency is begin if Queuing_Policy_Specified /= ' ' then Check_Consistent_Queuing_Policy; end if; if Locking_Policy_Specified /= ' ' then Check_Consistent_Locking_Policy; end if; if No_Component_Reordering_Specified then Check_Consistent_No_Component_Reordering; end if; if Partition_Elaboration_Policy_Specified /= ' ' then Check_Consistent_Partition_Elaboration_Policy; end if; if SSO_Default_Specified then Check_Consistent_SSO_Default; end if; if Zero_Cost_Exceptions_Specified then Check_Consistent_Exception_Handling; end if; Check_Consistent_Normalize_Scalars; Check_Consistent_Optimize_Alignment; Check_Consistent_Dynamic_Elaboration_Checking; Check_Consistent_Restrictions; Check_Consistent_Restriction_No_Default_Initialization; Check_Consistent_Interrupt_States; Check_Consistent_Dispatching_Policy; end Check_Configuration_Consistency; ----------------------- -- Check_Consistency -- ----------------------- procedure Check_Consistency is Src : Source_Id; -- Source file Id for this Sdep entry ALI_Path_Id : File_Name_Type; begin -- First, we go through the source table to see if there are any cases -- in which we should go after source files and compute checksums of -- the source files. We need to do this for any file for which we have -- mismatching time stamps and (so far) matching checksums. for S in Source.First .. Source.Last loop -- If all time stamps for a file match, then there is nothing to -- do, since we will not be checking checksums in that case anyway if Source.Table (S).All_Timestamps_Match then null; -- If we did not find the source file, then we can't compute its -- checksum anyway. Note that when we have a time stamp mismatch, -- we try to find the source file unconditionally (i.e. if -- Check_Source_Files is False). elsif not Source.Table (S).Source_Found then null; -- If we already have non-matching or missing checksums, then no -- need to try going after source file, since we won't trust the -- checksums in any case. elsif not Source.Table (S).All_Checksums_Match then null; -- Now we have the case where we have time stamp mismatches, and -- the source file is around, but so far all checksums match. This -- is the case where we need to compute the checksum from the source -- file, since otherwise we would ignore the time stamp mismatches, -- and that is wrong if the checksum of the source does not agree -- with the checksums in the ALI files. elsif Check_Source_Files then if not Checksums_Match (Source.Table (S).Checksum, Get_File_Checksum (Source.Table (S).Sfile)) then Source.Table (S).All_Checksums_Match := False; end if; end if; end loop; -- Loop through ALI files ALIs_Loop : for A in ALIs.First .. ALIs.Last loop -- Loop through Sdep entries in one ALI file Sdep_Loop : for D in ALIs.Table (A).First_Sdep .. ALIs.Table (A).Last_Sdep loop if Sdep.Table (D).Dummy_Entry then goto Continue; end if; Src := Source_Id (Get_Name_Table_Int (Sdep.Table (D).Sfile)); -- If the time stamps match, or all checksums match, then we -- are OK, otherwise we have a definite error. if Sdep.Table (D).Stamp /= Source.Table (Src).Stamp and then not Source.Table (Src).All_Checksums_Match then Error_Msg_File_1 := ALIs.Table (A).Sfile; Error_Msg_File_2 := Sdep.Table (D).Sfile; -- Two styles of message, depending on whether or not -- the updated file is the one that must be recompiled if Error_Msg_File_1 = Error_Msg_File_2 then if Tolerate_Consistency_Errors then Error_Msg ("?{ has been modified and should be recompiled"); else Error_Msg ("{ has been modified and must be recompiled"); end if; else ALI_Path_Id := Osint.Full_Lib_File_Name (ALIs.Table (A).Afile); if Osint.Is_Readonly_Library (ALI_Path_Id) then if Tolerate_Consistency_Errors then Error_Msg ("?{ should be recompiled"); Error_Msg_File_1 := ALI_Path_Id; Error_Msg ("?({ is obsolete and read-only)"); else Error_Msg ("{ must be compiled"); Error_Msg_File_1 := ALI_Path_Id; Error_Msg ("({ is obsolete and read-only)"); end if; elsif Tolerate_Consistency_Errors then Error_Msg ("?{ should be recompiled ({ has been modified)"); else Error_Msg ("{ must be recompiled ({ has been modified)"); end if; end if; if (not Tolerate_Consistency_Errors) and Verbose_Mode then Error_Msg_File_1 := Source.Table (Src).Stamp_File; if Source.Table (Src).Source_Found then Error_Msg_File_1 := Osint.Full_Source_Name (Error_Msg_File_1); else Error_Msg_File_1 := Osint.Full_Lib_File_Name (Error_Msg_File_1); end if; Error_Msg ("time stamp from { " & String (Source.Table (Src).Stamp)); Error_Msg_File_1 := Sdep.Table (D).Sfile; Error_Msg (" conflicts with { timestamp " & String (Sdep.Table (D).Stamp)); Error_Msg_File_1 := Osint.Full_Lib_File_Name (ALIs.Table (A).Afile); Error_Msg (" from {"); end if; -- Exit from the loop through Sdep entries once we find one -- that does not match. exit Sdep_Loop; end if; <> null; end loop Sdep_Loop; end loop ALIs_Loop; end Check_Consistency; ----------------------------------------- -- Check_Consistent_Dispatching_Policy -- ----------------------------------------- -- The rule is that all files for which the dispatching policy is -- significant must meet the following rules: -- 1. All files for which a task dispatching policy is significant must -- be compiled with the same setting. -- 2. If a partition contains one or more Priority_Specific_Dispatching -- pragmas it cannot contain a Task_Dispatching_Policy pragma. -- 3. No overlap is allowed in the priority ranges specified in -- Priority_Specific_Dispatching pragmas within the same partition. -- 4. If a partition contains one or more Priority_Specific_Dispatching -- pragmas then the Ceiling_Locking policy is the only one allowed for -- the partition. procedure Check_Consistent_Dispatching_Policy is Max_Prio : Nat := 0; -- Maximum priority value for which a Priority_Specific_Dispatching -- pragma has been specified. TDP_Pragma_Afile : ALI_Id := No_ALI_Id; -- ALI file where a Task_Dispatching_Policy pragma appears begin -- Consistency checks in units specifying a Task_Dispatching_Policy if Task_Dispatching_Policy_Specified /= ' ' then Find_Policy : for A1 in ALIs.First .. ALIs.Last loop if ALIs.Table (A1).Task_Dispatching_Policy /= ' ' then -- Store the place where the first task dispatching pragma -- appears. We may need this value for issuing consistency -- errors if Priority_Specific_Dispatching pragmas are used. TDP_Pragma_Afile := A1; Check_Policy : declare Policy : constant Character := ALIs.Table (A1).Task_Dispatching_Policy; begin for A2 in A1 + 1 .. ALIs.Last loop if ALIs.Table (A2).Task_Dispatching_Policy /= ' ' and then ALIs.Table (A2).Task_Dispatching_Policy /= Policy then Error_Msg_File_1 := ALIs.Table (A1).Sfile; Error_Msg_File_2 := ALIs.Table (A2).Sfile; Consistency_Error_Msg ("{ and { compiled with different task" & " dispatching policies"); exit Find_Policy; end if; end loop; end Check_Policy; exit Find_Policy; end if; end loop Find_Policy; end if; -- If no Priority_Specific_Dispatching entries, nothing else to do if Specific_Dispatching.Last >= Specific_Dispatching.First then -- Find out the maximum priority value for which one of the -- Priority_Specific_Dispatching pragmas applies. Max_Prio := 0; for J in Specific_Dispatching.First .. Specific_Dispatching.Last loop if Specific_Dispatching.Table (J).Last_Priority > Max_Prio then Max_Prio := Specific_Dispatching.Table (J).Last_Priority; end if; end loop; -- Now establish tables to be used for consistency checking declare -- The following record type is used to record locations of the -- Priority_Specific_Dispatching pragmas applying to the Priority. type Specific_Dispatching_Entry is record Dispatching_Policy : Character := ' '; -- First character (upper case) of corresponding policy name Afile : ALI_Id := No_ALI_Id; -- ALI file that generated Priority Specific Dispatching -- entry for consistency message. Loc : Nat := 0; -- Line numbers from Priority_Specific_Dispatching pragma end record; PSD_Table : array (0 .. Max_Prio) of Specific_Dispatching_Entry := (others => Specific_Dispatching_Entry' (Dispatching_Policy => ' ', Afile => No_ALI_Id, Loc => 0)); -- Array containing an entry per priority containing the location -- where there is a Priority_Specific_Dispatching pragma that -- applies to the priority. begin for F in ALIs.First .. ALIs.Last loop for K in ALIs.Table (F).First_Specific_Dispatching .. ALIs.Table (F).Last_Specific_Dispatching loop declare DTK : Specific_Dispatching_Record renames Specific_Dispatching.Table (K); begin -- Check whether pragma Task_Dispatching_Policy and -- pragma Priority_Specific_Dispatching are used in the -- same partition. if Task_Dispatching_Policy_Specified /= ' ' then Error_Msg_File_1 := ALIs.Table (F).Sfile; Error_Msg_File_2 := ALIs.Table (TDP_Pragma_Afile).Sfile; Error_Msg_Nat_1 := DTK.PSD_Pragma_Line; Consistency_Error_Msg ("Priority_Specific_Dispatching at {:#" & " incompatible with Task_Dispatching_Policy at {"); end if; -- Ceiling_Locking must also be specified for a partition -- with at least one Priority_Specific_Dispatching -- pragma. if Locking_Policy_Specified /= ' ' and then Locking_Policy_Specified /= 'C' then for A in ALIs.First .. ALIs.Last loop if ALIs.Table (A).Locking_Policy /= ' ' and then ALIs.Table (A).Locking_Policy /= 'C' then Error_Msg_File_1 := ALIs.Table (F).Sfile; Error_Msg_File_2 := ALIs.Table (A).Sfile; Error_Msg_Nat_1 := DTK.PSD_Pragma_Line; Consistency_Error_Msg ("Priority_Specific_Dispatching at {:#" & " incompatible with Locking_Policy at {"); end if; end loop; end if; -- Check overlapping priority ranges Find_Overlapping : for Prio in DTK.First_Priority .. DTK.Last_Priority loop if PSD_Table (Prio).Afile = No_ALI_Id then PSD_Table (Prio) := (Dispatching_Policy => DTK.Dispatching_Policy, Afile => F, Loc => DTK.PSD_Pragma_Line); elsif PSD_Table (Prio).Dispatching_Policy /= DTK.Dispatching_Policy then Error_Msg_File_1 := ALIs.Table (PSD_Table (Prio).Afile).Sfile; Error_Msg_File_2 := ALIs.Table (F).Sfile; Error_Msg_Nat_1 := PSD_Table (Prio).Loc; Error_Msg_Nat_2 := DTK.PSD_Pragma_Line; Consistency_Error_Msg ("overlapping priority ranges at {:# and {:#"); exit Find_Overlapping; end if; end loop Find_Overlapping; end; end loop; end loop; end; end if; end Check_Consistent_Dispatching_Policy; --------------------------------------------------- -- Check_Consistent_Dynamic_Elaboration_Checking -- --------------------------------------------------- -- The rule here is that if a unit has dynamic elaboration checks, -- then any unit it withs must meet one of the following criteria: -- 1. There is a pragma Elaborate_All for the with'ed unit -- 2. The with'ed unit was compiled with dynamic elaboration checks -- 3. The with'ed unit has pragma Preelaborate or Pure -- 4. It is an internal GNAT unit (including children of GNAT) -- 5. It is an interface of a Stand-Alone Library procedure Check_Consistent_Dynamic_Elaboration_Checking is begin if Dynamic_Elaboration_Checks_Specified then for U in First_Unit_Entry .. Units.Last loop declare UR : Unit_Record renames Units.Table (U); begin if UR.Dynamic_Elab then for W in UR.First_With .. UR.Last_With loop declare WR : With_Record renames Withs.Table (W); begin if Get_Name_Table_Int (WR.Uname) /= 0 then declare WU : Unit_Record renames Units.Table (Unit_Id (Get_Name_Table_Int (WR.Uname))); begin -- Case 1. Elaborate_All for with'ed unit if WR.Elaborate_All then null; -- Case 2. With'ed unit has dynamic elab checks elsif WU.Dynamic_Elab then null; -- Case 3. With'ed unit is Preelaborate or Pure elsif WU.Preelab or else WU.Pure then null; -- Case 4. With'ed unit is internal file elsif Is_Internal_File_Name (WU.Sfile) then null; -- Case 5. With'ed unit is a SAL interface elsif WU.SAL_Interface then null; -- Issue warning, not one of the safe cases else Error_Msg_File_1 := UR.Sfile; Error_Msg ("?{ has dynamic elaboration checks " & "and with's"); Error_Msg_File_1 := WU.Sfile; Error_Msg ("? { which has static elaboration " & "checks"); Warnings_Detected := Warnings_Detected + 1; end if; end; end if; end; end loop; end if; end; end loop; end if; end Check_Consistent_Dynamic_Elaboration_Checking; --------------------------------------- -- Check_Consistent_Interrupt_States -- --------------------------------------- -- The rule is that if the state of a given interrupt is specified -- in more than one unit, it must be specified with a consistent state. procedure Check_Consistent_Interrupt_States is Max_Intrup : Nat; begin -- If no Interrupt_State entries, nothing to do if Interrupt_States.Last < Interrupt_States.First then return; end if; -- First find out the maximum interrupt value Max_Intrup := 0; for J in Interrupt_States.First .. Interrupt_States.Last loop if Interrupt_States.Table (J).Interrupt_Id > Max_Intrup then Max_Intrup := Interrupt_States.Table (J).Interrupt_Id; end if; end loop; -- Now establish tables to be used for consistency checking declare Istate : array (0 .. Max_Intrup) of Character := (others => 'n'); -- Interrupt state entries, 'u'/'s'/'r' or 'n' to indicate an -- entry that has not been set. Afile : array (0 .. Max_Intrup) of ALI_Id; -- ALI file that generated Istate entry for consistency message Loc : array (0 .. Max_Intrup) of Nat; -- Line numbers from IS pragma generating Istate entry Inum : Nat; -- Interrupt number from entry being tested Stat : Character; -- Interrupt state from entry being tested Lnum : Nat; -- Line number from entry being tested begin for F in ALIs.First .. ALIs.Last loop for K in ALIs.Table (F).First_Interrupt_State .. ALIs.Table (F).Last_Interrupt_State loop Inum := Interrupt_States.Table (K).Interrupt_Id; Stat := Interrupt_States.Table (K).Interrupt_State; Lnum := Interrupt_States.Table (K).IS_Pragma_Line; if Istate (Inum) = 'n' then Istate (Inum) := Stat; Afile (Inum) := F; Loc (Inum) := Lnum; elsif Istate (Inum) /= Stat then Error_Msg_File_1 := ALIs.Table (Afile (Inum)).Sfile; Error_Msg_File_2 := ALIs.Table (F).Sfile; Error_Msg_Nat_1 := Loc (Inum); Error_Msg_Nat_2 := Lnum; Consistency_Error_Msg ("inconsistent interrupt states at {:# and {:#"); end if; end loop; end loop; end; end Check_Consistent_Interrupt_States; ------------------------------------- -- Check_Consistent_Locking_Policy -- ------------------------------------- -- The rule is that all files for which the locking policy is -- significant must be compiled with the same setting. procedure Check_Consistent_Locking_Policy is begin -- First search for a unit specifying a policy and then -- check all remaining units against it. Find_Policy : for A1 in ALIs.First .. ALIs.Last loop if ALIs.Table (A1).Locking_Policy /= ' ' then Check_Policy : declare Policy : constant Character := ALIs.Table (A1).Locking_Policy; begin for A2 in A1 + 1 .. ALIs.Last loop if ALIs.Table (A2).Locking_Policy /= ' ' and then ALIs.Table (A2).Locking_Policy /= Policy then Error_Msg_File_1 := ALIs.Table (A1).Sfile; Error_Msg_File_2 := ALIs.Table (A2).Sfile; Consistency_Error_Msg ("{ and { compiled with different locking policies"); exit Find_Policy; end if; end loop; end Check_Policy; exit Find_Policy; end if; end loop Find_Policy; end Check_Consistent_Locking_Policy; ---------------------------------------------- -- Check_Consistent_No_Component_Reordering -- ---------------------------------------------- -- This routine checks for a consistent No_Component_Reordering setting. -- Note that internal units are excluded from this check, since we don't -- in any case allow the pragma to affect types in internal units, and -- there is thus no requirement to recompile the run-time with the setting. procedure Check_Consistent_No_Component_Reordering is OK : Boolean := True; begin -- Check that all entries have No_Component_Reordering set for A1 in ALIs.First .. ALIs.Last loop if not Is_Internal_File_Name (ALIs.Table (A1).Sfile) and then not ALIs.Table (A1).No_Component_Reordering then OK := False; exit; end if; end loop; -- All do, return if OK then return; end if; -- Here we have an inconsistency Consistency_Error_Msg ("some but not all files compiled with No_Component_Reordering"); Write_Eol; Write_Str ("files compiled with No_Component_Reordering"); Write_Eol; for A1 in ALIs.First .. ALIs.Last loop if not Is_Internal_File_Name (ALIs.Table (A1).Sfile) and then ALIs.Table (A1).No_Component_Reordering then Write_Str (" "); Write_Name (ALIs.Table (A1).Sfile); Write_Eol; end if; end loop; Write_Eol; Write_Str ("files compiled without No_Component_Reordering"); Write_Eol; for A1 in ALIs.First .. ALIs.Last loop if not Is_Internal_File_Name (ALIs.Table (A1).Sfile) and then not ALIs.Table (A1).No_Component_Reordering then Write_Str (" "); Write_Name (ALIs.Table (A1).Sfile); Write_Eol; end if; end loop; end Check_Consistent_No_Component_Reordering; ---------------------------------------- -- Check_Consistent_Normalize_Scalars -- ---------------------------------------- -- The rule is that if any unit is compiled with Normalize_Scalars, -- then all other units in the partition must also be compiled with -- Normalize_Scalars in effect. -- There is some issue as to whether this consistency check is desirable, -- it is certainly required at the moment by the RM. We should keep a watch -- on the ARG and HRG deliberations here. GNAT no longer depends on this -- consistency (it used to do so, but that is no longer the case, since -- pragma Initialize_Scalars pragma does not require consistency.) procedure Check_Consistent_Normalize_Scalars is begin if Normalize_Scalars_Specified and No_Normalize_Scalars_Specified then Consistency_Error_Msg ("some but not all files compiled with Normalize_Scalars"); Write_Eol; Write_Str ("files compiled with Normalize_Scalars"); Write_Eol; for A1 in ALIs.First .. ALIs.Last loop if ALIs.Table (A1).Normalize_Scalars then Write_Str (" "); Write_Name (ALIs.Table (A1).Sfile); Write_Eol; end if; end loop; Write_Eol; Write_Str ("files compiled without Normalize_Scalars"); Write_Eol; for A1 in ALIs.First .. ALIs.Last loop if not ALIs.Table (A1).Normalize_Scalars then Write_Str (" "); Write_Name (ALIs.Table (A1).Sfile); Write_Eol; end if; end loop; end if; end Check_Consistent_Normalize_Scalars; ----------------------------------------- -- Check_Consistent_Optimize_Alignment -- ----------------------------------------- -- The rule is that all units which depend on the global default setting -- of Optimize_Alignment must be compiled with the same setting for this -- default. Units which specify an explicit local value for this setting -- are exempt from the consistency rule (this includes all internal units). procedure Check_Consistent_Optimize_Alignment is OA_Setting : Character := ' '; -- Reset when we find a unit that depends on the default and does -- not have a local specification of the Optimize_Alignment setting. OA_Unit : Unit_Id := No_Unit_Id; -- Id of unit from which OA_Setting was set C : Character; begin for U in First_Unit_Entry .. Units.Last loop C := Units.Table (U).Optimize_Alignment; if C /= 'L' then if OA_Setting = ' ' then OA_Setting := C; OA_Unit := U; elsif OA_Setting = C then null; else pragma Assert (Present (OA_Unit)); Error_Msg_Unit_1 := Units.Table (OA_Unit).Uname; Error_Msg_Unit_2 := Units.Table (U).Uname; Consistency_Error_Msg ("$ and $ compiled with different " & "default Optimize_Alignment settings"); return; end if; end if; end loop; end Check_Consistent_Optimize_Alignment; --------------------------------------------------- -- Check_Consistent_Partition_Elaboration_Policy -- --------------------------------------------------- -- The rule is that all files for which the partition elaboration policy is -- significant must be compiled with the same setting. procedure Check_Consistent_Partition_Elaboration_Policy is begin -- First search for a unit specifying a policy and then -- check all remaining units against it. Find_Policy : for A1 in ALIs.First .. ALIs.Last loop if ALIs.Table (A1).Partition_Elaboration_Policy /= ' ' then Check_Policy : declare Policy : constant Character := ALIs.Table (A1).Partition_Elaboration_Policy; begin for A2 in A1 + 1 .. ALIs.Last loop if ALIs.Table (A2).Partition_Elaboration_Policy /= ' ' and then ALIs.Table (A2).Partition_Elaboration_Policy /= Policy then Error_Msg_File_1 := ALIs.Table (A1).Sfile; Error_Msg_File_2 := ALIs.Table (A2).Sfile; Consistency_Error_Msg ("{ and { compiled with different partition " & "elaboration policies"); exit Find_Policy; end if; end loop; end Check_Policy; -- A No_Task_Hierarchy restriction must be specified for the -- Sequential policy (RM H.6(6/2)). if Partition_Elaboration_Policy_Specified = 'S' and then not Cumulative_Restrictions.Set (No_Task_Hierarchy) then Error_Msg_File_1 := ALIs.Table (A1).Sfile; Error_Msg ("{ has sequential partition elaboration policy, but no"); Error_Msg ("pragma Restrictions (No_Task_Hierarchy) was specified"); end if; exit Find_Policy; end if; end loop Find_Policy; end Check_Consistent_Partition_Elaboration_Policy; ------------------------------------- -- Check_Consistent_Queuing_Policy -- ------------------------------------- -- The rule is that all files for which the queuing policy is -- significant must be compiled with the same setting. procedure Check_Consistent_Queuing_Policy is begin -- First search for a unit specifying a policy and then -- check all remaining units against it. Find_Policy : for A1 in ALIs.First .. ALIs.Last loop if ALIs.Table (A1).Queuing_Policy /= ' ' then Check_Policy : declare Policy : constant Character := ALIs.Table (A1).Queuing_Policy; begin for A2 in A1 + 1 .. ALIs.Last loop if ALIs.Table (A2).Queuing_Policy /= ' ' and then ALIs.Table (A2).Queuing_Policy /= Policy then Error_Msg_File_1 := ALIs.Table (A1).Sfile; Error_Msg_File_2 := ALIs.Table (A2).Sfile; Consistency_Error_Msg ("{ and { compiled with different queuing policies"); exit Find_Policy; end if; end loop; end Check_Policy; exit Find_Policy; end if; end loop Find_Policy; end Check_Consistent_Queuing_Policy; ----------------------------------- -- Check_Consistent_Restrictions -- ----------------------------------- -- The rule is that if a restriction is specified in any unit, then all -- units must obey the restriction. The check applies only to restrictions -- which require partition wide consistency, and not to internal units. procedure Check_Consistent_Restrictions is Restriction_File_Output : Boolean; -- Shows if we have output header messages for restriction violation procedure Print_Restriction_File (R : All_Restrictions); -- Print header line for R if not printed yet ---------------------------- -- Print_Restriction_File -- ---------------------------- procedure Print_Restriction_File (R : All_Restrictions) is begin if not Restriction_File_Output then Restriction_File_Output := True; -- Find an ali file specifying the restriction for A in ALIs.First .. ALIs.Last loop if ALIs.Table (A).Restrictions.Set (R) and then (R in All_Boolean_Restrictions or else ALIs.Table (A).Restrictions.Value (R) = Cumulative_Restrictions.Value (R)) then -- We have found that ALI file A specifies the restriction -- that is being violated (the minimum value is specified -- in the case of a parameter restriction). declare M1 : constant String := "{ has restriction "; S : constant String := Restriction_Id'Image (R); M2 : String (1 .. 2000); -- big enough P : Integer; begin Name_Buffer (1 .. S'Length) := S; Name_Len := S'Length; Set_Casing (Mixed_Case); M2 (M1'Range) := M1; P := M1'Length + 1; M2 (P .. P + S'Length - 1) := Name_Buffer (1 .. S'Length); P := P + S'Length; if R in All_Parameter_Restrictions then M2 (P .. P + 4) := " => #"; Error_Msg_Nat_1 := Int (Cumulative_Restrictions.Value (R)); P := P + 5; end if; Error_Msg_File_1 := ALIs.Table (A).Sfile; Consistency_Error_Msg (M2 (1 .. P - 1)); Consistency_Error_Msg ("but the following files violate this restriction:"); return; end; end if; end loop; end if; end Print_Restriction_File; -- Start of processing for Check_Consistent_Restrictions begin -- We used to have a special test here: -- A special test, if we have a main program, then if it has an -- allocator in the body, this is considered to be a violation of -- the restriction No_Allocators_After_Elaboration. We just mark -- this restriction and then the normal circuit will flag it. -- But we don't do that any more, because in the final version of Ada -- 2012, it is statically illegal to have an allocator in a library- -- level subprogram, so we don't need this bind time test any more. -- If we have a main program with parameters (which GNAT allows), then -- allocators in that will be caught by the run-time check. -- Loop through all restriction violations for R in All_Restrictions loop -- Check for violation of this restriction if Cumulative_Restrictions.Set (R) and then Cumulative_Restrictions.Violated (R) and then (R in Partition_Boolean_Restrictions or else (R in All_Parameter_Restrictions and then Cumulative_Restrictions.Count (R) > Cumulative_Restrictions.Value (R))) then Restriction_File_Output := False; -- Loop through files looking for violators for A2 in ALIs.First .. ALIs.Last loop declare T : ALIs_Record renames ALIs.Table (A2); begin if T.Restrictions.Violated (R) then -- We exclude predefined files from the list of -- violators. This should be rethought. It is not -- clear that this is the right thing to do, that -- is particularly the case for restricted runtimes. if not Is_Internal_File_Name (T.Sfile) then -- Case of Boolean restriction, just print file name if R in All_Boolean_Restrictions then Print_Restriction_File (R); Error_Msg_File_1 := T.Sfile; Consistency_Error_Msg (" {"); -- Case of Parameter restriction where violation -- count exceeds restriction value, print file -- name and count, adding "at least" if the -- exact count is not known. elsif R in Checked_Add_Parameter_Restrictions or else T.Restrictions.Count (R) > Cumulative_Restrictions.Value (R) then Print_Restriction_File (R); Error_Msg_File_1 := T.Sfile; Error_Msg_Nat_1 := Int (T.Restrictions.Count (R)); if T.Restrictions.Unknown (R) then Consistency_Error_Msg (" { (count = at least #)"); else Consistency_Error_Msg (" { (count = #)"); end if; end if; end if; end if; end; end loop; end if; end loop; -- Now deal with No_Dependence indications. Note that we put the loop -- through entries in the no dependency table first, since this loop -- is most often empty (no such pragma Restrictions in use). for ND in No_Deps.First .. No_Deps.Last loop declare ND_Unit : constant Name_Id := No_Deps.Table (ND).No_Dep_Unit; begin for J in ALIs.First .. ALIs.Last loop declare A : ALIs_Record renames ALIs.Table (J); begin for K in A.First_Unit .. A.Last_Unit loop declare U : Unit_Record renames Units.Table (K); begin -- Exclude runtime units from this check since the -- user does not care how a runtime unit is -- implemented. if not Is_Internal_File_Name (U.Sfile) then for L in U.First_With .. U.Last_With loop if Same_Unit (Withs.Table (L).Uname, ND_Unit) then Error_Msg_File_1 := U.Sfile; Error_Msg_Name_1 := ND_Unit; Consistency_Error_Msg ("file { violates restriction " & "No_Dependence => %"); end if; end loop; end if; end; end loop; end; end loop; end; end loop; end Check_Consistent_Restrictions; ------------------------------------------------------------ -- Check_Consistent_Restriction_No_Default_Initialization -- ------------------------------------------------------------ -- The Restriction (No_Default_Initialization) has special consistency -- rules. The rule is that no unit compiled without this restriction -- that violates the restriction can WITH a unit that is compiled with -- the restriction. procedure Check_Consistent_Restriction_No_Default_Initialization is begin -- Nothing to do if no one set this restriction if not Cumulative_Restrictions.Set (No_Default_Initialization) then return; end if; -- Nothing to do if no one violates the restriction if not Cumulative_Restrictions.Violated (No_Default_Initialization) then return; end if; -- Otherwise we go into a full scan to find possible problems for U in Units.First .. Units.Last loop declare UTE : Unit_Record renames Units.Table (U); ATE : ALIs_Record renames ALIs.Table (UTE.My_ALI); begin if ATE.Restrictions.Violated (No_Default_Initialization) then for W in UTE.First_With .. UTE.Last_With loop declare AFN : constant File_Name_Type := Withs.Table (W).Afile; begin -- The file name may not be present for withs of certain -- generic run-time files. The test can be safely left -- out in such cases anyway. if AFN /= No_File then declare WAI : constant ALI_Id := ALI_Id (Get_Name_Table_Int (AFN)); WTE : ALIs_Record renames ALIs.Table (WAI); begin if WTE.Restrictions.Set (No_Default_Initialization) then Error_Msg_Unit_1 := UTE.Uname; Consistency_Error_Msg ("unit $ compiled without restriction " & "No_Default_Initialization"); Error_Msg_Unit_1 := Withs.Table (W).Uname; Consistency_Error_Msg ("withs unit $, compiled with restriction " & "No_Default_Initialization"); end if; end; end if; end; end loop; end if; end; end loop; end Check_Consistent_Restriction_No_Default_Initialization; ---------------------------------- -- Check_Consistent_SSO_Default -- ---------------------------------- -- This routine checks for a consistent SSO default setting. Note that -- internal units are excluded from this check, since we don't in any -- case allow the pragma to affect types in internal units, and there -- is thus no requirement to recompile the run-time with the default set. procedure Check_Consistent_SSO_Default is Default : Character; begin Default := ALIs.Table (ALIs.First).SSO_Default; -- The default must be set from a non-internal unit pragma Assert (not Is_Internal_File_Name (ALIs.Table (ALIs.First).Sfile)); -- Check all entries match the default above from the first entry for A1 in ALIs.First + 1 .. ALIs.Last loop if not Is_Internal_File_Name (ALIs.Table (A1).Sfile) and then ALIs.Table (A1).SSO_Default /= Default then Default := '?'; exit; end if; end loop; -- All match, return if Default /= '?' then return; end if; -- Here we have a mismatch Consistency_Error_Msg ("files not compiled with same Default_Scalar_Storage_Order"); Write_Eol; Write_Str ("files compiled with High_Order_First"); Write_Eol; for A1 in ALIs.First .. ALIs.Last loop if ALIs.Table (A1).SSO_Default = 'H' then Write_Str (" "); Write_Name (ALIs.Table (A1).Sfile); Write_Eol; end if; end loop; Write_Eol; Write_Str ("files compiled with Low_Order_First"); Write_Eol; for A1 in ALIs.First .. ALIs.Last loop if ALIs.Table (A1).SSO_Default = 'L' then Write_Str (" "); Write_Name (ALIs.Table (A1).Sfile); Write_Eol; end if; end loop; Write_Eol; Write_Str ("files compiled with no Default_Scalar_Storage_Order"); Write_Eol; for A1 in ALIs.First .. ALIs.Last loop if not Is_Internal_File_Name (ALIs.Table (A1).Sfile) and then ALIs.Table (A1).SSO_Default = ' ' then Write_Str (" "); Write_Name (ALIs.Table (A1).Sfile); Write_Eol; end if; end loop; end Check_Consistent_SSO_Default; ----------------------------------------- -- Check_Consistent_Exception_Handling -- ----------------------------------------- -- All units must have the same exception handling mechanism. procedure Check_Consistent_Exception_Handling is begin Check_Mechanism : for A1 in ALIs.First + 1 .. ALIs.Last loop if ALIs.Table (A1).Zero_Cost_Exceptions /= ALIs.Table (ALIs.First).Zero_Cost_Exceptions then Error_Msg_File_1 := ALIs.Table (A1).Sfile; Error_Msg_File_2 := ALIs.Table (ALIs.First).Sfile; Consistency_Error_Msg ("{ and { compiled with different exception handling " & "mechanisms"); end if; end loop Check_Mechanism; end Check_Consistent_Exception_Handling; ------------------------------- -- Check_Duplicated_Subunits -- ------------------------------- procedure Check_Duplicated_Subunits is begin for J in Sdep.First .. Sdep.Last loop if Sdep.Table (J).Subunit_Name /= No_Name then Get_Decoded_Name_String (Sdep.Table (J).Subunit_Name); Name_Len := Name_Len + 2; Name_Buffer (Name_Len - 1) := '%'; -- See if there is a body or spec with the same name for K in Boolean loop if K then Name_Buffer (Name_Len) := 'b'; else Name_Buffer (Name_Len) := 's'; end if; declare Unit : constant Unit_Name_Type := Name_Find; Info : constant Int := Get_Name_Table_Int (Unit); begin if Info /= 0 then Set_Standard_Error; Write_Str ("error: subunit """); Write_Name_Decoded (Sdep.Table (J).Subunit_Name); Write_Str (""" in file """); Write_Name_Decoded (Sdep.Table (J).Sfile); Write_Char ('"'); Write_Eol; Write_Str (" has same name as unit """); Write_Unit_Name (Units.Table (Unit_Id (Info)).Uname); Write_Str (""" found in file """); Write_Name_Decoded (Units.Table (Unit_Id (Info)).Sfile); Write_Char ('"'); Write_Eol; Write_Str (" this is not allowed within a single " & "partition (RM 10.2(19))"); Write_Eol; Osint.Exit_Program (Osint.E_Fatal); end if; end; end loop; end if; end loop; end Check_Duplicated_Subunits; -------------------- -- Check_Versions -- -------------------- procedure Check_Versions is VL : constant Natural := ALIs.Table (ALIs.First).Ver_Len; begin for A in ALIs.First .. ALIs.Last loop if ALIs.Table (A).Ver_Len /= VL or else ALIs.Table (A).Ver (1 .. VL) /= ALIs.Table (ALIs.First).Ver (1 .. VL) then -- Version mismatch found; generate error message. declare use Gnatvsn; Prefix : constant String := Verbose_Library_Version (1 .. Verbose_Library_Version'Length - Library_Version'Length); type ALI_Version is record Primary, Secondary : Int range -1 .. Int'Last; end record; No_Version : constant ALI_Version := (-1, -1); function Remove_Prefix (S : String) return String is (S (S'First + Prefix'Length .. S'Last)); function Extract_Version (S : String) return ALI_Version; -- Attempts to extract and return a pair of nonnegative library -- version numbers from the given string; if unsuccessful, -- then returns No_Version. --------------------- -- Extract_Version -- --------------------- function Extract_Version (S : String) return ALI_Version is pragma Assert (S'First = 1); function Int_Value (Img : String) return Int; -- Using Int'Value leads to complications in -- building the binder, so DIY. --------------- -- Int_Value -- --------------- function Int_Value (Img : String) return Int is Result : Nat := 0; begin if Img'Length in 1 .. 9 and then (for all C of Img => C in '0' .. '9') then for C of Img loop Result := (10 * Result) + (Character'Pos (C) - Character'Pos ('0')); end loop; return Result; else return -1; end if; end Int_Value; begin if S'Length > Prefix'Length and then S (1 .. Prefix'Length) = Prefix then declare Suffix : constant String := Remove_Prefix (S); Dot_Found : Boolean := False; Primary, Secondary : Int; begin for Dot_Index in Suffix'Range loop if Suffix (Dot_Index) = '.' then Dot_Found := True; Primary := Int_Value (Suffix (Suffix'First .. Dot_Index - 1)); Secondary := Int_Value (Suffix (Dot_Index + 1 .. Suffix'Last)); exit; end if; end loop; if not Dot_Found then Primary := Int_Value (Suffix); Secondary := 0; end if; if (Primary /= -1) and (Secondary /= -1) then return (Primary => Primary, Secondary => Secondary); end if; end; end if; return No_Version; end Extract_Version; -- Local constants V1_Text : constant String := ALIs.Table (A).Ver (1 .. ALIs.Table (A).Ver_Len); V2_Text : constant String := ALIs.Table (ALIs.First).Ver (1 .. VL); V1 : constant ALI_Version := Extract_Version (V1_Text); V2 : constant ALI_Version := Extract_Version (V2_Text); Include_Version_Numbers_In_Message : constant Boolean := (V1 /= V2) and (V1 /= No_Version) and (V2 /= No_Version); begin Error_Msg_File_1 := ALIs.Table (A).Sfile; Error_Msg_File_2 := ALIs.Table (ALIs.First).Sfile; if Include_Version_Numbers_In_Message then if V1.Secondary = V2.Secondary then -- Excluding equal secondary values from error -- message text matters for generating reproducible -- regression test outputs. Error_Msg_Nat_1 := V1.Primary; Error_Msg_Nat_2 := V2.Primary; Consistency_Error_Msg ("{ and { compiled with different GNAT versions" & ", v# and v#"); else Consistency_Error_Msg ("{ and { compiled with different GNAT versions" & ", v" & Remove_Prefix (V1_Text) & " and v" & Remove_Prefix (V2_Text)); end if; else Consistency_Error_Msg ("{ and { compiled with different GNAT versions"); end if; end; end if; end loop; end Check_Versions; --------------------------- -- Consistency_Error_Msg -- --------------------------- procedure Consistency_Error_Msg (Msg : String) is begin if Tolerate_Consistency_Errors then -- If consistency errors are tolerated, -- output the message as a warning. Error_Msg ('?' & Msg); -- Otherwise the consistency error is a true error else Error_Msg (Msg); end if; end Consistency_Error_Msg; --------------- -- Same_Unit -- --------------- function Same_Unit (U1 : Unit_Name_Type; U2 : Name_Id) return Boolean is begin -- Note, the string U1 has a terminating %s or %b, U2 does not if Length_Of_Name (U1) - 2 = Length_Of_Name (U2) then Get_Name_String (U1); declare U1_Str : constant String := Name_Buffer (1 .. Name_Len - 2); begin Get_Name_String (U2); return U1_Str = Name_Buffer (1 .. Name_Len); end; else return False; end if; end Same_Unit; end Bcheck;