Appendix C. Handling Elaboration Order

Table of Contents
C.1. Elaboration Code in Ada 95
C.2. Checking the Elaboration Order in Ada 95
C.3. Controlling the Elaboration Order in Ada 95
C.4. Controlling Elaboration in XGC Ada - Internal Calls
C.5. Controlling Elaboration in XGC Ada - External Calls
C.6. Default Behavior in XGC Ada - Ensuring Safety
C.7. What to do if the Default Elaboration Behavior Fails
C.8. Elaboration for Access-to-Subprogram Values
C.9. Summary of Procedures for Elaboration Control

This Appendix describes the handling of elaboration code in Ada 95, and in XGC Ada, and in particular discusses how the order of elaboration can be controlled, automatically or as specified explicitly by the program.

C.1. Elaboration Code in Ada 95

Ada 95 provides rather general mechanisms for executing code at elaboration time, that is before the main program starts executing. Such code arises in three contexts:

Initializers for variables:

Variables declared at the library level, in package specs or bodies, can require initialization that is performed at elaboration time, as is:


Power_Up_Latch : Boolean := Check_Power (High);
Package initialization code:

Code between begin and end at the outer level of a package body is executed as part of the package body elaboration code.

Subprogram calls are possible in any of these contexts, which means that any arbitrary part of the program may be executed as part of the elaboration code. It is even possible to write a program which does all its work at elaboration time, with a null main program, although stylistically this is considered an inappropriate way to structure a program.

An important concern arises in the context of this code, which is that we have to be sure that it is elaborated in an appropriate order. What we have is lots of little sections of elaboration code, potentially one section of code for each unit in the program. It is important that these execute in the correct order. Correctness here means that, taking the above example of the declaration of Sqrt_Half, that if some other piece of elaboration code references Sqrt_Half, then it must run after the section of elaboration code that contains the declaration of Sqrt_Half.

Now we would never have any elaboration order problems if we made a rule that whenever you "with" a unit, you must elaborate both the spec and body of that unit before elaborating the unit doing the with'ing:


with Unit_1;
package Unit_2 is ...

would require that both the body and spec of Unit_1 be elaborated before the spec of Unit_2. However, a rule like that would be far too restrictive. In particular, it would make it impossible to have routines in separate packages that were mutually recursive.

One might think that a clever enough compiler could look at the actual elaboration code and determine an appropriate correct order of elaboration, but in the general case, this is not possible. Consider the following example.

In the body of Unit_1, we have a procedure Func_1 that references the variable Sqrt_1, which is declared in the elaboration code of the body of Unit_1:


Sqrt_1 : Float := Sqrt (0.1);

The elaboration code of the body of Unit_1 also contains:


if expression_1 = 1 then
   Q := Unit_2.Func_2;
end if;

Unit_2 is exactly parallel, it has a procedure Func_2 that references the variable Sqrt_2, which is declared in the elaboration code of the body Unit_2:


Sqrt_2 : Float := Sqrt (0.1);

The elaboration code of the body of Unit_2 also contains:


if expression_2 = 2 then
   Q := Unit_1.Func_1;
end if;

Now the question is, which of the following orders of elaboration is acceptable:


Spec of Unit_1
Spec of Unit_2
Body of Unit_1
Body of Unit_2

or


Spec of Unit_2
Spec of Unit_1
Body of Unit_2
Body of Unit_1

If you carefully analyze the flow here, you will see that you cannot tell at compile time the answer to this question. If expression_1 is not equal to 1, and expression_2 is not equal to 2, then either order is acceptable, because neither of the function calls is executed. If both tests evaluate to true, then neither order is acceptable and in fact there is no correct order.

If one of the two expressions is true, and the other is false, then one of the above orders is correct, and the other is incorrect. For example, if expression_1 = 1 and expression_2 /= 2, then the call to Func_2 will occur, but not the call to Func_1. This means that it is essential to elaborate the body of Unit_1 before the body of Unit_2, so the first order of elaboration is correct and the second is wrong.

By making expression_1 and expression_2 depend on input data, or perhaps the time of day, we can make it impossible for the compiler or binder to figure out which of these expressions will be true, and hence it is impossible to guarantee a safe order of elaboration at run time.