[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9. Elaboration Order Handling in GNAT

9.1 Elaboration Code in Ada 95  
9.2 Checking the Elaboration Order in Ada 95  
9.3 Controlling the Elaboration Order in Ada 95  
9.4 Controlling Elaboration in GNAT - Internal Calls  
9.5 Controlling Elaboration in GNAT - External Calls  
9.6 Default Behavior in GNAT - Ensuring Safety  
9.7 What to do if the Default Elaboration Behavior Fails  
9.8 Elaboration for Access-to-Subprogram Values  
9.9 Summary of Procedures for Elaboration Control  

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] [ ? ]

9.1 Elaboration Code in Ada 95

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:

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:
 
   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 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] [ ? ]

9.2 Checking the Elaboration Order in Ada 95

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:

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.

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 95 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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.3 Controlling the Elaboration Order in Ada 95

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:

packages that do not require a body
In Ada 95, a library package that does not require a body does not permit a body. This means that if we have a such a package, as in:

 
   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

pragma Pure
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.

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.

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 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:

pragma Elaborate (unit)
This pragma is placed in the context clause, after a 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.

pragma Elaborate_All (unit)
This is a stronger version of the Elaborate pragma. Consider the following example:

 
   Unit A with's unit B and calls B.Func in elaboration code
   Unit B with's unit C, and B.Func calls C.Func

Now if we put a pragma Elaborate (B) in unit A, this ensures that the body of B is elaborated before the call, but not the body of C, so the call to C.Func could still cause Program_Error to be raised.

The effect of a pragma Elaborate_All is stronger, it requires not only that the body of the named unit be elaborated before the unit doing the with, but also the bodies of all units that the named unit uses, following with links transitively. For example, if we put a pragma Elaborate_All (B) in unit A, then it requires not only that the body of B be elaborated before A, but also the body of C, because B with's C.

We are now in a position to give a usage rule in Ada 95 for avoiding elaboration problems, at least if dynamic dispatching and access to subprogram values are not used. We will handle these cases separately later.

The rule is simple. If a unit has elaboration code that can directly or indirectly make a call to a subprogram in a with'ed unit, or instantiate a generic unit in a with'ed unit, then if the with'ed unit does not have pragma Pure, 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:

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 95 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 exists, 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 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] [ ? ]

9.4 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:

 
   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:

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] [ ? ]

9.5 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:

 
   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] [ ? ]

9.6 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 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] [ ? ]

9.7 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 -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.

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_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.

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, then you can use the -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] [ ? ]

9.8 Elaboration for Access-to-Subprogram Values

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] [ ? ]

9.9 Summary of Procedures for 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, 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] [ ? ]

This document was generated by Tom Bennet on August, 25 2000 using texi2html