In the case of internal calls, that is 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, and indeed XGC Ada 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 seen >>> 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 Y;
Note that in this particular case, it is probably the case that, because One does not access any global variables, the call really would be safe, but 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 elaboration 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, and no warning is generated, and no Program_Error exception will be raised. Things get a bit 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 XGC Ada 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" may occur before body is seen >>> warning: Program_Error may be raised at run time >>> 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 X;
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 on run-time flow of control in the general case. 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 till run time whether the incorrect call to A would actually occur, so Program_Error might or might not be raised. If XGC Ada felt more ambitious, it could 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 types given above will usually correspond to real errors, and should be examined carefully, and typically eliminated. In the rare case that a warning is bogus, it can be suppressed by any of the following methods:
Compile with the -gnatws switch set
Suppress Elaboration_Checks for the called subprogram
Use pragma Warnings_Off to turn warnings off for the call
For the internal elaboration check case, XGC Ada 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.