[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This chapter describes the handling of elaboration code in Ada 95 and in GNAT, and discusses how the order of elaboration of program units can be controlled in GNAT, either automatically or with explicit programming features.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Ada 95 provides rather general mechanisms for executing code at elaboration time, that is to say before the main program starts executing. Such code arises in three contexts:
Sqrt_Half : Float := Sqrt (0.5); |
BEGIN-END
section 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 would usually be considered an inappropriate way to structure a program.
An important concern arises in the context of elaboration code:
we have to be sure that it is executed in an appropriate order. What we
have is numerous sections of elaboration code, potentially one section
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
.
There would never be any order of elaboration problem 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.
You 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.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
In some languages that involve the same kind of elaboration problems, e.g. Java and C++, the programmer is expected to worry about these ordering problems himself, and it is common to write a program in which an incorrect elaboration order gives surprising results, because it references variables before they are initialized. Ada 95 is designed to be a safe language, and a programmer-beware approach is clearly not sufficient. Consequently, the language provides three lines of defense:
with
a unit, then its spec is always
elaborated before the unit doing the with
. Similarly, a parent
spec is always elaborated before the child spec, and finally
a spec is always elaborated before its corresponding body.
Program_Error
) is raised.
Let's look at these facilities in more detail. First, the rules for dynamic checking. One possible rule would be simply to say that the exception is raised if you access a variable which has not yet been elaborated. The trouble with this approach is that it could require expensive checks on every variable reference. Instead Ada 95 has two rules which are a little more restrictive, but easier to check, and easier to state:
Program_Error
is raised.
Program_Error
is raised.
The idea is that if the body has been elaborated, then any variables it references must have been elaborated; by checking for the body being elaborated we guarantee that none of its references causes any trouble. As we noted above, this is a little too restrictive, because a subprogram that has no non-local references in its body may in fact be safe to call. However, it really would be unsafe to rely on this, because it would mean that the caller was aware of details of the implementation in the body. This goes against the basic tenets of Ada.
A plausible implementation can be described as follows.
A Boolean variable is associated with each subprogram
and each generic unit. This variable is initialized to False, and is set to
True at the point body is elaborated. Every call or instantiation checks the
variable, and raises Program_Error
if the variable is False.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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 situation 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
statement,
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 A |
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, Preelaborate, or Elaborate_Body, then the client should have
an 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:
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.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
In the case of internal calls, i.e. calls within a single package, the programmer has full control over the order of elaboration, and it is up to the programmer to elaborate declarations in an appropriate order. For example writing:
function One return Float; Q : Float := One; function One return Float is begin return 1.0; end One; |
will obviously raise Program_Error at run time, because function One will be called before its body is elaborated. In this case GNAT will generate a warning that the call will raise Program_Error:
1. procedure y is 2. function One return Float; 3. 4. Q : Float := One; | >>> warning: cannot call "One" before body is elaborated >>> warning: Program_Error will be raised at run time 5. 6. function One return Float is 7. begin 8. return 1.0; 9. end One; 10. 11. begin 12. null; 13. end; |
Note that in this particular case, it is likely that the call is safe, because
the function One
does not access any global variables.
Nevertheless in Ada 95, we do not want the validity of the check to depend on
the contents of the body (think about the separate compilation case), so this
is still wrong, as we discussed in the previous sections.
The error is easily corrected by rearranging the declarations so that the body of One appears before the declaration containing the call (note that in Ada 95, declarations can appear in any order, so there is no restriction that would prevent this reordering, and if we write:
function One return Float; function One return Float is begin return 1.0; end One; Q : Float := One; |
then all is well, no warning is generated, and no
Program_Error
exception
will be raised.
Things are more complicated when a chain of subprograms is executed:
function A return Integer; function B return Integer; function C return Integer; function B return Integer is begin return A; end; function C return Integer is begin return B; end; X : Integer := C; function A return Integer is begin return 1; end; |
Now the call to C
at elaboration time in the declaration of X
is correct, because
the body of C
is already elaborated,
and the call to B
within the body of
C
is correct, but the call
to A
within the body of B
is incorrect, because the body
of A
has not been elaborated, so Program_Error
will be raised on the call to A
.
In this case GNAT will generate a
warning that Program_Error
may be
raised at the point of the call. Let's look at the warning:
1. procedure x is 2. function A return Integer; 3. function B return Integer; 4. function C return Integer; 5. 6. function B return Integer is begin return A; end; | >>> warning: call to "A" before body is elaborated may raise Program_Error >>> warning: "B" called at line 7 >>> warning: "C" called at line 9 7. function C return Integer is begin return B; end; 8. 9. X : Integer := C; 10. 11. function A return Integer is begin return 1; end; 12. 13. begin 14. null; 15. end; |
Note that the message here says "may raise", instead of the direct case,
where the message says "will be raised". That's because whether
A
is
actually called depends in general on run-time flow of control.
For example, if the body of B
said
function B return Integer is begin if some-condition-depending-on-input-data then return A; else return 1; end if; end B; |
then we could not know until run time whether the incorrect call to A would
actually occur, so Program_Error
might
or might not be raised. It is possible for a compiler to
do a better job of analyzing bodies, to
determine whether or not Program_Error
might be raised, but it certainly
couldn't do a perfect job (that would require solving the halting problem
and is provably impossible), and because this is a warning anyway, it does
not seem worth the effort to do the analysis. Cases in which it
would be relevant are rare.
In practice, warnings of either of the forms given above will usually correspond to real errors, and should be examined carefully and eliminated. In the rare case where a warning is bogus, it can be suppressed by any of the following methods:
-gnatws
switch set
Elaboration_Checks
for the called subprogram
Warnings_Off
to turn warnings off for the call
For the internal elaboration check case,
GNAT by default generates the
necessary run-time checks to ensure
that Program_Error
is raised if any
call fails an elaboration check. Of course this can only happen if a
warning has been issued as described above. The use of pragma
Suppress (Elaboration_Checks)
may (but is not guaranteed) to suppress
some of these checks, meaning that it may be possible (but is not
guaranteed) for a program to be able to call a subprogram whose body
is not yet elaborated, without raising a Program_Error
exception.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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
in fact always
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.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The default behavior in GNAT ensures elaboration safety. In its default mode GNAT implements the rule we previously described as the right approach. Let's restate it:
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
,
Preelaborate
, or Elaborate_Body
,
then the client should have an
Elaborate_All
for the with
'ed unit. By following this rule a client
is assured that calls and instantiations can be made without risk of an exception.
In this mode GNAT traces all calls that are potentially made from
elaboration code, and put in any missing implicit Elaborate_All
pragmas.
The advantage of this approach is that no elaboration problems
are possible if the binder can find an elaboration order that is
consistent with these implicit Elaborate_All
pragmas. The
disadvantage of this approach is that no such order may exist.
If the binder does not generate any diagnostics, then it means that it
has found an elaboration order that is guaranteed to be safe. However,
the binder may still be relying on implicitly generated
Elaborate_All
pragmas so portability to other compilers than
GNAT is not guaranteed.
If it is important to guarantee portability, then the compilations should
use the
-gnatwl
(warn on elaboration problems) switch. This will cause warning messages
to be generated indicating the missing Elaborate_All
pragmas.
Consider the following source program:
with k; package j is m : integer := k.r; end; |
where it is clear that there
should be a pragma Elaborate_All
for unit k
. An implicit pragma will be generated, and it is
likely that the binder will be able to honor it. However,
it is safer to include the pragma explicitly in the source. If this
unit is compiled with the
-gnatwl
switch, then the compiler outputs a warning:
1. with k; 2. package j is 3. m : integer := k.r; | >>> warning: call to "r" may raise Program_Error >>> warning: missing pragma Elaborate_All for "k" 4. end; |
and these warnings can be used as a guide for supplying manually the missing pragmas.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
If the binder cannot find an acceptable order, it outputs detailed diagnostics. For example:
error: elaboration circularity detected info: "proc (body)" must be elaborated before "pack (body)" info: reason: Elaborate_All probably needed in unit "pack (body)" info: recompile "pack (body)" with -gnatwl info: for full details info: "proc (body)" info: is needed by its spec: info: "proc (spec)" info: which is withed by: info: "pack (body)" info: "pack (body)" must be elaborated before "proc (body)" info: reason: pragma Elaborate in unit "proc (body)" |
In this case we have a cycle that the binder cannot break. On the one
hand, there is an explicit pragma Elaborate in proc
for
pack
. This means that the body of pack
must be elaborated
before the body of proc
. On the other hand, there is elaboration
code in pack
that calls a subprogram in proc
. This means
that for maximum safety, there should really be a pragma
Elaborate_All in pack
for proc
which would require that
the body of proc
be elaborated before the body of
pack
. Clearly both requirements cannot be satisfied.
Faced with a circularity of this kind, you have three different options.
-gnatE
(dynamic elaboration check) switch, then GNAT behaves in
a quite different manner. Dynamic checks are generated for all calls
that could possibly result in raising an exception. With this switch,
the compiler does not generate implicit Elaborate_All
pragmas.
The behavior then is exactly as specified in the Ada 95 Reference Manual.
The binder will generate an executable program that may or may not
raise Program_Error, and then it is the programmer's job to ensure
that it does not raise an exception. Note that it is important to
compile all units with the switch, it cannot be used selectively.
-f
switch for the
gnatbind
step, or
-bargs -f
if you are using
gnatmake
.
This switch tells the binder to ignore any implicit Elaborate_All
pragmas that were generated by the compiler, and suppresses any
circularity messages that they cause. The resulting executable will work
properly if there are no elaboration problems, but if there are some order of
elaboration problems they will not be detected, and unexpected
results may occur.
It is hard to generalize on which of these three approaches should be
taken. Obviously if it is possible to fix the program so that the default
treatment works, this is preferable, but this may not always be practical.
It is certainly simple enough to use
-gnatE
or
-f
but the danger in either case is that, even if the GNAT binder
finds a correct elaboration order, it may not always do so,
and certainly a binder from another Ada compiler might not. A
combination of testing and analysis (for which the warnings generated
with the
-gnatwl
switch can be useful) must be used to ensure that the program is free
of errors. One switch that is useful in this testing is the
-h (horrible elaboration order)
switch for
gnatbind
.
Normally the binder tries to find an order that has the best chance of
of avoiding elaboration problems. With this switch, the binder
plays a devil's advocate role, and tries to choose the order that
has the best chance of failing. If your program works even with this
switch, then it has a better chance of being error free, but this is still
not a guarantee.
For an example of this approach in action, consider the C-tests (executable tests) from the ACVC suite. If these are compiled and run with the default treatment, then all but one of them succeed without generating any error diagnostics from the binder. However, there is one test that fails, and this is not surprising, because the whole point of this test is to ensure that the compiler can handle cases where it is impossible to determine a correct order statically, and it checks that an exception is indeed raised at run time.
This one test must be compiled and run using the
-gnatE
switch, and then it passes. Alternatively, the entire suite can
be run using this switch. It is never wrong to run with the dynamic
elaboration switch if your code is correct, and we assume that the
C-tests are indeed correct (it is less efficient, but efficiency is
not a factor in running the ACVC tests.)
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The introduction of access-to-subprogram types in Ada 95 complicates the handling of elaboration. The trouble is that it becomes impossible to tell at compile time which procedure is being called. This means that it is not possible for the binder to analyze the elaboration requirements in this case.
If at the point at which the access value is created, the body of the subprogram is
known to have been elaborated, then the access value is safe, and its use
does not require a check. This may be achieved by appropriate arrangement
of the order of declarations if the subprogram is in the current unit,
or, if the subprogram is in another unit, by using pragma
Pure
, Preelaborate
, or Elaborate_Body
on the referenced unit.
If the referenced body is not known to have been elaborated at the point
the access value is created, then any use of the access value must do a
dynamic check, and this dynamic check will fail and raise a
Program_Error
exception if the body has not been elaborated yet.
GNAT will generate the necessary checks, and in addition, if the
-gnatwl
switch is set, will generate warnings that such checks are required.
The use of dynamic dispatching for tagged types similarly generates
a requirement for dynamic checks, and premature calls to any primitive
operation of a tagged type before the body of the operation has been elaborated,
will result in the raising of Program_Error
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
First, compile your program with the default options, using none of
the special elaboration control switches. If the binder successfully
binds your program, then you can be confident that, apart from issues
raised by the use of access-to-subprogram types and dynamic dispatching,
the program is free of elaboration errors. If it is important that the
program be portable, then use the
-gnatwl
switch to generate warnings about missing Elaborate_All
pragmas, and supply the missing pragmas.
If the program fails to bind using the default static elaboration
handling, then you can fix the program to eliminate the binder
message, or recompile the entire program with the
-gnatE
switch to generate dynamic elaboration checks,
or, if you are sure there really are no elaboration problems,
use the
-f
switch for the binder to cause it to ignore implicit Elaborate_All
pragmas generated by the compiler.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |