Version 1.1 of ai05s/ai05-0287-1.txt

Unformatted version of ai05s/ai05-0287-1.txt version 1.1
Other versions for file ai05s/ai05-0287-1.txt

!standard 3.2.4(0)          12-02-13 AI05-0287-1/01
!standard 8.6(17/2)
!class Amendment 12-02-13
!status Amendment 2012 12-02-13
!status work item 12-02-13
!status received 11-12-08
!priority Medium
!difficulty Medium
!subject Some questions on subtype predicates
!summary
We define the current instance of a subtype. We disallow recursive predicates.
** TBD **
!proposal
(1) The standard does not define the "current instance" of a subtype, but 3.2.4 uses it extensively. We need to correct this.
(2) It's possible to write recursive subtype predicates (that is, predicates that invoke the same predicate). This is very confusing at best and causes definitional problems for static predicates, so it should be banned.
(3) A dynamic subtype with a static predicate is allowed in a loop_parameter_specification. It would be fairly complex to determine the actual values to iterate at runtime; such a subtype should not be allowed in a loop_parameter_specification.
(4) Subtypes with static predicates cannot be used to define an array, because defining a slice of this sort of array is very difficult to implement in general (and would cause distributed overhead). We also don't allow 'First and 'Last on such types because S'First .. S'Last does not do the same thing as S in memberhips, case statements, and iteration.
So how do we define such an array? Usually, Ada programmers are told to avoid magic numbers and use 'First and 'Last instead. But here we cannot do that.
Some attributes should be provided for this purpose. ** Which ones TBD **
!wording
(1) Add the following as the penultimate sentence of 8.6(17/2):
Similarly, if a usage name appears within the declarative region of a subtype_declaration and denotes that same subtype_declaration, then it denotes the current instance of the subtype.
(2) Add after 3.2.4(15/3):
If a predicate applies to a subtype, then that predicate shall not mention any other subtype to which the same predicate applies.
AARM Reason: This is intended to prevent recursive predicates, which cause definitional problems for static predicates. Inside of the predicate, the subtype name refers to the current instance of the subtype, which is an object, so a direct use of the subtype name cannot be recursive. But other subtypes naming the same type might:
type Really_Ugly is private;
private subtype Ugly is Really_Ugly; type Really_Ugly is new Integer with Static_Predicate => Really_Ugly not in Ugly;
(3) Modify 3.2.4(18/3):
The discrete_subtype_definition of a loop_parameter_specification shall not denote a {nonstatic subtype to which predicate specifications apply or any} subtype to which Dynamic_Predicate specifications apply.
(4) *** TBD ***
Options: [A] Allow 'First and 'Last (but only for static subty[es with static predicates); these would be defined such that the smallest included value is 'First and the largest included value is 'Last. S'First .. S'Last does not involve the predicate, while S (the subty[e name) does. There potentially would be confusion with the range not being the same as S for case and loop purposes.
[B] Create a separate set of special attributes for this purpose, called 'Min_Val and 'Max_Val for this purpose. These would only be allowed on static subtypes, these would be defined such that the smallest included value is 'Min_Val and the largest included value is 'Max_Val. (If there is no predicate, these are the same as 'First and 'Last.) There still would be confusion with S'Min_Val .. S'Max_Val not being the same as S for case and loop purposes.
[C] Forget this, and let programmers like John fend for themselves. This is not very Ada-like; most Ada programmers are taught early on to avoid magic numbers and use the attributes instead. It's unfortunate that can't be done here.
[Editor's opinion: I don't see that [B] offers enough reduction in confusion for the clunky attribute names and the potential confusions as to when to use which attributes. Adopting [A] also means fewer cases where 'First and 'Last in a generic unit has to raise Program_Error. It seems un-Ada-like to not allow attributes for some subtype, so [C] is annoying (but is still acceptable to me if we can't get agreement on [A], mainly because some 'First uses will remain illegal no matter what rule we adopt.]
!discussion
*** TBD ***
!corrigendum 3.2.4(0)
Insert new clause:
[A placeholder to cause a conflict; the real wording is found in the conflict file.]
!corrigendum 8.6(17/2)
Replace the paragraph:
If a usage name appears within the declarative region of a type_declaration and denotes that same type_declaration, then it denotes the current instance of the type (rather than the type itself); the current instance of a type is the object or value of the type that is associated with the execution that evaluates the usage name. This rule does not apply if the usage name appears within the subtype_mark of an access_definition for an access-to-object type, or within the subtype of a parameter or result of an access-to-subprogram type.
by:
If a usage name appears within the declarative region of a type_declaration and denotes that same type_declaration, then it denotes the current instance of the type (rather than the type itself); the current instance of a type is the object or value of the type that is associated with the execution that evaluates the usage name. Similarly, if a usage name appears within the declarative region of a subtype_declaration and denotes that same subtype_declaration, then it denotes the current instance of the subtype. These rules do not apply if the usage name appears within the subtype_mark of an access_definition for an access-to-object type, or within the subtype of a parameter or result of an access-to-subprogram type.
!ACATS Test
!ASIS
No change needed.
!appendix

From: Steve Baird
Sent: Thursday, December  8, 2011  3:07 AM

The subtype predicate RM section (3.2.4) doesn't define the current instance of
a subtype - it assumes that is defined elsewhere. But RM 8.6 only defines the
current instance of a type, not of a subtype.

I don't think the current instance of a subtype is defined anywhere.

A simple solution might be to append something like the following to 8.6(17/2).

   Similarly, if a usage name appears within the declarative region
   of a subtype_declaration and denotes that same subtype_declaration,
   then it denotes the current instance of the subtype.

Do we want to have some sort of a special exemption for membership tests?

In the type case, 8.6(17/2) provides a special exemption ("This rule does not
apply if ... ") in some cases when the type name is used as the designated type
of an access type.

Do we want a similar exemption in order to allow something like the following
example (which would otherwise be illegal):

     subtype Power_Of_Two is Positive with
       Dynamic_Predicate => (if Power_Of_Two mod 2 = 1
                             then Power_Of_Two = 1
                             else (Power_Of_Two / 2) in Power_Of_Two); ?

Probably not, but I raise the question.

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

From: Yannick Moy
Sent: Thursday, December  8, 2011  5:08 AM

I agree this is not a good idea. It makes the predicate unreadable.

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

From: Bob Duff
Sent: Thursday, December  8, 2011  7:36 AM

> Probably not, but I raise the question.

I agree with "probably not".  If we did so, why would "in" be the only
exception?  I mean, why not allow (for ex.):

    subtype T is Integer with
        Static_Predicate => T < 100 or T = T'Last;

?

Answers: Because it's confusing, not all that useful, and causes implementation
difficulty.  Also, it's too late in the game to be adding functionality that was
never envisioned during the design phase.

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

From: Robert Dewar
Sent: Thursday, December  8, 2011  8:12 AM

Well I don't really understand why this should be illegal, but FWIW gnat allows
this now and handles it fine. If it is illegal, I guess we will have to
understand why and put in a (to me arbitrary, since I don't understand it)
restriction to reject this.

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

From: Bob Duff
Sent: Thursday, December  8, 2011  8:28 AM

Well, Steve showed me this example in private email:

with Text_IO;
procedure Pow is
  subtype Power_Of_Two is Positive with
    Dynamic_Predicate => (if Power_Of_Two mod 2 = 1
                          then Power_Of_Two = 1
                          else (Power_Of_Two / 2) in Power_Of_Two); begin
  for Iii in Integer range 1 .. 1234 loop
      if Iii in Power_Of_Two then
          Text_IO.Put_Line (Iii'Img);
      end if;
  end loop;
end Pow;

It prints one line containing " 1", which is not "fine".  ;-) If you look at the
-gnatD output, you'll see some wrong code.

The reason it should be illegal, is that within the declaration of Power_Of_Two,
"Power_Of_Two" denotes an object, not a subtype, so you can't say "in
Power_Of_Two". Unless, of course, we add some exception(s) to the current
instance rule as Steve outlined.

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

From: Robert Dewar
Sent: Thursday, December  8, 2011  8:35 AM

> The reason it should be illegal, is that within the declaration of
> Power_Of_Two, "Power_Of_Two" denotes an object, not a subtype, so you
> can't say "in Power_Of_Two".
> Unless, of course, we add some exception(s) to the current instance
> rule as Steve outlined.

OK, thanks for clarification, sorry for not understanding quicker, but yes, I
think this should be illegal, and I think GNAT is wrong to accept it!

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

From: Randy Brukardt
Sent: Thursday, December  8, 2011  12:46 PM

>I agree with "probably not".  If we did so, why would "in" be the only
>exception?  I mean, why not allow (for ex.):
>
>    subtype T is Integer with
>        Static_Predicate => T < 100 or T = T'Last;

We don't allow T'Last *anywhere* for a subtype that has a predicate, so I'd
hardly expect it to work here! (A restriction that I didn't think was necessary,
but I'm not going to re-argue that now.) But I could imagine allowing the name
in (sub)type conversions, aggregates, case expression limbs, etc. But that
doesn't make much sense (see below).

Bob also wrote:
> Robert Dewar <dewar@adacore.com> wrote:
>
> > > Do we want a similar exemption in order to allow something like
> > > the following example (which would otherwise be illegal):
> > >
> > >       subtype Power_Of_Two is Positive with
> > >         Dynamic_Predicate =>  (if Power_Of_Two mod 2 = 1
> > >                               then Power_Of_Two = 1
> > >                               else (Power_Of_Two / 2) in Power_Of_Two); ?
> > >
> > > Probably not, but I raise the question.
> >
> > Well I don't really understand why this should be illegal, but FWIW
> > gnat allows this now and handles it fine. If it is illegal, I guess
> > we will have to understand why and put in a (to me arbitrary, since
> > I don't understand it) restriction to reject this.
...
> The reason it should be illegal, is that within the declaration of
> Power_Of_Two, "Power_Of_Two" denotes an object, not a subtype, so you
> can't say "in Power_Of_Two".
> Unless, of course, we add some exception(s) to the current instance
> rule as Steve outlined.

And we don't want to add those exceptions, IMHO.

Almost all(?) uses of a subtype name that can appear in an expression involve
evaluating the predicate (membership, type conversion, case libs). Which is what
we are defining here! One could imagine that involving a recursive evaluation of
the predicate, but that usually would be an infinite recursion (the twisted
example above appears to have been designed so that recursive evaluation would
in fact terminate). I don't think it is worth the complication to compilers to
support this recursion, such uses are either mistakes or very tricky (as above),
and since the rules "naturally" ban this anyway, I don't think there is any
reason to create an exception to allow this.

In case anyone is not convinced, consider the following (which would be allowed
if we allowed the current instance to represent a subtype in all places where
that makes [some] sense):

    subtype Really_Ugly is Natural with
        Static_Predicate => (case Really_Ugly is
                               when Really_Ugly => True,
                               when others => False);

    Put (1 in Really_Ugly); -- What does this print??

[This is OK since the rules for predicate-static add no additional restrictions
to case limbs, assuming that the current instance can represent a subtype.]

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

From: Bob Duff
Sent: Thursday, December  8, 2011  1:01 PM

> We don't allow T'Last *anywhere* for a subtype that has a predicate,
> so I'd hardly expect it to work here!

Good point.  T'Size, then.

> > Unless, of course, we add some exception(s) to the current instance
> > rule as Steve outlined.
>
> And we don't want to add those exceptions, IMHO.

Agreed.

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

From: Brad Moore
Sent: Thursday, December  8, 2011  3:53 PM

Not that it matters much, but this particular problem
 is likely better expressed as a static predicate as in;


type Unsigned_32 is mod 2 ** 32;
subtype Power_Of_Two is Unsigned_32
with Static_Predicate =>
  Power_Of_Two = 0 or else
    (Power_Of_Two - 1 and Power_Of_Two = 0);

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

From: Robert Dewar
Sent: Thursday, December  8, 2011  4:42 PM

UGH! I particularly hate seeing fancy tricks like this in predicates, which
should be about clear documentation :-)

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

From: Randy Brukardt
Sent: Thursday, December  8, 2011  5:27 PM

Argeed. Which clarifies even more that we don't want to allow the current
subtype name to be used in memberships and type conversions, since those would
be recursive and thus could only be used in some form of fancy trick.

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

From: Brad Moore
Sent: Thursday, December  8, 2011  8:00 PM

> UGH! I particularly hate seeing fancy tricks like this in predicates,
> which should be about clear documentation :-)

I agree, except performance can be desirable attribute as well, and this is a
quick and efficient check.

Probably the think to do is have the predicate call an inline function, e.g,
Is_A_Power_Of_Two, and hide the implementation of the function in the body of
the package. (Along with suitable comments to explain the trick)

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

From: Bob Duff
Sent: Thursday, December  8, 2011  8:23 PM

> >> type Unsigned_32 is mod 2 ** 32;
> >> subtype Power_Of_Two is Unsigned_32 with Static_Predicate =>
> >> Power_Of_Two = 0 or else (Power_Of_Two - 1 and Power_Of_Two = 0);

You need some parentheses there.

Credit where credit is due:  Gary Dismukes reminded me of this.

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

From: Brad Moore
Sent: Thursday, December  8, 2011  9:41 PM

> Credit where credit is due:  Gary Dismukes reminded me of this.

Thanks for pointing that out.

> UGH! I particularly hate seeing fancy tricks like this in predicates,
> which should be about clear documentation :-)

The thought just occurred to me, this seems to be a tough crowd.
My jokes get "Aagh!", and my tricks get "UGH!"

I suspect what people really want to say is "ARG!", but fortunately that is a
reserved word. :-)

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

From: Tucker Taft
Sent: Thursday, December  8, 2011  10:46 PM

Unless I am really confused, this should be Power_Of_Two > 0 and then
((Power_Of_Two-1) and Power_Of_Two) = 0

I think your version implies that 0 is a power of 2 (I suppose if you are
considering 2**(-inf) a power of 2 then it is ;-), and has the wrong precedence
for "and" relative to "=".

Also, this would have to be a dynamic predicate, since you are using operators
that are not permitted in the non-static-expression part of static predicates
(bit-wise "and" and "-").

Static predicates really only allow relationals and logical operators in the
non-static-expression part.  No arithmetic.

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

From: Brad Moore
Sent: Thursday, December  8, 2011  11:14 PM

Thanks for the correction and clarification, Tuck.
I'll look forward to the day when I have an Ada 2012 compiler to help catch some
of my errors.;-)

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

From: Robert Dewar
Sent: Thursday, December  8, 2011  11:34 PM

> Probably the think to do is have the predicate call an inline
> function, e.g, Is_A_Power_Of_Two, and hide the implementation of the
> function in the body of the package. (Along with suitable comments to
> explain the
> trick)

But then it can't be a static predicate :-)

Would be interesting to be able to write user defined static functions :-)

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

From: Randy Brukardt
Sent: Friday, December  9, 2011  12:30 AM

That was one of the options in the expression function proposal - seriously.
I didn't pursue it, as it would require some sort of special rule to determine
whether the function could be used as static. (The easiest one would be to only
allow a function in a static expression when the function name statically
denotes an expression function, and the "substituted" function body is a static
expression. Defining "substituted" didn't look easy, unfortunately.)

Having this capability would allow breaking up giant static expressions (and
I've had a few of these). Conditional expressions help with that, but functions
would help more.

(The other options for expression functions were to allow expression functions
in interfaces similarly to null procedures, and to allow them as the default for
a generic formal subprogram. The former requires a bit of additional conformance
checking for overloading/overriding, and the latter doesn't have a particularly
good syntax [Ada already having used the obvious choice for something else]. So
neither of these were pursued, either).

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

From: Bob Duff
Sent: Friday, December  9, 2011  9:48 AM

> The thought just occurred to me, this seems to be a tough crowd.
> My jokes get "Aagh!", and my tricks get "UGH!"

You're misquoting -- I didn't write the UGH part -- I think that was Robert.

By the way, you might enjoy the book "Hacker's Delight", by Henry Warren.  It's
filled with all sorts of bit fiddling tricks.  I found it delightful.

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

From: Steve Baird
Sent: Tuesday, December 13, 2011  8:23 AM

> In case anyone is not convinced, consider the following (which would
> be allowed if we allowed the current instance to represent a subtype
> in all places where that makes [some] sense):
>
>     subtype Really_Ugly is Natural with
>         Static_Predicate => (case Really_Ugly is
>                                when Really_Ugly => True,
>                                when others => False);
>

Randy made the point (at least implicitly) that allowing recursive subtype
predicates in static predicates is even worse than allowing them in dynamic
predicates.

There seems to be a general consensus that we don't want to allow them in either
case, but the static case would introduce definitional problems in the language
(the dynamic case leads to unwanted implementation complexity and perhaps makes
programs harder to understand, but at least it seems to be well defined).

In private communication, I pointed out that we still have this problem even
with the simple no-special-exemptions rule we seem to be converging on for
determining when a name denotes the current instance of a subtype.

Steve Baird wrote:
> Unfortunately, I think we may need to do some more work to really
> eliminate this problem because, in the case of a first-named subtype,
> it is possible to (in effect) name the subtype as a subtype (not as an
> object) within the full view of the type declaration by means of an
> intervening subtype declaration.
>
> Consider:
>
>     type Really_Ugly is private;
>   private
>     subtype Ugly is Really_Ugly;
>     type Really_Ugly is new Integer with
>       Static_Predicate => Really_Ugly not in Ugly

After some discussion about the scope of the problem (e.g., can we get the same
sort of problem if the initial view of the type is introduced by an incomplete
type declaration instead of a private type declaration?), Bob suggested that if
we really want to ban recursive subtype predicates, then we should simply say so
explicitly.

Bob Duff wrote:
> Something like this:
>
>     If a predicate applies to a subtype, then that predicate shall
>     not mention any [other?] subtype to which the same predicate applies.
>
> The RM wording says that the predicate of Really_Ugly applies to Ugly,
> so the above would be forbidden.  Maybe "mention any [other?] subtype"
> needs to be "denotes the declaration of any [other?] subtype".

Is there general agreement that both
   a) there is a problem here that needs solving and
   b) explicitly banning recursive subtype predicates (for both static
      and dynamic predicates) is the right approach ?

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

From: Randy Brukardt
Sent: Tuesday, December 13, 2011  1:28 PM

...
> Is there general agreement that both
>    a) there is a problem here that needs solving and
>    b) explicitly banning recursive subtype predicates (for both static
>       and dynamic predicates) is the right approach ?

