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

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

!standard 6.1.1(22.1/4)          16-10-02 AI12-0198-1/03
!class binding interpretation 16-06-07
!status Amendment 1-2012 16-07-21
!status ARG Approved 11-0-0 16-06-11
!status work item 16-06-07
!status received 16-01-28
!priority Low
!difficulty Easy
!qualifier Omission
!subject Potentially unevaluated components of array aggregates
!summary
A component of an array aggregate which belongs to a nonstatic or null range of index values is "potentially unevaluated".
!question
We have rules for "potentially unevaluated" expressions in order to prevent issues with evaluating prefixes to Old attributes that are undefined.
For instance:
with Post => (if Some_Num /= 0 then F(N / Some_Num)'Old > 0);
If Some_Num is zero upon entry to the subprogram, the 'Old rules would require evaluating F(N / 0), which obviously won't work even though the value will never be used. The "potentially unevaluated" rules eliminate this problem by making the above illegal.
However, there is a similar case for aggregates:
with Post => Array_Type'(1 .. N => F(10 / N)'Old) =
Array_Type'(1 .. N => F(10 / N));
If N has the value 0 upon entry, the Old attribute's prefix will raise an exception, even though the array component will never be used (these aggregates are null arrays).
Should these also be fixed? (Yes.)
!recommendation
(See Summary.)
!wording
Add after 6.1.1(22.1/4):
* the expression of an array_component_association, if the associated choice is a subtype_indication or range that defines a nonstatic or null range, or is an others choice and the applicable index constraint is nonstatic or null;
[Editor's note: I put this after 21.1/4 as that's where the last insertion went, there doesn't seem to be any intentional order here (like the order that these constructs are defined in the RM), and by putting it here, I don't have to move punctuation and "or"s.]
!discussion
It has been noted that the "potentially unevaluated" rules aren't strictly necessary, as the evaluation of these Old prefixes is well-defined (just not helpful).
---
In general, the use of 'Old with a nonstatic prefix in an array aggregate is unlikely to get the results the programmer is expecting. Consider:
Running_Count : Natural := 0; function Counter return Natural is begin Running_Count := Running_Count + 1; return Running_Count; end Counter;
(1..5 => Counter) -- gives (1, 2, 3, 4, 5) [or some other permutation thereof], while (1..5 => Counter'Old) -- gives (1, 1, 1, 1, 1)
This sort of construction is not uncommon in Ada, as the preferred alternative
(for I in 1 .. 5 => I);
was just added in AI12-0061-1 (and thus no one has yet had a chance to use it).
---
This rule could cause an incompatibility, but only for aggregates for which components of Prefix'Old and Prefix usually would give different results (as discussed above).
!corrigendum 6.1.1(22.1/4)
Insert after the paragraph:
the new paragraph:
!ASIS
No ASIS effect.
!ACATS test
!appendix

From: Randy Brukardt
Sent: Thursday, January 28, 2016  11:44 PM

I've been working tonight on the detailed objectives for the Old attribute.
(Or is that "ye Olde attribute" ;-)

I just noticed that that the existing rule in 6.1.1(27/3), "The prefix of an Old
attribute_reference shall not contain ... a use of an entity declared within the
postcondition expression but not within prefix itself ..." applies to the array
index parameters added by AI12-0061-1. In particular, this wording was intended
to ban:

    procedure Foo1 (P : in out Integer)
       with Post => (for all I in 1 .. 10 => Arr(I)'Old > 10); -- Illegal

but of course the same sort of issue applies to an array aggregate:

    procedure Foo2 (P : in out Integer)
       with Post => String'(for all I in 1 .. 10 => Arr(I)'Old) > "ABCD"; -- Should be illegal, and I think it is.

The wording looks like it covers this, so this is mainly a problem for
implementers. I put an objective on my list to be activated when we get to that
point, so that at least the need for a test will be remembers.

However, I remembered that earlier in the clause, AI12-0032-1 had added
quantified expressions to the list of things that are "potentially unevaluated".
The following example was given to explain this:

    procedure Foo3 (P : in out Integer)
       with Post => (for all I in 1 .. P => Func'Old > 10);
                                -- Illegal by 6.1.1(22.1/4) and 6.1.1(27/3)

This is illegal, because we don't know how many times to evaluate Func at the
start of the body of Foo3 (when these things are evaluated); that depends on the
value of P passed back, which of course would require predicting the future. (We
do allow this if the prefix statically denotes an object, because then we only
need one copy, all of the values necessarily being the same.)

As you've probably guessed by now, the same thing can be done with an array
aggregate:

    procedure Foo4 (P : in out Integer)
       with Post => String'(for all I in 1 .. P => Func'Old) > "ABCD"; -- Doesn't trigger any existing rules.

Again we don't know how many times to evaluate Func (essentially, how many
constants to make).

Ouch! I just realized in writing this example that it already exists in Ada
2012; we don't need the index to make this problem happen:

    procedure Foo5 (P : in out Integer)
       with Post => String'(1 .. P => Func'Old) > "ABCD"; -- Doesn't trigger any existing rules.

How does this work?? How CAN it work?? We don't have any rule that considers the
expressions of an array aggregate as "potentially unevaluated", but there
probably ought to be.

If the index range is static, then it can be implemented, but it could be
nasty:

    procedure Foo5 (P : in out Integer)
       with Post => String'(1 .. Positive'Last => Func'Old) > "ABCD";

This would declare Positive'Last copies of Func, probably not a good idea. :-)
Still, probably should allow this; the compiler's capacity limits are always an
excuse for not compiling the silly expression.

Anyway, it seems to me that the list of 6.1.1(20-24/3) needs another bullet:

  * the expression of an array_component_association, if the associated choice
    is a nonstatic expression or is a subtype_indication or range that defines a
    nonstatic range, or is an others choice and the applicable index constraint
    is nonstatic;

(I borrowed most of this wording from 4.3.3; we only have to worry about
aggregates that are otherwise legal, so we only need to worry about aggregates
with explicit choices of some sort -- purely positional aggregate always have a
static number of components. I left out null ranges, because in that we *know*
that the item is unevaluated, and there doesn't seem to be any reason to worry
about that.)

Thoughts??

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

From: Steve Baird
Sent: Friday, January 29, 2016  12:01 PM

> but of course the same sort of issue applies to an array aggregate:
>
>      procedure Foo2 (P : in out Integer)
>         with Post => String'(for all I in 1 .. 10 => Arr(I)'Old) >
> "ABCD"; -- Should be illegal, and I think it is.
>

Right. The wording for AI12-0050 was also written with this case in mind and
doesn't need to be modified for AI12-0061.

> The wording looks like it covers this, so this is mainly a problem for
> implementers. I put an objective on my list to be activated when we
> get to that point, so that at least the need for a test will be remembers.
>
> However, I remembered that earlier in the clause, AI12-0032-1 had
> added quantified expressions to the list of things that are
> "potentially unevaluated". The following example was given to explain this:
>
>      procedure Foo3 (P : in out Integer)
>         with Post => (for all I in 1 .. P => Func'Old > 10); --
> Illegal by
> 6.1.1(22.1/4) and 6.1.1(27/3)
>
> This is illegal, because we don't know how many times to evaluate Func
> at the start of the body of Foo3 (when these things are evaluated);
> that depends on the value of P passed back, which of course would
> require predicting the future. (We do allow this if the prefix
> statically denotes an object, because then we only need one copy, all
> of the values necessarily being the same.)

I agree that this is illegal, but I think you are confused about the rationale
for this rule.

The number of times one static occurrence of the expression "Func'Old" would be
evaluated has no bearing on the number of implicit constants that are declared
at the start of the body of Foo3.

Note that this is a dynamic semantics question.

I think you have correctly identified an issue with the definition of
"potentially unevaluated", but that's unrelated to dynamic semantics.

> As you've probably guessed by now, the same thing can be done with an
> array aggregate:
>
>      procedure Foo4 (P : in out Integer)
>         with Post => String'(for all I in 1 .. P => Func'Old) >
> "ABCD"; -- Doesn't trigger any existing rules.
>
> Again we don't know how many times to evaluate Func (essentially, how
> many constants to make).

