Controlling the Elaboration Order in Ada (GNAT User’s Guide for Native Platforms)

### 9.4 Controlling the Elaboration Order in Ada ¶

Ada provides several idioms and pragmas to aid you in specifying your desired elaboration order and avoiding ABE problems.

- ‘Packages without a body’

  A library package that does not require a completing body does not suffer from ABE problems.

      package Pack is
         generic
            type Element is private;
         package Containers is
            type Element_Array is array (1 .. 10) of Element;
         end Containers;
      end Pack;

  In the example above, package `Pack` does not require a body because it does not contain any constructs which require completion in a body. As a result, generic `Pack.Containers` can be instantiated without encountering any ABE problems.

- ‘pragma Pure’

  Pragma `Pure` places sufficient restrictions on a unit to guarantee that no scenario within the unit can result in an ABE problem.

- ‘pragma Preelaborate’

  Pragma `Preelaborate` is slightly less restrictive than pragma `Pure`, but still strong enough to prevent ABE problems within a unit.

- ‘pragma Elaborate_Body’

  Pragma `Elaborate_Body` requires that the body of a unit is elaborated immediately after its spec. This restriction guarantees that no client scenario can invoke a server target before the target body has been elaborated because the spec and body are effectively “glued” together.

      package Server is
         pragma Elaborate_Body;

         function Func return Integer;
      end Server;

      package body Server is
         function Func return Integer is
         begin
            ...
         end Func;
      end Server;

      with Server;
      package Client is
         Val : constant Integer := Server.Func;
      end Client;

  In the example above, pragma `Elaborate_Body` guarantees the following elaboration order:

      spec of Server
      body of Server
      spec of Client

  because the spec of `Server` must be elaborated prior to `Client` by virtue of the ‘with’ clause and the body of `Server` must be elaborated immediately after the spec of `Server`.

  Removing pragma `Elaborate_Body` could result in the following incorrect elaboration order:

      spec of Server
      spec of Client
      body of Server

  where `Client` invokes `Server.Func`, but the body of `Server.Func` has not been elaborated yet.

The pragmas outlined above allow a server unit to guarantee safe elaboration use by client units. Thus it is a good rule to mark units as `Pure` or `Preelaborate`, and if this is not possible, mark them as `Elaborate_Body`.

There are however situations where `Pure`, `Preelaborate`, and `Elaborate_Body` are not applicable. Ada provides another set of pragmas for use by client units to help ensure the elaboration safety of server units they depend on.

- ‘pragma Elaborate (Unit)’

  You can place pragma `Elaborate` in the context clauses of a unit, after a ‘with’ clause. It guarantees that both the spec and body of its argument will be elaborated prior to the unit with the pragma. Note that other unrelated units may be elaborated in between the spec and the body.

      package Server is
         function Func return Integer;
      end Server;

      package body Server is
         function Func return Integer is
         begin
            ...
         end Func;
      end Server;

      with Server;
      pragma Elaborate (Server);
      package Client is
         Val : constant Integer := Server.Func;
      end Client;

  In the example above, pragma `Elaborate` guarantees the following elaboration order:

      spec of Server
      body of Server
      spec of Client

  Removing pragma `Elaborate` could result in the following incorrect elaboration order:

      spec of Server
      spec of Client
      body of Server

  where `Client` invokes `Server.Func`, but the body of `Server.Func` has not been elaborated yet.

- ‘pragma Elaborate_All (Unit)’

  You can place pragma `Elaborate_All` in the context clauses of a unit, after a ‘with’ clause. It guarantees that both the spec and body of its argument will be elaborated prior to the unit with the pragma as well as all units ‘with’ed by the spec and body of the argument, recursively. Note that other unrelated units may be elaborated in between the spec and the body.

      package Math is
         function Factorial (Val : Natural) return Natural;
      end Math;

      package body Math is
         function Factorial (Val : Natural) return Natural is
         begin
            ...;
         end Factorial;
      end Math;

      package Computer is
         type Operation_Kind is (None, Op_Factorial);

         function Compute
           (Val : Natural;
            Op  : Operation_Kind) return Natural;
      end Computer;

      with Math;
      package body Computer is
         function Compute
           (Val : Natural;
            Op  : Operation_Kind) return Natural
         is
            if Op = Op_Factorial then
               return Math.Factorial (Val);
            end if;

            return 0;
         end Compute;
      end Computer;

      with Computer;
      pragma Elaborate_All (Computer);
      package Client is
         Val : constant Natural :=
                 Computer.Compute (123, Computer.Op_Factorial);
      end Client;

  In the example above, pragma `Elaborate_All` can result in the following elaboration order:

      spec of Math
      body of Math
      spec of Computer
      body of Computer
      spec of Client

  Note that there are several allowable suborders for the specs and bodies of `Math` and `Computer`, but the point is that these specs and bodies will be elaborated prior to `Client`.

  Removing pragma `Elaborate_All` could result in the following incorrect elaboration order:

      spec of Math
      spec of Computer
      body of Computer
      spec of Client
      body of Math

  where `Client` invokes `Computer.Compute`, which in turn invokes `Math.Factorial`, but the body of `Math.Factorial` has not been elaborated yet.

All pragmas shown above can be summarized by the following rule:

‘If a client unit elaborates a server target directly or indirectly, then if the server unit requires a body and does not have pragma Pure, Preelaborate, or Elaborate_Body, then the client unit should have pragma Elaborate or Elaborate_All for the server unit.’

If you do not follow the rule outlined above, a program may fall in one of the following ways:

- ‘No elaboration order exists’

  In this case a compiler must diagnose the situation and refuse to build an executable program.

- ‘One or more incorrect elaboration orders exist’

  In this case a compiler can build an executable program, but `Program_Error` will be raised when the program is run.

- ‘Several elaboration orders exist, some correct, some incorrect’

  In this case, you have not controlled the elaboration order. As a result, a compiler may or may not pick one of the correct orders and the program may or may not raise `Program_Error` when it is run. This is the worst possible state because the program may fail on another compiler or even a different version of the same compiler.

- ‘One or more correct orders exist’

  In this case a compiler can build an executable program and the program is run successfully. This state may be guaranteed by following the outlined rules or may be the result of good program architecture.

Note that one additional advantage of using `Elaborate` and `Elaborate_All` is that the program continues to stay in the last state (one or more correct orders exist) even if maintenance changes the bodies of targets.