In the previous section we discussed the rules in Ada 95 which ensure
Program_Error is raised if an incorrect elaboration order is
chosen. This prevents erroneous executions, but we need mechanisms to
specify a correct execution and avoid the exception altogether.
To achieve this, Ada 95 provides a number of features for controlling
the order of elaboration. We discuss these features in this section.
First, there are several ways of indicating to the compiler that a given unit has no elaboration problems:
|package Definitions is generic type m is new integer; package Subp is type a is array (1 .. 10) of m; type b is array (1 .. 20) of m; end Subp; end Definitions;|
A package that
Definitions may safely instantiate
Definitions.Subp because the compiler can determine that there
definitely is no package body to worry about in this case
Ahas such a pragma, and unit
A. Recall that the standard rules require the spec of unit
Ato be elaborated before the
with'ing unit; given the pragma in
A, we also know that the body of
Awill be elaborated before
B, so that calls to
Aare safe and do not need a check.
Pure and pragma
the use of
Elaborate_Body does not guarantee that the program is
free of elaboration problems, because it may not be possible
to satisfy the requested elaboration order.
Let's go back to the example with
If a programmer
Unit_2, then the order of
elaboration will be:
Spec of Unit_2 Spec of Unit_1 Body of Unit_1 Body of Unit_2
Now that means that the call to
need not be checked,
it must be safe. But the call to
Unit_1 may still fail if
Expression_1 is equal to 1,
and the programmer must still take
responsibility for this not being the case.
If all units carry a pragma
Elaborate_Body, then all problems are
eliminated, except for calls entirely within a body, which are
in any case fully under programmer control. However, using the pragma
everywhere is not always possible.
In particular, for our
Unit_2 example, if
we marked both of them as having pragma
clearly there would be no possible elaboration order.
The above pragmas allow a server to guarantee safe use by clients, and
clearly this is the preferable approach. Consequently a good rule in
Ada 95 is to mark units as
Preelaborate if possible,
and if this is not possible,
mark them as
Elaborate_Body if possible.
As we have seen, there are situations where neither of these
three pragmas can be used.
So we also provide methods for clients to control the
order of elaboration of the servers on which they depend:
withclause, and it requires that the body of the named unit be elaborated before the unit in which the pragma occurs. The idea is to use this pragma if the current unit calls at elaboration time, directly or indirectly, some subprogram in the named unit.
with's unit B and calls B.Func in elab code Unit B
with's unit C, and B.Func calls C.Func
Now if we put a pragma
A, this ensures that the
B is elaborated before the call, but not the
the call to
C.Func could still cause
The effect of a pragma
Elaborate_All is stronger, it requires
not only that the body of the named unit be elaborated before the
unit doing the
with, but also the bodies of all units that the
named unit uses, following
with links transitively. For example,
if we put a pragma
Elaborate_All (B) in unit
then it requires
not only that the body of
B be elaborated before
but also the
We are now in a position to give a usage rule in Ada 95 for avoiding elaboration problems, at least if dynamic dispatching and access to subprogram values are not used. We will handle these cases separately later.
The rule is simple. If a unit has elaboration code that can directly or
indirectly make a call to a subprogram in a
with'ed unit, or instantiate
a generic unit in a
then if the
with'ed unit does not have
Preelaborate, then the client should have
with'ed unit. By following this rule a client is
assured that calls can be made without risk of an exception.
If this rule is not followed, then a program may be in one of four
Elaborate_Bodypragmas. In this case, an Ada 95 compiler must diagnose the situation at bind time, and refuse to build an executable program.
Program_Errorwill be raised when the program is run.
Note that one additional advantage of following our Elaborate_All rule is that the program continues to stay in the ideal (all orders OK) state even if maintenance changes some bodies of some subprograms. Conversely, if a program that does not follow this rule happens to be safe at some point, this state of affairs may deteriorate silently as a result of maintenance changes.
You may have noticed that the above discussion did not mention
the use of
Elaborate_Body. This was a deliberate omission. If you
Elaborate_Body unit, it still may be the case that
code in the body makes calls to some other unit, so it is still necessary
Elaborate_All on such units.