Version 1.3 of ai12s/ai12-0377-1.txt

Unformatted version of ai12s/ai12-0377-1.txt version 1.3
Other versions for file ai12s/ai12-0377-1.txt

!standard 6.4.1(5.1/4)          20-04-20 AI12-0377-1/01
!standard 6.4.1(5.2/4)
!standard 6.4.1(5.3/4)
!standard 6.4.1(13/3)
!standard 6.4.1(13.2/4)
!standard 6.4.1(13.3/4)
!class Amendment 20-04-20
!status work item 20-04-20
!status received 20-03-26
!priority Low
!difficulty Medium
!subject View conversions and out parameters passed by copy revisited
!summary
An actual of an out parameter that is a view conversion is illegal if either the target or operand type has Default_Value specified while the other does not. Program_Error is raised if this occurs where one of the types is a generic formal type.
An actual of an out parameter that is a view conversion of an access type is not illegal if the types are not related, rather null is passed if the access value fails a membership check on the subtype of the parameter (ignoring any exclusion or predicate).
!problem
(1) Consider this example:
procedure View_Conversion_With_Default_Value is
type Defaulted_Integer is new Integer with Default_Value => 123;
procedure P (X : out Integer) is begin null; -- X happens to be unassigned end P;
X : Defaulted_Integer; Y : Defaulted_Integer := 456;
begin P (Integer (X)); -- Deinitialize X?
P (Integer (Y)); -- Deinitialize Y? end View_Conversion_With_Default_Value;
Even though the type of X and Y has specified Default_Value, the value of the actual isn't copied into the formal in this case (it falls under the rule of 6.4.1(15)), leaving the formal parameter uninitialized. Upon return, the value of the formal will be converted to Defaulted_Integer and assigned to the actual object.
If the formal wasn't assigned to within the procedure, as in the above example, then the (uninitialized) value that comes back will deinitialize the actual object, which is the sort of thing that shouldn't normally be possible for an object whose type has a specified Default_Value.
(2) The legality rule that prevents view conversions of unrelated access types for out parameters is a compatibility problem in practice. When we implemented this in GNAT, a significant number of incompatibilities in our customer regression test suite failed to compile.
!proposal
(See Summary.)
!wording
Replace 6.4.1(5.1/3) with:
If the mode is out, the actual parameter is a view conversion, and the type of the formal parameter is a scalar type, then either: * neither the target nor operand type have the Default_Value aspect specified; or * there shall exist a type (other than a root numeric type) that is an ancestor of
both the target type and the operand type, and both the target and operand types have the Default_Value aspect specified.
[Author's note: This removes the access part of this rule, and makes a version apply to all view conversions.]
Modify 6.4.1(13/3):
[Author's Note: Broken into two bullets. I couldn't figure out how to factor out the "without checking" part of the rule; it would be best to do that to decrease the noise.]
For an access type,
{* if a membership test of the value of the actual against the subtype of the parameter, ignoring the result of any predicate or the exclusion of the null value, yields True, then} the formal parameter is initialized from the value of the actual, without checking that the value satisfies [any constraint, ]any predicate[,] or any exclusion of the null value;
{* otherwise, the the formal parameter is initialized to the null value of the formal type, without checking that the value satisfies any predicate or any exclusion of the null value.}
AARM Implementation Note: This rule means that any constraint checks, tag checks, and accessibility checks can be assumed to pass for an out parameter, but null exclusions and predicates cannot be assumed unless the compiler can prove that the object has been previously written within the subprogram.
Revert 6.4.1(13.1/4) to its previous version.
Delete 6.4.1(13.2-13.4/4), and AARM 6.4.1(13.d/4).
Add after 6.4.1(15):
* Furthermore, if the type is a scalar type, and the actual parameter is a
view conversion, then if neither:
* neither the target nor operand type have the Default_Value aspect specified; or
* there exists a type (other than a root numeric type) that is an ancestor of both the target type and the operand type, and both the target and operand types have the Default_Value aspect specified
then Program_Error is raised;
!discussion
For [1], note that "deinitialization" here cannot introduce an invalid value into an object that the compiler assumed to be valid (the conversion back to the actual would still do a subtype check, which would cause Constraint_Error if the value is invalid. Note that the check would be required since one cannot assume anything about out parameters). Even so, it seems dangerous enough that we should take steps to prevent it.
The best solution is to make any view conversion used in an out parameter illegal if one type has Default_Value and the other does not. Note that the repeal of 13.1(10/3) by AI12-0376-1 means that having the types be related no longer prevents problems (as a derived type can define a Default_Value when the parent type does not have one), so we would have to do something with this rule in any case.
The alternative of passing the default value in problematic cases would make the deinitialization noted in the problem worse (since it would clobber the current value of the object unconditionally, even if it would be in range), so that is not a viable solution to this problem.
For [2], it's fairly clear that we cannot have the original Legality Rule, as it seems too incompatible. However, this problem occurs not only for access types with different sizes (as outlined in the original AI12-0074-1), but also for any case where the generated code may need to know details about an out parameter. Three such cases were identified in the e-mail attached to AI12-0074-1:
[A] Cases involving discriminant checks; [B] Cases involving tag checks; [C] Cases involving accessibility checks.
These cases can be seen in the test program given at the start of the !appendix for this AI (more on this test program below).
An implementation could assume that all out parameters are unconstrained (and thus making the discriminant checks on every use), thus defanging problemg [A]. This would not be problematic. However, the tag and accessibility checks are not normally done as usage sites, so assuming implementations will do the right thing would impose a substantial implementation burden for a very unusual case (but one that is not quite as pathological as we expected in AI12-0074-1).
We created a test program to see what existing compilers do in these cases. The test program is reproduced at the start of the !appendix. The test program includes a few preliminary cases to check what an implementation does in cases not involved with the cases in question, then it tries each of the three problematic examples in turn.
Running the test program on a recent version of GNAT (from February 2020) shows that it side-steps this problem by passing null to any out parameter when the actual is an explicit view conversion. The implicit view conversion of derivation works as expected, however, so we have a case where one cannot write the same thing as the compiler does automatically.
Running the test program on a recent version of ObjectAda (thanks to PTC for providing these results): The discriminant and accessibility cases do not compile (ObjectAda rejects the conversions suggesting an Ada 2005 implementation of those). ObjectAda fully supports the derived type cases, including using explicit conversions. The tagged type case causes no errors nor obvious overwriting of memory (but it is not clear what actually was written).
Running the test program on a recent version of ApexAda (thanks to PTC for providing these results): The accessibility cases do not compile (ApexAda rejects the conversion with a static accessibility check). ObjectAda fully supports the derived type cases, including using explicit conversions. The discriminant case shows that ApexAda does not assume the constraints of an out parameter, so a check was made and Constraint_Error raised. The tagged type case causes no errors nor obvious overwriting of memory (but it is not clear what actually was written).
Running the test program on a recent version of Janus/Ada shows that it fully supports the derived type cases; it assumes that out parameters do not enforce their constraints (so that they are effectively unconstrained); other memory is overwritten by the tagged case (execution is erroneous); and the accessibility case does not compile (Janus/Ada does not yet support dynamic accessibility for stand-alone objects of anonymous access types).
---
These results suggest that having problematic cases use the GNAT rule of passing null would not be significantly incompatible in practice. We would only want to use such a rule in cases that are actually problems, in order that derivation works as expected (as noted in the preliminary parts of the test program), and so that explicit calls that are exactly like calls on inherited subprograms work the same way as the inherited call. Note that GNAT is the only compiler that passes null currently, other compilers implement the language as defined more closely.
Note that an out parameter can always be null, even if the parameter is declared with a null exclusion. Null exclusions are not checked when the parameter is passed in, and null is passed when the actual is not convertable. An implementation should not assume that null exclusions of an out parameter are enforced before the out parameter is written within the subprogram.
We could have required null to be passed for all out parameters of access types. That may have been a more sensible rule for a new language, but it would mean that changing an out parameter of a record type to an access-to-record type would also require changing the mode to "in out" if any discriminants or bounds need to be read. Presumably, that is why Ada initially made the choice it did. In any case, the runtime incompatiblity creating by generally making such a change would be intolerable, so it is much too late to contemplate that.
The only other alternative to requiring null to be passed would be to declare that the value of an out parameter whose actual is a view conversion and whose value does not pass a membership test for the subtype of the parameter is abnormal. In that case, reading the value within the subprogram makes the program erroneous (while assigning it first is fine). This would work but it introduces erroneous execution where none is really needed (the passing null solution involves no erroneous execution).
---
Note that we don't explicitly talk about converting between access types with different representations. We're expecting that an implementation with such representations will include them in conversion and membership checking in an appropriate manner. For instance, if one assumes that converting a bit pointer to a byte pointer raises an exception if the bit address is not an even byte, then so long as that is reflected in a membership test, all is well.
It's true that there doesn't seem to be any justification within the language to raise an exception in such a case, but it seems nasty to simply destroy an access value if the target type cannot properly represent the address. (Note that a similar thing can happen on a segmented machine, such as the original 8086.) One assumes that Ada implementations care enough about correctness to avoid user-beware cases. If they don't, it's not the language's job to try to fix them. [Author's note: We may want to add an AARM note somewhere in 4.6 to note this possibility, and possibly in 4.5.2 as well.]
!ASIS
No ASIS effect.
!ACATS test
ACATS B- and C-Tests are needed to check that the new rules are enforced, rather than the previous rules. The test program given below can be the basis of an ACATS C-Test.
!appendix