And again we don't care, except that static semantics cares about the
distinction between possibly-zero and guaranteed-to-be-nonzero (i.e., the
"potentially unevaluated" rule).

> Ouch! I just realized in writing this example that it already exists
> in Ada 2012; we don't need the index to make this problem happen:
>
>      procedure Foo5 (P : in out Integer)
>         with Post => String'(1 .. P => Func'Old) > "ABCD"; -- Doesn't
> trigger any existing rules.
>
> How does this work?? How CAN it work?? We don't have any rule that
> considers the expressions of an array aggregate as "potentially
> unevaluated", but there probably ought to be.

It works because only one Func'Old constant is implicitly declared and the value
of that one constant is used more than once.

> If the index range is static, then it can be implemented, but it could
> be nasty:
>
>      procedure Foo5 (P : in out Integer)
>         with Post => String'(1 .. Positive'Last => Func'Old) > "ABCD";
>
> This would declare Positive'Last copies of Func, probably not a good idea.
> :-)
> Still, probably should allow this; the compiler's capacity limits are
> always an excuse for not compiling the silly expression.

You are mixing a nonexistent (IMO) problem with dynamic semantics and a real
(IMO) problem with the definition of "potentially unevaluated".

> Anyway, it seems to me that the list of 6.1.1(20-24/3ta) needs another bullet:
>
>    * the expression of an array_component_association, if the
> associated choice is a nonstatic expression or is
>      a subtype_indication or range that defines a nonstatic range, or
> is an others choice and the applicable index
>      constraint is nonstatic;
>
> (I borrowed most of this wording from 4.3.3; we only have to worry
> about aggregates that are otherwise legal, so we only need to worry
> about aggregates with explicit choices of some sort -- purely
> positional aggregate always have a static number of components. I left
> out null ranges, because in that we *know* that the item is
> unevaluated, and there doesn't seem to be any reason to worry about
> that.)
>
> Thoughts??

The specific wording suggested looks good. The accompanying discussion is, IMO,
confused.

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

From: Steve Baird
Sent: Friday, January 29, 2016  12:38 PM

> The specific wording suggested looks good.

There is a separate question of whether the cure is worse than the disease.

I agree with Randy that the current definition of "potentially unevaluated" is
inconsistent, but it also seems worth noting that just leaving things as they
stand is a viable option.

There are no *definitional* problems associated with the current definition
(just as there would be no definitional problems if the entire "potentially
unevaluated" rule were removed).

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

From: Gary Dismukes
Sent: Friday, January 29, 2016  1:06 PM

Wouldn't the wording also need to cover cases of enclosing aggregates?
That is, when a choice of an enclosing association is nonstatic, etc.

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

From: Steve Baird
Sent: Friday, January 29, 2016  1:46 PM

RM says:
    An expression is potentially unevaluated if it occurs within ....

Note that it does not say "immediately within".

Doesn't that address the issue?

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

From: Gary Dismukes
Sent: Friday, January 29, 2016  2:14 PM

Yep.  I neglected to reread the outer wording when looking at the proposed new
bullet.

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

From: Randy Brukardt
Sent: Friday, January 29, 2016  1:31 PM

> I agree that this is illegal, but I think you are confused about the
> rationale for this rule.

That could be, there is no rationale given in the AI. I had to guess one.

> The number of times one static occurrence of the expression "Func'Old"
> would be evaluated has no bearing on the number of implicit constants
> that are declared at the start of the body of Foo3.

That would imply that Func'Old is dangerous in this sort of context, as the
results would vary a lot. See below.

> Note that this is a dynamic semantics question.
>
> I think you have correctly identified an issue with the definition of
> "potentially unevaluated", but that's unrelated to dynamic semantics.

OK.

...
> > Ouch! I just realized in writing this example that it already exists
> > in Ada 2012; we don't need the index to make this problem happen:
> >
> >      procedure Foo5 (P : in out Integer)
> >         with Post => String'(1 .. P => Func'Old) > "ABCD";
> -- Doesn't trigger any existing rules.
> >
> > How does this work?? How CAN it work?? We don't have any rule that
> > considers the expressions of an array aggregate as "potentially
> > unevaluated", but there probably ought to be.
>
> It works because only one Func'Old constant is implicitly declared and
> the value of that one constant is used more than once.

