This section has been entirely concerned with the issue of finding a valid elaboration order, as defined by the Ada Reference Manual. In a case where several elaboration orders are valid, the task is to find one of the possible valid elaboration orders (and the static model in GNAT will ensure that this is achieved).
The purpose of the elaboration rules in the Ada Reference Manual is to make sure that no entity is accessed before it has been elaborated. For a subprogram, this means that the spec and body must have been elaborated before the subprogram is called. For an object, this means that the object must have been elaborated before its value is read or written. A violation of either of these two requirements is an access before elaboration order, and this section has been all about avoiding such errors.
In the case where more than one order of elaboration is possible, in the sense that access before elaboration errors are avoided, then any one of the orders is “correct” in the sense that it meets the requirements of the Ada Reference Manual, and no such error occurs.
However, it may be the case for a given program, that there are constraints on the order of elaboration that come not from consideration of avoiding elaboration errors, but rather from extra-lingual logic requirements. Consider this example:
with Init_Constants; package Constants is X : Integer := 0; Y : Integer := 0; end Constants; package Init_Constants is procedure P; -- require a body end Init_Constants; with Constants; package body Init_Constants is procedure P is begin null; end; begin Constants.X := 3; Constants.Y := 4; end Init_Constants; with Constants; package Calc is Z : Integer := Constants.X + Constants.Y; end Calc; with Calc; with Text_IO; use Text_IO; procedure Main is begin Put_Line (Calc.Z'Img); end Main;
In this example, there is more than one valid order of elaboration. For example both the following are correct orders:
Init_Constants spec Constants spec Calc spec Init_Constants body Main body and Init_Constants spec Init_Constants body Constants spec Calc spec Main body
There is no language rule to prefer one or the other, both are correct
from an order of elaboration point of view. But the programmatic effects
of the two orders are very different. In the first, the elaboration routine
of Calc
initializes Z
to zero, and then the main program
runs with this value of zero. But in the second order, the elaboration
routine of Calc
runs after the body of Init_Constants has set
X
and Y
and thus Z
is set to 7 before Main
runs.
One could perhaps by applying pretty clever non-artificial intelligence to the situation guess that it is more likely that the second order of elaboration is the one desired, but there is no formal linguistic reason to prefer one over the other. In fact in this particular case, GNAT will prefer the second order, because of the rule that bodies are elaborated as soon as possible, but it's just luck that this is what was wanted (if indeed the second order was preferred).
If the program cares about the order of elaboration routines in a case like this, it is important to specify the order required. In this particular case, that could have been achieved by adding to the spec of Calc:
pragma Elaborate_All (Constants);
which requires that the body (if any) and spec of Constants
,
as well as the body and spec of any unit with
'ed by
Constants
be elaborated before Calc
is elaborated.
Clearly no automatic method can always guess which alternative you require,
and if you are working with legacy code that had constraints of this kind
which were not properly specified by adding Elaborate
or
Elaborate_All
pragmas, then indeed it is possible that two different
compilers can choose different orders.
The gnatbind
-p switch may be useful in smoking
out problems. This switch causes bodies to be elaborated as late as possible
instead of as early as possible. In the example above, it would have forced
the choice of the first elaboration order. If you get different results
when using this switch, and particularly if one set of results is right,
and one is wrong as far as you are concerned, it shows that you have some
missing Elaborate
pragmas. For the example above, we have the
following output:
gnatmake -f -q main main 7 gnatmake -f -q main -bargs -p main 0
It is of course quite unlikely that both these results are correct, so
it is up to you in a case like this to investigate the source of the
difference, by looking at the two elaboration orders that are chosen,
and figuring out which is correct, and then adding the necessary
Elaborate_All
pragmas to ensure the desired order.