From: Randy Brukardt
Sent: Never [Done April 20, 2020]

[Following is the test program for AI12-0377-1.]

with Ada.Text_IO, Ada.Exceptions;
procedure TestAI74 is
begin

    -- Note: The three "problems" were identified during the work on 
    -- AI12-0074-1. We're testing what they do on existing implementations
    -- during a revisit of these rules. Note: All of these "problems"
    -- were made illegal by AI12-0074-1, so a correct Ada 2012 implementation
    -- would reject this program. If so, please try each case individually
    -- to provide us the maximum information about runtime behavior.
    -- Note that many of these cases are allowed by Ada 95 and Ada 2005.
    
    Ada.Text_IO.Put_Line ("Check some nasty cases related to AI12-0377-1");
  
  
    -- Behavior of view conversions and derivation
    -- Note: This example is fully legal, no Ada 2012 compiler (or Ada 95
    -- compiler, for that matter) should reject this.
    declare
       package Nest is
           type T (D : Boolean := False) is -- no partial view
              record
                 case D is
                    when False => Bar : Character;
                    when True  => Foo : Integer;
                 end case;
              end record;

           type Unconstrained_Ref is access all T;

           procedure Q (X : out Unconstrained_Ref);
               -- A primitive of Unconstrained_Ref
               
           type Derived_Ref is new Unconstrained_Ref;
           
           -- Inherits Q.

       end Nest;
        
       package body Nest is
           procedure Q (X : out Unconstrained_Ref) is
           begin
              if X.D then
                  X.Foo := 456;
              else
                  X.Bar := 'B';
              end if;
           end Q;
       end Nest;
 
       X : aliased Nest.T := (D => False, Bar => 'R');
       X_Ref : Nest.Unconstrained_Ref := X'Access;
 
       Der_X_Ref : Nest.Derived_Ref := X'Access;     
       
    begin
        -- Direct call to Q:
        begin
          Nest.Q (X_Ref);
          if X.Bar /= 'B' then
             Ada.Text_IO.Put_Line ("** Component not changed");
          else
             Ada.Text_IO.Put_Line ("-- Expected result, no conversion");
          end if;
        exception
          when Err:others =>
             Ada.Text_IO.Put_Line ("** Failed: Exception raised (no conversion) - " & 
                                        Ada.Exceptions.Exception_Information (Err));
        end;
        -- View conversion to parent type:
        begin
          X.Bar := 'R';
          Nest.Q (Nest.Unconstrained_Ref (Der_X_Ref));
          if X.Bar /= 'B' then
             Ada.Text_IO.Put_Line ("** Component not changed");
          else
             Ada.Text_IO.Put_Line ("-- Expected result, explicit parent conversion");
          end if;
       exception
          when Err:others =>
             Ada.Text_IO.Put_Line ("** Failed: Exception raised (explicit parent conversion) - " & 
                                        Ada.Exceptions.Exception_Information (Err));
       end;
       -- Call to inherited routine (implicit view conversion to parent type):
       begin
          X.Bar := 'R';
          Nest.Q (Der_X_Ref);
          if X.Bar /= 'B' then
             Ada.Text_IO.Put_Line ("** Component not changed");
          else
             Ada.Text_IO.Put_Line ("-- Expected result, inherited routine");
          end if;
       exception
          when Err:others =>
             Ada.Text_IO.Put_Line ("** Failed: Exception raised (inherited routine) - " & 
                                        Ada.Exceptions.Exception_Information (Err));
       end;
    end;
        
    -- A discriminant problem
    declare
       type T (D : Boolean := False) is -- no partial view
          record
             case D is
                when False => Bar : Character;
                when True  => Foo : Integer;
             end case;
          end record;

       type Unconstrained_Ref is access all T;
       type Constrained_Ref is access all T (True);

       X : aliased T := (D => False, Bar => 'R');
       X_Ref : Unconstrained_Ref := X'Access;

       procedure P (X : out Constrained_Ref) is
       begin
          X.Foo := 123;
            --  We don't want to require a discriminant
            --  check here.
       end P;
 
    begin
       P (Constrained_Ref (X_Ref));
       if X.D /= False then
           Ada.Text_IO.Put_Line ("** Discriminant changed");
       else
           Ada.Text_IO.Put_Line ("?? Possible erroneous execution");
           Ada.Text_IO.Put_Line ("?? X.Bar'Pos = " & Natural'Image (Character'Pos (X.Bar)));
       end if;
    exception
       when Err2:Constraint_Error =>
          Ada.Text_IO.Put_Line ("-- Discriminant check made anyway - " &
                                    Ada.Exceptions.Exception_Information (Err2));
       when Err:others =>
          Ada.Text_IO.Put_Line ("** Exception raised (disc) - " & Ada.Exceptions.Exception_Information (Err));
    end;

    -- A tag problem
    declare
       type Root is tagged null record;
       type Ext is new Root with record F : Integer; end record;

       type Root_Ref is access all Root'Class;
       type Ext_Ref is access all Ext;

       procedure P (X : out Ext_Ref) is
       begin
          X.F := 123;
            -- No tag check is performed here and
            -- we don't want to add one.
          X := null;
       end P;

       R : aliased Root;
       O : Integer := 12;
       Ptr : Root_Ref := R'Access;
    begin
       P (Ext_Ref (Ptr));
       Ada.Text_IO.Put_Line ("?? Possible erroneous execution");
       if R not in Root then
           Ada.Text_IO.Put_Line ("** Object tag changed");
       end if;
       if O /= 12 then
           Ada.Text_IO.Put_Line ("** Following object clobbered to " & Integer'Image(O));
       end if;
    exception
       when Err:others =>
          Ada.Text_IO.Put_Line ("-- Exception raised (tag) - " & Ada.Exceptions.Exception_Information (Err));
    end;

    -- An accessibility problem
    declare
       type Int_Ref is access all Integer;
       Dangler : Int_Ref;
       procedure P (X : out Int_Ref) is
       begin
          Dangler := X;
            -- No accessibility checks are performed here.
            -- We rely here on the invariant that
            -- a value of type Int_Ref cannot designate
            -- an object with a shorter lifetime than Int_Ref.
          X := null;
       end P;

       procedure Q is
          Local_Int : aliased Integer;
          Standalone : access Integer := Local_Int'Access;
       begin
          P (Int_Ref (Standalone));
          null;
       end Q;
    begin
       Q;
       Dangler.all := 123; -- Assigns a non-existent object, can't check this.
       Ada.Text_IO.Put_Line ("?? Likely erroneous execution");
    exception
       when Err:others =>
          Ada.Text_IO.Put_Line ("-- Exception raised (acc) - " & Ada.Exceptions.Exception_Information (Err));
    end;
    
    Ada.Text_IO.Put_Line ("Test finished");

end TestAI74;

****************************************************************

From: Randy Brukardt
Sent: Never [Done April 20, 2020]

Here's the results of the above test program when run on GNAT 21.0w-200219:

Check some nasty cases related to AI12-0377-1
-- Expected result, no conversion
** Failed: Exception raised (explicit parent conversion) - raised CONSTRAINT_ERROR : testai74.adb:42 access check failed

-- Expected result, inherited routine
-- Discriminant check made anyway - raised CONSTRAINT_ERROR : testai74.adb:117 access check failed

-- Exception raised (tag) - raised CONSTRAINT_ERROR : testai74.adb:148 access check failed

-- Exception raised (acc) - raised CONSTRAINT_ERROR : testai74.adb:194 access check failed

Test finished

****************************************************************

From: Randy Brukardt
Sent: Never [Done April 20, 2020]

Here's the results of the above test program when run on Janus/Ada 3.2.2dev (4/20/20):

[Note: Janus/Ada needed the call in the third case commented out, since it 
does not support dynamic accessibility for stand-alone objects of an anonymous
access type. Thus the last case is uninteresting as it didn't set Dangler.]

Check some nasty cases related to AI12-0377-1
-- Expected result, no conversion
-- Expected result, explicit parent conversion
-- Expected result, inherited routine
-- Discriminant check made anyway - CONSTRAINT_ERROR
   Variant record field not available
On Line Number 117 In TESTAI74.LOOP.P
Called from line number 123 In TESTAI74

?? Possible erroneous execution
** Following object clobbered to  123
-- Exception raised (acc) - CONSTRAINT_ERROR
   Attempt to reference thru NULL/Uninitialized pointer
On Line Number 194 In TESTAI74

Test finished

****************************************************************

From: Randy Brukardt
Sent: Never [Done April 21, 2020]

Here's the results of the above test program when run on ObjectAda 10.1 
(4/21/20) [thanks to PTC for these results]

[Note: ObjectAda needed the first and third error cases commented out, as it
rejected both of the conversions in those two cases.]

Check some nasty cases related to AI12-0377-1
-- Expected result, no conversion
-- Expected result, explicit parent conversion
-- Expected result, inherited routine
?? Possible erroneous execution
Test finished

****************************************************************

From: Randy Brukardt
Sent: Never [Done April 21, 2020]

Here's the results of the above test program when run on ApexAda 5.2 
(4/21/20) [thanks to PTC for these results]

[Note: ApexAda needed the third error cases commented out, as it
rejected the conversion with a static accessibility failure.]

Check some nasty cases related to AI12-0377-1
-- Expected result, no conversion
-- Expected result, explicit parent conversion
-- Expected result, inherited routine
-- Discriminant check made anyway - CONSTRAINT_ERROR raised at 16#000000000040439B#, Exception Message: 
?? Possible erroneous execution

****************************************************************

From: Tucker Taft
Sent: Thursday, March 26, 2020  9:29 AM

AdaCore recently implemented this Corrigendum AI, and bumped into a 
significant number of incompatibilities in their customer regression test 
suite in the access-type-related part, which disallows view conversions 
between unrelated access types on an actual parameter if the formal is mode
"out."  The AI argued that such situations should be rare, but apparently it
is more common than we anticipated.

I would suggest we remove the access-type-related part from this AI, put it 
in a separate AI, and consider voting it "no action," or come up with a more
compatible approach (e.g. disallow only if the Sizes for the access types 
differ).

****************************************************************

From: Randy Brukardt
Sent: Tuesday, April 21, 2020  6:51 PM

It turns out that GNAT "solves" this problem by always passing null for an 
explicit view conversion of an out parameter. It completely ignores what the 
language says to do in such a case.

Probably some solution based on that would be best, although we need to take 
care not to break existing code that takes advantage of the language as 
written (since that goes back to Ada 95). Unless, of course, none exists; I've
asked implementers about this question in order to see if we get any feedback.

AI12-0377-1 will cover this issue and the other one that Gary posted (they're 
both related to AI12-0074-1, which is a Corrigendum AI, and which will need 
to be revisited in any case because of the intent to repeal 13.1(10). I'll 
post this AI later today.

****************************************************************

From: Gary Dismukes
Sent: Tuesday, April 14, 2020  5:43 PM

Recently, while fixing a GNAT problem related to passing view conversions to 
out-mode parameters when Default_Value is involved (related to the rules added
in AI12-0074), it occurred to me that there's a similar situation that can 
arise when the formal type doesn't have aspect Default_Value, but the type of
the operand of the view conversion does have the aspect.  (AI12-0074 addressed
the case of formal types that specific Default_Value, requiring the type of an
operand of a view conversion actual to also specify Default_Value, and this is
sort of the reverse of that.)

Consider this example, which is a minor variation of the one in the AI:

   procedure View_Conversion_With_Default_Value is

     type Defaulted_Integer is new Integer with Default_Value => 123;

     procedure P (X : out Integer) is
     begin
        null;  -- X happens to be unassigned
     end P;

     X : Defaulted_Integer;
     Y : Defaulted_Integer := 456;

   begin
     P (Integer (X));  -- Deinitialize X?

     P (Integer (Y));  -- Deinitialize Y?
   end View_Conversion_With_Default_Value;

Even though the type of X and Y has specified Default_Value, the value of the 
actual isn't copied into the formal in this case (it falls under the rule of 
6.4.1(15)), leaving the formal parameter uninitialized. Upon return, the value 
of the formal will be converted to Defaulted_Integer and assigned to the actual 
object.

If the formal wasn't assigned to within the procedure, as in the above example, 
then the (uninitialized) value that comes back will deinitialize the actual 
object, which is the sort of thing that shouldn't normally be possible for an
object whose type has a specified Default_Value.

You could argue that you shouldn't have a formal of mode out that isn't 
assigned to, but that's kind of beside the point, because the normal guarantee 
of Default_Value, that variables of the type always have a good value, is 
violated in this case, so we clearly have a safety hazard. To avoid this, the 
rules either need to require the value of the actual to be assigned to the 
formal going in, or such a conversion needs to be rejected.  I was initially 
leaning towards the former, but Tuck has convinced me that this should be 
illegal.  Basically we don't want to impose the penalty of preventing an 
optimization like transforming a procedure with a single elementary out formal 
into a function returning the parameter, just to protect against what is going 
to be the rare case of passing a view conversion of an operand with 
Default_Value.

So it seems that we need to augment the rules of 6.4.1(5.1-5.3), which require 
the type of the operand of a view conversion to have Default_Value when the 
formal's type has Default_Value.  We need to also disallow the case where a 
view conversion operand's type has Default_Value but the formal's type 
doesn't.

I believe we'll also need to add something along the lines of the rules in 
6.4.1(13.1-13.4) that require Program_Error be raised, to address call cases 
within generic bodies when formal types are involved (those rules were also 
added by AI12-0074).

****************************************************************

From: Tucker Taft
Sent: Tuesday, April 14, 2020  7:49 PM

We have discussed this internally at AdaCore, and concluded that we should
make it illegal to pass a scalar type with Default_Value in a view conversion
to an OUT formal parameter without a Default_Value.  Requiring "copy in" is 
not practical given the way that OUT parameters of a scalar type are sometimes
implemented (such as transforming the procedure to a function and returning 
the OUT parameter as a function result).

****************************************************************

From: Randy Brukardt
Sent: Tuesday, April 21, 2020  7:57 PM

> Recently, while fixing a GNAT problem related to passing view 
> conversions to out-mode parameters when Default_Value is 
> involved (related to the rules added in AI12-0074), it 
> occurred to me that there's a similar situation that can 
> arise when the formal type doesn't have aspect Default_Value, 
> but the type of the operand of the view conversion does have 
> the aspect.  (AI12-0074 addressed the case of formal types 
> that specific Default_Value, requiring the type of an operand 
> of a view conversion actual to also specify Default_Value, 
> and this is sort of the reverse of that.)

I'm not throughly convinced that this problem (in any form) is worth worrying 
about, since it can't cause an invalid value to be introduced to the program 
(at least in a correct implementation). If the implementation is assuming that
the actual object is valid, then a constraint check is necessary at the back 
assignment and that would prevent any invalid values from being stored. The 
value would indeed be junk, but that's what was written (and it happens for 
all scalar out parameters).

Anyway, assuming that we do want to keep this illegal in any case, we need to 
cover *this* case as well. Attached is an attempt at doing that, and also 
handling the access problem (with a solution close to what GNAT is currently 
doing).

I suspect that the AI and especially the wording will need some wordsmithing.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, April 22, 2020  1:44 PM

FYI, I asked PTC (and received almost immediately) about their compilers 
behavior on the test program. Both of their compilers behaved similarly to 
the results I reported for Janus/Ada (including not supporting dynamic 
accessibility on SAOAATs), with the exception of ObjectAda rejecting the 
conversion in the first test case. Details are found in the posted AI (not the 
one attached previously, I got an answer from PTC after I sent the previous 
e-mail).

I conclude that taking GNAT's behavior exactly would have a serious risk of 
being run-time incompatible with existing code (a risk we shouldn't take). A 
more limited form, however, seems to be the right solution (surely we don't 
want the erroneous execution that all of the non-GNAT compilers have).

****************************************************************

Questions? Ask the ACAA Technical Agent