That's not "working" in my view. The result would be completely different than
you would get with just a call:

Cnt : Natural := 0;
function Counter return Natural is
begin
   Cnt := Cnt + 1;
   return Cnt;
end Counter;

    (1..5 => Counter) gives (1, 2, 3, 4, 5) [or some other permutation thereof]
    (1..5 => Counter'Old) gives (1, 1, 1, 1, 1)

This is a construction I use frequently. In Ada 2020, I'd certainly use
    (for I in 1 .. 5 => I)
instead, but I'm not currently writing Ada 2020! (It's really too bad that we
didn't add this feature in Ada 2012, since it fits well with the things we did
do.)

> You are mixing a nonexistent (IMO) problem with dynamic semantics and
> a real (IMO) problem with the definition of "potentially unevaluated".
>
> > Anyway, it seems to me that the list of 6.1.1(20-24/3ta)
> > needs another bullet:
> >
> >    * the expression of an array_component_association, if the
> >      associated choice is a nonstatic expression or is
> >      a subtype_indication or range that defines a nonstatic range, or
> >      is an others choice and the applicable index
> >      constraint is nonstatic;
> >
> > (I borrowed most of this wording from 4.3.3; we only have to worry
> > about aggregates that are otherwise legal, so we only need to worry
> > about aggregates with explicit choices of some sort -- purely
> > positional aggregate always have a static number of components. I
> > left out null ranges, because in that we *know* that the item is
> > unevaluated, and there doesn't seem to be any reason to worry about
> > that.)
> >
> > Thoughts??
>
> The specific wording suggested looks good. The accompanying discussion
> is, IMO, confused.

And later:

>There is a separate question of whether the cure is worse than the disease.
>
>I agree with Randy that the current definition of "potentially unevaluated"
>is inconsistent, but it also seems worth noting that just leaving
>things as they stand is a viable option.
>
>There are no *definitional* problems associated with the current
>definition (just as there would be no definitional problems if the
>entire "potentially unevaluated" rule were removed).

Considering that the effect of Func'Old in such a context is so far from what a
programmer (including me, obviously) would expect, making it illegal would
prevent as many bugs as it would cause. One would be forced to rewrite:

Array_Type'(1..P => Counter'Old)

into:

Array_Type'(1..P => Counter)'Old

which would actually give the expected semantics. That seems like a good thing.

I'm well aware that the semantics of 'Old is often not what one would expect,
but I fail to see how the typical Ada programmer is going to be aware of that.
(For instance, A(I)'Old is not necessarily the same as A(I'Old); one has to be
very careful where the 'Old is placed.) That's the real purpose of the
"potentially unevaluated rules, IMHO -- to get rid of the worst offenders. It's
too bad that we can't ban dynamic 'Old in any array aggregate, but I have to
agree that we don't want to make more of an incompatibility than necessary.

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

From: Steve Baird
Sent: Friday, January 29, 2016  6:27 PM

>   I left out null ranges, because
> in that we*know*  that the item is unevaluated, and there doesn't seem
> to be any reason to worry about that.)

