Version 1.1 of ai05s/ai05-0297-1.txt
!standard 3.5.5(7) 12-02-26 AI05-0297-1/01
!class Amendment 12-02-26
!status Amendment 2012 12-02-26
!status ARG Approved 8-1-1 12-02-26
!status work item 12-02-26
!status received 12-02-03
!priority Low
!difficulty Easy
!subject First_Valid and Last_Valid attributes
!summary
The First_Valid attribute returns the smallest valid value of a static discrete
subtype that has at least one value; Last_Valid returns the largest valid value
if such a subtype.
!problem
Given a subtype which has a static_predicate (and therefore might have a non-contiguous set
of values), a user might want to know the bounds of smallest and largest values of the subtype.
This might be used, for example, in order to define a minimal subtype which includes all values
of the original (possibly non-contiguous) subtype and can be used as an index subtype of an
array type.
!proposal
Define two new attributes, First_Valid and Last_Valid.
The prefix of either attribute must be a static discrete subtype which has at least one value.
First_Valid returns the least value which satisfies both the range constraint and the predicate
of the subtype. Last_Valid returns the greatest such value.
These attributes always yield static values.
!wording
Change section name of 13.9.2 from
The Valid Attribute
to
Validity Attributes
Add after 13.9.2(13.c/2) (i.e. at the end of the existing Static Semantics section):
For each static discrete subtype S for which there exists at least
one value belonging to S, the following attributes
are defined:
S'First_Valid
S'First_Valid denotes the smallest value that belongs to S.
The value of this attribute is of the type of S.
S'Last_Valid
S'Last_Valid denotes the largest value that belongs to S.
The value of this attribute is of the type of S.
Redundant:
First_Valid and Last_Valid attribute values are always static.
AARM note:
These attributes are intended primarily for use in the case
where the Static_Predicate aspect of S has been specified.
!discussion
One could imagine defining these attributes for nonstatic subtypes but we always want
S'First_Valid and S'Last_Valid to yield values that belong to S (speaking imprecisely,
we want them to yield valid values).
----
If the "at least one value" wording above seems too odd, we could express the same idea
by saying that the attribute prefix need only be a static discrete subtype and then adding
a separate legality rule that the attribute prefix must denote a subtype which has at least
one value.
!ACATS Test
An ACATS C-Test should be created to test these attributes, especially in the Static_Predicate
case.
!ASIS
No change needed.
!appendix
From: John Barnes
Sent: Thursday, December 15, 2011 8:52 AM
I hate to bring up another irritation with subtype predicates but I am going to.
Randy has now posted the second part of the rationale (on aspects and contracts)
on the secret web site.
There is an example on page 15 about playing with a dartboard. At the bottom of
the right hand column there is a declaration
type Hit_Count is array (1 .. 60) of Integer...
Having to write the literals 1 and 60 is infuriating. They are the Min and Max
values of the subtype Score. We are not allowed to use 'First and 'Last on Score
and I think that is right because it would tempt one into using Range and that
seems inappropriate for a set with holes. But how about defining atttributes Min
and Max on such a subtype so I can write
type Hit_Count is array (Score'Min .. Score'Max) of ...
Just a thought.
Back to the Spark book ...
****************************************************************
From: Jean-Pierre Rosen
Sent: Thursday, December 15, 2011 9:19 AM
Well 'Min and 'Max exist already, so you'll have to find other names.
Hmm... 'Start and 'Stop?
****************************************************************
From: John Barnes
Sent: Thursday, December 15, 2011 9:48 AM
I thought we were good at overloading. Hmm.
****************************************************************
From: Randy Brukardt
Sent: Thursday, December 15, 2011 7:59 PM
We allow overloading functions, but First and Last are not functions, they're
values. And we don't allow overloading values or objects (remember that we don't
allow overloading constants, for instance).
Practically, attributes are implemented with special-case code for each
attribute. Overloading them would complicate the implementation quite a bit
(because there is no general case code that could be used).
Anyway, I don't buy the argument that these are somehow different from 'First
and 'Last. Why should you have to remember to use some other attribute names if
you want this information from a subtype with a static predicate compared to any
other subtype? The only reason that I can think of would be if there is some
confusion about the meaning.
(For the record, I properly defined 'First and 'Last in my old set constraint
proposal, because they are clearly needed; for some reason, that got dropped
from static predicates even though they still are clearly needed as John's
example shows. We could borrow the wording from the old set constraint proposal
if we wanted to define these -- or with some other names for that matter.)
The argument I remember for not defining them is that people think that
Score'First .. Score'Last should be the same as Score. I don't buy this
argument; the meta rule ought to be that the predicate is involved IFF the
subtype name is involved. So the predicate has an effect on the value of
Score'First, Score'Last, Score by itself, but ".." does not involve the name of
the subtype so no predicate is involved on that. In any case, I don't see that
saying Score'Min .. Score'Max (or Score'Start .. Score'Stop) changes meaning at
all, other than that we know have two very similar attributes.
Aside: while 'Last (or 'Max) is well-defined even for dynamic predicates, we
would not want to support that because it could be very expensive. Consider:
subtype Power_of_Two is Long_Long_Integer with Dynamic_Predicate => Is_Power_of_Two (Power_of_Two);
Assume that Long_Long_Integer'Last is (2**127)-1. In that case,
Power_of_Two'Last (or Max) = 2**126. But to figure that out, you would need a
loop that goes from Long_Long_Integer'Last down until the predicate is True -
requiring 2**126-1 iterations. Yikes!
Aside 2: The only legitimate reason I can think of for keeping 'First and 'Min
separate is the definitional one: the lower bound of the subtype for the
purposes of the defining the value set is that of the constraint (ignoring the
static predicate). There might be a tiny amount of value being able to talk
about these separately. But I think it would be OK to merge the two ideas (at
least for static subtypes) so that the lower bound takes both the constraint and
predicate into account. In which case we do not need separate attributes.
In any case, I agree with John that there is a problem here. We need some way to
define arrays for subtypes with static predicates, and having to use literals or
constants seems like a step backwards. [That's especially true in John's
dartboard example, where the "obvious" upper bound (the inner bullseye) isn't
the upper bound at all (that's the triple 20).] It will feel especially strange
for experienced Ada programmers that always use the attributes in such
circumstances.
****************************************************************
From: Bob Duff
Sent: Tuesday, December 20, 2011 10:16 AM
> > > type Hit_Count is array (Score'Min .. Score'Max) of ...
Well, the rationale given in the AI still seems to apply:
Note that 'First is forbidden even in the static case. It would be
easy for the compiler to calculate 'First as the smallest value that
obeys the predicate and range constraint (taking care with null
ranges), but it would still be error prone, because code that uses
'First tends to assume a contiguous range. If we need that
functionality, we should define a new attribute to return the minimum
value -- but we don't.
'First might mean the lower bound of the range, or it might mean the lowest
number that obeys the predicate. It's not 100% clear which it SHOULD mean;
making it illegal avoids that confusion.
'Min seems like the perfect name for this new concept, but I agree with J.P. and
Randy that overloading the names would cause trouble. I've been considering
exactly that for my hobby language, by the way.
How about 'Min_Value or 'Min_Val or something?
I don't much like 'Start.
I think it would be appropriate to consider this as a possible enhancement for
Ada 2020. It seems way too late for Ada 2012, especially given that the
workaround doesn't seem so bad: Declare Score_Range as 1..Single'Last*3, and
then "Score is Score_Range with Static_Predicate => ...". Then you can use
Score_Range as the array index, and it's then crystal clear that the programmer
has to deal with the "holes" (by explicitly putting -1).
> We allow overloading functions, but First and Last are not functions,
> they're values. And we don't allow overloading values or objects
> (remember that we don't allow overloading constants, for instance).
Right.
> Anyway, I don't buy the argument that these are somehow different from
> 'First and 'Last. Why should you have to remember to use some other
> attribute names if you want this information from a subtype with a
> static predicate compared to any other subtype? The only reason that I
> can think of would be if there is some confusion about the meaning.
Well, I still think there's potential confusion. But I'm half convinced by
Randy's argument. Maybe I could be convinced to allow 'First in the static
case. But please let's wait for Ada 2020.
I hope we all agree that 'First should not be allowed in the dynamic case!
> The argument I remember for not defining them is that people think
> that Score'First .. Score'Last should be the same as Score.
Well, if you asked a typical Ada programmer if that's the case, they'd say
"yes".
>... I don't buy this
> argument; the meta rule ought to be that the predicate is involved IFF
>the subtype name is involved. So the predicate has an effect on the
>value of Score'First, Score'Last, Score by itself, but ".." does not
>involve the name of the subtype so no predicate is involved on that.
>In any case, I don't see that saying Score'Min .. Score'Max (or
>Score'Start .. Score'Stop) changes meaning at all, other than that we know
>have two very similar attributes.
I see your point. I'm half convinced, but it still seems too subtle for the
average programmer -- a potential "gotcha".
----
John, I noticed a couple of typos in that section (didn't read the entire
thing):
In the first type Score:
| 22 | 24 | 25 | 26 | 27 | 28 ! 30 | 32 | 33
| 34 | 36 | 38 | 39 | 40 | 42 ! 45 | 48 | 50
^
|
Those look like exclamation points, which are allowed, but obsolescent.
Page 16:
for K in Score loop
New_LIne; Put(Hit); Put(Hit_Count(K));
^
|
I should be lower case.
P.S. It's a nice example!
P.P.S. I would have done more singing-the-praises of the full coverage rules,
which (as you know) is one of my favorite features of Ada.
****************************************************************
From: Randy Brukardt
Sent: Tuesday, December 20, 2011 1:39 PM
...
> Well, I still think there's potential confusion. But I'm half
> convinced by Randy's argument. Maybe I could be convinced to allow
> 'First in the static case. But please let's wait for Ada 2020.
We have another ARG meeting before Ada 2012 will be put to bed, so we can
properly consider this issue if time permits. (It's surely not the most
important issue we could talk about, but I'd rather talk about it than Steve's
latest accessibility bug... ;-)
Most of the things that I tried to put off to Ada 2020 ended up getting
discussed in Denver and in many cases handled. (I realize that you were not
there, so you probably don't know how much was changed there - I'm still working
on the minutes, it is very slow going.)
...
> >... I don't buy this
> > argument; the meta rule ought to be that the predicate is involved
> >IFF the subtype name is involved. So the predicate has an effect on
> >the value of Score'First, Score'Last, Score by itself, but ".." does
> >not involve the name of the subtype so no predicate is involved on that.
> >In any case, I don't see that saying Score'Min .. Score'Max (or
> >Score'Start .. Score'Stop) changes meaning at all, other
> than that we know have two very similar attributes.
>
> I see your point. I'm half convinced, but it still seems too subtle
> for the average programmer -- a potential "gotcha".
I understand the worry, but that means that we should make no solution rather
than adding some other attribute (which helps not at all IMHO). But I think most
programmers will eventually find this to a be a missing capability: John's
example seems pretty typical to me.
>I think it would be appropriate to consider this as a possible enhancement for
>Ada 2020. It seems way too late for Ada 2012, especially given that the
>workaround doesn't seem so bad: Declare Score_Range as 1..Single'Last*3,
>and then "Score is Score_Range with Static_Predicate => ...".
> Then you can use Score_Range as the array index, and it's then crystal clear
> that the programmer has to deal with the "holes" (by explicitly putting -1).
The problem with this workaround is that "Single'Last*3" is just as magic was
saying "60". You have to figure it out separately from the predicate, and that
leaves an obvious source of errors.
For instance, in this example, it would be very easy to think that the bullseye
should be the high score. (I've always been surprised that it is not.) So if you
declared Score_Range as 1 .. Inner_Bullseye; (where Inner_Bullseye = 50) you
would have a problem. And the problem would not be detected easily: the
Static_Predicate would still be legal, it just would be missing a few values
(51, 54, 57, 60).
(BTW, that is a complication for defining 'First or 'Min_Value for a static
predicate -- the underlying subtype can be dynamic, meaning that it isn't
necessarily possible at compile-time to figure out which values are in the set.
I had solved this for the old set constraint proposal by having a constraint
check if there were any values of the given set outside of the range of the
parent subtype (just as we do when you declare an new range constraint of an
existing subtype). But we don't have this for static predicates (they're boolean
expressions, not sets). A static predicate on a dynamic subtype isn't very
useful, I wonder if we really ought to be allowing it. More food for thought...)
****************************************************************
From: Bob Duff
Sent: Tuesday, December 20, 2011 3:08 PM
>... (I realize that you were not
> there, so you probably don't know how much was changed there - I'm
>still working on the minutes, it is very slow going.)
In other words, the fact that we have a meeting doesn't make discussing new
features "free". ;-)
> The problem with this workaround is that "Single'Last*3" is just as
> magic was saying "60". You have to figure it out separately from the
> predicate, and that leaves an obvious source of errors.
>
> For instance, in this example, it would be very easy to think that the
> bullseye should be the high score. (I've always been surprised that it
> is
> not.) So if you declared Score_Range as 1 .. Inner_Bullseye; (where
> Inner_Bullseye = 50) you would have a problem. And the problem would
> not be detected easily: the Static_Predicate would still be legal, it
> just would be missing a few values (51, 54, 57, 60).
Good point. But I still don't like changing things at the last minute. Why do
we think this particular limitation of Ada is more important than any others?
> (BTW, that is a complication for defining 'First or 'Min_Value for a
> static predicate -- the underlying subtype can be dynamic, meaning
> that it isn't necessarily possible at compile-time to figure out which
> values are in the set. I had solved this for the old set constraint
> proposal by having a constraint check if there were any values of the
> given set outside of the range of the parent subtype (just as we do
> when you declare an new range constraint of an existing subtype). But
> we don't have this for static predicates (they're boolean expressions, not
> sets).
They're expressed as boolean expressions, but in the static case, they represent
a statically-known set, which can be represented as a sequence of ranges, whose
size is roughly proportional to the text of the predicates. The AI makes this
intent clear, I think.
>...A static predicate on a
> dynamic subtype isn't very useful, I wonder if we really ought to be
>allowing it. More food for thought...)
I thought we treated a subtype with dynamic bounds as fully dynamic, even if it
has a static predicate as well -- so 'First/'Min_Val should be illegal for those
(and use in case statements, etc).
****************************************************************
From: Randy Brukardt
Sent: Tuesday, December 20, 2011 3:37 PM
...
> Good point. But I still don't like changing things at the last
> minute. Why do we think this particular limitation of Ada is more
> important than any others?
It's not so much that it is more important, it's that it is part of getting a
new feature right. And we still can make changes in those new features without
worrying about compatibility issues. So anything reasonably simple that is of
interest potentially will be discussed. After all, in Denver we ended up
discussing a whole bunch of such ideas, including quite a few that I didn't even
have time to create AIs for beforehand. (Several of those got AIs written during
the meeting and approved.)
If this was related to something that has existed for a long time (say "in out"
parameters), it would be much less worth discussing. But "little" changes
related to predicates, preconditions, conditional expressions, etc. seem worth
discussing now; it's better to get it right in the first place. After all, the
whole reason that we agreed to take another round of review was to get more
examples created in order to find rough edges that we missed. That's exactly
what John did here, so we would be remiss to not take the opportunity to see if
there is an improvement that we can agree on.
...
> >...A static predicate on a
> > dynamic subtype isn't very useful, I wonder if we really ought to be
> >allowing it. More food for thought...)
>
> I thought we treated a subtype with dynamic bounds as fully dynamic,
> even if it has a static predicate as well -- so 'First/'Min_Val should
> be illegal for those (and use in case statements, etc).
Humm, such a subtype is not a static subtype, but otherwise it has a static
predicate. The idea originally suggested was to try these attributes to static
predicates only. But you are suggesting that 'First/'Min_Val only be allowed for
static subtypes with static predicates (and of course all subtypes that don't
have a predicate). That probably would work (since staticness can already affect
legality, it is likely OK to have it affect other legality).
****************************************************************
From: Bob Duff
Sent: Tuesday, December 20, 2011 4:48 PM
> It's not so much that it is more important, it's that it is part of
> getting a new feature right.
OK, but note:
- The change being discussed is upward compatible (allow 'First in more
cases, or add new attribute 'Min_Val).
- Time I spend thinking about this is time I'm not spending on reviewing
the RM.
> Humm, such a subtype is not a static subtype, but otherwise it has a
> static predicate. The idea originally suggested was to try these
> attributes to static predicates only. But you are suggesting that
> 'First/'Min_Val only be allowed for static subtypes with static
> predicates (and of course all subtypes that don't have a predicate).
Yes. I mean, the following are illegal:
subtype S is Integer range 1..Non_Static(...) with
Static_Predicate => ...;
...array(S) of ... -- Illegal!
case ... is
when S => ...; -- Illegal!
Right?
So it makes sense that S'First/S'Min_Val would be illegal, too, even if we make
it legal for static subtypes with static predicates.
****************************************************************
From: Randy Brukardt
Sent: Tuesday, December 20, 2011 5:07 PM
...
> - Time I spend thinking about this is time I'm not spending on reviewing
> the RM.
You mean you're spending time *not* thinking about Ada 2012? Shame on you!
;-)
...
> Yes. I mean, the following are illegal:
>
> subtype S is Integer range 1..Non_Static(...) with
> Static_Predicate => ...;
>
> ...array(S) of ... -- Illegal!
>
> case ... is
> when S => ...; -- Illegal!
>
> Right?
Right. The first case isn't interesting (it's illegal for any form of
predicate), but the second is more interesting (it's only allowed for static
subtypes, which necessarily have to have static predicates if they have any).
But how about:
for I in S loop -- Legal (I think).
I think this is legal with the current rules. S is allowed if it has a static
predicate, and there is no requirement that the subtype itself be static. Surely
S'First/S'Min_Value is no harder to implement than this combination of dynamic
and static constraints. So the question becomes whether we ought to use the same
rule here and for the attribute (if any).
And note that changing this (if we do) is *not* upward compatible, so we need to
at least decide this much now.
****************************************************************
From: Bob Duff
Sent: Tuesday, December 20, 2011 5:45 PM
> Right. The first case isn't interesting (it's illegal for any form of
> predicate),
Right, you can throw that red herring back into the ocean. ;-)
>... but the second is more interesting (it's only allowed for static
>subtypes, which necessarily have to have static predicates if they have
>any).
Right.
>... But how about:
>
> for I in S loop -- Legal (I think).
>
> I think this is legal with the current rules. S is allowed if it has a
> static predicate, and there is no requirement that the subtype itself
> be static.
Hmm. I thought that was illegal. But you are right -- it appears to be legal
according to the RM. The discussion in AI05-0153-3 seems to indicate that this
is a mistake (mea culpa). AI05-0262-1 is indicated on this para, but I see no
change for that AI.
>...Surely S'First/S'Min_Value is no harder to implement than this
>combination of dynamic and static constraints. So the question becomes
>whether we ought to use the same rule here and for the attribute (if any).
Yes, it would make sense to use the same rule.
> And note that changing this (if we do) is *not* upward compatible, so
> we need to at least decide this much now.
Indeed. I'm inclined to make the above 'for' loop illegal.
****************************************************************
From: Randy Brukardt
Sent: Tuesday, December 20, 2011 6:15 PM
...
> Hmm. I thought that was illegal. But you are right -- it appears to
> be legal according to the RM. The discussion in
> AI05-0153-3 seems to indicate that this is a mistake (mea culpa).
> AI05-0262-1 is indicated on this para, but I see no change for that AI.
I think the paragraph was split, and this sentence now stands alone. But it
wasn't actually changed.
> >...Surely S'First/S'Min_Value is no harder to implement than this
> >combination of dynamic and static constraints. So the question
> >becomes whether we ought to use the same rule here and for the
> >attribute (if any).
>
> Yes, it would make sense to use the same rule.
>
> > And note that changing this (if we do) is *not* upward compatible,
> > so we need to at least decide this much now.
>
> Indeed. I'm inclined to make the above 'for' loop illegal.
I can believe that was the intent.
In any case, now that we've found a bug we'll have to discuss it in February, so
we might as well discuss the attributes as well. See how that works? :-)
{Note: The bug is handled in AI05-0287-1. - Editor}
[Note that this has nothing to do with the quality of your work. As Adam has
demonstrated repeatedly, look hard enough anywhere in the RM (any version of the
RM) and you'll find a bug.]
****************************************************************
From: Bob Duff
Sent: Tuesday, December 20, 2011 6:44 PM
> In any case, now that we've found a bug we'll have to discuss it in
> February, so we might as well discuss the attributes as well. See how
> that works? :-)
Yes, I see. We find a bug, and we thereby open Pandora's Box. ;-)
****************************************************************
Questions? Ask the ACAA Technical Agent