!standard 3.5.5(7) 12-03-13 AI05-0297-1/02 !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 Add after 3.5.5(7) (i.e. at the end of the existing Static Semantics section): For every static discrete subtype S for which there exists at least one value belonging to S that satisfies any predicate of S, the following attributes are defined: S'First_Valid S'First_Valid denotes the smallest value that belongs to S and satisfies any predicate of 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 and satisfies any predicate of S. The value of this attribute is of the type of S. [Redundant: First_Valid and Last_Valid attribute_references are always static expressions. Any predicate of S is always a static predicate.] AARM Reason: We require there to be at least one value so that these are always values of the subtype. (This sidesteps the question of what to return for a subtype with no values.) AARM discussion: These attributes are intended primarily for use in the case where the Static_Predicate aspect of S has been specified; First and Last are equivalent if these are allowed and there is no predicate. Add an AARM Note after 3.2.4(18/3): This is to prevent confusion about whether the First value is the lowest value of the subtype (which does not depend on the predicate) or the lowest value of the subtype which meets the predicate. (For a dynamic predicate, determining this latter value is expensive as it would usually require a loop.) For a static subtype that has a static predicate, the First_Valid and Last_Valid attributes (see 3.5.5) can be used instead. !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. ---- The idea of the naming of these attributes is that they return the smallest (largest) value for which Obj'Valid would be True (that is, the smallest (largest) valid value). ---- Note that the requirement that the prefix of these attributes be static prevents them from ever being used on a subtype descended from a generic formal type (these are never static). !corrigendum 3.5.5(7) @dinsa @xindent. For the evaluation of a call on S'Val, if there is no value in the base range of its type with the given position number, Constraint_Error is raised.> @dinss For every static discrete subtype S for which there exists at least one value belonging to S that satisfies any predicate of S, the following attributes are defined: @xhang<@xterm S'First_Valid denotes the smallest value that belongs to S and satisfies any predicate of S. The value of this attribute is of the type of S.> @xhang<@xterm S'Last_Valid denotes the largest value that belongs to S and satisfies any predicate of S. The value of this attribute is of the type of S.> First_Valid and Last_Valid attribute_references are always static expressions. Any predicate of S is always a static predicate. !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. ;-) **************************************************************** From: Randy Brukardt Sent: Tuesday, March 13, 2012 7:11 PM (Please Bob, forget the "I told you so" - Randy). I'm afraid that we were so focused on the names of these attributes that we never bothered to check the actual wording. (And I put it into the standard draft before I realized that it is complete junk.) Steve proposed: For every 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. The problem with this is that predicates don't modify the value set of a subtype, and they don't have any effect on the "belongs" relationship. Just look at 4.5.2(29/3): The membership_choice is a subtype_mark, the tested type is scalar, the value of the simple_expression belongs to the range of the named subtype, and the predicate of the named subtype evaluates to True. So the problem is that both the lead-in and the definitions are completely wrong. The problem with the lead-in is that just because there is "one value belonging to S" doesn't mean that there are any values that satisfy the predicate (and that's the point). The problem with the bullets is that they don't mention the predicates. The fix is fairly easy, I think (hope?): For every static discrete subtype S for which there exists at least one value belonging to S {that satisfies any predicate of S}, the following attributes are defined: S'First_Valid S'First_Valid denotes the smallest value that belongs to S {and satisfies any predicate of 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 {and satisfies any predicate of S}. The value of this attribute is of the type of S. **************************************************************** From: Tucker Taft Sent: Tuesday, March 13, 2012 8:30 PM Good catch. I think we all just believed that "belong" took static predicates into account, but alas, I think we argued with ourselves about that for a long time and ultimately decided it shouldn't. I don't remember all the reasons, but they are too late to reexamine at this point... ****************************************************************