Can't speak for the "general" :-), but I agree that there is a problem that
needs fixing and that Bob's solution seems like a good solution. (I would expect
that Bob would agree, since it is his solution.)

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

From: Gary Dismukes
Sent: Tuesday, December 13, 2011  2:21 PM

> Is there general agreement that both
>    a) there is a problem here that needs solving

I, at least, agree.

> and
>    b) explicitly banning recursive subtype predicates (for both static
>       and dynamic predicates) is the right approach ?

Yes, looks like the way to go.

(There's also the smaller matter that you pointed out of defining current
instance for subtypes.  Not a big concern, but seems like we should fix that as
well.)

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

From: Robert Dewar
Sent: Tuesday, December 13, 2011  2:52 PM

> Can't speak for the "general" :-), but I agree that there is a problem
> that needs fixing and that Bob's solution seems like a good solution.
> (I would expect that Bob would agree, since it is his solution.)

I agree with this too

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

From: Bob Duff
Sent: Tuesday, December 13, 2011  3:21 PM

Well, I don't think there are ANY problems with Ada that NEED solving.

But yes, this is a problem, and we ought to solve it if we can, and I agree with
myself about the best solution.  ;-)

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

From: Randy Brukardt
Sent: Monday, Feburary 13, 2011  7:58 PM

