The previous section discussed the case in which the execution of a particular thread of elaboration code occurred entirely within a single unit. This is the easy case to handle, because a programmer has direct and total control over the order of elaboration, and furthermore, checks need only be generated in cases which are rare and which the compiler can easily detect. The situation is more complex when separate compilation is taken into account. Consider the following:
package Math is function Sqrt (Arg : Float) return Float; end Math; package body Math is function Sqrt (Arg : Float) return Float is begin ... end Sqrt; end Math; with Math; package Stuff is X : Float := Math.Sqrt (0.5); end Stuff; with Stuff; procedure Main is begin ... end Main;
where Main is the main program. When this program is executed, the elaboration code must first be executed, and one of the jobs of the binder is to determine the order in which the units of a program are to be elaborated. In this case we have four units: the spec and body of Math, the spec of Stuff and the body of Main). In what order should the four separate sections of elaboration code be executed?
There are some restrictions in the order of elaboration that the binder can choose. In particular, if unit U has a `with' for a package X, then you are assured that the spec of X is elaborated before U , but you are not assured that the body of X is elaborated before U. This means that in the above case, the binder is allowed to choose the order:
spec of Math spec of Stuff body of Math body of Main
but that’s not good, because now the call to Math.Sqrt that happens during the elaboration of the Stuff spec happens before the body of Math.Sqrt is elaborated, and hence causes Program_Error exception to be raised. At first glance, one might say that the binder is misbehaving, because obviously you want to elaborate the body of something you `with' first, but that is not a general rule that can be followed in all cases. Consider
package X is ... package Y is ... with X; package body Y is ... with Y; package body X is ...
This is a common arrangement, and, apart from the order of elaboration problems that might arise in connection with elaboration code, this works fine. A rule that says that you must first elaborate the body of anything you `with' cannot work in this case: the body of X `with's Y, which means you would have to elaborate the body of Y first, but that `with's X, which means you have to elaborate the body of X first, but ... and we have a loop that cannot be broken.
It is true that the binder can in many cases guess an order of elaboration that is unlikely to cause a Program_Error exception to be raised, and it tries to do so (in the above example of Math/Stuff/Spec, the GNAT binder will by default elaborate the body of Math right after its spec, so all will be well).
However, a program that blindly relies on the binder to be helpful can get into trouble, as we discussed in the previous sections, so GNAT provides a number of facilities for assisting the programmer in developing programs that are robust with respect to elaboration order.