No_Dependence restriction

I was reading Chapter 13 in the Barnes text on Ada 2022 and came across the No_Dependence restriction.

OK, why? I’m sure there’s a reason, but all I could think of is: package A depends on package B, for some reason A is compatible with package C, and you want to make sure that someone working on B never, ever, ever introduces a dependency on C? (or things along those lines)

Pragma Restrictions provides a way for a project (or an individual developer) to impose restrictions on their own source code, and have them enforced by the compiler. So if in your project you want to be sure there is no use of a particular Ada library unit, say “Unchecked_Deallocation” or “Task_Attributes” for example, you can impose the No_Dependence restriction on your project specifying the library unit, and then the compiler and linker will work together to make sure you never build a program with such a dependence.

Short explanation: The reason you may want to do this is, for example, if you were writing an Ada runtime: you want to not depend on any runtime features, and have the compiler enforce this.

Detailed Explaination: There are several ‘dirty tricks’ that you can use in development that allow you to “lie to the compiler” to get some useful result. (See the first example following.) There are also methods for offloading tedious/complex tasks onto the runtime (ie “being lazy”), and the second example following shows a technique used in my compiler.

-- You can use this in your PURE units; so long as
-- you include DEBUG.IMPLEMENTATION in your final
-- and non-PURE dependencies.
Package Debug with Pure is
   Procedure Put_Line(X : String)
      with Import, Link_Name => "LYING_PUT";
End Debug;

-------
Package Debug.Implementation with Elaborate_Body is
Private
   Procedure Do_Put(X : String)
      with Export, Link_Name => "LYING_PUT";   
End Debug.Implementation;
--------
With Ada.Text_IO;
Package Body Debug.Implementation is
      Procedure Do_Put(X : String) is
      Begin
         Ada.Text_IO.Put_Line( X );
      End Do_Put;
End Debug.Implementation;

And now for the compiler example; note the use of Integer'Wide_Wide_Value on the Lexeme-text to parse the textual value into the Integer value, this is offloading all the various formatting forms onto the runtime:

Procedure Lexington.Aux.P13(Data : in out Token_Vector_Pkg.Vector) is

   Procedure Make_Literal(Position : Token_Vector_Pkg.Cursor) is
      Package TVP renames Token_Vector_Pkg;
      This       : Token renames TVP.Element( Position );
      This_ID    : Token_ID renames Token_Pkg.ID(This);
      This_Value : Wide_Wide_String renames Token_Pkg.Lexeme( This );
   begin
      if This_ID = Text then
         declare
            I : Integer renames Integer'Wide_Wide_Value( This_Value );
         begin
            Data.Replace_Element( Position, Token_Pkg.Make_Token(li_Integer, This_Value) );
         end;
      end if;
   exception
      when Constraint_Error => Null;
   End Make_Literal;

Begin
   Data.Iterate( Make_Literal'Access );
End Lexington.Aux.P13;

This, of course, requires that the runtime supports the Wide_Wide_Value function, obviously.
But there is the interesting quality that doing this [at least conceptually] allows for a staged bootstrap:
(1) If your T'Wide_Wide_Value is incomplete/incorrect, you can correct the function in the RTL and compile/link this with the correction;
(2) If you need to “get away” from a license, you can use this to depend on the RTL, then write a new RTL, recompiling with the new RTL, then recompile on that output to ‘launder’ your executable.
(3) If you have an incomplete RTL, supporting only, say, non-based non-exponent Integer, you can save your source, manually convert/normalize such to the simple form, compile your expansion in the RTL, then restore your original (based/exponential Integer source), and compile normally — thus bootstrapping your incomplete RTL into a [more] complete RTL, from smaller capabilities.