Way back in early December (in mail that I'm turning into an AI for the upcoming
meeting), Bob gave the following example:

> Well, Steve showed me this example in private email:
>
> with Text_IO;
> procedure Pow is
>   subtype Power_Of_Two is Positive with
>     Dynamic_Predicate => (if Power_Of_Two mod 2 = 1
>                           then Power_Of_Two = 1
>                           else (Power_Of_Two / 2) in Power_Of_Two);
> begin
>   for Iii in Integer range 1 .. 1234 loop
>       if Iii in Power_Of_Two then
>           Text_IO.Put_Line (Iii'Img);
>       end if;
>   end loop;
> end Pow;
>
> It prints one line containing " 1", which is not "fine".  ;-) If you
> look at the -gnatD output, you'll see some wrong code.
>
> The reason it should be illegal, is that within the declaration of
> Power_Of_Two, "Power_Of_Two" denotes an object, not a subtype, so you
> can't say "in Power_Of_Two".
> Unless, of course, we add some exception(s) to the current instance
> rule as Steve outlined.

Everyone agreed with Bob here -- except that GNAT is (or at least was) in fact
right on this program!

We all forgot that memberships now allow objects on the RHS of the "in" (added
in Ada 2012 to allow choice lists). So

(Power_Of_Two / 2) in Power_Of_Two

is legal (both Power_Of_Two being interpreted as objects) and essentially means:

  (Power_Of_Two / 2) = Power_Of_Two

which might as well have been written:

   Power_Of_Two = 0.

Substituting that in the example, we get exactly the result Bob reported.

I suspect that the writer of this code did not intend this result, but hey, you
get what you write. (Perhaps a warning would be a good idea [if the same object
is used on both sides of "in"], but it seems unlikely to come up in practice, so
I'm not sure it is worth the work.)

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

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 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