Next: Controlling Elaboration in GNAT - Internal Calls, Previous: Checking the Elaboration Order in Ada 95, Up: Elaboration Order Handling in GNAT
In the previous section we discussed the rules in Ada 95 which ensure
that 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 with's Definitions may safely instantiate
Definitions.Subp because the compiler can determine that there
definitely is no package body to worry about in this case
A has such a pragma,
and unit B does
a with of unit A. Recall that the standard rules require
the spec of unit A
to be elaborated before the with'ing unit; given the pragma in
A, we also know that the body of A
will be elaborated before B, so
that calls to A are safe and do not need a check.
Note that,
unlike pragma Pure and pragma Preelaborate,
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 Unit_1 and Unit_2.
If a programmer
marks Unit_1 as Elaborate_Body,
and not 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 Func_1 in Unit_2
need not be checked,
it must be safe. But the call to Func_2 in
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_1/Unit_2 example, if
we marked both of them as having pragma Elaborate_Body, then
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 Pure or 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:
with clause,
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.
Unit Awith's unit B and calls B.Func in elab code Unit Bwith's unit C, and B.Func calls C.Func
Now if we put a pragma Elaborate (B)
in unit A, this ensures that the
body of B is elaborated before the call, but not the
body of C, so
the call to C.Func could still cause Program_Error to
be raised.
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 A,
then it requires
not only that the body of B be elaborated before A,
but also the
body of C, because B with's C.
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 with'ed unit,
then if the with'ed unit does not have
pragma Pure or Preelaborate, then the client should have
a pragma Elaborate_All
for the 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
states:
Elaborate, Elaborate_All,
or Elaborate_Body pragmas. In
this case, an Ada 95 compiler must diagnose the situation at bind
time, and refuse to build an executable program.
Program_Error will 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
with an 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
to use Elaborate_All on such units.