blob: 90b64a7f17a0f113060c3f73818eba20b604fa8d [file] [log] [blame]
.. |with| replace:: *with*
.. |withs| replace:: *with*\ s
.. |withed| replace:: *with*\ ed
.. |withing| replace:: *with*\ ing
.. -- Example: A |withing| unit has a |with| clause, it |withs| a |withed| unit
.. _Elaboration_Order_Handling_in_GNAT:
**********************************
Elaboration Order Handling in GNAT
**********************************
.. index:: Order of elaboration
.. index:: Elaboration control
This appendix describes the handling of elaboration code in Ada 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.
.. _Elaboration_Code:
Elaboration Code
================
Ada 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:
* *Initializers for variables*
Variables declared at the library level, in package specs or bodies, can
require initialization that is performed at elaboration time, as in:
.. code-block:: ada
Sqrt_Half : Float := Sqrt (0.5);
* *Package initialization code*
Code in a `BEGIN-END` section at the outer level of a package body is
executed as part of the package body elaboration code.
* *Library level task allocators*
Tasks that are declared using task allocators at the library level
start executing immediately and hence can execute at elaboration time.
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 a series of elaboration code sections, 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`,
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 |withing|:
.. code-block:: ada
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`:
.. code-block:: ada
Sqrt_1 : Float := Sqrt (0.1);
The elaboration code of the body of `Unit_1` also contains:
.. code-block:: ada
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`:
.. code-block:: ada
Sqrt_2 : Float := Sqrt (0.1);
The elaboration code of the body of `Unit_2` also contains:
.. code-block:: ada
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_1`
will occur, but not the call to `Func_2.`
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.
.. _Checking_the_Elaboration_Order:
Checking the Elaboration Order
==============================
In some languages that involve the same kind of elaboration problems,
e.g., Java and C++, the programmer needs to take these
ordering problems into account, 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 is designed to be a safe language, and a programmer-beware approach is
clearly not sufficient. Consequently, the language provides three lines
of defense:
* *Standard rules*
Some standard rules restrict the possible choice of elaboration
order. In particular, if you |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.
.. index:: Elaboration checks
.. index:: Checks, elaboration
* *Dynamic elaboration checks*
Dynamic checks are made at run time, so that if some entity is accessed
before it is elaborated (typically by means of a subprogram call)
then the exception (`Program_Error`) is raised.
* *Elaboration control*
Facilities are provided for the programmer to specify the desired order
of elaboration.
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 has two
rules which are a little more restrictive, but easier to check, and
easier to state:
* *Restrictions on calls*
A subprogram can only be called at elaboration time if its body
has been elaborated. The rules for elaboration given above guarantee
that the spec of the subprogram has been elaborated before the
call, but not the body. If this rule is violated, then the
exception `Program_Error` is raised.
* *Restrictions on instantiations*
A generic unit can only be instantiated if the body of the generic
unit has been elaborated. Again, the rules for elaboration given above
guarantee that the spec of the generic unit has been elaborated
before the instantiation, but not the body. If this rule is
violated, then the exception `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.
Note that one might think that it would be good enough to have one Boolean
variable for each package, but that would not deal with cases of trying
to call a body in the same package as the call
that has not been elaborated yet.
Of course a compiler may be able to do enough analysis to optimize away
some of the Boolean variables as unnecessary, and `GNAT` indeed
does such optimizations, but still the easiest conceptual model is to
think of there being one variable per subprogram.
.. _Controlling_the_Elaboration_Order:
Controlling the Elaboration Order
=================================
In the previous section we discussed the rules in Ada 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 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:
* *packages that do not require a body*
A library package that does not require a body does not permit
a body (this rule was introduced in Ada 95).
Thus if we have a such a package, as in:
.. code-block:: ada
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 |withs| `Definitions` may safely instantiate
`Definitions.Subp` because the compiler can determine that there
definitely is no package body to worry about in this case
.. index:: pragma Pure
* *pragma Pure*
This pragma places sufficient restrictions on a unit to guarantee that
no call to any subprogram in the unit can result in an
elaboration problem. This means that the compiler does not need
to worry about the point of elaboration of such units, and in
particular, does not need to check any calls to any subprograms
in this unit.
.. index:: pragma Preelaborate
* *pragma Preelaborate*
This pragma places slightly less stringent restrictions on a unit than
does pragma Pure,
but these restrictions are still sufficient to ensure that there
are no elaboration problems with any calls to the unit.
.. index:: pragma Elaborate_Body
* *pragma Elaborate_Body*
This pragma requires that the body of a unit be elaborated immediately
after its spec. Suppose a unit `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 |withing| 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
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:
.. index:: pragma Elaborate
* *pragma Elaborate (unit)*
This pragma is placed in the context clause, after a |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.
.. index:: pragma Elaborate_All
* *pragma Elaborate_All (unit)*
This is a stronger version of the Elaborate pragma. Consider the
following example::
Unit A |withs| unit B and calls B.Func in elab code
Unit B |withs| 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` |withs| `C`.
We are now in a position to give a usage rule in Ada 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 |withed| unit, or instantiate
a generic package in a |withed| unit,
then if the |withed| unit does not have
pragma `Pure` or `Preelaborate`, then the client should have
a pragma `Elaborate_All`for the |withed| unit.**
By following this rule a client is
assured that calls can be made without risk of an exception.
For generic subprogram instantiations, the rule can be relaxed to
require only a pragma `Elaborate` since elaborating the body
of a subprogram cannot cause any transitive elaboration (we are
not calling the subprogram in this case, just elaborating its
declaration).
If this rule is not followed, then a program may be in one of four
states:
* *No order exists*
No order of elaboration exists which follows the rules, taking into
account any `Elaborate`, `Elaborate_All`,
or `Elaborate_Body` pragmas. In
this case, an Ada compiler must diagnose the situation at bind
time, and refuse to build an executable program.
* *One or more orders exist, all incorrect*
One or more acceptable elaboration orders exist, and all of them
generate an elaboration order problem. In this case, the binder
can build an executable program, but `Program_Error` will be raised
when the program is run.
* *Several orders exist, some right, some incorrect*
One or more acceptable elaboration orders exists, and some of them
work, and some do not. The programmer has not controlled
the order of elaboration, so the binder may or may not pick one of
the correct orders, and the program may or may not raise an
exception when it is run. This is the worst case, because it means
that the program may fail when moved to another compiler, or even
another version of the same compiler.
* *One or more orders exists, all correct*
One ore more acceptable elaboration orders exist, and all of them
work. In this case the program runs successfully. This state of
affairs can be guaranteed by following the rule we gave above, but
may be true even if the rule is not followed.
Note that one additional advantage of following our rules on the use
of `Elaborate` and `Elaborate_All`
is that the program continues to stay in the ideal (all orders OK) state
even if maintenance
changes some bodies of some units. 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.
.. _Controlling_Elaboration_in_GNAT_-_Internal_Calls:
Controlling Elaboration in GNAT - Internal Calls
================================================
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:
.. code-block:: ada
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, 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 as well as later versions of the Ada standard,
declarations can appear in any order, so there is no restriction that
would prevent this reordering, and if we write:
.. code-block:: ada
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:
.. code-block:: ada
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
.. code-block:: ada
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:
* Compile with the *-gnatws* switch set
* Suppress `Elaboration_Check` for the called subprogram
* Use pragma `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_Check)` 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.
.. _Controlling_Elaboration_in_GNAT_-_External_Calls:
Controlling Elaboration in GNAT - External Calls
================================================
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:
.. code-block:: ada
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
.. code-block:: ada
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` |withs| `Y`,
which means you would have to
elaborate the body of `Y` first, but that |withs| `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.
.. _Default_Behavior_in_GNAT_-_Ensuring_Safety:
Default Behavior in GNAT - Ensuring Safety
==========================================
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 |withed| unit, or instantiate a generic
package in a |withed| unit, then if the |withed| unit
does not have pragma `Pure` or `Preelaborate`, then the client should have an
`Elaborate_All` pragma for the |withed| unit.*
*In the case of instantiating a generic subprogram, it is always
sufficient to have only an `Elaborate` pragma for the
|withed| 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 puts in any missing implicit `Elaborate`
and `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` and
`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` and
`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 *-gnatel*
(info messages for elaboration pragmas) switch. This will cause info messages
to be generated indicating the missing `Elaborate` and
`Elaborate_All` pragmas.
Consider the following source program:
.. code-block:: ada
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, if you want
to port this program to some other Ada compiler than GNAT.
it is safer to include the pragma explicitly in the source. If this
unit is compiled with the *-gnatel*
switch, then the compiler outputs an information message::
1. with k;
2. package j is
3. m : integer := k.r;
|
>>> info: call to "r" may raise Program_Error
>>> info: missing pragma Elaborate_All for "k"
4. end;
and these messages can be used as a guide for supplying manually
the missing pragmas. It is usually a bad idea to use this
option during development. That's because it will tell you when
you need to put in a pragma, but cannot tell you when it is time
to take it out. So the use of pragma `Elaborate_All` may lead to
unnecessary dependencies and even false circularities.
This default mode is more restrictive than the Ada Reference
Manual, and it is possible to construct programs which will compile
using the dynamic model described there, but will run into a
circularity using the safer static model we have described.
Of course any Ada compiler must be able to operate in a mode
consistent with the requirements of the Ada Reference Manual,
and in particular must have the capability of implementing the
standard dynamic model of elaboration with run-time checks.
In GNAT, this standard mode can be achieved either by the use of
the *-gnatE* switch on the compiler (*gcc* or
*gnatmake*) command, or by the use of the configuration pragma:
.. code-block:: ada
pragma Elaboration_Checks (DYNAMIC);
Either approach will cause the unit affected to be compiled using the
standard dynamic run-time elaboration checks described in the Ada
Reference Manual. The static model is generally preferable, since it
is clearly safer to rely on compile and link time checks rather than
run-time checks. However, in the case of legacy code, it may be
difficult to meet the requirements of the static model. This
issue is further discussed in
:ref:`What_to_Do_If_the_Default_Elaboration_Behavior_Fails`.
Note that the static model provides a strict subset of the allowed
behavior and programs of the Ada Reference Manual, so if you do
adhere to the static model and no circularities exist,
then you are assured that your program will
work using the dynamic model, providing that you remove any
pragma Elaborate statements from the source.
.. _Treatment_of_Pragma_Elaborate:
Treatment of Pragma Elaborate
=============================
.. index:: Pragma Elaborate
The use of `pragma Elaborate`
should generally be avoided in Ada 95 and Ada 2005 programs,
since there is no guarantee that transitive calls
will be properly handled. Indeed at one point, this pragma was placed
in Annex J (Obsolescent Features), on the grounds that it is never useful.
Now that's a bit restrictive. In practice, the case in which
`pragma Elaborate` is useful is when the caller knows that there
are no transitive calls, or that the called unit contains all necessary
transitive `pragma Elaborate` statements, and legacy code often
contains such uses.
Strictly speaking the static mode in GNAT should ignore such pragmas,
since there is no assurance at compile time that the necessary safety
conditions are met. In practice, this would cause GNAT to be incompatible
with correctly written Ada 83 code that had all necessary
`pragma Elaborate` statements in place. Consequently, we made the
decision that GNAT in its default mode will believe that if it encounters
a `pragma Elaborate` then the programmer knows what they are doing,
and it will trust that no elaboration errors can occur.
The result of this decision is two-fold. First to be safe using the
static mode, you should remove all `pragma Elaborate` statements.
Second, when fixing circularities in existing code, you can selectively
use `pragma Elaborate` statements to convince the static mode of
GNAT that it need not generate an implicit `pragma Elaborate_All`
statement.
When using the static mode with *-gnatwl*, any use of
`pragma Elaborate` will generate a warning about possible
problems.
.. _Elaboration_Issues_for_Library_Tasks:
Elaboration Issues for Library Tasks
====================================
.. index:: Library tasks, elaboration issues
.. index:: Elaboration of library tasks
In this section we examine special elaboration issues that arise for
programs that declare library level tasks.
Generally the model of execution of an Ada program is that all units are
elaborated, and then execution of the program starts. However, the
declaration of library tasks definitely does not fit this model. The
reason for this is that library tasks start as soon as they are declared
(more precisely, as soon as the statement part of the enclosing package
body is reached), that is to say before elaboration
of the program is complete. This means that if such a task calls a
subprogram, or an entry in another task, the callee may or may not be
elaborated yet, and in the standard
Reference Manual model of dynamic elaboration checks, you can even
get timing dependent Program_Error exceptions, since there can be
a race between the elaboration code and the task code.
The static model of elaboration in GNAT seeks to avoid all such
dynamic behavior, by being conservative, and the conservative
approach in this particular case is to assume that all the code
in a task body is potentially executed at elaboration time if
a task is declared at the library level.
This can definitely result in unexpected circularities. Consider
the following example
.. code-block:: ada
package Decls is
task Lib_Task is
entry Start;
end Lib_Task;
type My_Int is new Integer;
function Ident (M : My_Int) return My_Int;
end Decls;
with Utils;
package body Decls is
task body Lib_Task is
begin
accept Start;
Utils.Put_Val (2);
end Lib_Task;
function Ident (M : My_Int) return My_Int is
begin
return M;
end Ident;
end Decls;
with Decls;
package Utils is
procedure Put_Val (Arg : Decls.My_Int);
end Utils;
with Text_IO;
package body Utils is
procedure Put_Val (Arg : Decls.My_Int) is
begin
Text_IO.Put_Line (Decls.My_Int'Image (Decls.Ident (Arg)));
end Put_Val;
end Utils;
with Decls;
procedure Main is
begin
Decls.Lib_Task.Start;
end;
If the above example is compiled in the default static elaboration
mode, then a circularity occurs. The circularity comes from the call
`Utils.Put_Val` in the task body of `Decls.Lib_Task`. Since
this call occurs in elaboration code, we need an implicit pragma
`Elaborate_All` for `Utils`. This means that not only must
the spec and body of `Utils` be elaborated before the body
of `Decls`, but also the spec and body of any unit that is
|withed| by the body of `Utils` must also be elaborated before
the body of `Decls`. This is the transitive implication of
pragma `Elaborate_All` and it makes sense, because in general
the body of `Put_Val` might have a call to something in a
|withed| unit.
In this case, the body of Utils (actually its spec) |withs|
`Decls`. Unfortunately this means that the body of `Decls`
must be elaborated before itself, in case there is a call from the
body of `Utils`.
Here is the exact chain of events we are worrying about:
* In the body of `Decls` a call is made from within the body of a library
task to a subprogram in the package `Utils`. Since this call may
occur at elaboration time (given that the task is activated at elaboration
time), we have to assume the worst, i.e., that the
call does happen at elaboration time.
* This means that the body and spec of `Util` must be elaborated before
the body of `Decls` so that this call does not cause an access before
elaboration.
* Within the body of `Util`, specifically within the body of
`Util.Put_Val` there may be calls to any unit |withed|
by this package.
* One such |withed| package is package `Decls`, so there
might be a call to a subprogram in `Decls` in `Put_Val`.
In fact there is such a call in this example, but we would have to
assume that there was such a call even if it were not there, since
we are not supposed to write the body of `Decls` knowing what
is in the body of `Utils`; certainly in the case of the
static elaboration model, the compiler does not know what is in
other bodies and must assume the worst.
* This means that the spec and body of `Decls` must also be
elaborated before we elaborate the unit containing the call, but
that unit is `Decls`! This means that the body of `Decls`
must be elaborated before itself, and that's a circularity.
Indeed, if you add an explicit pragma `Elaborate_All` for `Utils` in
the body of `Decls` you will get a true Ada Reference Manual
circularity that makes the program illegal.
In practice, we have found that problems with the static model of
elaboration in existing code often arise from library tasks, so
we must address this particular situation.
Note that if we compile and run the program above, using the dynamic model of
elaboration (that is to say use the *-gnatE* switch),
then it compiles, binds,
links, and runs, printing the expected result of 2. Therefore in some sense
the circularity here is only apparent, and we need to capture
the properties of this program that distinguish it from other library-level
tasks that have real elaboration problems.
We have four possible answers to this question:
* Use the dynamic model of elaboration.
If we use the *-gnatE* switch, then as noted above, the program works.
Why is this? If we examine the task body, it is apparent that the task cannot
proceed past the
`accept` statement until after elaboration has been completed, because
the corresponding entry call comes from the main program, not earlier.
This is why the dynamic model works here. But that's really giving
up on a precise analysis, and we prefer to take this approach only if we cannot
solve the
problem in any other manner. So let us examine two ways to reorganize
the program to avoid the potential elaboration problem.
* Split library tasks into separate packages.
Write separate packages, so that library tasks are isolated from
other declarations as much as possible. Let us look at a variation on
the above program.
.. code-block:: ada
package Decls1 is
task Lib_Task is
entry Start;
end Lib_Task;
end Decls1;
with Utils;
package body Decls1 is
task body Lib_Task is
begin
accept Start;
Utils.Put_Val (2);
end Lib_Task;
end Decls1;
package Decls2 is
type My_Int is new Integer;
function Ident (M : My_Int) return My_Int;
end Decls2;
with Utils;
package body Decls2 is
function Ident (M : My_Int) return My_Int is
begin
return M;
end Ident;
end Decls2;
with Decls2;
package Utils is
procedure Put_Val (Arg : Decls2.My_Int);
end Utils;
with Text_IO;
package body Utils is
procedure Put_Val (Arg : Decls2.My_Int) is
begin
Text_IO.Put_Line (Decls2.My_Int'Image (Decls2.Ident (Arg)));
end Put_Val;
end Utils;
with Decls1;
procedure Main is
begin
Decls1.Lib_Task.Start;
end;
All we have done is to split `Decls` into two packages, one
containing the library task, and one containing everything else. Now
there is no cycle, and the program compiles, binds, links and executes
using the default static model of elaboration.
* Declare separate task types.
A significant part of the problem arises because of the use of the
single task declaration form. This means that the elaboration of
the task type, and the elaboration of the task itself (i.e., the
creation of the task) happen at the same time. A good rule
of style in Ada is to always create explicit task types. By
following the additional step of placing task objects in separate
packages from the task type declaration, many elaboration problems
are avoided. Here is another modified example of the example program:
.. code-block:: ada
package Decls is
task type Lib_Task_Type is
entry Start;
end Lib_Task_Type;
type My_Int is new Integer;
function Ident (M : My_Int) return My_Int;
end Decls;
with Utils;
package body Decls is
task body Lib_Task_Type is
begin
accept Start;
Utils.Put_Val (2);
end Lib_Task_Type;
function Ident (M : My_Int) return My_Int is
begin
return M;
end Ident;
end Decls;
with Decls;
package Utils is
procedure Put_Val (Arg : Decls.My_Int);
end Utils;
with Text_IO;
package body Utils is
procedure Put_Val (Arg : Decls.My_Int) is
begin
Text_IO.Put_Line (Decls.My_Int'Image (Decls.Ident (Arg)));
end Put_Val;
end Utils;
with Decls;
package Declst is
Lib_Task : Decls.Lib_Task_Type;
end Declst;
with Declst;
procedure Main is
begin
Declst.Lib_Task.Start;
end;
What we have done here is to replace the `task` declaration in
package `Decls` with a `task type` declaration. Then we
introduce a separate package `Declst` to contain the actual
task object. This separates the elaboration issues for
the `task type`
declaration, which causes no trouble, from the elaboration issues
of the task object, which is also unproblematic, since it is now independent
of the elaboration of `Utils`.
This separation of concerns also corresponds to
a generally sound engineering principle of separating declarations
from instances. This version of the program also compiles, binds, links,
and executes, generating the expected output.
.. index:: No_Entry_Calls_In_Elaboration_Code restriction
* Use No_Entry_Calls_In_Elaboration_Code restriction.
The previous two approaches described how a program can be restructured
to avoid the special problems caused by library task bodies. in practice,
however, such restructuring may be difficult to apply to existing legacy code,
so we must consider solutions that do not require massive rewriting.
Let us consider more carefully why our original sample program works
under the dynamic model of elaboration. The reason is that the code
in the task body blocks immediately on the `accept`
statement. Now of course there is nothing to prohibit elaboration
code from making entry calls (for example from another library level task),
so we cannot tell in isolation that
the task will not execute the accept statement during elaboration.
However, in practice it is very unusual to see elaboration code
make any entry calls, and the pattern of tasks starting
at elaboration time and then immediately blocking on `accept` or
`select` statements is very common. What this means is that
the compiler is being too pessimistic when it analyzes the
whole package body as though it might be executed at elaboration
time.
If we know that the elaboration code contains no entry calls, (a very safe
assumption most of the time, that could almost be made the default
behavior), then we can compile all units of the program under control
of the following configuration pragma:
.. code-block:: ada
pragma Restrictions (No_Entry_Calls_In_Elaboration_Code);
This pragma can be placed in the :file:`gnat.adc` file in the usual
manner. If we take our original unmodified program and compile it
in the presence of a :file:`gnat.adc` containing the above pragma,
then once again, we can compile, bind, link, and execute, obtaining
the expected result. In the presence of this pragma, the compiler does
not trace calls in a task body, that appear after the first `accept`
or `select` statement, and therefore does not report a potential
circularity in the original program.
The compiler will check to the extent it can that the above
restriction is not violated, but it is not always possible to do a
complete check at compile time, so it is important to use this
pragma only if the stated restriction is in fact met, that is to say
no task receives an entry call before elaboration of all units is completed.
.. _Mixing_Elaboration_Models:
Mixing Elaboration Models
=========================
So far, we have assumed that the entire program is either compiled
using the dynamic model or static model, ensuring consistency. It
is possible to mix the two models, but rules have to be followed
if this mixing is done to ensure that elaboration checks are not
omitted.
The basic rule is that
**a unit compiled with the static model cannot
be |withed| by a unit compiled with the dynamic model**.
The reason for this is that in the static model, a unit assumes that
its clients guarantee to use (the equivalent of) pragma
`Elaborate_All` so that no elaboration checks are required
in inner subprograms, and this assumption is violated if the
client is compiled with dynamic checks.
The precise rule is as follows. A unit that is compiled with dynamic
checks can only |with| a unit that meets at least one of the
following criteria:
* The |withed| unit is itself compiled with dynamic elaboration
checks (that is with the *-gnatE* switch.
* The |withed| unit is an internal GNAT implementation unit from
the System, Interfaces, Ada, or GNAT hierarchies.
* The |withed| unit has pragma Preelaborate or pragma Pure.
* The |withing| unit (that is the client) has an explicit pragma
`Elaborate_All` for the |withed| unit.
If this rule is violated, that is if a unit with dynamic elaboration
checks |withs| a unit that does not meet one of the above four
criteria, then the binder (`gnatbind`) will issue a warning
similar to that in the following example::
warning: "x.ads" has dynamic elaboration checks and with's
warning: "y.ads" which has static elaboration checks
These warnings indicate that the rule has been violated, and that as a result
elaboration checks may be missed in the resulting executable file.
This warning may be suppressed using the *-ws* binder switch
in the usual manner.
One useful application of this mixing rule is in the case of a subsystem
which does not itself |with| units from the remainder of the
application. In this case, the entire subsystem can be compiled with
dynamic checks to resolve a circularity in the subsystem, while
allowing the main application that uses this subsystem to be compiled
using the more reliable default static model.
.. _What_to_Do_If_the_Default_Elaboration_Behavior_Fails:
What to Do If the Default Elaboration Behavior Fails
====================================================
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 -gnatel
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.
* *Fix the program*
The most desirable option from the point of view of long-term maintenance
is to rearrange the program so that the elaboration problems are avoided.
One useful technique is to place the elaboration code into separate
child packages. Another is to move some of the initialization code to
explicitly called subprograms, where the program controls the order
of initialization explicitly. Although this is the most desirable option,
it may be impractical and involve too much modification, especially in
the case of complex legacy code.
* *Perform dynamic checks*
If the compilations are done using the *-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` or `Elaborate_All` pragmas. The behavior then is
exactly as specified in the :title:`Ada 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.
* *Suppress checks*
The drawback of dynamic checks is that they generate a
significant overhead at run time, both in space and time. If you
are absolutely sure that your program cannot raise any elaboration
exceptions, and you still want to use the dynamic elaboration model,
then you can use the configuration pragma
`Suppress (Elaboration_Check)` to suppress all such checks. For
example this pragma could be placed in the :file:`gnat.adc` file.
* *Suppress checks selectively*
When you know that certain calls or instantiations in elaboration code cannot
possibly lead to an elaboration error, and the binder nevertheless complains
about implicit `Elaborate` and `Elaborate_All` pragmas that lead to
elaboration circularities, it is possible to remove those warnings locally and
obtain a program that will bind. Clearly this can be unsafe, and it is the
responsibility of the programmer to make sure that the resulting program has no
elaboration anomalies. The pragma `Suppress (Elaboration_Check)` can be
used with different granularity to suppress warnings and break elaboration
circularities:
* Place the pragma that names the called subprogram in the declarative part
that contains the call.
* Place the pragma in the declarative part, without naming an entity. This
disables warnings on all calls in the corresponding declarative region.
* Place the pragma in the package spec that declares the called subprogram,
and name the subprogram. This disables warnings on all elaboration calls to
that subprogram.
* Place the pragma in the package spec that declares the called subprogram,
without naming any entity. This disables warnings on all elaboration calls to
all subprograms declared in this spec.
* Use Pragma Elaborate.
As previously described in section :ref:`Treatment_of_Pragma_Elaborate`,
GNAT in static mode assumes that a `pragma` Elaborate indicates correctly
that no elaboration checks are required on calls to the designated unit.
There may be cases in which the caller knows that no transitive calls
can occur, so that a `pragma Elaborate` will be sufficient in a
case where `pragma Elaborate_All` would cause a circularity.
These five cases are listed in order of decreasing safety, and therefore
require increasing programmer care in their application. Consider the
following program:
.. code-block:: ada
package Pack1 is
function F1 return Integer;
X1 : Integer;
end Pack1;
package Pack2 is
function F2 return Integer;
function Pure (x : integer) return integer;
-- pragma Suppress (Elaboration_Check, On => Pure); -- (3)
-- pragma Suppress (Elaboration_Check); -- (4)
end Pack2;
with Pack2;
package body Pack1 is
function F1 return Integer is
begin
return 100;
end F1;
Val : integer := Pack2.Pure (11); -- Elab. call (1)
begin
declare
-- pragma Suppress(Elaboration_Check, Pack2.F2); -- (1)
-- pragma Suppress(Elaboration_Check); -- (2)
begin
X1 := Pack2.F2 + 1; -- Elab. call (2)
end;
end Pack1;
with Pack1;
package body Pack2 is
function F2 return Integer is
begin
return Pack1.F1;
end F2;
function Pure (x : integer) return integer is
begin
return x ** 3 - 3 * x;
end;
end Pack2;
with Pack1, Ada.Text_IO;
procedure Proc3 is
begin
Ada.Text_IO.Put_Line(Pack1.X1'Img); -- 101
end Proc3;
In the absence of any pragmas, an attempt to bind this program produces
the following diagnostics::
error: elaboration circularity detected
info: "pack1 (body)" must be elaborated before "pack1 (body)"
info: reason: Elaborate_All probably needed in unit "pack1 (body)"
info: recompile "pack1 (body)" with -gnatel for full details
info: "pack1 (body)"
info: must be elaborated along with its spec:
info: "pack1 (spec)"
info: which is withed by:
info: "pack2 (body)"
info: which must be elaborated along with its spec:
info: "pack2 (spec)"
info: which is withed by:
info: "pack1 (body)"
The sources of the circularity are the two calls to `Pack2.Pure` and
`Pack2.F2` in the body of `Pack1`. We can see that the call to
F2 is safe, even though F2 calls F1, because the call appears after the
elaboration of the body of F1. Therefore the pragma (1) is safe, and will
remove the warning on the call. It is also possible to use pragma (2)
because there are no other potentially unsafe calls in the block.
The call to `Pure` is safe because this function does not depend on the
state of `Pack2`. Therefore any call to this function is safe, and it
is correct to place pragma (3) in the corresponding package spec.
Finally, we could place pragma (4) in the spec of `Pack2` to disable
warnings on all calls to functions declared therein. Note that this is not
necessarily safe, and requires more detailed examination of the subprogram
bodies involved. In particular, a call to `F2` requires that `F1`
be already elaborated.
It is hard to generalize on which of these four 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*
but the danger in this 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
information messages generated with the *-gnatel*
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
*-p (pessimistic elaboration order)* switch for `gnatbind`.
Normally the binder tries to find an order that has the best chance
of avoiding elaboration problems. However, if this switch is used, 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 ACATS 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 ACATS tests.)
.. _Elaboration_for_Indirect_Calls:
Elaboration for Indirect Calls
==============================
.. index:: Dispatching calls
.. index:: Indirect calls
In rare cases, the static elaboration model fails to prevent
dispatching calls to not-yet-elaborated subprograms. In such cases, we
fall back to run-time checks; premature calls to any primitive
operation of a tagged type before the body of the operation has been
elaborated will raise `Program_Error`.
Access-to-subprogram types, however, are handled conservatively, and
do not require run-time checks. This was not true in earlier versions
of the compiler; you can use the *-gnatd.U* debug switch to
revert to the old behavior if the new conservative behavior causes
elaboration cycles. Here, 'conservative' means that if you do
`P'Access` during elaboration, the compiler will assume that you
might call `P` indirectly during elaboration, so it adds an
implicit `pragma Elaborate_All` on the library unit containing
`P`. The *-gnatd.U* switch is safe if you know there are
no such calls. If the program worked before, it will continue to work
with *-gnatd.U*. But beware that code modifications such as
adding an indirect call can cause erroneous behavior in the presence
of *-gnatd.U*.
.. _Summary_of_Procedures_for_Elaboration_Control:
Summary of Procedures for Elaboration Control
=============================================
.. index:: Elaboration control
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 to other compilers than GNAT, then use the
*-gnatel*
switch to generate messages about missing `Elaborate` or
`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,
and, if you are sure there really are no elaboration problems,
use a global pragma `Suppress (Elaboration_Check)`.
.. _Other_Elaboration_Order_Considerations:
Other Elaboration Order Considerations
======================================
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:
.. code-block:: ada
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:
.. code-block:: ada
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 |withed| 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.
However, GNAT does attempt to diagnose the common situation where there
are uninitialized variables in the visible part of a package spec, and the
corresponding package body has an elaboration block that directly or
indirectly initialized one or more of these variables. This is the situation
in which a pragma Elaborate_Body is usually desirable, and GNAT will generate
a warning that suggests this addition if it detects this situation.
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:
.. code-block:: sh
$ 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` or `Elaborate_All` pragmas to ensure the desired order.
.. _Determining_the_Chosen_Elaboration_Order:
Determining the Chosen Elaboration Order
========================================
To see the elaboration order that the binder chooses, you can look at
the last part of the file:`b~xxx.adb` binder output file. Here is an example::
System.Soft_Links'Elab_Body;
E14 := True;
System.Secondary_Stack'Elab_Body;
E18 := True;
System.Exception_Table'Elab_Body;
E24 := True;
Ada.Io_Exceptions'Elab_Spec;
E67 := True;
Ada.Tags'Elab_Spec;
Ada.Streams'Elab_Spec;
E43 := True;
Interfaces.C'Elab_Spec;
E69 := True;
System.Finalization_Root'Elab_Spec;
E60 := True;
System.Os_Lib'Elab_Body;
E71 := True;
System.Finalization_Implementation'Elab_Spec;
System.Finalization_Implementation'Elab_Body;
E62 := True;
Ada.Finalization'Elab_Spec;
E58 := True;
Ada.Finalization.List_Controller'Elab_Spec;
E76 := True;
System.File_Control_Block'Elab_Spec;
E74 := True;
System.File_Io'Elab_Body;
E56 := True;
Ada.Tags'Elab_Body;
E45 := True;
Ada.Text_Io'Elab_Spec;
Ada.Text_Io'Elab_Body;
E07 := True;
Here Elab_Spec elaborates the spec
and Elab_Body elaborates the body. The assignments to the :samp:`E{xx}` flags
flag that the corresponding body is now elaborated.
You can also ask the binder to generate a more
readable list of the elaboration order using the
`-l` switch when invoking the binder. Here is
an example of the output generated by this switch::
ada (spec)
interfaces (spec)
system (spec)
system.case_util (spec)
system.case_util (body)
system.concat_2 (spec)
system.concat_2 (body)
system.concat_3 (spec)
system.concat_3 (body)
system.htable (spec)
system.parameters (spec)
system.parameters (body)
system.crtl (spec)
interfaces.c_streams (spec)
interfaces.c_streams (body)
system.restrictions (spec)
system.restrictions (body)
system.standard_library (spec)
system.exceptions (spec)
system.exceptions (body)
system.storage_elements (spec)
system.storage_elements (body)
system.secondary_stack (spec)
system.stack_checking (spec)
system.stack_checking (body)
system.string_hash (spec)
system.string_hash (body)
system.htable (body)
system.strings (spec)
system.strings (body)
system.traceback (spec)
system.traceback (body)
system.traceback_entries (spec)
system.traceback_entries (body)
ada.exceptions (spec)
ada.exceptions.last_chance_handler (spec)
system.soft_links (spec)
system.soft_links (body)
ada.exceptions.last_chance_handler (body)
system.secondary_stack (body)
system.exception_table (spec)
system.exception_table (body)
ada.io_exceptions (spec)
ada.tags (spec)
ada.streams (spec)
interfaces.c (spec)
interfaces.c (body)
system.finalization_root (spec)
system.finalization_root (body)
system.memory (spec)
system.memory (body)
system.standard_library (body)
system.os_lib (spec)
system.os_lib (body)
system.unsigned_types (spec)
system.stream_attributes (spec)
system.stream_attributes (body)
system.finalization_implementation (spec)
system.finalization_implementation (body)
ada.finalization (spec)
ada.finalization (body)
ada.finalization.list_controller (spec)
ada.finalization.list_controller (body)
system.file_control_block (spec)
system.file_io (spec)
system.file_io (body)
system.val_uns (spec)
system.val_util (spec)
system.val_util (body)
system.val_uns (body)
system.wch_con (spec)
system.wch_con (body)
system.wch_cnv (spec)
system.wch_jis (spec)
system.wch_jis (body)
system.wch_cnv (body)
system.wch_stw (spec)
system.wch_stw (body)
ada.tags (body)
ada.exceptions (body)
ada.text_io (spec)
ada.text_io (body)
text_io (spec)
gdbstr (body)