You mean we'll disallow (1 .. N => Foo'Old), where N is a variable, because N
might be zero, but we'll allow
    (1 .. Pkg1.Static_Constant - Pkg2.Other_Static_Constant => Foo'Old) in the
case where the two constants have a difference of zero?

That seems peculiar.

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

From: Randy Brukardt
Sent: Friday, January 29, 2016  7:02 PM

I didn't see anything *potentially* unevaluated about such a case, and it
simplified the wording somewhat (it's too complicated as it is). It seemed weird
either way (to specifically include null ranges or to exclude them). The thing
that is weird is the aggregate rules treating a static null range as if it is
dynamic.

Besides, if the difference between those two constants is positive, then the
expression is clearly legal (it's still misleading, but we're not going to try
to fix that). I hate having Legality Rules depend on the values of something.

I do suppose it means that the rules would have to avoid creating a temporary
constant in the static null range case. (That's what I thought happened, but you
proved that I was wrong.) If that's too complicated, then I suppose we'll have
to hair up the wording more to include the null range case. If we do avoid the
temporary, we ought to do something similar for static conditions in if
expressions and similar cases. It's weird to have:

          (if Tracing then Trace_Glob'Old else Func(P)'Old)

be illegal if Tracing is a static constant - either Func(P)'Old is evaluated or
it isn't -- there's never any doubt.

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

From: Randy Brukardt
Sent: Friday, January 29, 2016  7:24 PM

...
> I do suppose it means that the rules would have to avoid creating a
> temporary constant in the static null range case.

Steve pointed out privately that we already have the concept of statically
unevaluated that is essentially right for this purpose.

We'd have to add this case to "statically unevaluated" in 4.9:

* the expression of an array_component_association if the associated choice is a
  subtype_indication or range that defines a static null range;

[Editor's note: the rules of aggregates say this has to be a single choice.]

And then in 6.1.1(26/4):

Each X'Old in a postcondition expression that is enabled {and does not appear in
a statically unevaluated (see 4.9) expression} denotes a constant that ...

And finally, we'd have exclude statically unevaluated from potentially
unevaluated in 6.1.1(20/3):

An expression is potentially unevaluated if it {is not part of a statically
unevaluated expression} and it occurs within:

That would make my example of:

           Post => (if Tracing then Trace_Glob'Old else Func(P)'Old) /= ...;

legal, and Func(P)'Old would not be evaluated on entry to the subprogram if
Tracing is True.

The only downside I can see is additional implementation complexity, but that
doesn't seem particularly bad assuming that one has a predicate for a statically
unevaluated subexpression (which I would expect would be needed to implement
static expressions properly). The upside is that it further reduces the cases of
'Old that are illegal but pose no understanding problem nor any implementation
impossibility. No one likes nagging mysterious errors in their reasonable (to
them) expressions.

P.S. I think Steve suggested this as a joke - he enjoyed getting "statically
unevaluated" and "potentially unevaluated" in the same sentence. Sorry, Steve, I
like the idea. ;-) And I don't think it is particularly confusing; both ideas
are quite intuitive.

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

From: Steve Baird
Sent: Friday, January 29, 2016  7:42 PM

> And finally, we'd have exclude statically unevaluated from potentially
> unevaluated in 6.1.1(20/3):

If we have terms Maybe_Blah and Definitely_Blah, then the latter should always
imply the former. Let's not do violence to the definitions.

To do something like what you've described, I'd let the terms keep their
intuitive meanings and than specify in the legality rule that an occurrence of
Foo'Old cannot occur in a potentially unevaluated expression which is not
statically unevaluated.

I'm not endorsing this; just saying that if we are going to do this then let's
keep the definitions clean.

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

From: Randy Brukardt
Sent: Friday, January 29, 2016  8:00 PM

Yes, you're right. Better to hair up the Legality Rule (which is where the
problem is) and not the definitions.

Specifically, don't change 6.1.1(20/3), and instead change 6.1.1(27/3) to:

The prefix of an Old attribute_reference that is potentially unevaluated {but
not a portion of a statically unevaluated expression} shall statically denote an
entity.

This is just a bit clunky as "statically unevaluated" only is defined for the
top-level expression, while "potentially unevaluated" includes any contents.
We're always *so* consistent in the RM.

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


Questions? Ask the ACAA Technical Agent