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

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

!standard 3.3.1(8.1/2)          16-07-21 AI12-0192-1/02
!class binding interpretation 16-06-06
!status Amendment 1-2012 16-07-21
!status ARG Approved 8-0-3 16-06-11
!status work item 16-06-06
!status received 16-05-31
!priority Low
!difficulty Easy
!qualifier Omission
!subject "requires late initialization" and protected types
!summary
Components of a protected type (including the type of a single protected object) require late initialization if their initialization includes (implicitly) the current instance of the type.
!question
Consider:
type T is ... ;
protected type PR is ... private function Make_T return T; T_Comp : T := Make_T; ... <other components> ... end PR;
protected body PR is ... function Make_T return T is ... end PR;
PR_Obj : PR;
The default initialization of PR_Obj.T_Comp includes a call to Make_T. Inside Make_T, we can read any of the components of the target protected object. Thus, it would be better if the T_Comp component's initialization occurred after the initialization of any other components. We have the "requires late initialization" rules to handle similar cases. Should they be extended to cover this case? (Yes.)
!recommendation
(See Summary.)
!wording
Replace 3.3.1(8.1/2):
A component of an object is said to require late initialization if it has an access discriminant value constrained by a per-object expression, or if it has an initialization expression that includes a name denoting the current instance of the type or denoting an access discriminant.
with:
A component of an object is said to require late initialization if:
* it has an access discriminant value constrained by a per-object expression;
* it has an initialization expression that includes a name denoting an access discriminant; or
* it has an initialization expression that includes a reference to the current instance of the type either by name or implicitly as the target object of a call.
!discussion
These rules prevent most (but not all) access to uninitialized components. It makes sense for them to be extended to this case as well.
This is inconsistent (runtime incompatible); most of the time it would prevent bugs or be neutral.
However, it is possible (but very unlikely) for this change to make legal, correct, portable Ada 2012 code erroneous. For instance, consider the following type:
protected type Gack is ... private function Make_T2 return T2; T1_Comp : T1(Gack'access); T2_Comp : T2 := Make_T2; end Gack;
The T1_Comp component "requires late initialization" by the existing Ada 2012 rules. The T2_Comp component would now "require late initialization" by the proposed new rule. 3.3.1(20.4/3) says that components that require late initialization are initialized in textual order (to avoid implementation-dependencies and allow a work-around from problematic cases).
Thus, Ada 2012 requires T1_Comp to be initialized last, with T2_Comp initialized before it. OTOH, with this new rule, Ada would require T2_Comp to be initialized last, with T1_Comp to be initialized before it.
If, in fact, the implementation of type Gack deepends on the initialization of T1_Comp being last (because it referenced T2_Comp), then the introduction of this new rule would break this code (making it erroneous). The fix is trivial (swap the two components), but finding the error would be difficult as nothing predictable need happen for erroneous code.
The reason that most of us consider this acceptable is simply that it is very unlikely: it requires four unlikely events -- (1) that a protected object contain a component that currently requires late initialization; (2) that the same protected object contain another component that will require late initialization under this rule (that is, is initialized by a function call of the PO itself); (3) that the components are in the "wrong" order (the existing "required late initialization" component is not last); and (4) the existing "required late initialization" depends somehow on the other components of the type (and the other components do not). This seems unlikely enough that it probably has never happened in Ada code.
More generally, one could have code that depends on the order of evaluation of components chosen by a particular Ada implementation. This rule probably will change the order of evaluation, and thus could break existing code. However, this sort of code may not work on another Ada implementation, including the next version of the same implementation, so this rule change doesn't change the situation appreciably in the more likely case. (That is still not very likely, as it requires a protected type with multiple components where the initialization of one of them depends on the value of one or more of the others.)
In most cases, this rule will not have any effect, and in the unusual case that it does have some effect, its most likely to fix bugs, not cause them.
!corrigendum 3.3.1(8.1/2)
Replace the paragraph:
A component of an object is said to require late initialization if it has an access discriminant value constrained by a per-object expression, or if it has an initialization expression that includes a name denoting the current instance of the type or denoting an access discriminant.
by:
A component of an object is said to require late initialization if:
!ASIS
No ASIS effect.
!ACATS test
An ACATS C-Test is needed to check this.
!appendix

From: Steve Baird
Sent: Tuesday, May 31, 2016  2:55 PM

The "requires late initialization" rules were added in order to make it harder
(but admittedly not impossible) to access a component of an object before that
component has been initialized.

We might want to extend those rules to apply to another construct.

Consider (thanks to Ed S. for the original example):

    type T is ... ;

    protected type PR is
       ...
    private
       function Make_T return T;
       T_Comp : T := Make_T;
       ... <other components> ...
    end PR;

    protected body PR is
       ...
       function Make_T return T is ...
    end PR;

    PR_Obj : PR;

The default initialization of PR_Obj.T_Comp includes a call to Make_T. Inside
Make_T, we can read any of the components of the target protected object.
Thus, it would be better if the T_Comp component's initialization occurred
after the initialization of any other components.

Tucker wrote (in an internal AdaCore discussion):
> I think this part of the definition of "requires late initialization" should
> be modified:
>
>   "... or if it has an initialization expression that includes a
>    name denoting the current instance of the type or denoting an access
>    discriminant."
>
> perhaps to the following:
>
>   "... or if it has an initialization expression that includes a
>    name denoting an access discriminant, or a reference to the current
>    instance of the type either by name or implicitly as the target object of
>    a call."

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

From: Randy Brukardt
Sent: Monday, June 6, 2016 8:37 PM

In writing this up, it occurs to me that this is inconsistent (runtime
incompatible), in that the order that components are initialized could change.
In the vast majority of cases, this would fix bugs (remove
implementation-dependencies), but it's possible for this change to *introduce*
a bug that wasn't previously there.

For instance, consider the following type:

    protected type Gack is
       ...
    private
       function Make_T2 return T2;
       T1_Comp : T1(Gack'Access);
       T2_Comp : T2 := Make_T2;
    end Gack;

The T1_Comp component "requires late initialization" by the existing Ada 2012
rules. The T2_Comp component would now "require late initialization" by the
proposed new rule. 3.3.1(20.4/3) says that components that require late
initialization are initialized in textual order (to avoid
implementation-dependencies and allow a work-around from problematic cases).

Thus, Ada 2012 requires T1_Comp to be initialized last, with T2_Comp
initialized before it. OTOH, with this new rule, Ada would require T2_Comp
to be initialized last, with T1_Comp to be initialized before it.

If, in fact, the initialization of T1_Comp had to be last (because it
referenced T2_Comp), then the introduction of this new rule would break this
code (making it erroneous - ugh!). The fix is trivial (swap the two
components), but finding the error would be difficult as nothing predictable
need happen for erroneous code.

This is risk is small, but this is the worst kind of possible incompatibility
(making correct, portable, legal code erroneous!). This is especially annoying
as this change would usually fix, not cause, bugs.

We could make the new rule apply only if the PT does not have any "old-style"
requires late initialization components -- that would usually be the case --
but that seems like a bizarre wart. I don't know what to think there - it's
hard to accept making any correct, legal Ada code silently erroneous.

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

From: Tucker Taft
Sent: Monday, June 6, 2016 8:52 PM

> ... We could make the new rule apply only if the PT does not have any 
> "old-style" requires late initialization components -- that would 
> usually be the case -- but that seems like a bizarre wart. I don't 
> know what to think there - it's hard to accept making any correct, 
> legal Ada code silently erroneous.

This is a corner of a corner of a corner case.  I recommend that we document
the inconsistency, but otherwise not worry about it.

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

From: Randy Brukardt
Sent: Monday, June 6, 2016  9:47 PM

That's how I wrote it up.

But, as I said, I'm not sure how I'll vote on this one. I can't remember any
other case where we introduced an incompatibility that caused correct, legal,
portable code to become erroneous. (I can think of cases where that happened
with implementation-defined (non-portable) code, but that's a really different
thing.) Certainly not outside of the case of a language bug (and there's no
language bug here).

I'd think that there are certain communities that would be appalled at such a
thing. (Probably they'd never allow use of the problematic features anyway,
but the mere presence of such a thing could erode confidence in Ada.)

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

From: Steve Baird
Sent: Tuesday, June 7, 2016  2:31 AM

> In writing this up, it occurs to me that this is inconsistent (runtime 
> incompatible), in that the order that components are initialized could 
> change.

If this change causes a program to break, then the program already has a bug 
in it because even without this change the compiler had the option (even if it
didn't exercise that option) to choose the same initialization order that it
would choose if this change is adopted.

I suppose you could view this as an incompatibility, but that seems like a
stretch to me.

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

From: Randy Brukardt
Sent: Tuesday, June 7, 2016  2:29 PM

> If this change causes a program to break, then the program already has 
> a bug in it because even without this change the compiler had the 
> option (even if it didn't exercise that
> option) to choose the same initialization order that it would choose 
> if this change is adopted.

That's clearly not true as shown in the example I provided.

> I suppose you could view this as an incompatibility, but that seems 
> like a stretch to me.

I'm mainly worried about the FUD factor of Ada users not knowing if this
(admittedly unlikely) case appears in their code. That could cause people to
avoid moving to Ada 2020, and that would be bad.

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

From: Tucker Taft
Sent: Tuesday, June 7, 2016  3:13 PM

If that stops them, then Ada 2020 isn't worth the paper it is written on. ;-)

So long as we are up front about it, I *really* don't see the problem.  This is
so far down the stack of possible problems relative to the fundamental risk of
taking on a new version of any compiler, that it is of negligible concern.

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


Questions? Ask the ACAA Technical Agent