Elaboration Order (GNAT User’s Guide for Native Platforms)

### 9.2 Elaboration Order ¶

The sequence by which the elaboration code of all units within a partition is executed is referred to as ‘elaboration order’.

Within a single unit, elaboration code is executed in sequential order.

>     package body Client is
>        Result : ... := Server.Func;

>        procedure Proc is
>           package Inst is new Server.Gen;
>        begin
>           Inst.Eval (Result);
>        end Proc;
>     begin
>        Proc;
>     end Client;

In the example above, the elaboration order within package body `Client` is as follows:

1. The object declaration of `Result` is elaborated.
   - Function `Server.Func` is invoked.
2. The subprogram body of `Proc` is elaborated.
3. Procedure `Proc` is invoked.
   - Generic unit `Server.Gen` is instantiated as `Inst`.
   - Instance `Inst` is elaborated.
   - Procedure `Inst.Eval` is invoked.

The elaboration order of all units within a partition depends on the following factors:

- ‘with’ed units
- parent units
- purity of units
- preelaborability of units
- presence of elaboration-control pragmas
- invocations performed in elaboration code

A program may have several possible elaboration orders depending on its structure:

>     package Server is
>        function Func (Index : Integer) return Integer;
>     end Server;

>     package body Server is
>        Results : array (1 .. 5) of Integer := (1, 2, 3, 4, 5);

>        function Func (Index : Integer) return Integer is
>        begin
>           return Results (Index);
>        end Func;
>     end Server;

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

>     with Client;
>     procedure Main is begin null; end Main;

The following elaboration order exhibits a fundamental problem referred to as ‘access-before-elaboration’ or simply ‘ABE’.

>     spec of Server
>     spec of Client
>     body of Server
>     body of Main

The elaboration of `Server`’s spec materializes function `Func`, making it callable. The elaboration of `Client`’s spec elaborates the declaration of `Val`. This invokes function `Server.Func`, however the body of `Server.Func` has not been elaborated yet because `Server`’s body comes after `Client`’s spec in the elaboration order. As a result, the value of constant `Val` is now undefined.

Without any guarantees from the language, an undetected ABE problem may hinder proper initialization of data, which in turn may lead to undefined behavior at run time. To prevent such ABE problems, Ada employs dynamic checks in the same vein as index or null exclusion checks. A failed ABE check raises exception `Program_Error`.

The following elaboration order avoids the ABE problem and the program can be successfully elaborated.

>     spec of Server
>     body of Server
>     spec of Client
>     body of Main

Ada states that a total elaboration order must exist, but it does not define what this order is. A compiler is thus tasked with choosing a suitable elaboration order which satisfies the dependencies imposed by ‘with’ clauses, unit categorization, elaboration-control pragmas, and invocations performed in elaboration code. Ideally, an order that avoids ABE problems should be chosen, however a compiler may not always find such an order due to complications with respect to control and data flow.