Version 1.15 of ai05s/ai05-0153-1.txt

Unformatted version of ai05s/ai05-0153-1.txt version 1.15
Other versions for file ai05s/ai05-0153-1.txt

!standard 3.2.4(0)          10-06-30 AI05-0153-1/07
!class Amendment 09-05-27
!status No Action (10-0-0) 10-10-29
!status work item 09-05-27
!status received 09-05-27
!priority Medium
!difficulty Medium
!subject Subtype predicates
!summary
Add subtype predicates to the language. Predicates are checked on every subtype conversion (which includes assignments, most cases of parameter passing, etc), and on all parameter passing.
!problem
Ada's constraints are a powerful way to enhance the contract of an object (including formal parameters). But the constraints that can be expressed are limited.
For instance, it isn't possible to specify that a record type may have any of several discriminant values - for discriminants we can only specify a single value or allow all discriminants.
!proposal
(See summary.)
!wording
This AI depends on AI05-0183-1, which defines a syntax for aspect_specifications, which may be attached to declarations.
Informal: We define a new aspect "Predicate", which takes a condition (i.e. a BOOLEAN_expression). The condition can use the defining_identifier of the subtype, to mean the "current instance" of the subtype (that is, the object to test the predicate against), as defined in AI05-0183-1.
AARM-3.2(1.b/2) says:
1.b/2 Glossary entry: {Subtype} A subtype is a type together with a
constraint or null exclusion, which constrains the values of the subtype to satisfy a certain condition. The values of a subtype are a subset of the values of its type.
Change it to:
1.b/2 Glossary entry: {Subtype} A subtype is a type together with optional
constraints, null exclusions, and predicates, which constrain the values of the subtype to satisfy a certain condition. The values of a subtype are a subset of the values of its type.
Add new section 3.2.4:
3.2.4 Subtype Predicates
For any subtype, the following language-defined aspect is defined:
Subtype_Predicate
This aspect shall be specified by an expression. The expected type for the expression is any boolean type. A Subtype_Predicate may be specified on a type_declaration or a subtype_declaration; if none is given, an implicit "with Subtype_Predicate => True" is assumed.
Legality Rules
Within the expression of a Subtype_Predicate aspect_specification for a composite type C or an access to a composite type C, the only components of C referenced shall be discriminants, and a name that denotes the current instance of the (sub)type shall be used only as a prefix (including a dereference) of a selected_component for a discriminant, or as a prefix (including a dereference) of an attribute_reference with attribute_designator being Length, First, Last, or Range.
Static Semantics
The /predicate of a subtype/ is defined as follows:
- For a (first) subtype defined by a derived type declaration, the
specified Subtype_Predicate, and-ed with the predicate of the parent subtype, and-ed with the predicates of any progenitor subtypes.
- For a (first) subtype defined by a non-derived type declaration,
the specified Subtype_Predicate.
- For a subtype created by a subtype_declaration, the specified Subtype_Predicate,
and-ed with the predicate of the subtype denoted by the subtype_mark.
- For a subtype created by a subtype_indication that is not that of
a subtype_declaration, the predicate of the subtype denoted by the subtype_mark.
- For a base subtype, True.
[Editor's note: The predicate has no effect on the static or dynamic semantics of the subtype indication except as noted here. In particular, it has no effect on the range of scalar subtypes.]
Dynamic Semantics
If the Assertion_Policy in effect is Check, then:
On every subtype conversion, the predicate of the target subtype is evaluated, and a check is made that the predicate is True. Redundant[This includes all parameter passing, except for certain parameters passed by reference, which are covered by the following rule: ] After normal completion and leaving of a subprogram, for each in out or out parameter that is passed by reference, the predicate of the subtype of the actual is evaluated, and a check is made that the predicate is True. For an object created by an object_declaration with no explicit initialization expression, or by an uninitialized allocator, if any subcomponents have explicit default values, the predicate of the nominal subtype is evaluated, and a check is made that the predicate is True. Assertions.Assertion_Error is raised if any of these checks fail.
AARM Ramification: Predicates are not evaluated at the point of the [sub]type declaration.
AARM Ramification: Predicates are not checked on object creation for elementary types. For composite types, they are checked only if there are default values, and only if those defaults are used. Also, only for explicit defaults -- the implicit default of null for access types doesn't count.
Legality Rules
An index subtype, discrete_range of an index_constraint or slice, or a discrete_subtype_definition is illegal if it statically denotes a subtype with a user-specified predicate.
Dynamic Semantics
The elaboration of the declaration or body of an instance of a generic unit raises Program_Error if any of the following occurs within that declaration or body, but not further nested within a generic unit: an index subtype, discrete_range of an index_constraint or slice, or a discrete_subtype_definition with a user-specified predicate.
AARM Reason: We don't want to have arrays with holes determined by arbitrary predicate expressions -- that would be very confusing. The same goes for entry families. It would be particularly troublesome for slices that are required to be passed by reference. The run-time check is needed to prevent generic contract model problems, but we also have a legality rule for cases where we can detect the problem at compile time. Note that the run-time check happens at the elaboration of each instance, not just when the offending construct is reached. Therefore, the check could fail even if the offending construct cannot be reached.
AARM Reason: The wording forbids "for X in S loop ..." when S has a user-specified predicate. Although it might make sense for it to mean:
for X in S'range loop if X'Predicate then ...
we decided that would be too confusing.
Implementation Permissions
A predicate check may be omitted if the subtype with the predicate statically matches the nominal subtype of the value being checked.
AARM Reason: Well-behaved predicates should not have side effects that matter, so omitting the check is pretty harmless. It is possible to write non-well-behaved predicates, which is why the permission is needed. If the implementation does omit a predicate check, it cannot later assume that the predicate was True.
NOTE: A Predicate does not cause a subtype to be considered "constrained".
NOTE: A Predicate is not necessarily True for all objects of the subtype at all times. Predicates are checked as specified above, but can become False at other times. For example, the Predicate of a record is not checked when a component is modified.
[End of 3.2.4.]
Modify 4.5.2(29):
The tested type is scalar, and the value of the simple_expression belongs to the given range, or the range of the named subtype {and any predicate of the named subtype evaluates to True}; or
AARM Ramification: If S has a predicate, S'First in S may evaluate to False. Similarly for S'Last. We certainly don't want to require the implementation to calculate the lowest/highest values that satisfy the predicate. Nor do we want to require a check that the bounds satisfy the predicate, because that would just complicate the programmer's job. For example, if you want to attach a predicate like "is even" to a subtype of Integer, you don't want to have to carefully construct a subrange of Integer first.
Modify AARM-4.5.2(29.a):
Ramification: The scalar membership test only does a range check {and a predicate check}. It does not perform any other check, such as whether a value falls in a "hole" of a "holey" enumeration type. The Pos attribute function can be used for that purpose.
Modify 4.5.2(30/2):
The tested type is not scalar, and the value of the simple_expression satisfies any constraints of the named subtype, {any predicate of the named subtype evaluated to True,} and:
Add at the end of 4.6(51/2):
If the target subtype has a predicate, and the Assertion_Policy in effect is Check, the predicate is applied to the value and Assertions.Assertion_Error is raised if the result is False.
Modify 4.9.1(2/2):
A subtype statically matches another subtype of the same type if they have statically matching constraints, {all predicate_clauses that apply to them come from the same declarations, }and, for access subtypes, either both or neither exclude null. ...
Modify 6.4.1(13/3) (as modifed by AI05-0196-1):
For an access type, the formal parameter is initialized from the value of the actual, without checking that the value satisfies any constraint{, any predicate,} or any exclusion of the null value;
Modify 13.9.2(3):
X'Valid Yields True if and only if the object denoted by X is normal[ and]{,}
has a valid representation{, and the predicate of the nominal subtype of X evaluates to True}. The value of this attribute is of the predefined type Boolean.
!discussion
Predicates are similar to constraints. The differences are:
- Constraints are restricted to certain particular forms (range
constraints, discriminant constraints, and so forth), whereas predicates can be arbitrary conditions.
- Constraints can only be violated for invalid values, whereas predicates
can be violated in various ways (important side effects in predicates, for example, could cause reevaluation of the predicate to get a different answer). However, it is possible to write well-behaved predicates. We don't know how to FORCE the programmer to write well-behaved predicates without being too restrictive.
Predicates are similar to type invariants. The differences are:
- A type invariant is a requirement on all values of a type outside of the
type's defining package(s). That is, invariants are specifically allowed to become False "within the abstraction".
- Therefore, invariants are only allowed for private TYPES,
whereas predicates are allowed for any SUBtype.
---
The model here is that a predicate has no effect on the static or dynamic semantics of a constraint. That is, if a predicate is applied to an indefinite type, the resulting subtype is indefinite. If a predicate is applied to an unconstrained subtype, the resulting subtype is unconstrained. And so on.
This mirrors the semantics of null exclusions (which also are not constraints).
---
We define predicates to be part of static matching, so that subtypes with different predicates are not considered identical.
---
Note that in general, predicates are only known to be True at the point where the language defines the check, so the compiler can't always assume they are True at some later point in the code. However, there are cases where the compiler can usefully deduce such later truths.
For example:
type Rec is record A : Natural; end record; subtype Decimal_Rec is Rec with Predicate => Rec.A mod 10 = 0;
Obj : Decimal_Rec := (A => 10); -- (1)
procedure Do_It (P : in out Rec) is begin P.A := 5; end Do_It;
Do_It (Obj); -- (2) Put (Obj in Decimal_Rec); -- (3)
The predicate on Decimal_Rec will be checked at (1). However, after the call at (2), the predicate is no longer True for Obj. The call at (3) will print False.
Such loopholes are unfortunate, but we really have no choice. There is no way to prevent "bad" predicates without being overly restrictive. Compilers may, of course, give warnings about questionable predicates. Note that predicates are no worse than preconditions in this regard.
Consider the alternatives: One can already specify predicates in comments, but of course such predicates can be false. Alternatively, one can sprinkle preconditions and pragmas Assert all over the place, but those can be false, too -- for the same reasons predicates can be false, and for the additional reason that one might forget some places to sprinkle.
---
We considered allowing implementations to check predicates at places where such checks are not required. However, that seems too complicated, since we can't allow checks anywhere at all -- that would allow the implementation to introduce arbitrary race conditions into the program!
---
RM-3.2(8/2) says:
...The set of values of a subtype consists of the values of its type that satisfy its constraint and any exclusion of the null value.
We considered changing it to:
...The set of values of a subtype consists of the values of its type that satisfy its constraint, any exclusion of the null value, and any predicate.
because conceptually, the values of the subtype include only values that obey the predicate, and indeed membership tests reflect that (X in T returns False if X the predicate is False). This view makes sense for well-defined predicates. However, 13.9.1(2) says:
A scalar object can have an invalid representation, which means that the object's representation does not represent any value of the object's subtype.
and there are all kinds of permissions to check for invalid data. We don't want to allow optional evaluation of predicates in such cases. Anyway, an implementation can always have a mode where it does additional predicate checks. An implementation-defined Assertion_Policy could be used for this.
However, we do say that the X'Valid is False if the predicate is False.
!example
In a compiler familar to the author, type Symbol_Ptr references a symbol table entry. Most routines that take a symbol table entry only allow certain kinds of entry. It would be valuable to be able to specify those kinds of entry as part of the profile of the routine.
A simplified example:
type Entity_Kind is (Proc, Func, A_Type, A_Subtype, A_Package, An_Entry);
type Symbol_Record (Entity : Entity_Kind) is record ...
type Symbol_Ptr is access all Symbol_Record;
subtype Type_Symbol_Ptr is not null Symbol_Ptr with Predicate => Type_Symbol_Ptr.Entity = A_Type; subtype Callable_Symbol_Ptr is not null Symbol_Ptr with Predicate => Callable_Symbol_Ptr.Entity = Proc or Callable_Symbol_Ptr.Entity = Func or Callable_Symbol_Ptr.Entity = An_Entry;
function Type_Size (A_Type : Type_Symbol_Ptr) return Size_Type;
procedure Generate_Call_Parameters (Callee : Callable_Symbol_Ptr; ...);
Now, a call to Type_Size or Generate_Call_Parameters with a pointer to the wrong kind of symbol record will be detected at the call site rather than at some later point. The call site is closer to the source of the error; in addition, it is possible that the compiler can prove that the predicate will succeed and be able to remove the check altogether. That can't happen for a check inside of a subprogram.
Other examples of useful predicates:
If you plan to divide by X, its subtype should exclude zero.
An OS interface requires a size-in-bytes, but it must be a multiple of the page size.
Ada measures sizes in bits, but it's sometimes necessary to measure in storage units. "Predicate => Blah mod System.Storage_Unit = 0" might come in handy.
A Sort function produces an array (or other sequence) of subtype Sorted_Sequence. Binary_Search takes a parameter of that subtype. Very useful to keep track of which objects have been sorted (can I pass this to Binary_Search, or must I sort it first?).
To prevent SQL injection bugs, keep track of which data is "tainted" and which is "trusted", using predicates.
In a program that processes objects through multiple phases, you want to keep track of which phase(s) have been applied to each object, via subtypes.
Temporal logic: you can't land the airplane until the wheels are down. So Land_Airplane takes a parameter of subtype Airplane_That_Is_Ready_To_Land, which is a subtype of Airplane.
--!corrigendum 3.2.2(2)
!ACATS test
Add ACATS B and C tests for this feature.
!appendix

This AI was created after discussion at the Tallahassee ARG meeting; it
is partially based on an old idea found in AC-0157.

From the Tallahassee minutes about type Invariants:

   Randy asks whether the user-defined constraint idea is worth looking at
   (either as an alternative or replacement). After discussion, we decide
   that it seems to solve a somewhat different problem - it allows adding
   contracts to particular parameters, objects, etc. User-defined constraints
   would be a way to deal with non-contiguous sets of discriminants, one-sided
   array constraints, and so on. There is sufficient interest to have that
   written up (it previously was discussed on Ada-Comment and filed as AC-0157).
   It's not very necessary on scalar types, so if the rules get too messy for
   them, don't allow them. (Randy notes when writing up these minutes that that
   would probably be a generic contract problem.) Steve notes that it would
   need a bounded error if the expression does not return the same value
   when called with the same value (we would want to be able to eliminate
   duplicate checks) -- the bounds are that the check is either made or not.

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

From: Randy Brukardt
Date: Wednesday, May 20, 2009  5:28 PM

At the last meeting, I was directed to write a proposal about user-defined
constraints. I was told to try to restrict them only to composite types,
because the "satisfability" rules would be hard to make work for scalar types,
while they are pretty much right for composite types already.

To do that brings up a generic contract model issue. We would need to use
an assume-the-worst rule for generic bodies. Essentially, any subtype that
is of a generic formal type (or a type derived from such a type) could not
have a user-defined constraint (since we don't allow reconstraining or use
on types that might not be composite). It would be possible to move those
constraints to the private part, however.

Is the latter workaround enough? It seems like it would be to me (it is the
same workaround we suggest for 'Access, for instance), but I wanted to get
other people's opinions on that before I spend a lot of effort writing up
the proposal. (I was going to ask the accessibility subcommittee in one of
our periodic calls, but we never got to it.)

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

From: Randy Brukardt
Date: Wednesday, May 27, 2009  11:59 PM

Here is a more fundamental question about user-defined constraints: Do they act
like real constraints (that is, they apply to the view during the entire time of
its existence) or do they only apply at the point that the language defines
subtype conversions?

If they act like real constraints, then they can only depend on bounds and
discriminants; that seems very limiting. (Only bounds and discriminants have the
necessary rules to prevent changes between checks.)

If they are just checks applied at particular points, then we have anomalies
where "constrained" values does not satisfy the constraint. Moreover, whether or
not Constraint_Error is raised can depend on the parameter passing mode:

   type Rec is record
       A : Natural;
   end record;
   subtype Decimal_Rec is Rec when Rec.A mod 10 = 0;

   Obj : Decimal_Rec := (A => 10);

   procedure Do_It (P : in out Decimal_Rec) is
   begin
       P.A := 5;
   end Do_It;

   Do_It (Obj); -- (1)

The call at (1) will raise Constraint_Error if Obj is passed by copy: the copy
back will fail the subtype conversion back to the original object. But if Obj is
passed by reference, the view conversion will succeed and there will not be any
check after the call.

I was considering only allowing these constraints on private types, but that is
uncomfortable for two reasons (1) you lose capability when you see the full type
[note however that this is the same rule implied for type invariants: they can
only be given for a private type]; (2) it still allows the body (anywhere in the
scope of the full declaration, in fact) to break the constraint.

    package Pack is
       type Priv is private;
       function Is_Decimal (P : Priv) return Boolean;
       function Decimal (N : Natural) return Priv;

       procedure Half (P : in out Priv);
    private
       type Priv is record
           A : Natural;
       end record;
    end Pack;

    package body Pack is
       function Is_Decimal (P : Priv) return Boolean is
       begin
           return P.A mod 10 = 0;
       end Is_Decimal;

       function Decimal (N : Natural) return Priv is
       begin
           return (A => N*10);
       end Decimal;

       procedure Half (P : in out Priv) is
       begin
           P.A := P.A / 2;
       end Half;
    end Pack;

    with Pack;
    procedure Test2 is
       subtype Decimal_Priv is Pack.Priv when Pack.Is_Decimal (Decimal_Priv);
       Obj : Decimal_Priv := Pack.Decimal(1);
    begin
       Pack.Half (Obj); -- (2)
    end Test2;

(2) is very much like (1); the user constraint is only checked on return if Priv
is passed by copy. So Obj most likely does not meet its constraint after the
call to Pack.Half.

We could probably fix this particular problem with some additional checking rule
on "in out" and "out" parameters, but I worry that this is just the camel's nose
-- it might turn into a mess of creeping additional run-time checks (especially
once Steve starting thinking about it).


I originally ran across this issue thinking about the limitations of access
types constrained by user-defined constraints. But eventually I realized that
the issue was pretty general -- it's not just a problem with designated objects
of access types.


So what do you all think? Should I write this up:

(1) Allowing these to apply only to bounds and discriminants? (The issues with
    those are well-understood, of course.)
(2) Allowing these to apply only to private types, with suitable additional
    checks defined on the return from the "defining subsystem" (the package
    containing the private type and its subsystem). The constraints would not
    meaningfully apply within the defining subsystem (it would be easy to change
    an object after the check).
(3) Combining (1) and (2).
(4) Neither (1) nor (2). (a) I've got a better idea; (b) don't bother.

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

From: Micronian
Date: Tuesday, June 9, 2009  7:13 PM

I just read this AI and think it is an interesting proposal. One minor thing I
was wondering about is what was the reason for deciding on using "when" rather
than something like "requiring"? Personally, I find a keyword such as
"requiring" to be much more clearer and gives a stronger emphasis than a strange
use of "when".

Example:

subtype My_Int is Integer when (My_Int mod 2 = 0);

versus

subtype My_Int is Integer requiring (My_Int mod 2 = 0);


When I see "requiring" I immediately get that unmistakable "ok I should be
careful how I use this"  feeling.

Just curious.

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

From: Martin Dowie
Date: Wednesday, June 17, 2009  2:10 PM

Probably because it's an existing keyword => no one will be using it as an
identifier => no upwards compatibility issues.

E.g.
procedure Was_Valid is
    Requiring : Boolean := ...; -- oops!!
begin
    if Requiring then
       ...

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

From: Adam Beneschan
Date: Wednesday, June 17, 2009  2:34 PM

Yuck!  Requiring *what*?  This name (a present participle with a missing object)
seems like a ghastly name for a variable, worse even than Overriding.  I
understand the compatibility issue, but the language designers have seen fit to
add keywords after convincing themselves that the words are unlikely enough to
be used as identifiers that it won't cause any problems---and I'd guess that
Requiring is a much less likely name for an identifier than any of Overriding,
Synchronized, or Interface.  I think Micronian has a good point here.

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

From: Martin Dowie
Date: Wednesday, June 17, 2009  2:48 PM

Yup, it would be a horrible choice of name in that situation. You could come up
with examples where it might made more sense, e.g.  perhaps as the discriminant
in a variant record indicating what variant is required. Not matter - it was
just a suggestion as to why 'when' might have been picked over a new keyword.

Personally, I have no objections to new keywords - if I make the decision to
'upgrade' to a new language standard with a new keyword then that's my choice.
Find/Replace tools are two-a-penny these days.

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

From: Bob Duff
Date: Wednesday, June 17, 2009  2:47 PM

> Probably because it's an existing keyword => no one will be using it
> as an identifier => no upwards compatibility issues.

Exactly.

It is possible to add new reserved words in really important cases, but we
should not do so lightly.  In this case, "when" works OK -- it means "when X is
true, the value is in this subtype".

Any language-change proposal that adds a new reserved word is harder to get
approved.  And that's as it should be.

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

From: Randy Brukardt
Date: Wednesday, June 17, 2009  2:52 PM

Guys, we don't have a clue at this point if we can even get the semantics of
this to work out (initial returns don't look good); arguing about the syntax is
wildly premature.

For now, we are not planning to add any new keywords in Amendment 2. That could
change of course, but this is supposed to be a modest update, and loads of new
syntax would look more than "modest".

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

From: Stephen Leake
Date: Wednesday, June 17, 2009  4:03 PM

> It is possible to add new reserved words in really important cases,
> but we should not do so lightly.  In this case, "when" works OK -- it
> means "when X is true, the value is in this subtype".

I'd rather see 'with':

    subtype My_Int is Integer with (My_Int mod 2 = 0);

as in "with this additional constraint".

> Any language-change proposal that adds a new reserved word is harder
> to get approved.  And that's as it should be.

Yes.

And I get Randy's point about getting the semantics right first, but I just
couldn't resist :).

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

From: Christoph Grein
Date: Thursday, June 18, 2009  5:01 AM

> and I'd guess that Requiring is a much less likely name for an
> identifier than any of Overriding, Synchronized, or Interface.

I was bitten by Interface. But not a big deal to invent new names (eg.
ABC_Interface for some ABC equipment).

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

From: Dmitry A. Kazakov
Date: Thursday, June 18, 2009  5:28 AM

It is unclear to me what is the reason to introduce reserved keywords when there
is no syntactic necessity. In fact, only few reserved keywords are really need
to be reserved.

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

From: Bob Duff
Date: Thursday, June 18, 2009  7:38 AM

The ARG has considered several times the idea of having non-reserved keywords in
the syntax.  I think it's a good idea, but many people think it's an
abomination.

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

From: Steve Baird
Date: Friday, June 19, 2009  7:15 PM

At the Brest meeting, Randy and I were tasked with exploring two different
approaches to this AI. The difference was only in finding the appropriate
wording to capture the intention of the AI.

Irrelevant detail for those who are curious:
   As I understand it (Randy - speak up if I am incorrectly
   stating your position), Randy advocates viewing the
   user-defined predicate as a "constraint" in the technical
   sense of the word; I felt that too much RM upheaval
   would result if a subtype such as
      subtype Nul_Terminated_String is String when
         (String'Length/= 0
          and then String (String'Last) = Character'First);
   were defined to have a constraint. It was reminiscent of the
   idea introduced in AI05-57 that "an unconstrained type may
   have a non-null constraint".

After thinking about this problem a little bit, I would like to back up and
reexamine the intention of the AI before diving into the details of wording.

The generality of the mechanism described in the AI leads to implementation
problems. I would like to consider adding a restriction that the predicate may
only reference discriminants and array bounds, at least if the type in question
is not scalar.

Consider the Nul_Terminated_String example given above (and let's not worry at
this point about whether the reference to String'Last is confusing).

We pass an actual of this subtype (by reference) to a procedure which takes an
in-out mode formal parameter of subtype  String; the procedure assigns the
character 'X' to the last element of the array. A constraint check after the
procedure returns might discover the problem, but it is too late: the object has
been corrupted. If subcomponent modification is allowed, things get messy fairly
quickly.

An approach which was discussed in Brest would be to require that all objects of
this subtype must be constant. This eliminates the problem of subcomponent
modification, but it means that attempting the use of of these subtypes as
either a component subtype or as an actual parameter in an instantiation would
have to be either illegal or subject to some ugly restrictions.

The current language design works because array  bounds are never mutable and
discriminant values are never mutable for a constrained object. Similarly, a
user-defined constraint which only depends on array bounds and discriminant
values could be made to work.

I think we should give up on the generality of the Nul_Terminated_String example
and impose these restrictions in order to get to something that we know how to
implement.

This would still allow the two most important uses for this construct:
non-contiguous discriminant constraints and low-bound-only constraints for
arrays.

Does this seem reasonable?

Other questions include:
   1) What about scalars? Do we want to support something like
         subtype Power_Of_Two is Positive when Positive = 1
           or else
             (Positive mod 2 = 0 and then Positive / 2 in Power_Of_Two);

   2) Do we really want to use the name of the subtype being
      "constrained" to denote the value in the predicate as the AI
      suggests, or would it be better to use the name of the subtype
      being defined?
      That would complicate recursive membership tests as in the
      Power_Of_Two example above. Perhaps Subtype_Name'Some_Attribute?

   3) If someone doesn't play by the rules, as in
         subtype Future is Calendar.Day_Duration
           when Day_Duration > Calendar.Seconds (Calendar.Clock);
      , must this be rejected at compile time, or is this
      just erroneous?

    4) Is there a constraint check associated with object initialization
       for an object of one of these subtypes? Perhaps only for
       non-scalars? For the following example,
          X : Some_Subtype;
          Y : Boolean := X in Some_Subtype;
       the current language design ensures that Y is True if
       Some_Subtype is non-scalar (ignoring privacy).

I'd like to get these questions resolved before I worry about wording.

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

From: Steve Baird
Date: Sunday, June 21, 2009  1:01 PM

>   1) What about scalars? Do we want to support something like
>         subtype Power_Of_Two is Positive when Positive = 1
>           or else
>             (Positive mod 2 = 0 and then Positive / 2 in
> Power_Of_Two);
>

Answering my own question, I don't see a good approach for scalars.

First and Last attribute values, for-loops (for discretes), and array
indexing (for discretes) would all pose problems. Assuming Standard.Integer
is is a 32-bit type, would
      X : array (Power_Of_Two) of My_Task_Type; declare 31 tasks?
      Would Power_Of_Two'Last equal 2 ** 30?
This does not sound like a good idea.

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

From: Tucker Taft
Date: Sunday, June 21, 2009  4:25 PM

My recommended syntax would be:

     subtype Even is Integer
       with Predicate => Even mod 2 = 0;

I don't see the need to invent yet another syntax now that we have begun to get
comfortable with the "aspect specification" syntax:

    with <aspect> => <expr> {, <aspect> => <expr>};

I think we should avoid the term "constraint" because it already has too many
connotations in Ada.  I think if we see this as the "Predicate aspect" of the
subtype, then we can piggy-back on the syntax and a lot of the semantics for
aspects.

I think we clearly want to use the newly defined subtype rather than the old
subtype in the expression to represent the value.

Your point about allowing the "predicate" to depend only on the
bounds/discriminants of a composite type seems reasonable.  Our idea of
restricting it to constants does seem to create significant generic contract
problems.

For a scalar type, there seems no harm in having it depend on the value.  The
checking only need be performed on assignment or similar operations, and seems
to impose no contract model problems.

For access types, there are the usual problems if we allow it to depend on the
pointed-to value, since that could be changed through some other access path, so
I wouldn't allow a Predicate as part of an access subtype declaration, or we
have to make all the same restrictions as we have now for access subtype
constraints, and it would only be for access-to-composite.

If we are going to enforce a requirement that the predicate for a composite type
depend only on its discriminants/bounds, I would think we could also require
that it involve only constants and pure functions.

I would certainly require a predicate check when a composite object is
initialized, by default or by an initial expression.

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

From: Tucker Taft
Date: Sunday, June 21, 2009  4:32 PM

I'm not really convinced by these concerns. We already have subtypes, albeit
weird ones, where 'Last is not an element of the subtype. Even "not null" has
some weirdness, given that the default value of the [sub]type doesn't satisfy
the null exclusion.

By the way, I would not allow recursion in these Predicates.  I think a
reasonable freezing rule should prevent it, especially if the subtype being
defined refers to the current instance, rather than to itself.

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

From: Steve Baird
Date: Monday, June 22, 2009  9:46 AM

> My recommended syntax would be:
>
>     subtype Even is Integer
>       with Predicate => Even mod 2 = 0;
>
> I don't see the need to invent yet another syntax now that we have
> begun to get comfortable with the "aspect specification" syntax:
>
>    with <aspect> => <expr> {, <aspect> => <expr>};

Good point. I agree.

> I think we should avoid the term "constraint"
> because it already has too many connotations in Ada.  I think if we
> see this as the "Predicate aspect" of the subtype, then we can
> piggy-back on the syntax and a lot of the semantics for aspects.

Agreed.

> I think we clearly want to use the newly defined subtype rather than
> the old subtype in the expression to represent the value.

Agreed.

> Your point about allowing the "predicate" to
> depend only on the bounds/discriminants of a
> composite type seems reasonable.  Our idea of
> restricting it to constants does seem to
> create significant generic contract problems.

Agreed.

> For a scalar type, there seems no harm in having
> it depend on the value.  The checking only need
> be performed on assignment or similar operations,
> and seems to impose no contract model problems.

What rules do you suggest for the problematic
cases that I mentioned:
    'First, 'Last, 'Succ, 'Pred, etc.
    use of the subtype in a for-loop
    use of the subtype as an array index subtype
?

One possible answer is that the predicate is
ignored in these cases. It participates in subtype
conversion constraint checking and in membership tests
and is ignored elsewhere (dynamically - statically it
would participate at least in conformance checking and
probably in the definition of a static subtype).

I think this would be too confusing; simply disallowing
predicates for scalars would be preferable to this solution.

Given the following example
    for I in S loop
       if I not in S then
          Foo;
       end if;
    end if;
, I think it would be very odd if Foo were called.

Allowing an array element which is effectively
unnameable (because attempts to index it fail a
subtype conversions constraint check) also seems
like a bad idea to me.

> For access types, there are the usual problems
> if we allow it to depend on the pointed-to value,
> since that could be changed through some other
> access path, so I wouldn't allow a Predicate
> as part of an access subtype declaration, or
> we have to make all the same restrictions as
> we have now for access subtype constraints,
> and it would only be for access-to-composite.

Agreed.

> If we are going to enforce a requirement that
> the predicate for a composite type depend only
> on its discriminants/bounds, I would think we
> could also require that it involve only
> constants and pure functions.

Sounds good to me.

Anytime you have a rule that depends on constancy,
you have to give some thought to the case of
a constant that ends up being modified (e.g.,
via a finalization routine), but let's ignore
that for now.

> I would certainly require a predicate check
> when a composite object is initialized,
> by default or by an initial expression.

Agreed. We can discuss the rules for scalars in this
case after we have decided whether predicates for
scalar subtypes should be completely abandoned.

> By the way, I would not allow recursion in these
> Predicates.  I think a reasonable freezing rule
> should prevent it, especially if the subtype being
> defined refers to the current instance, rather than
> to itself.

Right. If the name of the subtype denotes the current
instance, then there is no way to use it as a
subtype name, so that problem goes away. If a pure
function is called in the predicate expression, that
function could, of course, be recursive.

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

From: Tucker Taft
Date: Monday, June 22, 2009  10:21 AM

> What rules do you suggest for the problematic cases that I mentioned:
>    'First, 'Last, 'Succ, 'Pred, etc.
>    use of the subtype in a for-loop
>    use of the subtype as an array index subtype

All good questions.  'Succ and 'Pred are unrelated to subtypes in general, and
S'Succ can be applied to any value of the underlying type.  'First and 'Last are
more interesting.  One possibility would be to require that S'First and S'Last
satisfy the predicate.  That would at least help a bit.

> One possible answer is that the predicate is ignored in these cases.
> It participates in subtype conversion constraint checking and in
> membership tests and is ignored elsewhere (dynamically - statically it
> would participate at least in conformance checking and probably in the
> definition of a static subtype).
>
> I think this would be too confusing; simply disallowing predicates for
> scalars would be preferable to this solution.
>
> Given the following example
>    for I in S loop
>       if I not in S then
>          Foo;
>       end if;
>    end if;
> , I think it would be very odd if Foo were called.

Perhaps.  But what about "for I in S'Range loop"?
Would that be different?  And what about "for I in S range S'Range loop"?

We could limit where these subtypes are used, and use the old "raise
Program_Error" trick to avoid generic contract model problems.  Alternatively,
we could disallow such subtypes as generic actual subtypes, unless the formal
has, say, "with Predicate => <>". And then, of course, the formal would face the
same restrictions as any subtype with a Predicate.

Clearly the main goal would be to use them in parameter and result subtypes.
For example, I could imagine something like:

     subtype Non_Zero is Integer
       with Predicate => Non_Zero /= 0;

and use that for parameters when you are going to be using them as a divisor.

It seems a lot nicer to simply use "Non_Zero" or "Non_Zero range -5..5" rather
than having to add preconditions and/or postconditions on subprograms just for
this relatively common restriction. If I can't use these in "for loops" and for
array index subtypes, I don't see that as a big limitation.  I do think it makes
sense to require 'First and 'Last of a subtype to satisfy the subtype's
predicate.

> Allowing an array element which is effectively unnameable (because
> attempts to index it fail a subtype conversions constraint check) also
> seems like a bad idea to me.

You have convinced me we should disallow them as index subtypes and in for
loops.  I don't want to get into the holey-enumeration pain with these.

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

From: Tucker Taft
Date: Monday, June 22, 2009  10:39 AM

We also should disallow such subtypes as a choice in a case/variant alternative,
to avoid any confusion.

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

From: Jean-Pierre Rosen
Date: Monday, June 22, 2009  11:02 AM

>>> I think this would be too confusing; simply disallowing predicates
>>> for scalars would be preferable to this solution.

Totally disallowing predicates for scalar subtype would disapoint many people,
but we could maybe allow them only for variables:

Even : Integer with Even/2 = 0;

(OK, we should slightly change the rule about where the variable's name becomes
available).

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

From: Bob Duff
Date: Monday, June 22, 2009  11:10 AM

> I think this would be too confusing; simply disallowing predicates for
> scalars would be preferable to this solution.

I'll reply to the various e-mails in more detail later, but right now I want to
say:

It would be a real shame to disallow scalars!

I think the primary use of this feature will be for making arbitrary subsets of
enumeration types.  And use these for parameter subtypes, function returns,
local vars, discriminants, and record components.

Letting 'for' loop oddities get in the way of these would be a shame.  It's so
frustrating when a programmer says "How come I can't add 2 plus 2 in Ada", and
as a language lawyer, I'm forced to say, "Because in a generic discriminanted
select statement, a requeue might blah blah blah."  And the programmer says, I
don't WANT no stinkin' discriminated requeue blah... -- I just want to add 2+2!

For example, in GNAT we have (some comments removed):

   type Entity_Kind is (
      E_Void,

      E_Component,
      E_Constant,
      E_Discriminant,
      E_Loop_Parameter,
      E_Variable,

      ... -- dozens more
    );

   ...
   subtype Overloadable_Kind           is Entity_Kind range
       E_Enumeration_Literal ..
   --  E_Function
   --  E_Operator
   --  E_Procedure
       E_Entry;

   ...many similar subtypes

It's a kludge that (e.g.) Overloadable_Kind has to be defined partly in
comments, because Entity_Kind is conceptually unordered.  And it's a maintenance
hazard.  More importantly, we can't define arbitrary subsets of Entity_Kind.

I also very badly want to be able to say things like:

    type Entity (Kind : Entity_Kind) is private;

    subtype Overloadable_Entity is new Entity
      with Predicate => Overloadable_Entity.Kind in Overloadable_Kind;

These are the uses I have in mind.  Things like Prime_Integer and Even_Integer
are nothing but interesting curiosities by comparison.  Nonzero_Integer might be
useful occassionally, but I don't divide that often, so ... ("I'm a uniter, not
a divider." ;-) )

If we drop scalars, I'm (almost?) inclined to drop the whole proposal -- not
sure about that.

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

From: Bob Duff
Date: Monday, June 22, 2009  11:14 AM

> We also should disallow such subtypes as a choice in a case/variant
> alternative, to avoid any confusion.

I see what you mean, but it seems a shame.  Hmm...

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

From: Robert Dewar
Date: Monday, June 22, 2009  11:21 AM

> It would be a real shame to disallow scalars!
>
> I think the primary use of this feature will be for making arbitrary
> subsets of enumeration types.  And use these for parameter subtypes,
> function returns, local vars, discriminants, and record components.

I agree entirely on both points

> Letting 'for' loop oddities get in the way of these would be a shame.
> It's so frustrating when a programmer says "How come I can't add 2
> plus 2 in Ada", and as a language lawyer, I'm forced to say, "Because
> in a generic discriminanted select statement, a requeue might blah
> blah blah."  And the programmer says, I don't WANT no stinkin'
> discriminated requeue blah... -- I just want to add 2+2!
>
> For example, in GNAT we have (some comments removed):
>
>    type Entity_Kind is (
>       E_Void,
>
>       E_Component,
>       E_Constant,
>       E_Discriminant,
>       E_Loop_Parameter,
>       E_Variable,
>
>       ... -- dozens more
>     );
>
>    ...
>    subtype Overloadable_Kind           is Entity_Kind range
>        E_Enumeration_Literal ..
>    --  E_Function
>    --  E_Operator
>    --  E_Procedure
>        E_Entry;
>
>    ...many similar subtypes
>
> It's a kludge that (e.g.) Overloadable_Kind has to be defined partly
> in comments, because Entity_Kind is conceptually unordered.  And it's
> a maintenance hazard.  More importantly, we can't define arbitrary
> subsets of Entity_Kind.
>
> I also very badly want to be able to say things like:
>
>     type Entity (Kind : Entity_Kind) is private;
>
>     subtype Overloadable_Entity is new Entity
>       with Predicate => Overloadable_Entity.Kind in Overloadable_Kind;
>
> These are the uses I have in mind.  Things like Prime_Integer and
> Even_Integer are nothing but interesting curiosities by comparison.
> Nonzero_Integer might be useful occassionally, but I don't divide that
> often, so ... ("I'm a uniter, not a divider." ;-) )

I agree, basically my only interest in this feature is for scalars for the type
of programming I do!

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

From: Robert Dewar
Date: Monday, June 22, 2009  11:22 AM

> I see what you mean, but it seems a shame.  Hmm...

Indeed, a real shame, enough for me to think, back to square 1 to rethink!

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

From: Steve Baird
Date: Monday, June 22, 2009  11:50 AM

>> What rules do you suggest for the problematic cases that I mentioned:
>>    'First, 'Last, 'Succ, 'Pred, etc.
>>    use of the subtype in a for-loop
>>    use of the subtype as an array index subtype
>
> All good questions.  'Succ and 'Pred are unrelated to subtypes in
> general, and S'Succ can be applied to any value of the underlying
> type.

Good point.

> 'First and 'Last
> are more interesting.  One possibility would be to require that
> S'First and S'Last satisfy the predicate.  That would at least help a
> bit.
>> ?

Do you mean a runtime check at the point of the subtype declaration?

This would mean that your earlier example
    subtype Even is Integer
       with Predicate => Even mod 2 = 0; would not elaborate successfully.

Or do you mean that the implementation generates a loop when evaluating S'First
or S'Last at some point, looking for the least/greatest value in the subtype
being constrained (i.e., the subtype named by the subtype_mark in the
declaration of S) which satisfies the predicate (with appropriate rules to cope
with the case where no such value is found)?


>> Given the following example
>>    for I in S loop
>>       if I not in S then
>>          Foo;
>>       end if;
>>    end if;
>> , I think it would be very odd if Foo were called.
>
> Perhaps.  But what about "for I in S'Range loop"?
> Would that be different?  And what about "for I in S range S'Range
> loop"?

Let's ignore reals for a moment and focus on discretes.
(Although
    subtype Positive_Float is Float range 0.0 .. Float'Last
      with predicate Positive_Float /= 0.0; does make me wonder about the value
           of Positive_Float'First).

A subtype defines a subset of the set of values of the basetype.

If an existing subtype is referenced via the subtype_mark in a
subtype_indication, then the set of values associated with the subtype defined
by the subtype_indication is a subset of the set of values associated with that
existing subtype. This can lead to a conjunction of multiple predicates.

The same ideas apply for reals, but specifying the set of values associated with
a real subtype requires a bit more care.

Thus, I would think that if S is a subtype with an associated predicate, then
S'Range might include values that are not in S (i.e. values for which the
predicate is False).

On the other hand,
    S range S'Range
would, I imagine, be synonymous with S.

> We could limit where these subtypes are used, and use the old "raise
> Program_Error" trick to avoid generic contract model problems.
> Alternatively, we could disallow such subtypes as generic actual
> subtypes, unless the formal has, say, "with Predicate => <>".
> And then, of course, the formal would face the same restrictions as
> any subtype with a Predicate.

I agree that either of these approaches would work.
I prefer the latter.
This is only an issue for Formal_Discrete, Formal_Signed_Integer, and
Formal_Modular types, right? I'm not aware of any problems in the float or fixed
cases.

> Clearly the main goal would be to use them in parameter and result
> subtypes.

I completely agree that scalar subtypes with predicates could be very useful.
You don't have to convince me of that.

I'm just concerned about some of the language interactions that have nothing to
do with the primary motivation that you mentioned, but that would nonetheless be
introduced. Disallowing these subtypes in contexts where we really rely on
having a contiguous range (don't forget entry families!), sounds like a
promising approach.

> You have convinced me we should disallow them as index subtypes and in
> for loops.  I don't want to get into the holey-enumeration pain with
> these.

Agreed. These subtypes are somewhat like what you would get if dynamic
expressions in enumeration representation clauses were allowed.

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

From: Steve Baird
Date: Monday, June 22, 2009  12:09 PM

> We also should disallow such subtypes as a choice in a case/variant
> alternative, to avoid any confusion.

Agreed.

We haven't discussed how this feature interacts with
the definition of "static subtype", but I was assuming
that a subtype with an associated predicate is never
a static subtype.

That would address this issue, right?

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

From: Steve Baird
Date: Monday, June 22, 2009  11:56 AM

> If we drop scalars, I'm (almost?) inclined to drop the whole proposal
> -- not sure about that.

I completely agree that scalars are an important case.
It's just a question of whether we can get them to work.

It sounds like Tuck's "just don't allow these guys where they cause problems"
approach may get us out of the woods here.

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

From: Stephen Michell
Date: Monday, June 22, 2009  12:13 PM

I am concerned that this proposal is further complicating the language by
effectively adding another complete set of per-object constraints for elementary
types. Why are we considering putting it on an arbitrary subtype, and not just
the first named subtype (i.e. type T is new TT?

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

From: Steve Baird
Date: Monday, June 22, 2009  12:52 PM

> We also should disallow such subtypes as a choice in a case/variant
> alternative, to avoid any confusion.

Ditto for cases where staticness is not required?
I'm thinking of a one-choice array aggregate and slice bounds.

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

From: Edmond Schonberg
Date: Monday, June 22, 2009  12:52 PM

>I think the primary use of this feature will be for making arbitrary subsets of
>enumeration types.  And use these for parameter subtypes, function returns,
>local vars, discriminants, and record components.

A natural way of expressing such constraints would be with our new membership
test:

subype Overloadable_Kind is Entity_Kind with
   predicate =>  Overloadable_Kind in (E_Enumeration_Literal, E_Function, E_Procedure,
                                       E_Entry)

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

From: Steve Baird
Date: Monday, June 22, 2009  1:19 PM

I said earlier that I was assuming that a subtype with an associated predicate
would never be a static subtype.

It sounds like that assumption may need to be revisited, and that noncontiguous
static subtypes may need to be a fundamental part of this feature.

Without getting into the details of how this might be accomplished, does it seem
like this approach might salvage the situation?

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

From: Steve Baird
Date: Monday, June 22, 2009  1:40 PM

> I am concerned that this proposal is further complicating the language
> by effectively adding another complete set of per-object constraints
> for elementary types. Why are we considering putting it on an
> arbitrary subtype, and not just the first named subtype (i.e. type T is new TT?

I don't see the motivation for this; what problem would this restriction solve?

If a user wants to write something like

     subtype Speckled_Foo is Foo
       with Predicate => Is_Speckled (Speckled_Foo);
     function Make_Speckled_Record (Kind : Speckled_Foo)
       return Some_Record;

, it seems that a requirement that Speckled_Foo must be declared as a derived
type rather than a subtype would only make things a bit more awkward.

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

From: Bob Duff
Date: Monday, June 22, 2009  1:56 PM

> I am concerned that this proposal is further complicating the language
> by effectively adding another complete set of per-object constraints
> for elementary types. Why are we considering putting it on an
> arbitrary subtype, and not just the first named subtype (i.e. type T is new TT?

Did you see my Entity_Kind example?  And then how I used that to constrain a
discriminant?  (I'm not sure if my message got sent out yet.)

I think that example illustrates why you want these "predicates" on pretty-much
any subtype.  It would be next to useless otherwise.

Anyway, if "predicates" are analogous to "constraints", then clearly they belong
in the same places -- the whole point of this feature is to generalize the
concept of "constraint" to allow arbitrary Boolean expressions, instead of the
meager range constraints and discrim constraints we already have. (Constraining
discriminants to a single value instead of a subrange (or better, arbitrary
subset) has always seemed like an annoying restriction to me).

Example:  Natural makes sense as a subtype of Integer.  So of course Non_Zero
makes sense as a subtype of Integer -- it would be annoying to require a new
type.

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

From: Tucker Taft
Date: Monday, June 22, 2009  2:05 PM

Allowing such a predicate only on a first subtype defeats the purpose of it
representing a *subset* of some type.  We have a separate proposal for a type
"invariant" which is true for *all* values of the type, but that is for private
types, and is only true *outside* the package.  The idea of a "predicate" is
that different subtypes of the same type have different predicates, and thereby
represent different subsets of the same type.

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

From: Bob Duff
Date: Monday, June 22, 2009  2:03 PM

> , it seems that a requirement that Speckled_Foo must be declared as a
> derived type rather than a subtype would only make things a bit more
> awkward.

Right.

As an aside, you'd want "return Speckled_Record" above, so the information
doesn't get lost across calls -- could be helpful in proofs.

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

From: Steve Baird
Date: Monday, June 22, 2009  2:39 PM

I thought of that, but I wanted to keep the size of the example down.

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

From: Tucker Taft
Date: Monday, June 22, 2009  1:57 PM

>> 'First and 'Last
>> are more interesting.  One possibility would be to require that
>> S'First and S'Last satisfy the predicate.  That would at least help a
>> bit.
>>> ?
>
> Do you mean a runtime check at the point of the subtype declaration?

Yes.

>
> This would mean that your earlier example
>    subtype Even is Integer
>       with Predicate => Even mod 2 = 0; would not elaborate
> successfully.

Good point, so it would have to be:

    subtype Even is Integer range -2**31 .. +2**31-2
       with Predicate => Even mod 2 = 0;

>
> Or do you mean that the implementation generates a loop when
> evaluating S'First or S'Last at some point, looking for the
> least/greatest value in the subtype being constrained (i.e., the
> subtype named by the subtype_mark in the declaration of S) which
> satisfies the predicate (with appropriate rules to cope with the case
> where no such value is found)?

Heaven forbid.  I would hope that the dynamic semantics are identical to those
for the underlying subtype except for some addition checks performed at
strategic points. I really don't want any fancy new dynamic semantics that imply
adding implicit loops or other kinds of control flow.


>>> Given the following example
>>>    for I in S loop
>>>       if I not in S then
>>>          Foo;
>>>       end if;
>>>    end if;
>>> , I think it would be very odd if Foo were called.
>>
>> Perhaps.  But what about "for I in S'Range loop"?
>> Would that be different?  And what about "for I in S range S'Range
>> loop"?
>>
>
> Let's ignore reals for a moment and focus on discretes.
> (Although
>    subtype Positive_Float is Float range 0.0 .. Float'Last
>      with predicate Positive_Float /= 0.0; does make me wonder about
> the value of Positive_Float'First).

This would raise Constraint_Error based on the suggested rule that you check
that 'First satisfies the predicate.

> A subtype defines a subset of the set of values of the basetype.
>
> If an existing subtype is referenced via the subtype_mark in a
> subtype_indication, then the set of values associated with the subtype
> defined by the subtype_indication is a subset of the set of values
> associated with that existing subtype.
> This can lead to a conjunction of multiple predicates.
>
> The same ideas apply for reals, but specifying the set of values
> associated with a real subtype requires a bit more care.

I don't see the problem if you don't try to magically figure out 'First or
'Last, but simply require that you get a Constraint_Error if they don't satisfy
the predicate.

> Thus, I would think that if S is a subtype with an associated
> predicate, then S'Range might include values that are not in S (i.e.
> values for which the predicate is False).
>
> On the other hand,
>    S range S'Range
> would, I imagine, be synonymous with S.

Agreed, though admittedly a bit confusing.

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

From: Bob Duff
Date: Monday, June 22, 2009  2:00 PM

> Indeed, a real shame, enough for me to think, back to square 1 to
> rethink!

Yeah, that's what I meant by "Hmm...".  ;-)

> A natural way of expressing such constraints would be with our new
> membership test:
>
> subype Overloadable_Kind is Entity_Kind with
>     predicate =>  Overloadable_Kind in (E_Enumeration_Literal,
> E_Function, E_Procedure, E_Entry)

Yeah, maybe that's part of the solution.  Can somebody remind me which AI that
is, s'il vous plait?

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

From: Tucker Taft
Date: Monday, June 22, 2009  2:02 PM

Good point -- these guys are non-static.

I suppose we could go further and allow
a very specific form to be static:

    subtype S is <static_subtype> [range <static range>]
       with Predicate => S in <static sequence>;

where "<static sequence>" is whatever syntax we adopt for representing a
sequence of values (e.g. "(3|5|7..9)").

These would still be verboten at least for index subtypes.
Using in "for loops" is a separate discussion, but clearly you would want to
allow them in case statements if static, since that is the point.

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

From: Steve Baird
Date: Monday, June 22, 2009  2:25 PM

> Heaven forbid.  I would hope that the dynamic semantics are identical
> to those for the underlying subtype except for some addition checks
> performed at strategic points.
> I really don't want any fancy new dynamic semantics that imply adding
> implicit loops or other kinds of control flow.

I completely agree.
I was just trying to come up with a rule that I could reconcile with the
successful elaboration of the original "Even" example.

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

From: Edmond Schonberg
Date: Monday, June 22, 2009  2:41 PM

> Yeah, maybe that's part of the solution.  Can somebody remind me which
> AI that is, s'il vous plait?

Its AI05-0158:  Generalizing membership tests.  Much discussion about syntax at
the ARG meeting, no consensus on whether it should be usable in loops, but
agreement about intent.

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

From: Tucker Taft
Date: Monday, June 22, 2009  2:42 PM

It sounds like we are moving toward some kind of common view here, thanks in
part to your throwing up some scary straw men along the way... ;-)

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

From: Robert Dewar
Date: Monday, June 22, 2009  3:13 PM

> These would still be verboten at least for index subtypes.
> Using in "for loops" is a separate discussion, but clearly you would
> want to allow them in case statements if static, since that is the
> point.

Yes indeed, they really must work in case statements if all the choices are
static.

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

From: Bob Duff
Date: Monday, June 22, 2009  4:52 PM

> Its AI05-0158:  Generalizing membership tests.

Thanks.  I'll read it when I'm done with my current high-prio AdaCore ticket (or
while I'm waiting for builds).

>...Much discussion about
> syntax at the ARG meeting, no consensus on whether it should be usable
>in loops, but agreement about intent.

Good to know -- I like the "intent", too.

I think I think it should be allowed in loops.  Seems very useful for enums.  I
just did this at a 'csh' prompt:

foreach i (blha indf a)
? echo $i
? end
blha
indf
a

Don't tell anybody I said a good word about 'csh' or any other Unix "shell"
language, but I must say that particular feature is very useful.

And "I think I think" is not a typo.  ;-)

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

From: Tucker Taft
Date: Monday, June 22, 2009  5:07 PM

I'll admit to being of two minds.

I would really like to see a generalized iteration mechanism where one could
"program" this kind of loop, rather than adding yet more special looping syntax.
On the other hand, if we do add this general sequence notation, then allowing it
for loops does seem like a natural extension.

As an implementation strategy, I presume one would either create an implicit
array of individual values, if the total sequence (including any ranges) is
relatively short, or an implicit array of low/high pairs, to handle arbitrarily
large ranges.  Then the iteration "for I in <sequence> loop" becomes essentially
either:

    for index in implicit_array'Range loop
      declare
        I : constant := implicit_array(index);
      begin
         <loop_body>
      end;
    end loop;

or

    for index in implicit_array_of_pairs'Range loop
      for I in implicit_array_of_pairs(index).first ..
        implicit_array_of_pairs(index).last loop
          <loop_body>
      end loop;
    end loop;

Definitely not rocket science, though definitely some new dynamic semantics
expansion routines.

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

From: Robert Dewar
Date: Monday, June 22, 2009  8:41 PM

> I think I think it should be allowed in loops.  Seems very useful for
> enums.  I just did this at a 'csh' prompt:

Seems useful, but greatly increases the implementation burden I would say!

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

From: Randy Brukardt
Date: Monday, June 22, 2009  3:49 PM

> It sounds like we are moving toward some kind of common view here,
> thanks in part to your throwing up some scary straw men along the
> way... ;-)

So many messages have come in here that I haven't been able to get a word in
edgewise. So let me just throw out my thoughts without any attempt to relate
them to the previous mail.

(1) The proposed rules here are so different for scalar and composite types that
    we are really talking about two different features. That doesn't seem likely
    to help understanding, they probably ought to be separated with different
    names.

(2) The restrictions on use for scalar "predicates" are likely to cause annoying
    generic contract issues -- or they will have to be illegal to use as generic
    actuals, which of course will cause annoying generic usage issues.

(3) I can't see the benefit of trying to provide static versions of these; the
    predicate is can contain anything, after all. If we really wanted static
    versions, then we should support first-class discontinuous scalar subtypes.
    (It appears that we have the syntax for that.) That seems to be Bob's
    thinking as to the most likely usage in practice. In that case, we don't
    need scalar predicates at all - the extra complication of having both is not
    worth it. (Do we really need subtype Even or subtype Power_of_Two??)

(4) I don't think it makes sense to try to check for inappropriate usage in
    composite predicates. Just make it a bounded error to write anything or read
    anything other than bounds or discriminants. The suggestion that they be
    checked to only depend on bounds, discriminants, constants, and pure
    functions is laudable, but problematic: We don't have a formal definition of
    pure functions for one thing (surely we'd want to include predefined
    operators, but those are rarely declared in a pure package). In the past, we
    couldn't come to agreement on such a concept, and everytime I've suggested
    it I've been shot down. The GNAT definition of a pure function is
    essentially to make it a bounded error (or erroneous??) if it is not one; I
    think that is the most that we could agree to here. But we don't need to
    agree to that at all, because all we need to do is say it as I described
    above (no mention of functions is needed). Let's not waste our time arguing
    about what is a pure function since t isn't important for this concept.

(5) Steve's proposal (as best as I understand it) seems to disallow using any
    such predicates on access types. That seems to eliminate my original usage
    case as a possibility (of course, it could still be written as
    preconditions). That seems bad. Similarly, restricting these to
    discriminants is going to eliminate many other usage cases. That makes me
    wonder if the whole thing is worth it (at this point, I would say no - based
    on the discussion here, we should simply (and completely) define
    discontiguous discrete subtypes and forget the rest).

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

From: Steve Baird
Date: Monday, June 22, 2009  4:59 PM

> (1) The proposed rules here are so different for scalar and composite
> types that we are really talking about two different features. That
> doesn't seem likely to help understanding, they probably ought to be
> separated with different names.

They seem similar to me. In both cases, we are using a predicate in restricting
the elements of a subtype.

> (2) The restrictions on use for scalar "predicates" are likely to
> cause annoying generic contract issues -- or they will have to be
> illegal to use as generic actuals, which of course will cause annoying
> generic usage issues.

The latter, and you are right. It is annoying that you can't pass one of these
types to a generic that takes a formal discrete unless the formal also has an
associated (formal) predicate. The problems with for-loops, arrays, etc.
prevented a completely satisfactory solution. As Bob and others pointed out,
users want to use this construct to define parameter and result subtypes. They
won't be happy with an explanation that they can't have these subtypes because
we couldn't figure out how they should work when used as an entry family index.

> (3) I can't see the benefit of trying to provide static versions of
> these; the predicate is can contain anything, after all. If we really
> wanted static versions, then we should support first-class discontinuous scalar subtypes.

In some sense that is what we are doing. If the predicate meets certain
restrictions and the subtype meets the other conditions for a static subtype,
then you get a discontinuous scalar subtype. What about it is 2nd-class?  We are
following the existing structure of the language here - a static subtype is just
a "normal" subtype that happens to meet some additional restrictions.

> (It appears that we have the syntax for that.) That seems to be Bob's
> thinking as to the most likely usage in practice. In that case, we
> don't need scalar predicates at all - the extra complication of having
> both is not worth it. (Do we really need subtype Even or subtype
> Power_of_Two??)

I'm not sure I understand you. Are you saying that we should only support static
scalar subtypes with predicates and give up on the non-static case?

That is an interesting idea because it would allow us to eliminate the
restrictions on for-loops, array-indexing, etc. A noncontiguous static subtype
is not essentially different than a holey enumeration type and can use
essentially the same implementation model.

Perhaps we can come close to getting the best of both worlds my making the
restrictions less stringent. A "static choice list"-predicate wouldn't have to
trigger the rules disallowing uses in arrays, for-loops, instances, etc.

> (4) I don't think it makes sense to try to check for inappropriate
> usage in composite predicates. Just make it a bounded error to write
> anything or read anything other than bounds or discriminants. The
> suggestion that they be checked to only depend on bounds,
> discriminants, constants, and pure functions is laudable, but
> problematic: We don't have a formal definition of pure functions for
> one thing (surely we'd want to include predefined operators, but those
> are rarely declared in a pure package). In the past, we couldn't come
> to agreement on such a concept, and everytime I've suggested it I've
> been shot down. The GNAT definition of a pure function is essentially
> to make it a bounded error (or erroneous??) if it is not one; I think
> that is the most that we could agree to here. But we don't need to
> agree to that at all, because all we need to do is say it as I
> described above (no mention of functions is needed). Let's not waste our
> time arguing about what is a pure function since it isn't important for
> this concept.

We haven't talked much about this point yet.

> (5) Steve's proposal (as best as I understand it) seems to disallow
> using any such predicates on access types.

True.

> That seems to eliminate my original
> usage case as a possibility (of course, it could still be written as
> preconditions). That seems bad.

I agree. I didn't see how to make it work without introducing the possibility of
an object going from valid to invalid in very dangerous ways. If we want to put
more of the burden on the user and simply say that misuse of these predicates is
erroneous, then we could support predicates on access types and I could revive
my Nul_Terminated_String example. I don't think we should do that. If you have
another approach, I'd like to hear about it.

> Similarly, restricting these to
> discriminants is going to eliminate many other usage cases. That makes
> me wonder if the whole thing is worth it (at this point, I would say
> no - based on the discussion here, we should simply (and completely)
> define discontiguous discrete subtypes and forget the rest).

Predicates for scalar types and for discriminated types do seem to work very
nicely together.

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

From: Randy Brukardt
Date: Monday, June 22, 2009  5:34 PM

> > (1) The proposed rules here are so different for scalar and
> > composite types that we are really talking about two different
> > features. That doesn't seem likely to help understanding, they
> > probably ought to be separated with different names.
>
> They seem similar to me. In both cases, we are using a predicate in
> restricting the elements of a subtype.

That's a very high level view. But the details are completely different:

- A scalar predicate subtype can't be used in array indexes or loops or (most
  likely) case statements. A scalar predicate subtype doesn't match a generic
  formal. But the expression can be any Boolean expression (bounded error if it
  has side effects or depends on non-constant globals, I hope) - in particular,
  it can reference the object value.
- A composite predicate subtype can't be used in most access types (echoing the
  rules for composite constraints). But it does match generic formals. The
  expression cannot reference anything other than bounds and discriminants.
  (What this means is TBD.)

> > (2) The restrictions on use for scalar "predicates" are likely to
> > cause annoying generic contract issues -- or they will have to be
> > illegal to use as generic actuals, which of course will cause
> > annoying generic usage issues.
> >
>
> The latter, and you are right. It is annoying that you can't pass one
> of these types to a generic that takes a formal discrete unless the
> formal also has an associated (formal) predicate.
> The problems with for-loops, arrays, etc. prevented a completely
> satisfactory solution.
> As Bob and others pointed out, users want to use this construct to
> define parameter and result subtypes. They won't be happy with an
> explanation that they can't have these subtypes because we couldn't
> figure out how they should work when used as an entry family index.

Right, but for scalar types, I don't see much use to the general power of this
concept. Rather, we really want discontiguous "sets" of values. Why not just
define that for real??

> > (3) I can't see the benefit of trying to provide static versions of
> > these; the predicate is can contain anything, after all. If we
> > really wanted static versions, then we should support first-class
> discontinuous scalar subtypes.
>
> In some sense that is what we are doing. If the predicate meets
> certain restrictions and the subtype meets the other conditions for a
> static subtype, then you get a discontinuous scalar subtype. What
> about it is 2nd-class?  We are following the existing structure of the
> language here - a static subtype is just a "normal" subtype that
> happens to meet some additional restrictions.

This still makes no sense to me. A predicate is just a Boolean expression (no
restrictions), and it would never be static (a current instance is never static,
is it?) So you are saying that some special form of a non-static Boolean
expression makes a static subtype? That is just too weird for words.

> > (It appears that we have the syntax for that.) That seems to be
> > Bob's thinking as to the most likely usage in practice. In that
> > case, we don't need scalar predicates at all - the extra
> > complication of having both is not worth it. (Do we really need
> > subtype Even or subtype
> > Power_of_Two??)
>
> I'm not sure I understand you. Are you saying that we should only
> support static scalar subtypes with predicates and give up on the
> non-static case?

No, I'm saying we should completely give up on the idea of scalar predicates and
support non-contiguous subtype constraints instead. (Perhaps they ought to be
required to be static, I'd have to think about that.)

Something like:
    subtype Foo is new Integer range (1 | 3 | 7 .. 11);

or more generally:
    subtype Identifier is new Subtype_Mark range Sequence;

> That is an interesting idea because it would allow us to eliminate the
> restrictions on for-loops, array-indexing, etc.
> A noncontiguous static subtype is not essentially different than a
> holey enumeration type and can use essentially the same implementation
> model.

Exactly. But they are no longer predicates at that point, they're just
non-contiguous discrete (or scalar?) type constraints. They work everywhere, and
have the same semantics as any other constraint (for scalar types).

Predicates for composite types are a very different beast with a very different
set of rules (and they are *not* constraints).

> Perhaps we can come close to getting the best of both worlds my making
> the restrictions less stringent. A "static choice list"-predicate
> wouldn't have to trigger the rules disallowing uses in arrays,
> for-loops, instances, etc.

Right. They'd be completely normal scalar (or discrete?) constraints. Little new
wording required (but more work for implementors because they appear
everywhere).

> > (4) I don't think it makes sense to try to check for inappropriate
> > usage in composite predicates. Just make it a bounded error to write
> > anything or read anything other than bounds or discriminants. The
> > suggestion that they be checked to only depend on bounds,
> > discriminants, constants, and pure functions is laudable, but
> > problematic: We don't have a formal definition of pure functions for
> > one thing (surely we'd want to include predefined operators, but
> > those are rarely declared in a pure package). In the past, we
> > couldn't come to agreement on such a concept, and everytime I've
> > suggested it I've been shot down. The GNAT definition of a pure
> > function is essentially to make it a bounded error (or erroneous??)
> > if it is not one; I think that is the most that we could agree to
> > here. But we don't need to agree to that at all, because all we need
> > to do is say it as I described above (no mention of functions is
> > needed). Let's not waste our time arguing about what is a pure
> > function since it isn't important for this concept.
>
> We haven't talked much about this point yet.

Well, we need to do so. It's very important to determining if composite
predicates are even worth having.

> > (5) Steve's proposal (as best as I understand it) seems to disallow
> > using any such predicates on access types.
>
> True.
>
> > That seems to eliminate my original
> > usage case as a possibility (of course, it could still be written as
> > preconditions). That seems bad.
>
> I agree. I didn't see how to make it work without introducing the
> possibility of an object going from valid to invalid in very dangerous
> ways. If we want to put more of the burden on the user and simply say
> that misuse of these predicates is erroneous, then we could support
> predicates on access types and I could revive my Nul_Terminated_String
> example. I don't think we should do that.
> If you have another approach, I'd like to hear about it.

My proposal is pretty much making any misuse (or surprising use) into a bounded
error. Erroneous is not acceptable to me (this is supposed to *increase* safety,
but the possible errors seem bounded (since these don't affect object shapes at
all): either a check is made, or it is not, or (of course) Program_Error is
raised. Seems harmless (if not as portable as I would like). As Bob said, it
would be annoying that you couldn't write a predicate on an access type to use
in a formal parameter, because of some language lawyer reason, in this case that
a renaming that depends on a discriminant could be blown up because of some
change of a discriminant -- none of which has anything to do with checking the
precondition of a formal parameter of an access type!

Indeed, if these *aren't* constraints, I don't see why we care about them
changing in this case. It would mean that a compiler could not presume them to
be true in some cases (such as dereferencing of access types), but that doesn't
seem very important, since it is unlikely that a compiler is going to be able to
eliminate these very dynamic checks anyway.

So I'm suggesting that we make all misuse a bounded error and pretty much allow
anything goes with them. Static checkers could complain if they want, but the
language doesn't have the mechanisms needed. (If we want to add them, that would
be OK by me, but I don't see much point if it just moves the bounded error from
the predicate to some function it calls.)

> > Similarly, restricting these to
> > discriminants is going to eliminate many other usage cases. That
> > makes me wonder if the whole thing is worth it (at this point, I
> > would say no - based on the discussion here, we should simply (and
> > completely) define discontiguous discrete subtypes and forget the rest).
>
> Predicates for scalar types and for discriminated types do seem to
> work very nicely together.

Except that you can't actually use the latter (unless you've managed to
eliminate all access types from your program, a pretty unlikely scenario).

Anyway, lots more thought is needed here.

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

From: Bob Duff
Date: Monday, June 22, 2009  6:28 PM

> > It sounds like we are moving toward some kind of common view here,
> > thanks in part to your throwing up some scary straw men along the
> > way... ;-)
>
> So many messages have come in here that I haven't been able to get a
> word in edgewise.

Yeah.  Me, too.  ;-)

>...So let me just throw out my thoughts without any attempt to relate
>them to the previous mail.

Wise thoughts from Randy, here, but getting up to speed on these e-mails is a
quadratic algorithm, like when you append strings to nul-terminated strings... I
need to go back to the start and read until the end (repeatedly?) :-(

> (1) The proposed rules here are so different for scalar and composite
> types that we are really talking about two different features. That
> doesn't seem likely to help understanding, they probably ought to be
> separated with different names.

But:

    subtype S10 is Integer range 1..10;
    subtype S4 is S10 range 1..4; -- OK

    subtype Str10 is String (1..10);
    subtype Str4 is Str10 (1..4); -- Wrong!

So they're different.  Too bad.

> (2) The restrictions on use for scalar "predicates" are likely to
> cause annoying generic contract issues -- or they will have to be
> illegal to use as generic actuals, which of course will cause annoying
> generic usage issues.

Yes, but we have two ideas:

Tuck suggested a contract-retaining syntax.

There's always the "raise Program_Error" hack, also suggested by Tuck.

> (3) I can't see the benefit of trying to provide static versions of
> these; the predicate is can contain anything, after all. If we really
> wanted static versions, then we should support first-class discontinuous scalar subtypes.
> (It appears that we have the syntax for that.) That seems to be Bob's
> thinking as to the most likely usage in practice. In that case, we
> don't need scalar predicates at all - the extra complication of having
> both is not worth it. (Do we really need subtype Even or subtype
> Power_of_Two??)

I thought I wanted arbitrary predicates, like even/power-of-two, but now I'm
leaning toward your idea -- I really want arbitrary _static_ enumerated subtypes
of enumerated types.  Usable in case stms.

But I also want those for discriminants (of enum types, especially).

> (4) I don't think it makes sense to try to check for inappropriate
> usage in composite predicates. Just make it a bounded error ...

Yes, or something like that.

>...to write anything or read
> anything other than bounds or discriminants. The suggestion that they
>be  checked to only depend on bounds, discriminants, constants, and
>pure  functions is laudable, but problematic: We don't have a formal
>definition of  pure functions for one thing (surely we'd want to
>include predefined  operators, but those are rarely declared in a pure
>package). In the past, we  couldn't come to agreement on such a
>concept, and everytime I've suggested  it I've been shot down. The GNAT
>definition of a pure function is  essentially to make it a bounded
>error (or erroneous??) if it is not one;

The GNAT Ref Man says:

    It specifies that the function `Entity' is to be considered
    pure for the purposes of code generation.  This means that the compiler
    can assume that there are no side effects, and in particular that two
    calls with identical arguments produce the same result.

To me, "assume" here implies "erroneous", but I'm not sure that's what it really
means.

>... I
> think that is the most that we could agree to here. But we don't need
>to  agree to that at all, because all we need to do is say it as I
>described  above (no mention of functions is needed). Let's not waste
>our time arguing  about what is a pure function since it isn't important for this concept.

I agree with the "not waste time" part, and I agree that this concept need not
involve "pure functions", whatever that might mean.

> (5) Steve's proposal (as best as I understand it) seems to disallow
> using any such predicates on access types. That seems to eliminate my
> original usage case as a possibility (of course, it could still be
> written as preconditions). That seems bad. Similarly, restricting
> these to discriminants is going to eliminate many other usage cases.
> That makes me wonder if the whole thing is worth it (at this point, I
> would say no - based on the discussion here, we should simply (and
> completely) define discontiguous discrete subtypes and forget the rest).

I agree, I want to be able to say something about access types.

And if I can't, why not just static scalar stuff?

But what about discriminants?  If I can say "subtype Speckled_Enum is.." then I
also want to assert something about Speckled_Records, and access thereto.

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

From: Steve Baird
Date: Monday, June 22, 2009  6:48 PM

> But what about discriminants?  If I can say "subtype Speckled_Enum is.."
> then I also want to assert something about Speckled_Records, and
> access thereto.

If we are talking about a case where the object designated by an access value is
guaranteed to be constrained for one reason or another, then it would be safe to
allow a predicate for an access subtype to refer to the discriminant value of
the designated object. And it would always be safe to refer to the bounds of a
designated array object.

Should this be included?

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

From: Tucker Taft
Date: Monday, June 22, 2009  8:22 PM

Known to be constrained?

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

From: Steve Baird
Date: Tuesday, June 23, 2009  8:32 AM

Right.

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

From: Bob Duff
Date: Wednesday, February 24, 2010  3:29 PM

I like this AI a lot.

My homework assignment says "Examples for AI05-0153-1 (subtype predicates)".
I don't understand what I'm supposed to do, because this AI already has a nice example.

So instead I'm commenting on some details.

> !standard  3.2.2(2)                                   10-02-11    AI05-0153-1/04
...
> The actual predicate is the given expression anded with the predicate
> (if any) of the parent subtype. (The predicate of any subtype that
> does not have one can be assumed to be the boolean literal True.)

Parent subtype doesn't mean what you want it to mean:

8.d/2     To be honest: {AI95-00442-01} Any name of a category of types (such
          as "discrete", "real", or "limited") is also used to qualify its
          subtypes, as well as its objects, values, declarations, and
          definitions, such as an "integer type declaration" or an "integer
          value." In addition, if a term such as "parent subtype" or "index
          subtype" is defined, then the corresponding term for the type of the
          subtype is "parent type" or "index type."

3/2   {AI95-00251-01} {AI95-00401-01} {AI95-00419-01} {parent subtype}
{parent type} The parent_subtype_indication defines the parent subtype; its type
is the parent type. The interface_list defines the progenitor types (see 3.9.4).
A derived type has one parent type and zero or more progenitor types.

...
> [Editor's note: We allow Program_Error because all bounded errors can
> raise Program_Error.

There is one exception to that, which I'm too lazy to look up right now.
I think it's related to controlled types.

I don't see any value to the user in allowing more than one exception.

>... The "proceeds normally" wording is the same as 4.8(11.1/2).]
>
> AARM Ramification: An implementation can evaluate a predicate any time
> an object is accessed in any way, if it desires. We have to say this
> so that any side-effects of the predicate (bad practice, but surely
> allowed) cannot be depended upon. Note that this isn't the same as
> being allowed to check the predicate anywhere at all; there has to be
> some use of a value or object that has the subtype. That's necessary
> so that it is possible to write the predicate without an evaluation of itself
> being triggered.

I don't understand that last sentence.

But I agree we can't allow "anywhere at all" -- that would mean the compiler
could insert race conditions at will, and other bad things.

My understanding is that implementations are REQUIRED to check predicates in
many cases, such as parameter passing (because that depends on subtype
conversion).  Is that correct?  It might be easier to understand if we said that
first, because otherwise my first impression is "this feature is totally
optional".

> Add to the end of 3.3.1(18/2) (which describes the meaning of
> "initialized by default"):
>
> If the nominal subtype has a predicate, the predicate is applied to
> the object and Assertions.Predicate_Error is raised if the result is False.
>
> [Editor's note: We need to check that default initialized objects
> don't violate their predicate.]

I don't think I agree with this.  At least not in all cases.
E.g., for integers:

    X : T; -- (1)
    ...
    if Flag then
        X := 123;
    end if;
    ...
    if Flag then
        X := X + 1;
    end if;

I don't think I want the possibly-invalid value checked at (1).

> Modify 3.4(18-22/2):
>
> Informally, the predicate of the corresponding subtype is that of the
> subtype of the parent/progenitor type anded with the predicate of the
> derived type. The new (sub)type is considered to have a predicate
> (even if none is explicitly given).
>
> AARM Reason: If a subprogram has parameter(s) whose subtype(s) have
> defined predicate(s), the generated code of subprogram body may depend
> on
                                             ^
                                             the

> those predicates being checked. (A compile could assume that they had
> been
                                     compileR

> checked at the point of a call.) If the predicates did not compose, a
> call of the derived subprogram might not actually check the
> predicates, and that would cause big trouble.
>
> Add as the last sentence of 3.6(9):
>
> An index subtype shall not statically denote a subtype with a predicate.

"statically denote"?  Why "statically"?  Why not just "be"?

> checked at the point of a call.) If the predicates did not compose, a
> call of the derived subprogram might not actually check the
> predicates, and that would cause big trouble.
>
> Add as the last sentence of 3.6(9):
>
> An index subtype shall not statically denote a subtype with a predicate.

"statically denote"?  Why "statically"?  Why not just "be"?

> Add as the last sentence of 3.6(21):
>
> The elaboration of an array_type_definition raises Program_Error if
> the index subtype has a predicate.
>
> AARM Reason: We don't want to create "holey" array types; it is very
> confusing as to whether the values for which the predicate returns
> False have associated elements. By raising Program_Error, we prevent
> generic contract problems. But we also have a legality rule so when it
> is statically known (outside of a generic) we detect the problem at
> compile-time.]

Ah, I see.

This is inconsistent with similar cases, where we just make it a run time
error, and trust compilers to give warnings when statically known.

> Add after 3.6.1(5):
>
> The discrete_range of an index_constraint shall not statically denote
> a subtype with a predicate.
>
> Add as the last sentence of 3.6.1(8):
>
> The elaboration of an index_constraint raises Program_Error if any
> discrete_range is a subtype with a predicate.
>
> AARM Reason: We don't want to create "holey" array subtypes. By
> raising Program_Error, we prevent generic contract problems. But we
> also have a legality rule so when it is statically known (outside of a
> generic) we detect the problem at compile-time.]

You don't need to repeat this.  Just refer to the earlier comment.

> Add after 4.1.2(4):
>
> Legality Rules
>
> The discrete_range of a slice shall not statically denote a subtype
> with a predicate.
>
> Add as the last sentence of 4.1.2(7):
>
> The evaluation of a slice raises Program_Error if any discrete_range
> is a subtype with a predicate.
>
> AARM Reason: We don't want to create "holey" slices, especially as
> slices can be required to be passed by reference (for by-reference
> component types), and ignoring the predicate would be very confusing.
> By raising Program_Error, we prevent generic contract problems. But we
> also have a legality rule so when it is statically known (outside of a
> generic) we detect the problem at compile-time.]

Same here.

...
> Add after 4.6(58):
>
> Implementation Permissions
>
> If the target subtype of a conversion has a predicate, and the nominal
> subtype of the operand is that same subtype, an implementation may
> omit the application of a predicate to the operand.

We might need to define "same subtype".  Or use "statically matching"?

...
> !discussion
>
> This proposal seems similar to type invariants on the surface.
> However, these two constructs solve different problems. ...

I think this philosophical discussion is rubbish, but so long as we can agree on
the language rules (we're very close!), we don't need to hash that out.

>...A type invariant is a
> requirement on all values of a type outside of the type's defining
>package(s). In particular, it does not vary depending on the view of
>an object. A constraint and/or predicate is a requirement on a
>particular  view of an object. It can be different on different views
>of the same object  (as in a formal parameter). Thus it can be used to
>specify temporary or  transient requirements on an object.
>
> ---
>
> The model here is that a predicate has *no* effect on the static or
> dynamic semantics of a constraint. That is, if a predicate is applied
> to an indefinite type, the resulting subtype is indefinite. If a
> predicate is applies to an
                                                               applieD

> unconstrained subtype, the resulting subtype is unconstrained. And so on.
>
> This mirrors the semantics of null exclusions (which also are not constraints).

...for reasons only language lawyers can grok.  Sigh.

> Note that this can lead to some unusual circumstances:
>
>      subtype Even is Integer range 1 .. 10 when Predicate => Even mod
> 2 = 0;
                                             WITH

>      type Evens is array (Even) of Boolean; -- 5 or 10 elements?

But you outlawed this above.  Maybe you're trying to explain why, but the
wording says "can", not "could (if it were legal)" and so forth.

>      Obj : Evens;
>      Obj(1) := False; -- Would Raise Predicate_Error.
>
> Type Evens has length 10. Evens'range goes from 1 .. 10. However,
> attempting to index component 1 will raise Predicate_Error. (That's
> because 1 will be converted to subtype Even, as noted in 4.1.1(7), and
> that will trigger a predicate check.) However, the situation gets
> nastier still when arrays are sliced by subtypes with a
> predicate:
>
>     Str : String := "1234567890";
>
>     Put(Str(Even));
>
> One might expect to have "24680" printed, but since the constraints
> are unchanged, "1234567890" would actually be printed. Because of this
> weird behavior, we do not allow predicates on index subtypes. In order
> to avoid breaking the contract model, we raise Program_Error in generic
> bodies if the subtype has a predicate.

Here's a radical notion: Raise P_E when an instance is elaborated, if it
contains any such evil things, even in unreachable/unreached code.

> ---
>
> Originally, this proposal made predicates "user-defined constraints".
> This does not work, however, for a number of reasons:

Bah!  We can call them whatever we like, it doesn't make any difference to the
rules.

...
> For example:
>
>    type Rec is record
>        A : Natural;
>    end record;
>    subtype Decimal_Rec is Rec with Predicate => Rec.A mod 10 = 0;
>
>    Obj : Decimal_Rec := (A => 10); -- (1)
>
>    procedure Do_It (P : in out Rec) is
>    begin
>        P.A := 5;
>    end Do_It;
>
>    Do_It (Obj); -- (2)
>    Put (Obj in Decimal_Rec); -- (3)
>
> The predicate on Decimal_Rec will be checked on the aggregate at (1).
> However, after the call at (2), the predicate is no longer true for Obj.
> The call at (3) will print False. Implementations are allowed to check

So why don't we require a predicate check on the way out?
That's the one common case where subtype conversion doesn't cover it.

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

From: Randy Brukardt
Date: Wednesday, February 24, 2010  8:15 PM

...
> My homework assignment says "Examples for AI05-0153-1 (subtype
> predicates)".
> I don't understand what I'm supposed to do, because this AI already
> has a nice example.

My recollection is that you said something to the effect that you had "many
examples" of how this would help. Steve said that he wanted to see some of them.
Thus you got an action item.

Essentially, the one example doesn't seem to have been enough to sway others. If
you want to keep this AI alive, I think you need to find some additional
examples of usage. (In e-mail, you've typically used variations of this
particular compiler example that I used in the original AI; I guess the feeling
is that not enough Ada users are writing compilers to matter.)

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

From: Randy Brukardt
Date: Wednesday, February 24, 2010  8:52 PM

...
> > The actual predicate is the given expression anded with the
> > predicate (if any) of the parent subtype. (The predicate of any
> > subtype that does not have one can be assumed to be the boolean
> > literal True.)
>
> Parent subtype doesn't mean what you want it to mean:
...

OK, but at this point, you have to propose wording that *does* mean the right
thing. Nothing at all comes to mind.

...
> > [Editor's note: We allow Program_Error because all bounded errors
> > can raise Program_Error.
>
> There is one exception to that, which I'm too lazy to look up right now.
> I think it's related to controlled types.

If there is, it would violate 1.1.5(8): "...but in any case one possible effect
of a bounded error is the raising of the exception Program_Error.".

> I don't see any value to the user in allowing more than one exception.

I'm not going to try to ignore 1.1.5(8); it would allow raising Program_Error
even if we didn't say that.

...
> > AARM Ramification: An implementation can evaluate a predicate any
> > time an object is accessed in any way, if it desires. We have to say
> > this so that any side-effects of the predicate (bad practice, but
> > surely
> > allowed) cannot be depended upon. Note that this isn't the same as
> > being allowed to check the predicate anywhere at all; there has to
> > be some use of a value or object that has the subtype. That's
> > necessary so that it is possible to write the predicate without an
> > evaluation of itself being triggered.
>
> I don't understand that last sentence.

If we allowed evaluation of the predicate *anywhere*, that would include during
the evaluation of the predicate. Which wouldn't work very well. :-) I can
imagine implementations that might do that. Beyond that, there is the tasking
issues of evaluating these anywhere; I didn't want to talk about those
specifically because its hard to define what shouldn't be allowed.

If you can word this better, suggest away.

> But I agree we can't allow "anywhere at all" -- that would mean the
> compiler could insert race conditions at will, and other bad things.
>
> My understanding is that implementations are REQUIRED to check
> predicates in many cases, such as parameter passing (because that
> depends on subtype conversion).  Is that correct?  It might be easier
> to understand if we said that first, because otherwise my first
> impression is "this feature is totally optional".

The requirement to write wording in RM order often leads to non-optimal orders.
In this case, though, I simply have no idea where this text belongs. I suggested
perhaps in 13.9.1, which would avoid the problem as it would be buried in the
back of the RM.

> > Add to the end of 3.3.1(18/2) (which describes the meaning of
> > "initialized by default"):
> >
> > If the nominal subtype has a predicate, the predicate is applied to
> > the object and Assertions.Predicate_Error is raised if the
> result is False.
> >
> > [Editor's note: We need to check that default initialized objects
> > don't violate their predicate.]
>
> I don't think I agree with this.  At least not in all cases.
> E.g., for integers:
>
>     X : T; -- (1)
>     ...
>     if Flag then
>         X := 123;
>     end if;
>     ...
>     if Flag then
>         X := X + 1;
>     end if;
>
> I don't think I want the possibly-invalid value checked at (1).

That's not the case I was thinking of. I was thinking of a record type:

    type Foo with Predicate (B*2 = C) is record
        B : Natural := 10;
        C : Natural := 15;
    end record

    X : Foo; -- Violates the predicate.

In this case, there is no reason to ever check this (given the permission to
skip checks when the subtype doesn't change). So it would never be required to
be detected, which is nasty.

...
> > AARM Reason: We don't want to create "holey" array types; it is very
> > confusing as to whether the values for which the predicate returns
> > False have associated elements. By raising Program_Error, we prevent
> > generic contract problems. But we also have a legality rule so when
> > it is statically known (outside of a generic) we detect the problem
> > at compile-time.]
>
> Ah, I see.
>
> This is inconsistent with similar cases, where we just make it a run
> time error, and trust compilers to give warnings when statically
> known.

It's consistent with accessibility checks. In most other cases, we've made the
body cases always illegal. I don't know of any cases where we make such things
runtime errors.

> > Add after 3.6.1(5):
> >
> > The discrete_range of an index_constraint shall not
> statically denote
> > a subtype with a predicate.
> >
> > Add as the last sentence of 3.6.1(8):
> >
> > The elaboration of an index_constraint raises Program_Error if any
> > discrete_range is a subtype with a predicate.
> >
> > AARM Reason: We don't want to create "holey" array subtypes. By
> > raising Program_Error, we prevent generic contract problems. But we
> > also have a legality rule so when it is statically known
> (outside of a
> > generic) we detect the problem at compile-time.]
>
> You don't need to repeat this.  Just refer to the earlier comment.

Yes I do, it's in a different clause, and I want the RM to be reasonable useful as a reference. This particular case is iffy, but the others (like the 4.1.2) are much more valuable - no one reading 4.1.2 is going to go read 3.6 to figure out why the rules 
are the way they are.

> > This proposal seems similar to type invariants on the surface.
> > However, these two constructs solve different problems. ...
>
> I think this philosophical discussion is rubbish, but so long as we
> can agree on the language rules (we're very close!), we don't need to
> hash that out.

Well, if you disagree with this, then you are saying that you don't want both
AI-146 and AI-153-1. And in that case, it's this AI that is likely to disappear.
So we need to be able to explain the difference, or prepare to drop one.


> >      subtype Even is Integer range 1 .. 10 when Predicate
> => Even mod
> > 2 = 0;
>                                              WITH
>
> >      type Evens is array (Even) of Boolean; -- 5 or 10 elements?
>
> But you outlawed this above.  Maybe you're trying to explain why, but
> the wording says "can", not "could (if it were legal)" and so forth.

It wasn't illegal when this was written, and the rewrite was very simple and
quick, as this AI was left on life-support.

...
> > Originally, this proposal made predicates "user-defined constraints".
> > This does not work, however, for a number of reasons:
>
> Bah!  We can call them whatever we like, it doesn't make any
> difference to the rules.

Only if you are willing to abandon the current model of constraints in the
language. I don't know of anyone (other than you, perhaps) that's willing to do
that.

...
> > The predicate on Decimal_Rec will be checked on the aggregate at (1).
> > However, after the call at (2), the predicate is no longer true for Obj.
> > The call at (3) will print False. Implementations are allowed to
> > check
>
> So why don't we require a predicate check on the way out?
> That's the one common case where subtype conversion doesn't cover it.

Don't copy-backs use subtype conversion? (At least for by-copy types). For
by-reference types, it would be a completely new check in a place where none
currently exists. That seems bad, at least for this feature (which was designed
to be minimum cost).

Anyway, you've done a pretty good job of reminding me why, while I'd like some
way to do this, it really doesn't work very well. I'm probably going to vote to
kill this in the absense of new examples of use.

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

From: Bob Duff
Date: Thursday, February 25, 2010  2:31 PM

One other comment: This AI says you can put a predicate on a
subtype_declaration.  But we should also allow it on a type_declaration, because
type_decls declare subtypes.  As in:

    type T is range A..B with Predicate => Is_Good(T);

It would be really annoying to force that to be split in two, with two different
names for the same thing.

> ...
> > > The actual predicate is the given expression anded with the
> > > predicate (if any) of the parent subtype. (The predicate of any
> > > subtype that does not have one can be assumed to be the boolean
> > > literal True.)
> >
> > Parent subtype doesn't mean what you want it to mean:
> ...
>
> OK, but at this point, you have to propose wording that *does* mean
> the right thing. Nothing at all comes to mind.

I thought that was your job.  ;-)

I don't think it's all that hard.  It might take some time to track down all the
places where subtypes are created.

    For a subtype created by a subtype_declaration, the predicate is
    the given expression and-ed with the predicate (if any) of the subtype
    denoted by the subtype_mark.

    For a subtype created by a subtype_indication that is not that of
    a subtype_declaration, the predicate is that (if any) of the subtype
    denoted by the subtype_mark.

    Maybe we need to say something about ranges and/or discrete_ranges, etc.

I think if you say:

    type Page_Count is range 0..Max_Int
        with Predicate => Page_Count mod Page_Size = 0;

    for X in Page_Count'(0) .. 10*Page_Size loop

We don't want any predicate on X's subtype.

Or maybe you already forbid that.

(I'm getting tired of the Even example, which people have scorned as being
silly.  But multiple-of-page-size does come up in real code.)

> ...
> > > [Editor's note: We allow Program_Error because all bounded errors
> > > can raise Program_Error.
> >
> > There is one exception to that, which I'm too lazy to look up right now.
> > I think it's related to controlled types.
>
> If there is, it would violate 1.1.5(8): "...but in any case one
> possible effect of a bounded error is the raising of the exception Program_Error.".

Yup.  Since you don't (yet) see the light, I've tracked down the case (see
7.6.1):

  20      * For a Finalize invoked by a transfer of control due to an abort or
        selection of a terminate alternative, the exception is ignored; any
        other finalizations due to be performed are performed.
...
  20.b      To be honest:
            {Program_Error (raised by failure of run-time check)} This violates
            the general principle that it is always possible for a bounded error
            to raise Program_Error (see 1.1.5, "Classification of Errors").

> > I don't see any value to the user in allowing more than one exception.
>
> I'm not going to try to ignore 1.1.5(8); it would allow raising
> Program_Error even if we didn't say that.

This seems like language-lawyering run amok!  Foolish consistency, hobgoblins.

We made up the meta-rule in 1.1.5(8), and we can break it (or modify it).
In this case, there is no benefit (and some harm) caused by allowing two
different exceptions to be raised.

> ...
> > > AARM Ramification: An implementation can evaluate a predicate any
> > > time an object is accessed in any way, if it desires. We have to
> > > say this so that any side-effects of the predicate (bad practice,
> > > but surely
> > > allowed) cannot be depended upon. Note that this isn't the same as
> > > being allowed to check the predicate anywhere at all; there has to
> > > be some use of a value or object that has the subtype. That's
> > > necessary so that it is possible to write the predicate without an
> > > evaluation of itself being triggered.
> >
> > I don't understand that last sentence.
>
> If we allowed evaluation of the predicate *anywhere*, that would
> include during the evaluation of the predicate. Which wouldn't work
> very well. :-)

I'm still not sure I understand.  Suppose we have:

    subtype S is Integer with Predicate => Is_Good(S);

    X : S;

    X := 123;

As I understand it, we require Is_Good(123) to be evaluated here.
Are you suggesting that somewhere in the body of Is_Good, the compiler could
insert a call Is_Good(X) -- or for that matter, Is_Good(Some_Other_Object)?
Yes, that would be bad.

It would be at least as bad to insert a call Is_Good(X) before X has been
elaborated!

But these seem like oddities.

>...I
> can imagine implementations that might do that.

I can't.  ;-)

>...Beyond that, there is the
> tasking issues of evaluating these anywhere; I didn't want to talk
> about those specifically because its hard to define what shouldn't be allowed.

I think the tasking issue should be mentioned -- it's the most obvious and
compelling reason.  I don't think it's hard to define what shouldn't be allowed
-- in fact you have already defined it perfectly well.  In particular, the
compiler is allowed to insert a check on any use of the object, and nowhere
else.  (And it is _required_ to insert a check on certain uses -- implicit
subtype conversions, most importantly.)

> If you can word this better, suggest away.

Your normative wording is fine.  The AARM wording could be to replace the
"That's necessary ..." sentence with "Otherwise, the compiler could insert
arbitrary race conditions, for example."

> > But I agree we can't allow "anywhere at all" -- that would mean the
> > compiler could insert race conditions at will, and other bad things.
> >
> > My understanding is that implementations are REQUIRED to check
> > predicates in many cases, such as parameter passing (because that
> > depends on subtype conversion).  Is that correct?  It might be
> > easier to understand if we said that first, because otherwise my
> > first impression is "this feature is totally optional".
>
> The requirement to write wording in RM order often leads to
> non-optimal orders. In this case, though, I simply have no idea where this
> text belongs. I suggested perhaps in 13.9.1, which would avoid the problem
> as it  would be buried in the back of the RM.

Sounds good.  I take it your answer to "Is that correct?" above is "Yes."

> > > Add to the end of 3.3.1(18/2) (which describes the meaning of
> > > "initialized by default"):
> > >
> > > If the nominal subtype has a predicate, the predicate is applied
> > > to the object and Assertions.Predicate_Error is raised if the
> > result is False.
> > >
> > > [Editor's note: We need to check that default initialized objects
> > > don't violate their predicate.]
> >
> > I don't think I agree with this.  At least not in all cases.
> > E.g., for integers:
> >
> >     X : T; -- (1)
> >     ...
> >     if Flag then
> >         X := 123;
> >     end if;
> >     ...
> >     if Flag then
> >         X := X + 1;
> >     end if;
> >
> > I don't think I want the possibly-invalid value checked at (1).
>
> That's not the case I was thinking of. I was thinking of a record type:
>
>     type Foo with Predicate (B*2 = C) is record
>         B : Natural := 10;
>         C : Natural := 15;
>     end record
>
>     X : Foo; -- Violates the predicate.
>
> In this case, there is no reason to ever check this (given the
> permission to skip checks when the subtype doesn't change). So it
> would never be required to be detected, which is nasty.

Hmm.  Deserves more thought.

Maybe it's analogous to parameter passing: We could check if the type has
discriminants or defaulted components (parts?).  So uninitialized scalars would
not be checked.

Consider also the case of 'out' params of access type -- they get passed 'in',
but without any constraint check (and I think without any null exclusion check).

> ...
> > > AARM Reason: We don't want to create "holey" array types; it is
> > > very confusing as to whether the values for which the predicate
> > > returns False have associated elements. By raising Program_Error,
> > > we prevent generic contract problems. But we also have a legality
> > > rule so when it is statically known (outside of a generic) we
> > > detect the problem at compile-time.]
> >
> > Ah, I see.
> >
> > This is inconsistent with similar cases, where we just make it a run
> > time error, and trust compilers to give warnings when statically
> > known.
>
> It's consistent with accessibility checks. In most other cases, we've
> made the body cases always illegal. I don't know of any cases where we
> make such things runtime errors.

Maybe I'm wrong.  I can't find any cases to back me up just now.

> > > Add after 3.6.1(5):
> > >
> > > The discrete_range of an index_constraint shall not
> > statically denote
> > > a subtype with a predicate.
> > >
> > > Add as the last sentence of 3.6.1(8):
> > >
> > > The elaboration of an index_constraint raises Program_Error if any
> > > discrete_range is a subtype with a predicate.
> > >
> > > AARM Reason: We don't want to create "holey" array subtypes. By
> > > raising Program_Error, we prevent generic contract problems. But
> > > we also have a legality rule so when it is statically known
> > (outside of a
> > > generic) we detect the problem at compile-time.]
> >
> > You don't need to repeat this.  Just refer to the earlier comment.
>
> Yes I do, it's in a different clause, and I want the RM to be
> reasonable useful as a reference. This particular case is iffy, but
> the others (like the 4.1.2) are much more valuable - no one reading
> 4.1.2 is going to go read
> 3.6 to figure out why the rules are the way they are.

This is the AARM.  I think it's perfectly fine to say "see 3.6 for why".

> > > This proposal seems similar to type invariants on the surface.
> > > However, these two constructs solve different problems. ...
> >
> > I think this philosophical discussion is rubbish, but so long as we
> > can agree on the language rules (we're very close!), we don't need
> > to hash that out.
>
> Well, if you disagree with this, then you are saying that you don't
> want both AI-146 and AI-153-1.

No, I'm saying nothing of the kind.  I think it's OK to have both features.  I
think "subtype predicates" are far more useful than "type invariants" (as
currently defined), and I would therefore strongly object to passing AI-146 but
not AI-153-1; I'd vote against AI-146 in that case.

I also think that with some work, we might be able to combine these two things
into one coherent feature.  I'm not sure about that, and I haven't had time to
think it through.

>...And in that case, it's this AI that is likely to  disappear. So we
>need to be able to explain the difference, or prepare to  drop one.

I can explain the difference between the two features (as currently defined)
just fine: type invariants allow the invariant to be temporarily violated,
whereas predicates do not.  I think you agree with that, but you think that
difference is somehow FUNDAMENTAL, whereas I think it's a minor point.

I'm trying to think like a user, here, not a language lawyer.

Analogy: You (with your language lawyer hat on) probably think "parent type" and
"progenitor type" are fundamentally different. To normal Ada users, they're
roughly the same thing. Likewise, "null exclusion" and "constraint" are not the
same -- but they're roughly the same to normal users.

> > >      subtype Even is Integer range 1 .. 10 when Predicate
> > => Even mod
> > > 2 = 0;
> >                                              WITH
> >
> > >      type Evens is array (Even) of Boolean; -- 5 or 10 elements?
> >
> > But you outlawed this above.  Maybe you're trying to explain why,
> > but the wording says "can", not "could (if it were legal)" and so
> > forth.
>
> It wasn't illegal when this was written, and the rewrite was very
> simple and quick, as this AI was left on life-support.
>
> ...
> > > Originally, this proposal made predicates "user-defined constraints".
> > > This does not work, however, for a number of reasons:
> >
> > Bah!  We can call them whatever we like, it doesn't make any
> > difference to the rules.
>
> Only if you are willing to abandon the current model of constraints in
> the language. I don't know of anyone (other than you, perhaps) that's
> willing to do that.

OK, never mind.  This is part of the "philosophy" that I said we can safely
agree to disagree on.

> ...
> > > The predicate on Decimal_Rec will be checked on the aggregate at (1).
> > > However, after the call at (2), the predicate is no longer true for Obj.
> > > The call at (3) will print False. Implementations are allowed to
> > > check
> >
> > So why don't we require a predicate check on the way out?
> > That's the one common case where subtype conversion doesn't cover it.
>
> Don't copy-backs use subtype conversion? (At least for by-copy types).

Yes, I think so.

>...For
> by-reference types, it would be a completely new check in a place
>where none  currently exists. That seems bad, at least for this feature
>(which was  designed to be minimum cost).

Yes, it's new in the by-ref case.  So what?  It's extremely useful, and not hard
to implement.

Furthermore, for many types, the compiler can choose by-copy or by-ref, and it
seems bad to make it implementation dependent whether or not the check is
required on the way out.

All I'm asking for here is that for a by-ref parameter of mode 'out'
or 'in out', a predicate check is required after returning from the body.

> Anyway, you've done a pretty good job of reminding me why, while I'd
> like some way to do this, it really doesn't work very well.

I think it works just fine, and you've done a good job of writing it up.  We
just need to work out the details.  I think you are confusing the complexity of
the discussion with the complexity of the final result, which is easy to do,
because we haven't seen the final result yet.

I think you may also be expecting too much: if you expect predicates to always
be 100% guaranteed true, then you will be disappointed, because there are many
loopholes (predicates with side effects, predicates that become false via
"X.all.Blah := ...;", etc). But if you see that it's just a bunch of checks
sprinkled around the place, that are 98% likely to be true, then you won't be
disappointed.

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

From: Bob Duff
Date: Thursday, February 25, 2010  2:53 PM

> My recollection is that you said something to the effect that you had
> "many examples" of how this would help. Steve said that he wanted to
> see some of them. Thus you got an action item.
>
> Essentially, the one example doesn't seem to have been enough to sway
> others. If you want to keep this AI alive, I think you need to find
> some additional examples of usage. (In e-mail, you've typically used
> variations of this particular compiler example that I used in the
> original AI; I guess the feeling is that not enough Ada users are
> writing compilers to matter.)

Well, unfortunately, I'm a compiler writer, so most examples that pop into my
head are compiler examples, or similar tools.

I don't think the general principle of these examples is compiler specific,
though.  The general class of examples is an unordered enumeration type, and you
want arbitrary subsets for subtypes of that enum, and also for records whose
discriminant is that enum, and also for access types pointing to such records.

It's a kludge to say:

    type Color is (Red, Green, Blue, Yellow, Orange, Purple);
    subtype Primary_Color is Color range Red..Blue;

because you're depending on the order.  And it's a maintenance hazard that has
come up many times.  Much better to say:

    subtype Primary_Color is Color with Predicate => Primary_Color in (Red, Green, Blue);

or something.

And of course it's impossible to use subranges when the predicate is
noncontiguous.

Other examples (not necessarily compiler specific):

If you plan to divide by X, its subtype should exclude zero.

An OS interface requires a size-in-bytes, but it must be a multiple of the page
size.

Ada measures sizes in bits, but it's sometimes necessary to measure in bytes (or
storage units, if you prefer).  "Predicate => Blah mod 8 = 0" might come in
handy.

A Sort function produces an array (or other sequence) of subtype
Sorted_Sequence.  Binary_Search takes a parameter of that subtype. Very useful
to keep track of which objects have been sorted (can I pass this to
Binary_Search, or must I sort it first?).

To prevent SQL injection bugs, keep track of which data is "tainted" and which
is "trusted", using predicates. See here:

    http://xkcd.com/327/

In a program that processes objects through multiple phases (could be a
compiler!), you want to keep track of which phase(s) have been applied to each
object, via subtypes.

Temporal logic: you can't land the airplane until the wheels are down.  So
Land_Airplane takes a parameter of subtype Airplane_That_Is_Ready_To_Land, which
is a subtype of Airplane.

This is such a general feature that it seems hard to believe it's restricted to
compiler-like programs in any way. It's just like constraints, except you can do
more than just subranges of scalars, and single-values for discrims, etc. (And
it's not quite as airtight as those, but oh, well.)

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

From: Bob Duff
Date: Monday, April 12, 2010  4:48 PM

We've been discussing subtype predicates at AdaCore recently.  Quote:

I wrote:
> Robert Dewar wrote:
>
> > By the way, in Ada 2012, you will be able to say something like
> >
> >      if X in (Button_Pressed, Button_Released, Button_Broken) then ...
> >
> > making it easier to have a full list of the alternatives.
>
> Right, and that way, they don't need to be a contiguous subrange.
>
> Also (if I get my way) something like this:
>
>     subtype Button_Event is Event
>         with Predicate =>
>             Button_Event in (Button_Pressed, Button_Released, Button_Broken);
>
> And then:
>
>     if X in Button_Event then...

Can we (ARG) please discuss this some more?  My thinking is that this feature is
far more important than "type invariants" (as currently defined).

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

From: Robert Dewar
Date: Monday, April 12, 2010  5:00 PM

> Can we (ARG) please discuss this some more?  My thinking is that this
> feature is far more important than "type invariants"
> (as currently defined).

The reason I think this is more important than type invariants is that it is
much more generally useful. Invariants are only useful if you adopt this style
of programming.

Subtype predicates if done right allow the definition of non-contiguous subtypes
of enumeration types.

I am tired of having to juggle the order of enumeration types so I can create
the subtypes I need (the solution to this in general is NP complete of course,
and of course there may not BE a solution).

Would be much nicer to just define non-contiguous subtypes with an appropriate
predicate (which can use set notation).

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

From: Randy Brukardt
Date: Monday, April 12, 2010  5:23 PM

> Can we (ARG) please discuss this some more?  My thinking is that this
> feature is far more important than "type invariants"
> (as currently defined).

Discuss what? Supposedly, you guys decided how to fix all problems during the
last ARG meeting (Burlington) [I was out of the room for most of that discussion
and there are no minutes, at least of anything that fixes any problems.] There
hasn't been a new draft since that meeting, so either you (a) want us to discuss
old, superceded ideas (which is pointless), or (b) discuss new ideas that
haven't been fleshed out (or even understood by me and I suspect others).

I'd suggest that you (Bob) make a new draft that we can then discuss OR ask some
specific questions that we can answer! Else we're discussing ghosts...

P.S. I'm on record as opposing all of these ideas unless we have a mechanism for
being able to safely (and correctly) eliminate them. That necessarily includes a
way for the compiler to tell when these expressions have any (significant) side
effects. The Global In/Global Out annotations being felt too complex (at least
by many people), we need an alternative approach or we are wasting our time
inventing things that don't meet the mandate to "improve contracts".

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

From: Bob Duff
Date: Sunday, April 18, 2010  4:37 PM

Suppose we have:

    type Color is (Red, Orange, Yellow, Green, Blue, Indigo, Violet);
    subtype Primary_Color is Color
        with Predicate => Primary_Color in (Red, Green, Blue);

It would be really nice if this played well with the full coverage rules:

    procedure P (X: Primary_Color) is
    begin
        case X is
            when Red => ...;
            when Green => ...;
            when Blue => ...;
        end case;
    end P;

So that "when Orange" is neither required nor allowed.

So I suggest we say that if the predicate is given by "in" of a list of static
expressions and subtypes and ranges (and nothing else), that the subtype be
considered static.

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

From: Tucker Taft
Date: Sunday, April 18, 2010  4:59 PM

Couldn't we generalize this to say that the subtype is static if it would be in
the absence of the predicate expression, and the predicate expression would be
static if the type name were replaced with a static value? This is similar to
the static-in-the-instance rule -- see AARM 12.3(15.f).

It will be annoying to have to use some
very specific syntax for the predicate.

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

From: Bob Duff
Date: Sunday, April 18, 2010  6:09 PM

> Couldn't we generalize this to say
> that the subtype is static if it would be in the absence of the
> predicate expression, and the predicate expression would be static if
> the type name were replaced with a static value?

Yes.  Good idea.  And I suppose the rules for static expressions of the "blah in
blah" sort make this work?

> This is similar to the static-in-the-instance rule -- see AARM
> 12.3(15.f).

Right.

> It will be annoying to have to use some very specific syntax for the
> predicate.

Agreed.  So you apparently agree with me that one should not have to put

    when others => raise Program_Error; -- can't get here

or

    when Orange | ...etc => raise Program_Error; -- can't get here

in such case statements!  In fact, not allowed to.

Shall I wax poetic once again to sing praises to the full coverage rules?  ;-)

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

From: Tucker Taft
Date: Sunday, April 18, 2010  10:16 PM

Yes, we can all sing in harmony about how much we love full coverage.  While we
are at it, I would love to solve the problem some day that nested variants
always need a "when others" which is logically unnecessary.  It would be nice if
the discriminant's subtype, when inside a particular variant, effectively had a
Predicate expression added that corresponds to the "when Red | Green =>" that
started the variant.

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

From: John Barnes
Date: Monday, April 19, 2010  2:09 AM

I must say that this topic seems really useful - unlike a lot of other stuff we
have put into or contemplated putting into Ada lately.

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

From: Bob Duff
Date: Monday, April 19, 2010  9:24 AM

I'm glad at least SOMEbody agrees with me!

I think this feature is far more useful than:

AI05-0146-1 -- Type and Package Invariants

because predicates are not restricted to private types, and they work for
subtypes.

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

From: Edmond Schonberg
Date: Monday, April 19, 2010  9:45 AM

Note also that other languages have proposed type invariants, but subtypes as we
understand them are Ada-specific, and it's reasonable to have additional formal
properties of subtypes.

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

From: Robert Dewar
Date: Monday, April 19, 2010  10:01 AM

>> I must say that this topic seems really useful - unlike a lot of
>> other stuff we have put into or contemplated putting into Ada lately.
>
> I'm glad at least SOMEbody agrees with me!

Me too! I agree with you and John.

> I think this feature is far more useful than:
>
> AI05-0146-1 -- Type and Package Invariants

I strongly agree, the invariants stuff is useful only to a subset of programmers
who want to use this style of programming. The more flexible subtypes will be a
boon to all Ada programmers.

> because predicates are not restricted to private types, and they work
> for subtypes.

I do think full case coverage is important as noted by Bob

Also, I have the impression that the intent is to allow dynamic ranges and
values. I find this 100% useless, and I sure hope that this does not stand in
the way of the useful case which is a fully static list.

I think this is something that GNAT will probably implement anyway, but it would
be nice to have it offcial, rather than be an extension requiring the -gnatX
flag :-)

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

From: Robert Dewar
Date: Monday, April 19, 2010  10:04 AM

By the way, there seems to be some concern here that variables of the subtype may not have valid values. So what? I see that as a problem with formal invariants, but for subtype predicates I don't even see if as a concern.

When I write

subtype RBG is RBGE range R .. G;

I have no guarantee that objects of type R are in this range, so why should I
care if I say

subtype RBE is RBGE range (R, B, E); -- or wjatever the syntax is

that the same is true.

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

From: Randy Brukardt
Date: Monday, April 19, 2010  2:20 PM

> > Couldn't we generalize this to say
> > that the subtype is static if it would be in the absence of the
> > predicate expression, and the predicate expression would be static
> > if the type name were replaced with a static value?
>
> Yes.  Good idea.  And I suppose the rules for static expressions of
> the "blah in blah" sort make this work?

I agree that if we do this, this is the way to go.

But I don't agree that this is a good idea *for this particular feature*.

You apparently want predicates that *happen* to occur on an elementary type and
*happen* to have the form of a static expression to work exactly like a
first-class constraint, while all other predicates work completely differently
(and aren't even safe; they're just a automatic assertion).

I think this is bad language design. We have a mostly worked out proposal for
first-class set constraints, which have all of the properties that you seem to
want *and* proper syntax and legality rules. The requirements for static set
constraints and dynamic predicates are different (and could be *very* different
if we wanted them to be). I don't think that it makes sense to mix them,
especially given the unsafe nature of dynamic predicates.

> Agreed.  So you apparently agree with me that one should not have to
> put
>
>     when others => raise Program_Error; -- can't get here
>
> or
>
>     when Orange | ...etc => raise Program_Error; -- can't get here
>
> in such case statements!  In fact, not allowed to.

I have no objection to such a feature for constraints, but not for predicates
that could be dynamic (with side-effects!) or on composite types (and not hold
after checking). If you want static checking, then you need static constraints.
A predicate is not a constraint! (Surely not for composite types, it's arguable
for scalar types, but it would be immensely confusing to have something that
acts as a constraint sometimes but not always).

There is also an implementation concern. For an arbitrary predicate expression
(unlike the limited static set of the set constraint), the only way to implement
a coverage check is to try every possible value in the expression and see if it
is true or false. That would be very expensive for a 64-bit integer type!

A compiler could recognize various patterns and eliminate the problem in some
special cases, but I don't see any way to do it in general. The result would be
that some case statements would be uncompilable (even if technically legal).

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

From: Robert Dewar
Date: Monday, April 19, 2010  2:30 PM

> You apparently want predicates that *happen* to occur on an elementary
> type and *happen* to have the form of a static expression to work
> exactly like a first-class constraint, while all other predicates work
> completely differently (and aren't even safe; they're just a automatic assertion).
>
> I think this is bad language design. We have a mostly worked out
> proposal for first-class set constraints, which have all of the
> properties that you seem to want *and* proper syntax and legality
> rules. The requirements for static set constraints and dynamic
> predicates are different (and could be
> *very* different if we wanted them to be). I don't think that it makes
> sense to mix them, especially given the unsafe nature of dynamic predicates.

Why don't we allow them ONLY if they are completely static. This is really the
only useful case anyway. Dynamic ranges are VERY rare on enumeration types, I am
not sure I have ever written one. It would be a real shame if we let the design
of a really useful feature (static non-contiguous subsets of enumeration types)
be kept out because of theoretical concerns about the dynamic case, which is
totally useless.

I have no objection to allowing the dynamic case if it can be done without compromising the static case, but I *seriously* object to compromising the static case.

> I have no objection to such a feature for constraints, but not for
> predicates that could be dynamic (with side-effects!)

So disallow them

> There is also an implementation concern. For an arbitrary predicate
> expression (unlike the limited static set of the set constraint), the
> only way to implement a coverage check is to try every possible value
> in the expression and see if it is true or false. That would be very
> expensive for a 64-bit integer type!

These are really only useful for enumeration types, again, worrying about the
use on integer types seems bogus.

> A compiler could recognize various patterns and eliminate the problem
> in some special cases, but I don't see any way to do it in general.
> The result would be that some case statements would be uncompilable
> (even if technically legal).

I agree this is horrible, so let's leave it out!

I really think that the business of delicately arranging enumeration types in an
order that allows all interesting subtypes to be continguous is one of the most
horrible and unmaintainable features of Ada. It wastes a huge amount of time,
and is really unpleasant.

We have recently implemented a feature in GNAT that helps with this, which is a
warning flag that disallows comparisons and subranges of an enumeration type in
a client, unless a pragma Ordered is given for the enumeration type.

At least now, when you modify an enumeration type, you don't have to worry about
clients imposing additional buried constraints on the order.

But having a proper way to deal with this would be SUCH an imrpovement and it
seems like we are very nearly there.

Please don't let absolute-best be the enemy of highly-useful when it comes to
this feature.

I see language design concerns and implementation concerns in what Randy writes
with no apparent appreication for the enormous value of a subset of this feature
(static non-contiguous constraints on enumeration types).

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

From: Randy Brukardt
Date: Monday, April 19, 2010  2:32 PM

> Me too! I agree with you and John.

I think the *goal* here is important, but I'm not convinced this is the right
way to get to that goal. It's very similar to the situation with instantiating
containers with private types in a package specification -- everyone agrees it
is important, but we've never found anything that seems like a understandable,
useful, and maintainable solution.

...
> > because predicates are not restricted to private types, and they
> > work for subtypes.
>
> I do think full case coverage is important as noted by Bob
>
> Also, I have the impression that the intent is to allow dynamic ranges
> and values. I find this 100% useless, and I sure hope that this does
> not stand in the way of the useful case which is a fully static list.

I'm confused here. "A fully static list" and "full case coverage" only makes
sense for discrete types. The primary intent of this feature (and I can say that
because it was originally my idea) was to get the effect of user-defined
constraints on *composite* types (an idea which just doesn't work,
unfortunately). I've always felt that it was best for discrete types to have
*real* static set constraints (a-la AI05-0153-2). Indeed, I'd prefer to allow
predicates only on composite types (but generic contract issues prevent that
from being possible), given that they aren't air-tight. (There are many, many
rules about discriminants that exist solely to prevent a discriminant constraint
from being broken -- none of those will exist for [composite] predicates and
thus it will be impossible to depend on most predicates -- they could be made
False in many ways that wouldn't require them to be rechecked.)

Anyway, Robert, could you expand on what you view as important? Are you talking
about predicates on all types, or just discrete types, or something else? I'd
like to make sure that I'm thinking about the same things that you are.

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

From: Randy Brukardt
Date: Monday, April 19, 2010  2:40 PM

> By the way, there seems to be some concern here that variables of the
> subtype may not have valid values. So what?

That's not the concern. The concern is that for composite predicates (by far the
most important kind), the predicate can be made False on an object for which the
predicate was previously checked and passed without a further check. That is,
even though previous checks occurred, the value might still not meet its
predicate.

With existing Ada constraints, once the check has been made and passes (that is,
the object is valid), the object will always be within the constraint (excepting
of course erroneous execution). That is definitely not true for composite
predicates (no matter what rules for rechecking are used). It probably can be
made true for scalar predicates, but that is a very minor use case for the
feature as a whole.

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

From: Tucker Taft
Date: Monday, April 19, 2010  2:54 PM

I had the sense we had chosen to go toward the more general subtype predicates
rather than the somewhat special case of set constraints, given the natural
combination of the new membership test and aspect specification constructs.

It is already the case that you can have static scalar subtypes and static
string subtypes, but not other kinds of static subtypes, so I don't see it odd
that you can have static scalar predicated subtypes without necessarily having
static non-scalar predicated subtypes.

And it is true that staticness makes more of a difference for discrete types
than for other scalar types.  Again, that doesn't bother me.

So to me, your objection to allowing these to be static seems mostly in the
language-design- philosophy realm, but your concerns don't seem to jibe with
existing Ada differences in staticness.

The set constraints seem like a very special purpose construct, while this
notion of subtype predicates seems more generally useful, with additional useful
properties when everything is known statically.

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

From: Robert Dewar
Date: Monday, April 19, 2010  2:58 PM

> I'm confused here. "A fully static list" and "full case coverage" only
> makes sense for discrete types.

I am only interested in enumeration types, I think this feature is highly
dubious for other types, and given the difficulties in agreeing on a
formulation, should be abandoned.

> The primary intent of this feature (and I can say that because it was
> originally my idea) was to get the effect of user-defined constraints
> on *composite* types (an idea which just doesn't work, unfortunately).
> I've always felt that it was best for discrete types to have *real*
> static set constraints (a-la AI05-0153-2).

That's what we really want I agree

> Indeed, I'd prefer
> to allow predicates only on composite types (but generic contract
> issues prevent that from being possible), given that they aren't
> air-tight. (There are many, many rules about discriminants that exist
> solely to prevent a discriminant constraint from being broken -- none
> of those will exist for [composite] predicates and thus it will be
> impossible to depend on most predicates -- they could be made False in
> many ways that wouldn't require them to be rechecked.)

Fine but then I think this feature is unimportant and as far as I am concerned
can be delayed till Ada 2112, when I won't have to worry about it.

> Anyway, Robert, could you expand on what you view as important? Are
> you talking about predicates on all types, or just discrete types, or
> something else? I'd like to make sure that I'm thinking about the same
> things that you are.

I am ONLY interested in static precicates (whether you call them predicates or
constraints is unimporant to me) on enumeration types, that's the only place I
see a use for this in the way I code Ada!

So very likely we aren't thinking about the same thing :-)

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

From: Robert Dewar
Date: Monday, April 19, 2010  3:01 PM

>> By the way, there seems to be some concern here that variables of the
>> subtype may not have valid values. So what?
>
> That's not the concern. The concern is that for composite predicates
> (by far the most important kind), the predicate can be made False on
> an object for which the predicate was previously checked and passed
> without a further check. That is, even though previous checks
> occurred, the value might still not meet its predicate.

Well you see I think these are the LEAST important kind, so I don't care!

> With existing Ada constraints, once the check has been made and passes
> (that is, the object is valid), the object will always be within the
> constraint (excepting of course erroneous execution). That is
> definitely not true for composite predicates (no matter what rules for
> rechecking are used). It probably can be made true for scalar
> predicates, but that is a very minor use case for the feature as a whole.

A good reason to leave out composite predicates.

But to me scalar predicates are NOT a "very minor use case", they are a highly
valuable addition to the language (more important in my opinion than all the
pre/post condition stuff put together -- How could I say such a thing? Because I
think nearly all Ada programmers can and will take advantage of non-contiguous
subtypes of enumeration types, I think only a subset will take advantage of
pre/post conditions. Of course that subset will find them VERY useful, so I
agree with their inclusion.

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

From: Robert Dewar
Date: Monday, April 19, 2010  3:07 PM

> I had the sense we had chosen to go toward the more general subtype
> predicates rather than the somewhat special case of set constraints,
> given the natural combination of the new membership test and aspect
> specification constructs.

I don't necessarily mind that generalization as long as it does not turn into a
classic case of what I call the "system programmer syndrome".

User: I need feature X, should be easy to implement

System Programmer: Great, but that's just a special case of Y which is much more
general.

User: Fine, I am not sure how much more useful Y will be, but if you want to
implement Y, go ahead it will give me X and that's what I want.

Time passes .....

User: whatever happened to feature X

System Programmer: Ah, well we found that feature Y was much more complex than
we thought, so we have postponed it till version 537 three years from now.

> So to me, your objection to allowing these to be static seems mostly
> in the language-design- philosophy realm, but your concerns don't seem
> to jibe with existing Ada differences in staticness.

That seems right

> The set constraints seem like a very special purpose construct, while
> this notion of subtype predicates seems more generally useful, with
> additional useful properties when everything is known statically.

That also seems right to me, providing it does not lose us the useful spcial
purpose feature :-)

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

From: Randy Brukardt
Date: Monday, April 19, 2010  3:46 PM

> It is already the case that you can have static scalar subtypes and
> static string subtypes, but not other kinds of static subtypes, so I
> don't see it odd that you can have static scalar predicated subtypes
> without necessarily having static non-scalar predicated subtypes.

Predicates are just a special form of assertions, and I find the notion of
"static assertions" to be extremely odd (harmful, even).

It's fairly clear that the rules for composite (and access) predicates could and
probably should be very different from those for elementary types. If we want
vastly different rules, then I believe that they should *look* different.

> And it is true that staticness makes more of a difference for discrete
> types than for other scalar types.  Again, that doesn't bother me.
>
> So to me, your objection to allowing these to be static seems mostly
> in the language-design- philosophy realm, but your concerns don't seem
> to jibe with existing Ada differences in staticness.

My point is that I don't like taking arbitrary dynamic expressions and treating
them as static. The more I think about it, the more I think that you're analogy
with instances just does not hold true. In the instance case, all of the
constituents of the expression are known and are known to be static. The effect
is to evaluate the expression at the point of the instance.

In the case statement case, you have no idea what the value is, only some
(possibly very little) information about its range. This is much more like the
state inside the generic template (with an unknown formal object) than it is
related to the instance (with a known static actual object). The only way to
determine the result of the expression for a particular value is to try it:
which means evaluating up to 2**64 static expressions. That's going to take too
long.

Since we need to restrict the expressions to a form that can be evaluated in a
reasonable time, it makes much more sense to use a dedicated syntax for it. In
that case, we get proper static matching (there is no way to match predicate
expressions, at least without creating a bunch of new rules), use in for loops,
case statement/variant/aggregate choices, as well as case completeness.

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

From: Randy Brukardt
Date: Monday, April 19, 2010  4:00 PM

> > I had the sense we had chosen to go toward the more general subtype
> > predicates rather than the somewhat special case of set constraints,
> > given the natural combination of the new membership test and aspect
> > specification constructs.
>
> I don't necessarily mind that generalization as long as it does not
> turn into a classic case of what I call the "system programmer
> syndrome".
...
> > The set constraints seem like a very special purpose construct,
> > while this notion of subtype predicates seems more generally useful,
> > with additional useful properties when everything is known statically.
>
> That also seems right to me, providing it does not lose us the useful
> spcial purpose feature :-)

I think we're very much at risk of "system programmer syndrome" with predicates.
The people who voted against "keep it alive" at the last ARG meeting were
concerned about the holes in the composite model. And tacking staticness and for
loops and the like onto it is not going to make them feel any better (it is
getting more and more complex with little additional benefit).

Indeed, the original proposal for "predicates" came about as Steve and I were
trying to justify the apparently heavier "set constraint" proposal. I created it
as a sort of straw-man to show that the predicate idea doesn't work without a
lot of funny restrictions and knick-knacks (and I never *conceived* of making
them static and essentially using the semantics the set constraints).

My understanding of the reasons we moved on from set constraints (which are very
different than Tuck's understanding) was that they just looked to be too
heavyweight for the benefit. Surely putting all of the same semantics into
predicates is going to make this somehow lighter.

Part of the problem here is that people are trying to use this for two unrelated
purposes: one is to abstract pre and postconditions on single parameters (and
make them much more Ada-like), and the other is to provide static set
functionality. The overlap between these is pretty small, and trying to use the
same feature for both causes it to get really clunky.

Keep in mind that we don't use the same syntax to describe composite
(discriminant, index) and scalar (range) constraints. Set constraints are really
constraints in every reasonable sense, and ought to be described as such
(especially if you want legality rules to depend on them). Composite predicates
are constraints in *no* sense -- they don't provide the information needed to
describe the "shape" (discriminants, bounds) of a value, so they can't be
constraints. So these are very different things and as such, they ought to look
different.

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

From: Tucker Taft
Date: Monday, April 19, 2010  4:27 PM

...
> My point is that I don't like taking arbitrary dynamic expressions and
> treating them as static. The more I think about it, the more I think
> that you're analogy with instances just does not hold true. In the
> instance case, all of the constituents of the expression are known and
> are known to be static. The effect is to evaluate the expression at
> the point of the instance.
>

I am losing you here.  I am talking about considering a subtype with a predicate
as a "static" subtype only if it meets all the usual criteria (constraint is
static, "parent" subtype is static, etc.), plus the predicate will be static if
you substitute in a static value for the subtype.  So I am confused by the term
"arbitrary dynamic expression."  But perhaps the example I give below
illustrates your issue?

> In the case statement case, you have no idea what the value is, only
> some (possibly very little) information about its range. This is much
> more like the state inside the generic template (with an unknown
> formal object) than it is related to the instance (with a known static
> actual object). The only way to determine the result of the expression
> for a particular value is to try it: which means evaluating up to
> 2**64 static expressions. That's going to take too long.

So let me see if I understand your concern:

    subtype Very_Even is Long_Long_Integer range 0..2**62;
      with Predicate => Very_Even mod 2**60 = 0;

    procedure Use_Very_Even(X : Very_Even) is
    begin
        case X is
           when 0 => ...
           when 2**60 => ...
           when 2**62 => ...
        end case;
    end Use_Very_Even;

The question is, does the above provide complete coverage?  Well, to decide
that, we need to find at least one value that is in Very_Even but not covered by
the case statement (e.g. 2**61).

So the values that aren't covered in the overall range of Very_Even are:
{1..2**60-1, 2**60+1..2**62-1} We will have to iterate through this very long
sequence checking the predicate.  If the predicate is False in many cases in
this range, then we might go through a lot of cases before we found one for
which Very_Even's predicate evaluated to True. In this case, we might check
something like 2**61-2 values before discovering that 2**61 is not covered.

That does seem to be an issue, given the age of the universe... ;-).

> Since we need to restrict the expressions to a form that can be
> evaluated in a reasonable time, it makes much more sense to use a
> dedicated syntax for it. In that case, we get proper static matching
> (there is no way to match predicate expressions, at least without
> creating a bunch of new rules), use in for loops, case
> statement/variant/aggregate choices, as well as case completeness.

So much for generalization.  I guess you have convinced me that checking
completeness with arbitrary "static" predicates is not feasible.  But
unfortunately we do seem to be going in circles on this idea.

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

From: Randy Brukardt
Date: Monday, April 19, 2010  4:50 PM

> I am losing you here.  I am talking about considering a subtype with a
> predicate as a "static" subtype only if it meets all the usual
> criteria (constraint is static, "parent"
> subtype is static, etc.), plus the predicate will be static if you
> substitute in a static value for the subtype.  So I am confused by the
> term "arbitrary dynamic expression."  But perhaps the example I give
> below illustrates your issue?

Yes, the example below illustrates this point perfectly. I consider the
expression "dynamic" because it itself is not static (just as an expression that
depends on a formal object is not static, even though it might be in an
instance).

...
> That does seem to be an issue, given the age of the universe... ;-).

Right. And this is worse because the compiler has to do it even if all of needed
values are given (since it has to check that none are missed). So even case
statements that are correct have this time penalty.

I can imagine having special cases to prevent this for common cases, but it will
always be possible to write a more complex predicate that didn't match any of
the special cases.

> > Since we need to restrict the expressions to a form that can be
> > evaluated in a reasonable time, it makes much more sense to use a
> > dedicated syntax for it. In that case, we get proper static matching
> > (there is no way to match predicate expressions, at least without
> > creating a bunch of new rules), use in for loops, case
> > statement/variant/aggregate choices, as well as case completeness.
>
> So much for generalization.  I guess you have convinced me that
> checking completeness with arbitrary "static"
> predicates is not feasible.  But unfortunately we do seem to be going
> in circles on this idea.

I realize that. The "softer" issue of "good" language design is rather
subjective in any case. But I've always felt since I worked out the initial
version of the details of these two proposals that the set constraint version
was preferable for discrete types (because it naturally supported the needed
legality rules, it automatically gives for loop iteration and case statements,
it probably could be used to fix the variant problem you noted, etc.).

What I didn't succeed at is finding a decent replacement for the composite
predicates (you didn't like my restricted discriminant constraints at all). And
I think that problem deserves a solution as well - as does Bob - which is how we
ended up back with the predicates. I've personally come to the conclusion that
these things are different enough that perhaps we ought to solve them
differently, but I may not win that case.

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

From: Bob Duff
Date: Monday, April 19, 2010  5:56 PM

> I am only interested in enumeration types, I think this feature is
> highly dubious for other types, and given the difficulties in agreeing
> on a formulation, should be abandoned.

I think you're confusing two different features, here.  In my message about
case-full-coverage, I combined these two features.

I strongly believe that "subtype predicates" need to be a general purpose
feature, without arbitrary restrictions.  They should be allowed on any subtype,
and should allow arbitrary expressions.

The other feature is the "X in (Red, Green, Blue)" syntax.  I think it's fine
for that to be restrictive (require X to be discrete, and require Red, etc to be
static).  That's probably already the case.

For subtype predicates, generality simplifies.
For the "X in ..." thing, removing generality simplifies.

Restricting subtype predicates to static expressions would be a disaster.  It
would disallow the single most important tool for abstraction that has ever been
invented (the subroutine call, with parameter passing)!  I definitely want to be
able to say "with Predicate => Is_Gnarly (S)".

I am not saying I want to put side effects in assertions.  Is_Gnarly probably
doesn't have any (or only has benign ones).  But the compiler can't know that.

Restricting subtype predicates to enum types would also be a disaster.
They are desperately needed also on discriminated types, and on pointers to
discriminated types, and on things that behave like those (like Node_Id in the
GNAT compiler, which is conceptually a pointer-to-record, but is actually
implemented as an index into a table).

Here's an example:

In GNAT, we have:

   type Node_Id is range Node_Low_Bound .. Node_High_Bound;
   --  Type used to identify nodes in the tree

   subtype Entity_Id is Node_Id;
   --  A synonym for node types, used in the Einfo package to refer to nodes
   --  that are entities (i.e. nodes with an Nkind of N_Defining_xxx). ...

   type Entity_Kind is (...); -- dozens of enum lits

   subtype Access_Subprogram_Kind is Entity_Kind range
       E_Access_Subprogram_Type ..
   --  E_Anonymous_Access_Subprogram_Type
   --  E_Access_Protected_Subprogram_Type
       E_Anonymous_Access_Protected_Subprogram_Type;

and dozens of subtypes like that.

What I want is:

  subtype Access_Subprogram_Kind is Entity_Kind with
    predicate =>
      Access_Subprogram_Kind in
       (E_Access_Subprogram_Type,
        E_Anonymous_Access_Subprogram_Type,
        E_Access_Protected_Subprogram_Type,
        E_Anonymous_Access_Protected_Subprogram_Type);

  subtype Access_Subprogram_Entity_Id is Entity_Id with
    predicate =>
      Ekind (Access_Subprogram_Entity_Id) in Access_Subprogram_Kind;
  --  Here we have a predicate on a non-enum type, and we are calling
  --  the function Ekind, so it is not a static expression.

We have thousands of procedures like this:

    procedure Do_Something (N : Entity_Id) is
        Thing : Node_Id;
        ...

which could be hugely improved:

    procedure Do_Something (N : Access_Subprogram_Entity_Id) is
        Thing : Case_Statement_Node_Id;
        ...

documenting what parameters and locals are supposed to be, and ensuring via
run-time checks that the documentation is likely correct.

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

From: Bob Duff
Date: Monday, April 19, 2010  5:56 PM

>     subtype Very_Even is Long_Long_Integer range 0..2**62;
>       with Predicate => Very_Even mod 2**60 = 0;
...
> That does seem to be an issue, given the age of the universe... ;-).

I'm not asking for that much generality!

From my earlier message:

  So I suggest we say that if the predicate is given by "in" of
  a list of static expressions and subtypes and ranges (and nothing else),
  that the subtype be considered static.

This seems easy to define, easy to understand, easy to implement, and easy to
implement efficiently.

But it's just a nice-to-have.  I can do without this, but I insist that
predicates are an important feature, and should not have arbitrary restrictions.

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

From: Bob Duff
Date: Monday, April 19, 2010  5:57 PM

ARG tends to fall into the trap of letting "perfect" be the enemy of "good
enough"!  That's the case here.

I understand the discomfort that Randy and others have with subtype predicates
-- the predicate could become False in some circumstances, so can't be 100%
relied upon.

But I think we should focus on comparing what we have now with what subtype
predicates provide.  Right now, we have (from my earlier GNAT example):

   subtype Entity_Id is Node_Id;
   --  A synonym for node types, used in the Einfo package to refer to nodes
   --  that are entities (i.e. nodes with an Nkind of N_Defining_xxx). ...

Right now, that predicate ("nodes with an Nkind of N_Defining_xxx") is just a
comment and is totally unchecked.

With subtype predicates, we get checks sprinkled all over the place, so it
becomes highly unlikely that an object of subtype Entity_Id will have a wrong
Nkind.  Not impossible, but highly unlikely. And if it happens, it will likely
be caught soon after.

That seems like a huge benefit.  We don't know how to make it impossible (too
bad).  So let's go for "good enough".

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

From: Robert Dewar
Date: Monday, April 19, 2010  6:15 PM

> The other feature is the "X in (Red, Green, Blue)" syntax.  I think
> it's fine for that to be restrictive (require X to be discrete, and
> require Red, etc to be static).  That's probably already the case.

OK, so perhaps I am mixing two features, but then this "other feature" is the
one I am interested in. I can't get very excited about the more general subtype
predicates that constitute the other feature. I don't object to them, I just
don't find them a very important feature to add.

> Restricting subtype predicates to static expressions would be a
> disaster.  It would disallow the single most important tool for
> abstraction that has ever been invented (the subroutine call, with
> parameter passing)!  I definitely want to be able to say "with Predicate => Is_Gnarly (S)".

I don't object to that, I just don't think I would find it that useful.

> Restricting subtype predicates to enum types would also be a disaster.
> They are desperately needed also on discriminated types, and on
> pointers to discriminated types, and on things that behave like those
> (like Node_Id in the GNAT compiler, which is conceptually a
> pointer-to-record, but is actually implemented as an index into a table).

OK, well I never felt the desparate need I guess in code that I have written.

...
> which could be hugely improved:
>
>     procedure Do_Something (N : Access_Subprogram_Entity_Id) is
>         Thing : Case_Statement_Node_Id;

Sorry I don't see it, how is this a huge improvement over an assertion inside
Do_Something that says

     Assert (Ekind (N) in Access_Subprogram_Kind);

I see it is a bit neater, but I don't see any huge gain.
Indeed in this case we can simply have a precondition for Do_Something that does
this check. Why will the notation you suggest be any improvement, I don't get
it, I must be missing something. I don't see that your notation allows some kind
of static checking, it will still result in a test, just like the precondition
or assertion. ... > documenting what parameters and locals are supposed to be,
and > ensuring via run-time checks that the documentation is likely correct.

But surely preconditions do this also?

Actually, in many many of the compiler cases, error conditions can lead to
violations of what you expect anyway, and you don't have assertions, you have
error tests indicating diagnostics.

Well I am unconvinced. But as long as I end up with a solution to the serious
problem of not being able to manage any more to deinfe the enumeration type with
all the subtypes I want, I have no objection to a more general feature. I just
don't think I will find it very useful, certainly not in the compiler. I know
Bob postulates a useful use, but I can't say from my experience that this will
be helpful.

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

From: Robert Dewar
Date: Monday, April 19, 2010  6:17 PM

> Right now, that predicate ("nodes with an Nkind of N_Defining_xxx") is
> just a comment and is totally unchecked.

That is false to me, in many many places we have explicit or implicit checks. If
we pass a Node_Id where an Entity_Id is required, then almost always there will
be a call to a function that contains an assertion that it is being applied to
an entity. So the "totally unchecked" here is bogus.

> With subtype predicates, we get checks sprinkled all over the place,
> so it becomes highly unlikely that an object of subtype Entity_Id will
> have a wrong Nkind.  Not impossible, but highly unlikely.
> And if it happens, it will likely be caught soon after.

It's highly unlikely now!
>
> That seems like a huge benefit.  We don't know how to make it
> impossible (too bad).  So let's go for "good enough".

I am unconvinced by this example.

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

From: Bob Duff
Date: Monday, April 19, 2010  6:17 PM

You said you were out of the room during part of the discussion if this AI at
the meeting at SofCheck.  I don't remember that, but I believe you. Too bad -- I
rely on your excellent Minutes to remember what we discussed/decided.

Anyway, I thought I had convinced most ARG members that it's OK for subtype
predicates to have some loopholes -- they're still useful. The fact that we
can't plug the loopholes shouldn't kill the whole idea.

By the way, in one of your messages, you said, "A predicate is not a
constraint!" and some other stuff along those lines.  I'm confused by such
remarks -- when you say such things, could you please explain what you mean by
"constraint" and the like?  I know what a "constraint" is in Ada, but we're
changing Ada, so we could change the meaning of "constraint", and I don't know
of any particular constraints on what "constraint" means (or OUGHT to mean).

Let's not get too hung up on terminology.  We have (or might have) "constraints"
(5 kinds), "null exclusions", "invariants", "predicates". For us language
lawyers, there are important distinctions amongst these things.  But let's
remember the users -- to an Ada programmer, those are all basically the same.
They all serve basically the same purpose, and the differences are minor
details.

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

From: Randy Brukardt
Date: Monday, April 19, 2010  6:54 PM

> You said you were out of the room during part of the discussion if
> this AI at the meeting at SofCheck.  I don't remember that, but I
> believe you.
> Too bad -- I rely on your excellent Minutes to remember what we
> discussed/decided.

Well, unfortunately someone else is going to have to remember. When I came back,
Tucker claimed that "the hole had been plugged", which is an amazing statement
to me, and I have no idea how that was supposely accomplished.

> Anyway, I thought I had convinced most ARG members that it's OK for
> subtype predicates to have some loopholes -- they're still useful.
> The fact that we can't plug the loopholes shouldn't kill the whole
> idea.

Apparently not all, however, as there was a "no" vote on the "keep alive"
motion. I don't recall how I voted, but I suspect I abstained given that I was
out of the room for quite a while.

> By the way, in one of your messages, you said, "A predicate is not a
> constraint!" and some other stuff along those lines.
>  I'm confused by such remarks -- when you say such things, could you
> please explain what you mean by "constraint" and the like?  I know
> what a "constraint"
> is in Ada, but we're changing Ada, so we could change the meaning of
> "constraint", and I don't know of any particular constraints on what
> "constraint" means (or OUGHT to mean).

I explained that in another message today. For scalar predicates, there is no
interesting difference. But a composite predicate is missing a very important
property of a constraint: it doesn't provide values for the bounds or
discriminants. That means that one cannot declare an object of a subtype of an
indefinite type that has a predicate unless it also has a real constraint. Thus
we need to keep the two things separate.

I sort of worked out how one could have "partial" discriminant constraints (that
would allow only subsets of discriminants), but (A) people, especially Tucker,
hated the idea; (B) no extension to index constraints is obvious. So I think it
remains critical that these aren't constraints (at least in the composite case).

> Let's not get too hung up on terminology.  We have (or might
> have) "constraints" (5 kinds), "null exclusions", "invariants",
> "predicates".
> For us language lawyers, there are important distinctions amongst
> these things.  But let's remember the users -- to an Ada programmer,
> those are all basically the same.  They all serve basically the same
> purpose, and the differences are minor details.

I don't think I'm hung up on terminology. I just don't like the idea of saying
that:

    subtype Reds is Colors with Predicate => Reds in (Red | Yellow | Orange);

has magic static properties, while

    subtype Blues is Colors with Predicate => (Blues = Blue or Blues = Violet or Blues = Indigo);

does not have the same properties. (And we've already proven that generalizing
what expressions are static doesn't work in general.)

The set constraint doesn't have this problem because there it is the only syntax
allowed (period):

   subtype Reds is Colors when Red | Yellow | Orange;

If the most important thing is the static sets (as Robert has said he feels),
then that is a far better solution (since it doesn't bring in any other problems
- lack of optimizability, holes for composite types, etc. - and it allows a bit
more as well -- for loops in particular).

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

From: Robert Dewar
Date: Monday, April 19, 2010  6:55 PM

> You said you were out of the room during part of the discussion if
> this AI at the meeting at SofCheck.  I don't remember that, but I believe you.
> Too bad -- I rely on your excellent Minutes to remember what we
> discussed/decided.
>
> Anyway, I thought I had convinced most ARG members that it's OK for
> subtype predicates to have some loopholes -- they're still useful.
> The fact that we can't plug the loopholes shouldn't kill the whole idea.

I definitely agree with this. After all there are loopholes with ordinary static
constraints:

    type R is range 1 .. 10;
    A : R := 5;

No other assignments to A exist

Do we know that A will always be 1 .. 10

Answer: No, there are several ways for A to get messed up. We declare some of
these to be erroneous, and some to be OK, e,g, overlaying A with a float and
modifying the float.

But we don't get upset that the range is not a 100% guarantee.

> By the way, in one of your messages, you said, "A predicate is not a
> constraint!" and some other stuff along those lines.  I'm confused by
> such remarks -- when you say such things, could you please explain
> what you mean by "constraint" and the like?  I know what a "constraint"
> is in Ada, but we're changing Ada, so we could change the meaning of
> "constraint", and I don't know of any particular constraints on what
> "constraint" means (or OUGHT to mean).
>
> Let's not get too hung up on terminology.  We have (or might have)
> "constraints" (5 kinds), "null exclusions", "invariants", "predicates".
> For us language lawyers, there are important distinctions amongst
> these things.  But let's remember the users -- to an Ada programmer,
> those are all basically the same.  They all serve basically the same
> purpose, and the differences are minor details.

I agree, I don't understand the distinction Randy tries to draw between a
constraint and a predicate, they seem the same fundamental thing to me.

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

From: Randy Brukardt
Date: Monday, April 19, 2010  7:16 PM

...
> Sorry I don't see it, how is this a huge improvement over an assertion
> inside Do_Something that says
>
>      Assert (Ekind (N) in Access_Subprogram_Kind);
>
> I see it is a bit neater, but I don't see any huge gain.
> Indeed in this case we can simply have a precondition for Do_Something
> that does this check. Why will the notation you suggest be any
> improvement, I don't get it, I must be missing something. I don't see
> that your notation allows some kind of static checking, it will still
> result in a test, just like the precondition or assertion.

Yes, of course you could write these as assertions or preconditions. But unlike
assertions or preconditions, these aren't "bolted" on to the language -- they're
directly taking advantage of an important Ada feature - subtyping. After all,
one of the most important uses of subtypes in Ada to date is to provide
preconditions on individual parameters. Predicates were intended to expand this
well-understood Ada feature to user-written checks.

Also note that there is an improvement in checking, in that the compiler can
eliminate redundant checks (subject to rules that are still TBD). (This isn't a
huge deal, but it helps.) In particular, a subtype conversion to the same
subtype can usually eliminate the check, even if the compiler does not know
whether the call has side-effects. Doing that with preconditions or assertions
would be wrong.

As an example (another compilery thing): (Assume Expensive_Function cannot be
declared Pure.)

    subtype Object_Node is Node with Predicate => Expensive_Function (Object_Node);

    function Get_Object_Node (Symbol : in Node_Access) return Object_Node;

    function Nominal_Subtype (Node : in Object_Node) return Type_Index;

    ... Nominal_Subtype (Get_Object_Node (Some_Symbol)); -- No call on Expensive_Function here.

If this was written as a precondition instead, the compiler could not eliminate
the call on Expensive_Function.

In addition, reusing this "precondition" on many subprograms becomes trivial.
OTOH, full preconditions tend to differ on different subprograms (because
multiple parameters need to be checked, interactions between parameters need to
be checked, and the like). Thus using preconditions to do the job of subtypes
makes the code larger, harder to read, and a lot more redundant (meaning harder
to maintain).

Besides, if Preconditions "are just tests", I don't want them either. I think we
have to *allow* nasty expressions with side-effects in preconditions and
predicates, but we must give the compiler the tools to do various sorts of
static analysis on preconditions and predicates (and to be able to warn about
ones that it cannot do analysis on, as these are likely to be dangerous). That
implies (optionally) making side-effects visible in the contracts of functions.
Pascal has noted that he considered preconditions that don't hold within the
called subprogram to be actively harmful -- we already have assertions for tests
that don't hold afterwards, why have more such things?

Anyway, I think it is clear that there are two separate features here, and
trying to mix them might be a problem. It's better that each stand and fall
based on their own merits.

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

From: Randy Brukardt
Date: Monday, April 19, 2010  7:22 PM

...
> I agree, I don't understand the distinction Randy tries to draw
> between a constraint and a predicate, they seem the same fundamental
> thing to me.

Please read my earlier responses to you and to Bob.

But keep in mind that predicates are intended for all types. If we're *only*
talking about discrete types, we've all agreed that we don't want predicates at
all (the added ability of being able to say "Is_Even (Even)" is not worth it. If
we're only doing discrete types, we surely would use set constraints as
described in AI05-0153-2. It's the strong desire for a more general construct
that forces the difference.

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

From: Bob Duff
Date: Monday, April 19, 2010  7:23 PM

> I definitely agree with this. After all there are loopholes with
> ordinary static constraints:
>
>     type R is range 1 .. 10;
>     A : R := 5;
>
> No other assignments to A exist
>
> Do we know that A will always be 1 .. 10
>
> Answer: No, there are several ways for A to get messed up. We declare
> some of these to be erroneous, and some to be OK, e,g, overlaying A
> with a float and modifying the float.

Well, that involves a chapter-13-ish feature, which is a different sort of
loophole, so I think a better example would be:

    Blah : R; -- uninitialized
    ...
    A := Blah; -- A might be outside 1..10 now

> But we don't get upset that the range is not a 100% guarantee.

Yes!

We hate such bugs, but we don't throw the baby out with the bathwater, as in
"then everything should be Integer".

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

From: Bob Duff
Date: Monday, April 19, 2010  7:27 PM

> I agree, I don't understand the distinction Randy tries to draw
> between a constraint and a predicate, they seem the same fundamental
> thing to me.

And there's some damage from all this profusion of jaw-breaking terminology.
I mean, how many Ada programmers understand the subtle distinction between a
"parent type" and a "progenitor type"?

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

From: Robert Dewar
Date: Monday, April 19, 2010  7:28 PM

> Yes, of course you could write these as assertions or preconditions.
> But unlike assertions or preconditions, these aren't "bolted" on to
> the language
> -- they're directly taking advantage of an important Ada feature -
> subtyping. After all, one of the most important uses of subtypes in
> Ada to date is to provide preconditions on individual parameters.
> Predicates were intended to expand this well-understood Ada feature to user-written checks.

OK, so it's really only an aesthetic issue, not one of any fundamental
capability.

> Also note that there is an improvement in checking, in that the
> compiler can eliminate redundant checks (subject to rules that are
> still TBD). (This isn't a huge deal, but it helps.) In particular, a
> subtype conversion to the same subtype can usually eliminate the
> check, even if the compiler does not know whether the call has
> side-effects. Doing that with preconditions or assertions would be wrong.

Why, you don't have to evaluate a precondition you know will succeed, very often
the compiler can be sure there are no side effects. Actually I would like to see
an implementation permission that says

It is not necessary to call a function in a precondition or postcondition or
assertion, if the only reason for making the call is to execute possible side
effects. if the value of the assertion can be established without such calls,
they need not be made.

This is similar to some of the things we say in 11.6, where if you know an
exception will be raised, you don't have to evaluate things just for the sake of
side effects.

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

From: Robert Dewar
Date: Monday, April 19, 2010  7:31 PM

> Well, that involves a chapter-13-ish feature, which is a different
> sort of loophole, so I think a better example would be:
>
>     Blah : R; -- uninitialized
>     ...
>     A := Blah; -- A might be outside 1..10 now

Well it's a different kind of error indeed, but I don't see chapter 13 as
somehow different, and the interesting thing about my example is that it can
happen even if your coding standards require absolutely everything to be
initialized.

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

From: Robert Dewar
Date: Monday, April 19, 2010  7:33 PM

> And there's some damage from all this profusion of jaw-breaking terminology.
> I mean, how many Ada programmers understand the subtle distinction
> between a "parent type" and a "progenitor type"?

As long as compilers  don't make the mistake of using obscure technical terms in
error messages, they don't need to know :-)

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

From: Randy Brukardt
Date: Monday, April 19, 2010  7:44 PM

> > I agree, I don't understand the distinction Randy tries to draw
> > between a constraint and a predicate, they seem the same fundamental
> > thing to me.
>
> And there's some damage from all this profusion of jaw-breaking
> terminology.
> I mean, how many Ada programmers understand the subtle distinction
> between a "parent type" and a "progenitor type"?

If you can figure out how to define a composite "constraint" that doesn't
constrain anything, be my guest. I'll let you face the wrath-of-Baird (and
of-Adam). :-) Rewriting large parts of the standard to shoe-horn in the same
terminology for something that does not currently exist does not sound appealing
to me.

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

From: Bob Duff
Date: Monday, April 19, 2010  7:53 PM

> OK, so it's really only an aesthetic issue, not one of any fundamental
> capability.

But that's like saying "type T is range 1..10;" is just an aesthetic issue.
After all, you could put "Assert (X in 1..10);" on every assignment or parameter
passing to every object of that subtype.

The point of pre/post/invariant/predicate/constraint is that the "asserts" get
sprinkled around in (almost) all relevant places automatically.

> Why, you don't have to evaluate a precondition you know will succeed,
> very often the compiler can be sure there are no side effects.
> Actually I would like to see an implementation permission that says
>
> It is not necessary to call a function in a precondition or
> postcondition or assertion, if the only reason for making the call is
> to execute possible side effects. if the value of the assertion can be
> established without such calls, they need not be made.

I think I agree with that, but...

> This is similar to some of the things we say in 11.6, where if you
> know an exception will be raised, you don't have to evaluate things
> just for the sake of side effects.

...but it's less important in this case, because the compiler doesn't HAVE to do
anything at all -- there are modes in which assertions are totally unchecked, so
there can be intermediate modes in which they are somewhat checked according to
whatever rules the compiler writer likes.

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

From: Randy Brukardt
Date: Monday, April 19, 2010  7:59 PM

> > And there's some damage from all this profusion of jaw-breaking terminology.
> > I mean, how many Ada programmers understand the subtle distinction
> > between a "parent type" and a "progenitor type"?
>
> If you can figure out how to define a composite "constraint"
> that doesn't constrain anything, be my guest. I'll let you face the
> wrath-of-Baird (and of-Adam). :-) Rewriting large parts of the
> standard to shoe-horn in the same terminology for something that does
> not currently exist does not sound appealing to me.

This actually makes me angry, because I spent a lot of effort doing precisely
what you are advocating: define these as user-defined constraints. And my thanks
for that work was Steve spewing out enough loopholes and problems to make me
want to jump off of those cliffs in Brest.

So I renamed them "predicates". That wasn't my first choice at all. Now you are
making the claim that "a constraint can be anything we want it to be". Well,
maybe: if we're willing to check and change every existing use of the term
"constraint" in the Standard - 40 clauses have such uses, many have multiple
uses. What is surely true is that we can't just claim that it is fine to call it
a "constraint" because it is not by the current definition of "constraint" in
the standard. And we'd need to change the rules for composite types to allow
constraints that still leave the subtype indefinite and to allow multiple
constraints on composite types -- both of which would be major changes to the
language definition and likely to compilers as well. A whole lot larger change
than that of the language as a whole.

Anyway, either put up or shut up on this one. Either propose a set of changes to
allow these to be called constraints (and let the rest of us tear them up), or
stop griping about the name. You did that for "build-in-place" and succeeded in
changing the mindset, so it can be done.

P.S. If I wasn't the editor, I would set a kill bit on this thread so I didn't
have to see any more of it.

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

From: Bob Duff
Date: Monday, April 19, 2010  8:26 PM

> This actually makes me angry, because I spent a lot of effort doing...

Please don't be angry about this stuff.  It's not good for you.

> Anyway, either put up or shut up on this one.

OK, fair enough.

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

From: Bob Duff
Date: Monday, April 19, 2010  9:11 PM

> Yes, we can all sing in harmony about how much we love full coverage.
> While we are at it, I would love to solve the problem some day that
> nested variants always need a "when others" which is logically
> unnecessary.  It would be nice if the discriminant's subtype, when
> inside a particular variant, effectively had a Predicate expression
> added that corresponds to the "when Red | Green =>" that started the
> variant.

Yeah, that's an annoyance, and I support fixing it.

But it's not very important.

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

From: Bob Duff
Date: Wednesday, April 28, 2010  5:33 PM

Here's a new version of AI05-0153-1 (subtype predicates), for tomorrow's
discussion.

We might want to decide that this stuff is too immature to include in Ada 2012.
If so, I think GNAT will experiment with implementing this stuff, and using it.
That might be a better approach than standardizing something that we're not sure
is 100% right.

If this AI is not included in Ada 2012, I am strongly opposed to including type
invariants, for reasons I've already stated. In that case, perhaps compiler
writers can experiment with invariants, too.

I did a lot of rewriting.  One thing I should mention: I removed the
philosophical ramblings that I found objectionable, and replaced them with an
objective (I hope) list of differences between predicates and invariants (and
between predicates and constraints). Hopefully, we can all agree on what the
differences are, even if I think those differences are minor, whereas Randy and
others think they are major.

[Following is version /05 of the AI.]

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

From: Randy Brukardt
Date: Wednesday, April 28, 2010  11:15 PM

> Here's a new version of AI05-0153-1 (subtype predicates), for
> tomorrow's discussion.

A few comments.

...
> RM-3.2(8/2) says:
>
>     ...The set of values of a subtype consists of the values of its type that
>     satisfy its constraint and any exclusion of the null value.
>
> Change it to:
>
>     ...The set of values of a subtype consists of the values of its type that
>     satisfy its constraint, any exclusion of the null value, and any predicate.

Here is where we disagree. I don't think a predicate can or should have any
effect on the values of a subtype, because that changes all of the rules
involving validity, how iterations work, and the like. If we're doing those
things, we need to greatly restrict the forms of the predicates, and I think
that is a very bad idea (this "looks" general). If we're *not* doing those
things, then lots of rewording is needed.

> Add new section 3.2.4:
>
> 3.2.4 Subtype Predicates
...

>                                Dynamic Semantics
>
> On every subtype conversion, the predicate of the target subtype is
> evaluated, and a check is made that the predicate is True.
> Redundant[This includes all parameter passing, except for certain
> parameters passed by reference, which are covered by the following
> rule: ] After normal completion and leaving of a subprogram, for each
> in out or out parameter that is passed by reference, the predicate of
> the subtype of the actual is evaluated, and a check is made that the
> predicate is True. For an object created by an object_declaration with
> no explicit initialization expression, or by an uninitialized
> allocator, if any subcomponents have explicit default values, the
> predicate of the nominal subtype is evaluated, and a check is made
> that the predicate is True.
> Assertions.Assertion_Error is raised if any of these checks fail.

Rules about subtype conversions belong in 4.6. We've been *very* careful to
maintain that up to this point, and I don't think we should start distributing
those (especially only in a few cases).

...
> An index subtype, discrete_range of an index_constraint or slice, or a
> discrete_subtype_definition is illegal if it statically denotes a
> subtype with a user-specified predicate.

All of these forward references need section references. (That's why I put those
rules in the appropriate clauses: to avoid the forward references.)

...
>
> 6.4.1(13) says:
>
> 13      * For an access type, the formal parameter is initialized from the
>           value of the actual, without a constraint check;
>
> Change it to:
>
> 13      * For an access type, the formal parameter is initialized from the
>           value of the actual, without any constraint, null-exclusion, or
>           predicate checks;

We changed this wording in AI05-0196-1, approved in Burlington. Moreover this is
out of order in the AI (clauses are supposed to come in order). The current
wording is:

For an access type, the formal parameter is initialized from the value of the
actual, without checking that the value satisfies any constraint or any
exclusion of the null value;

because what a "constraint check" is not well-defined.

...
> Add at the end of 4.6(51/2):
>
> If the target subtype has a predicate, the predicate is applied to the
> value and Assertions.Predicate_Error is raised if the result is False.

I think you mean Assertion_Error here. I changed it.

> !discussion
>
> Predicates are similar to constraints. The differences are:
>
>     - Constraints are restricted to certain particular forms (range
>       constraints, discriminant constraints, and so forth), whereas predicates
>       can be arbitrary conditions.
>
>     - Constraints can only be violated for invalid values, whereas predicates
>       can be violated in various ways (important side effects in predicates,
>       for example, could cause reevaluation of the predicate to get a different
>       answer). However, it is possible to write well-behaved predicates. We
>       don't know how to FORCE the programmer to write well-behaved predicates
>       without being too restrictive.

On this bullet, I think our only difference is a matter of degree. It's unlikely
that anyone will write well-behaved predicates for composite types. The only
ones that could qualify only involve bounds or discriminants (directly, no
dereferencing). It's easy to see that: all of the fancy rules that keep access
to constrained discriminated types from causing problems aren't going to be
enforced for predicates. So depending on anything other than discriminants will
not be "well-behaved".

The other major difference between a predicate and a constraint is that the
predicate cannot affect the value set for the type. If it did, then validity
would depend on the predicate, and since that cannot be trusted in general, it
would be impossible to do any reasoning about validity of objects. Which would
make any check elimination (not just predicate checks, but any check)
technically wrong - a result we surely don't want.

We had a HUGE amount of trouble with the value set for null exclusions, and this
would be many times worse. I really don't even want to think about it -- you
would have to do the careful research to prove that there is no problem (and
explain why you think that) before I'd even consider voting to abstain on a
proposal changing the value set based on arbitrary expressions potentially with
side-effects.

...
> ???The following para is obsolete, but I'm leaving it in for
> discussion purposes.
>
>   The exception "Constraint_Error" seems wrong for something that is not a
>   constraint. Therefore, failed predicates raise Predicate_Error. This seems
>   necessary in order to avoid confusing constraint checks (which generally can
>   be assumed to remain True after the first check) with predicate checks (which
>   generally cannot be assumed to be remain True).
>
> Bob replies: If we have Predicate_Error, then we should have
> Precondition_Error and Postcondition_Error. But we don't:
> Those just use Assertion_Error. So I got rid of Predicate_Error, and
> used Assertion_Error.

They *should* use Precondition_Error and Postcondition_Error; they're not
assertions (not in my world anyway; I don't want them at all if they are "just"
assertions).

> I'd be just as happy with Constraint_Error. Null exclusions use
> Constraint_Error, even though they aren't constraints.
> And Constraint_Error is used for a whole bunch of other
> not-quite-constraint things.

That's true; it really depends on whether you use the names of the exceptions as
debugging information or you just want to use one and assume your IDE+runtime
bails you out as to the cause of the problem. With Claw, we used a set of
different exceptions for different causes, but not one for every possible error
(about the same level as IO_Exceptions). I think the same would be appropriate
here.

So I could see using the same exception for Preconditions and Postconditions
(and maybe even predicates), but I don't think those should be shared with
anything else.

This is something we could literally argue forever. Not a big deal in any case.

...
>     subtype Callable_Symbol_Ptr is not null Symbol_Ptr with
>        Predicate => Callable_Symbol_Ptr.Entity in (Proc, Func, An_Entry);

I'd prefer that you don't use unapproved AIs in examples, unless you really need
to. This one has serious resolution issues and I for one don't think that they
can be solved in the general case currently proposed. I left this as I
originally had it.

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

From: Bob Duff
Date: Monday, May 10, 2010  1:56 PM

New version of AI05-0153-1, Subtype predicates, modified as per the April 29
telephone meeting.

Randy and Steve were given the following homework:

    AI05-0153-1: Check if there are any rules where dynamically changing value
    sets breaks the language.

I did your homework.  ;-)  I ended up deciding to go along with Randy and Steve,
here.  It's kind of unintuitive, but it seemed easiest.  See the last part of
the discussion, about validity, for (rather weak) rationale.

If you want to argue against this decision (I expect Tuck to object!), then
please address the validity issue.

Steve was given the following homework:

    AI05-0153-1: Explain the problem with "contains" for generics, showing
    examples.

I think I see what Steve was getting at, but I don't think it's a big problem.
I added some AARM verbiage to clarify the intent.  I don't feel strongly about
this: feel free to propose wording for the opposite choice.

[Editor's Note: This is version /06 of the AI.]

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

From: Steve Baird
Date: Monday, May 10, 2010  2:16 PM

> > Steve was given the following homework:
>     AI05-0153-1: Explain the problem with "contains" for generics, showing
>     examples.
>
> ...
> AARM To Be Honest: "Contains" means physically contains; instances of
> generic child packages are not included.

This seems adequate, but we could also consider ignoring generics entirely when
making this "contains" check. This would, in the following case,

     generic
        type Index is (<>);
     package G is
         generic package Nested is end Nested;
     end G;

     package body G is
         package body Nested is
            type Vec is array (Index) of Integer;
         end Nested;
     end G;

mean that no runtime check would fail if G were instantiated with a "predicated"
subtype, but that an instance of G.Nested would fail the AI's check.

The advantage of treating generics uniformly is that it eliminates the need to
even mention "sprouted" generics; the rule for this odd corner case just falls
out as a consequence of the more general rule.

I only feel strongly that the rule ought to be well-defined; I don't much care
which answer we pick.

P.S. Am I now done with my homework, or is more explaining needed?

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

From: Bob Duff
Date: Monday, May 10, 2010  2:57 PM

> This seems adequate, but we could also consider ignoring generics
> entirely when making this "contains" check.

You mean, like this:

The elaboration of the declaration or body of an instance of a generic unit
raises Program_Error if any of the following occurs within that declaration or
body, but not further nested within a generic unit: an index subtype,
discrete_range of an index_constraint or slice, or a discrete_subtype_definition
with a user-specified predicate.

?

> This would, in the following case,
>
>      generic
>         type Index is (<>);
>      package G is
>          generic package Nested is end Nested;
>      end G;
>
>      package body G is
>          package body Nested is
>             type Vec is array (Index) of Integer;
>          end Nested;
>      end G;
>
> mean that no runtime check would fail if G were instantiated with a
> "predicated" subtype, but that an instance of G.Nested would fail the
> AI's check.

I guess that's best.  Then the compiler needn't walk Instance_Of_G.Nested for
this check.

> The advantage of treating generics uniformly is that it eliminates the
> need to even mention "sprouted" generics; the rule for this odd corner
> case just falls out as a consequence of the more general rule.
>
> I only feel strongly that the rule ought to be well-defined; I don't
> much care which answer we pick.

I agree with that.

> P.S. Am I now done with my homework, or is more explaining needed?

See above.  ;-)

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

From: Steve Baird
Date: Monday, May 10, 2010  4:33 PM

...
> You mean, like this:
>
> The elaboration of the declaration or body of an instance of a generic
> unit raises Program_Error if any of the following occurs within that
> declaration or body, but not further nested within a generic unit:
> an index subtype, discrete_range of an index_constraint or slice, or a
> discrete_subtype_definition with a user-specified predicate.
>
> ?

Looks good to me.

>> P.S. Am I now done with my homework, or is more explaining needed?
>
> See above.  ;-)

I'll take that as a "yes".

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

From: Robert Dewar
Date: Sunday, September 26, 2010  5:38 AM

It seems plain horrible that 'First and 'Last are legal for a discrete type with
a predicate so that as noted in the RM

     S'First in S

can be False.

This is language lawyer sophistry in my opinion.
What possible use are 'First and 'Last if they give blatantly wrong results? If
you don't allow

     for J in S loop ...

it is plain silly and dangerous to allow

     for J in S'First .. S'Last ...

If I have to, I will implement this as described, but for sure I will generate
loud warnings with no special way to suppress them for these obviously wrong
usages.

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

From: Tucker Taft
Date: Sunday, September 26, 2010  10:25 AM

We had some serious battles about this one.
You should read the ARG minutes in any case.
It might shed some light on the deliberations.
I agree the final answer is not terribly satisfying.
We may need to have another go-round on what to do with these guys with relation
to 'first, 'last, use in case statements, etc. It is not a simple problem...

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

From: Robert Dewar
Date: Sunday, September 26, 2010  10:45 AM

For me, I would like to see ONE example where it is useful to apply First or
Last to such a type.

Anyway, for now I think I will consider it illegal (that may be the best way to
dig up such an example, and I don't count some arbitrary ACATS example as
meeting that requirement).

What bugs me here, is that for me, the original requirement was non-contiguous
enumeration types.

I get told, oh, this is a subset of the amazing predicate feature

OK, don't need the amazing predicate feature, but if it will do what I want,
fine.

But then I discover it doesn't do what I want :-(

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

From: Robert Dewar
Date: Sunday, September 26, 2010  5:43 AM

I am having trouble seeing what rule prohibits discrete subtype names as case
choices if there is a predicate present.

If this is allowed and ignores the predicate, that's plain horrible, even worse
than allowing First/Last.

If it is not allowed at all, that's plain horrible, since it means predicates
are useless for (to me) their main purpose, static non-contiguous enum subtypes.

If either of these is the case, I would be inclined to implement (under the GNAT
language extension switch (*) if necessary), a recognition of a predicate as
static if it is a membership test of the form

    typename IN ...

where ... are all static choices (and of course can use the extended syntax), so
that

    type R is (R, G, Y, O, B);

    subtype RBG is R with
      RBG in R .. G | B;

would define RBG as having a static predicate, and thus be usable in case
statements.

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

From: Robert Dewar
Date: Sunday, September 26, 2010  5:47 AM

what does it mean for two subtypes to have the "same" predicate.

Are these the same

     with Predicate => X = A or else X = B;

     with Predicate => X = B or else X = A;

answer, I hope, of course not, though if you introduce the notion of static
predicates, then you have a clear notion of the set of values and hence of the
idea of a predicate's effect and you can compare effects.

Are these the same

     with Predicate => (X.test);

     with Predicate => test;

where X is the package containing test?

i.e. is there some notion of conformance checking. If so, what about inherited
predicates?

I really think we should say that two subtypes do not match if either has a
predicate unless they are the same subtype.

Perhaps the rules do say that, but I don't read them that way clearly.

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

From: Randy Brukardt
Date: Monday, September 27, 2010  3:21 PM

...
> What bugs me here, is that for me, the original requirement was
> non-contiguous enumeration types.
>
> I get told, oh, this is a subset of the amazing predicate feature
>
> OK, don't need the amazing predicate feature, but if it will do what I
> want, fine.
>
> But then I discover it doesn't do what I want :-(

This is not really true. What you want is set constraints (see AI05-0153-2 for a
proposal). I created the predicate AI to show how they don't really work so as
to justify the more complex features proposed in AI05-0153-2. However, not all
of the ARG seems to understand this and the predicates were what went forward.

The predicates cannot do everything that a set constraint can (in particular, a
predicate cannot be static, so it can't be used in a case statement, while a set
constraint is *required* to be static, so it of course can be). It's not
possible in general to have static predicates (there was some e-mail in the past
on why). If you need staticness, you need set constraints, period.

Also note that 'First and 'Last have to be defined (they don't have to be usable
of course) because the model of values for Ada subtypes is based on the 'First
and 'Last values. It isn't possible to invent some alternative that doesn't use
'First and 'Last since the predicate expression can be arbitrary and could have
side-effects and other nasties. Set constraints define 'First and 'Last for this
reason (but these are defined so that 'First and 'Last always belong to the set
unless the set is null). It probably would be possible to have a separate value
model for set constraints but it didn't seem worth it.

I think you need to take a really serious look at the set constraint proposal to
see if that better meets your needs; if so, you need to tell us before it is too
late for this go-round.

P.S. Can you tell that I am not happy with the predicate proposal, either??
;-)

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

From: Robert Dewar
Date: Monday, September 27, 2010  5:54 PM

> The predicates cannot do everything that a set constraint can (in
> particular, a predicate cannot be static, so it can't be used in a
> case statement, while a set constraint is *required* to be static, so
> it of course can be). It's not possible in general to have static
> predicates (there was some e-mail in the past on why). If you need
> staticness, you need set constraints, period.

Actually it is just fine to have static predicates. What I implemented was that
a predicate is static if it is stand alone (does not inherit anything), and is
of the form of a set membership, where all choices are static. Then static
predicates are allowed in case statements, but not non-static predicates. The
*ONLY* use of the staticness in predicates is wrt case statements, so I don't
need "static predicates in general", just this one case.

> Also note that 'First and 'Last have to be defined (they don't have to
> be usable of course) because the model of values for Ada subtypes is
> based on the 'First and 'Last values. It isn't possible to invent some
> alternative that doesn't use 'First and 'Last since the predicate
> expression can be arbitrary and could have side-effects and other nasties.

Who cares about side effects in predicate expressions, I just don't see your
point here at all.

> Set constraints
> define 'First and 'Last for this reason (but these are defined so that
> 'First and 'Last always belong to the set unless the set is null). It
> probably would be possible to have a separate value model for set
> constraints but it didn't seem worth it.

The point is that you should simply not be allowed to reference 'First and 'Last
attributes, if you need to define them for some descriptive reason (I don't see
it), fine, but you should not allow explicit references.

> I think you need to take a really serious look at the set constraint
> proposal to see if that better meets your needs; if so, you need to
> tell us before it is too late for this go-round.

I looked, I *MUCH* prefer the predicate proposal with a bit of tweaking, to
eliminate First/Last and to allow the limited form

> P.S. Can you tell that I am not happy with the predicate proposal, either??
> ;-)

But your alternative is much too limiting, and I don't think you should use my
suggestions for improving the predicate proposal as an excuse for trying to push
the set constraint proposal, which I think is dead at this stage for lack of
support (you certainly can't get support from me :-))

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

From: Tucker Taft
Date: Monday, September 27, 2010  3:56 PM

> ... Also note that 'First and 'Last have to be defined (they don't
> have to be usable of course) because the model of values for Ada
> subtypes is based on the 'First and 'Last values...

One possible way to "solve" the S'First and S'Last problem is to define them as:

    S'(S'First) and S'(S'Last)

That is, you apply the predicate check to the value, and Constraint_Error is
raised if they don't satisfy it.

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

From: Robert Dewar
Date: Monday, September 27, 2010  6:12 PM

I suppose, I would just not allow explicit use of them in a program, rather than
give them useless definitions! If you want S'Base'First and S'Base'Last, you can
reference them, if you want the strange effect Tuck suggests you can write

    S'(S'Base'First)

:-)

I don't mind if I have to implement them, I will just generate a warning that
they are useless and almost certainly represent bugs in the program.

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

From: Bob Duff
Date: Monday, September 27, 2010  6:32 PM

> One possible way to "solve" the S'First and S'Last problem is to
> define them as:
>
>     S'(S'First) and S'(S'Last)
>
> That is, you apply the predicate check to the value, and
> Constraint_Error is raised if they don't satisfy it.

That's a reasonable idea.

I still think I prefer Robert's idea: define "staticness"
somehow, and disallow direct references to 'First, 'Last and 'Range.  Or, we
could allow 'First only in the static case, and make it mean "the smallest value
that obeys the predicate" (taking care of the case where the range is empty, or
no value obeys the predicate).

The one case where disallowing 'First won't quite work is for a generic formal
subtype -- we don't know if it has a predicate.  That's OK, we can do the usual
kludge that preserves the letter of the "contract model" law, while violating
the spirit, as usual.

Randy is correct that the CONCEPT of 'First and 'Last need to be there, because
we're going to be checking values against that range (in addition to checking
the predicate).  But Robert's idea of outlawing references to those attributes
makes good sense -- it's quite similar to the fact that you can't say
"array(subtype_with_predicate) of ...".

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

From: Robert Dewar
Date: Monday, September 27, 2010  6:41 PM

> The one case where disallowing 'First won't quite work is for a
> generic formal subtype -- we don't know if it has a predicate.  That's
> OK, we can do the usual kludge that preserves the letter of the
> "contract model" law, while violating the spirit, as usual.

Let's remember that the idea of the generic contract model is that you write a
generic and if it compiles OK, you are guaranteed that it will work for any
allowed instantiation.

There is no point in getting so fanatic over this model that we rig up things to
compile, but the result is useless.

After all I suppose that we could say that if you specify a size clause in a
generic, it's legal even if inapplicable, and raises Program_Error at run time,
but that would NOT be useful, though it would preserve the letter of the GCM.
Instead we make the instantiation illegal, considering rep clauses to be a
special case, I don't see it as so terrible to take the same view of the
Predicate attribute :-)

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

From: Randy Brukardt
Date: Monday, September 27, 2010  6:58 PM

> After all I suppose that we could say that if you specify a size
> clause in a generic, it's legal even if inapplicable, and raises
> Program_Error at run time, but that would NOT be useful, though it
> would preserve the letter of the GCM.
> Instead we make the instantiation illegal, considering rep clauses to
> be a special case, I don't see it as so terrible to take the same view
> of the Predicate attribute :-)

We surely do not want to make instantiations of generics with subtypes including
a predicate illegal - that would make doing I/O impossible for such types. And
I'm dubious that there is any value at all to preventing the use of 'First at
all in a generic: the value has to be well-defined, so there is no semantic
problem with allowing it, only a methodological one. We don't use the
Program_Error trick for methodological checks, because it can only be annoying,
not valuable.

So I would simply allow 'First on generic formals with the appropriate
definition: if that value is not covered by the predicate, you might get
Constraint_Error, but so what? That's better than getting Program_Error even
when everything is OK.

(Honestly, I don't see any value to preventing the use of 'First. The problem is
with the range S'First .. S'Last, which isn't meaningful for an S with a
predicate. But since the range is of the base type (which has no predicate) - it
is well defined. We should have S range <anything> be illegal, though, for the
same reasons we don't allow array indexes. Not sure if we did that.)

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

From: Robert Dewar
Date: Monday, September 27, 2010  7:22 PM

> We surely do not want to make instantiations of generics with subtypes
> including a predicate illegal - that would make doing I/O impossible
> for such types. And I'm dubious that there is any value at all to
> preventing the use of 'First at all in a generic: the value has to be
> well-defined, so there is no semantic problem with allowing it, only a
> methodological one. We don't use the Program_Error trick for
> methodological checks, because it can only be annoying, not valuable.

To me this is not methodological, it is pretty fundamental, 'First is just
meaningless. I would like to see ONE program where it made sense! If you raise
Program_Error, then it gives a clear basis for a warning in the instance ("will
raise PE at run-time").

> (Honestly, I don't see any value to preventing the use of 'First. The
> problem is with the range S'First .. S'Last, which isn't meaningful
> for an S with a predicate. But since the range is of the base type
> (which has no
> predicate) - it is well defined. We should have S range<anything>  be
> illegal, though, for the same reasons we don't allow array indexes.
> Not sure if we did that.)

Just because something is defined does not mean it is reasonable to include it
in the language. But as I say, I can settle for a loud warning that you have
written something meaningless.

I don't see making S'First be equal to S'Base'First, if the latter is what you
want. I do understand the unease at the generic case ... and for SURE you don't
want to make instantiations of generics with subtypes including a predicate
illegal, although if they DO use 'First and 'Last they most likely don't work
right for such an instantiation, would be interesting to see counterexamples

Anyway, 'First and 'Last are a minor annoyance, the business of using limited
static forms in case statements is much more important.

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

From: Jean-Pierre Rosen
Date: Tuesday, September 28, 2010  12:58 AM

Hmmm... don't we have a similar problem with S'Succ and S'Pred?

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

From: Tucker Taft
Date: Tuesday, September 28, 2010  9:31 AM

S'Succ, S'Pred, S'Image, S'Value, S'Pos, S'Val, etc are all based on the *type*
not the *subtype*.  So they are unaffected by the whole subtype predicate
business.

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

From: Robert Dewar
Date: Tuesday, September 28, 2010  9:58 AM

I have always found that surprising, so have many users, who expect e.g. a
constraint error of S'Succ (S'Last). Given these surprises, junk behavior of
S'First and S'Last is of the same kind. So let's just make

S'First = S'Base'First if there is a predicate and same for S'Last

then I will give a warning for ANY of these attributes applied to a subtype with
predicates.

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

From: Tucker Taft
Date: Tuesday, September 28, 2010  10:15 AM

I don't understand the benefit of this.  It seems even more misleading:

    subtype Positive_Even is Positive
      with Predicate => Positive_Even mod 2 = 0;

    Positive_Even'First = -2billion?

I would rather disallow use of 'First/'Last, raising Program_Error in a generic.

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

From: Tucker Taft
Date: Tuesday, September 28, 2010  10:18 AM

... Or require that S'First and S'Last satisfy the predicate.  The argument that
that is more work for the user seems to be outweighed by all the complexity we
are facing with the possibility that S'First not in S.

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

From: Robert Dewar
Date: Tuesday, September 28, 2010  10:31 AM

> I don't understand the benefit of this.  It seems even more
> misleading:
>
>      subtype Positive_Even is Positive
>        with Predicate =>  Positive_Even mod 2 = 0;
>
>      Positive_Even'First = -2billion?

Doesn't seem any worse than

        Positive'Pred (0) = -1?
>
> I would rather disallow use of 'First/'Last, raising Program_Error in
> a generic.

I officially don't care, but don't want the bother about this minor issue to be
used by Randy as a reason to try to undermine the proposal :-) :-)

why is X'First any worse than a "non-working" X'Succ.
Yes I know the misbehavior of Succ is documented, but in my experience it comes
as a surprise to nearly all Ada programmers, and now this misbehavior is much
more apparent when you have predicates.

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

From: Robert Dewar
Date: Tuesday, September 28, 2010  10:31 AM

> ... Or require that S'First and S'Last satisfy the predicate.  The
> argument that that is more work for the user seems to be outweighed by
> all the complexity we are facing with the possibility that S'First not
> in S.

Again, why go this far with First and Last when all the other predicates have
similar surprising behavior.

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

From: Robert Dewar
Date: Tuesday, September 28, 2010  11:07 AM

(running into interesting things implementing stuff :-))

Consider

package q is
     type r is new integer;
     subtype s is r;
     type v is new s range 1 .. 10;
     vv : v;
     for s'value_size use 40;
     for v'value_size use 48;
end q;

Does the type declaration for v freeze s?

In GNAT the answer is no, is this right?

>      1. package q is
>      2.    type r is new integer;
>      3.    subtype s is r;
>      4.    type v is new s range 1 .. 10;
>      5.    vv : v;
>            |
>         >>> warning: no more representation items for type
>             "v" defined at line 4
>
>      6.    for s'value_size use 40;
>      7.    for v'value_size use 48;
>            |
>         >>> representation item appears too late
>
>      8. end q;

As you see v is frozen but s is not.

Now if this is a GNAT bug, it needs fixing

If it is NOT a GNAT bug, then I point out that if s has a predicate, we MUST
have v freeze s, because v inherits a predicate which could be used before s is
frozen, and that can't be right (note that the predicate expression of the
predicate for s is analyzed at the freeze point of s, which cannot be after the
freeze point for v!)

Are there other cases where the presence of predicates affects freezing rules
(or are there any cases if the above is a GNAT bug).

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

From: Bob Duff
Date: Tuesday, September 28, 2010  11:09 AM

> ... Or require that S'First and S'Last satisfy the predicate.

You mean your earlier suggestion, where S'First does the check?  I hope you're
not suggesting to do the check on the subtype decl, because I think we want to
allow:

    subtype Even is Natural with Predicate => Even mod 2 = 0;

without worrying about whether Natural'Last is even.

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

From: Bob Duff
Date: Tuesday, September 28, 2010  11:13 AM

> > I don't understand the benefit of this.  It seems even more
> > misleading:
> >
> >      subtype Positive_Even is Positive
> >        with Predicate =>  Positive_Even mod 2 = 0;
> >
> >      Positive_Even'First = -2billion?
>
> Doesn't seem any worse than
>
>         Positive'Pred (0) = -1?

It seems much worse to me.

Failing to raise an exception is a surprise, but much less dangerous than
returning a wrong answer.  I mean, Pred(0) really is -1.

And suppose you started with:

    subtype S1 is something range 1..100;
    subtype S2 is S1;

Then adding a predicate to S2 changes the value of S2'First?
Better to make all occurrences of S2'First illegal.

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

From: Robert Dewar
Date: Tuesday, September 28, 2010  11:20 AM

OK, I am convinced, I withdraw my idiotic suggestion about 'Base'First :-)

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

From: Tucker Taft
Date: Tuesday, September 28, 2010  11:24 AM

> You mean your earlier suggestion, where S'First does the check?  I
> hope you're not suggesting to do the check on the subtype decl,
> because I think we want to allow:
>
>      subtype Even is Natural with Predicate =>  Even mod 2 = 0;
>
> without worrying about whether Natural'Last is even.

I *am* suggesting that the programmer should do the extra work when they define
a subtype with a predicate to make sure that 'First and 'Last satisfy the
predicate.  This seems better than all the other problems we are now discussing.
There will be relatively few subtype declarations with predicates of the above
form, I predict, and making the definer of the subtype face up to the issue of
S'First and S'Last at that point seems better than having a random user of the
subtype need to worry about it.

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

From: Robert Dewar
Date: Tuesday, September 28, 2010  11:47 AM

> I *am* suggesting that the programmer should do the extra work when
> they define a subtype with a predicate to make sure that 'First and
> 'Last satisfy the predicate.

AARGH! That's horrible

That means if I have

    type X is (giant list)

    subtype S is X with
      predicate => S in (J, K, L);

you expect the programmer to go the trouble of writing a range after finding out
the minimum and maximum among J, K, L, just so that 'First and 'Last sort of
work?

I find that an absurd amount of extra work, and also highly unmaintainable, this
means that if you change the order of literals in X, you can invalidate subtype
declarations. the whole idea of subtype declarations is to free you of such
considerations.

And what about

    subtype S is Integer with
       Predicate => Some_Very_Complex_Predicate (S);

Now I am expected to run a program to find out the minimum and maximum integer
values that satisfy this predicate (it may be out of the question to do by
hand). And what if Some_Very_Complex_Predicate depends on the day of the month?
Am I supposed to rewrite the program every day.

I *MUST* be misunderstanding this proposal!

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

From: Bob Duff
Date: Tuesday, September 28, 2010  12:15 PM

> And suppose you started with:
>
>     subtype S1 is something range 1..100;
>     subtype S2 is S1;

If somebody somewhere says "for X in S2'First .. S2'Last - 1 loop", then adding
a predicate to S2 will introduce a bug.  The more I think about it, the more I
like Robert's idea of outlawing 'First on subtypes with predicates, so if you
add a predicate to S2, such things will be detected at compile time.

We already decided to outlaw "for X in S2 loop", if S2 has a predicate.  You
shouldn't be allowed to sneak around that rule by using attributes.

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

From: Bob Duff
Date: Tuesday, September 28, 2010  12:11 PM

> AARGH! That's horrible
>
> That means if I have
>
>     type X is (giant list)
>
>     subtype S is X with
>       predicate => S in (J, K, L);

Right, we need to keep in mind that (as Robert keeps saying) enumeration
[sub]types are the main uses of predicates. And (as I keep saying), types
related to enumeration types (e.g. a predicate on a discriminant that is of enum
type).

How about:

   1. Define staticness of predicates as Robert suggested
      earlier.

   2. Subtype with predicate is allowed in a case 'when',
      only if static.

   3. 'First &c not allowed (or raise exc in generic)
      if there's a non-static predicate.

   4. If the predicate is static, 'First returns the smallest
      value for which the predicate is True; if there is none,
      then <something>.  This can be calculated easily at
      compile time by sorting the values.

Actually, never mind (4).  'First isn't really useful when you have predicates
-- just outlaw it. That seems simpler than Tuck's most recent proposal(s).

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

From: Robert Dewar
Date: Tuesday, September 28, 2010  12:39 PM

and MUCH better, I would skip 4, not worth the effort

What does &c mean, what does it include, just 'Last and 'Range?

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

From: Bob Duff
Date: Tuesday, September 28, 2010  12:55 PM

Yes.  I think Pred and Image and so on can be ignored with respect to predicates
-- they are type based (possibly confusing, but we're not changing that fact).

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

From: Robert Dewar
Date: Tuesday, September 28, 2010  1:02 PM

Right, I agree, I think I may output a warning!

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

From: Robert Dewar
Date: Tuesday, September 28, 2010  12:40 PM

...
> Right, we need to keep in mind that (as Robert keeps saying)
> enumeration [sub]types are the main uses of predicates.

Well if you say that too loudly, Randy will demand that we go back to the
enumeration set constraint proposal :-)

And for the record, Bob has convinced me that the more general form of
predicates is valuable!

But for sure enumeration subtypes must work, and for that we do need the notion
of staticness. The staticness would presumably affect two things

      static value in static subtype

would be static

and it would be allowed in a case statement

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

From: Tucker Taft
Date: Tuesday, September 28, 2010  1:30 PM

I'm wondering whether we should disallow passing a scalar subtype with a
predicate to a generic formal scalar subtype unless the formal scalar subtype is
declared as:

    type T is (<>)
      with Predicate;

or

    type T is range <>
      with Predicate;

etc.  Such a formal would allow a subtype with or without a predicate.

This would eliminate the need to ever raise Program_Error on T'Last in the
instance, as you would know at compile-time whether the subtype might have a
predicate. The various Text_IO generic subpackages would presumably specify
"with Predicate".

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

From: Steve Baird
Date: Tuesday, September 28, 2010  1:54 PM

Would/should this generalize to allow specifying that a formal limited type has
available streaming attributes?

Maybe this is a just a solution in search of a problem, but it did come to mind
when I saw your suggestion.

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

From: Robert Dewar
Date: Tuesday, September 28, 2010   2:03 PM

This seems fine to me, more implementation work, but not that significant!

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

From: Randy Brukardt
Date: Thursday, September 30, 2010  10:45 PM

...
> But for sure enumeration subtypes must work, and for that we do need
> the notion of staticness. The staticness would presumably affect two
> things
>
>       static value in static subtype
>
> would be static
>
> and it would be allowed in a case statement

Despite previous statements, I don't care that much about the syntax. What I do
care about is the effort needed to get the semantics well-defined.

As I'm sure you know, there is no correlation between the effort needed to
describe something in Standard wording, the implementation effort, and the "big
picture" description.

What you are asking for in a "static predicate" is really for the semantics of
set constraints using the predicate syntax. This matters because a predicate
does not change set of values that "belongs" to a subtype, while a constraint
does change this set. Predicates are just an additional runtime check on the
value, they don't change the value set or any other semantics.

In order for the case statement to work, we need to change the set of values
that "belongs" to a subtype, or some other change to the semantics of the
subtype. (Given that the currrent wording in 3.8.1(9-13) includes subtypes in
"discrete_range", the easiest fix is to change the rules for "belongs to a
discrete_range" to take into account set values. That's what I did for the set
constraint. But given the bizarreness of calling a static set predicate a
"discrete_range" when 'First and 'Last aren't even being allowed to be
referenced explicitly, maybe we need to do more major surgery).

Set constraints also allow iteration (unlike non-static predicates). I think
this is important, as it is common to use a loop to try all possible values when
unit testing. For a predicate (not allowed to directly iterate), the intent was
that you would write something like:

          for P in S'First .. S'Last loop
              if P in S then -- Call tested subprogram
              -- else not in set, ignore.
              end if;
          end loop;

However, since you don't want to allow the use of 'First and 'Last here, you'll
have to use S'Base'First and S'Base'Last instead. (That will be a disaster if
the base type is 64-bit integers; indeed, this is a reason that we don't allow
iteration of subtypes with predicate as the compiler would have to generate some
similar thing for many predicates, and that is not going to work well for 64-bit
types.)

Now, for a "static predicate", it is necessary that the compiler to be able to
enumerate all of the members of the set (else it is impossible to check the
coverage for case statements). So the reason to disallow iteration does not
exist. Also note that since we're already allowing more uses for "static
predicates" than normal "predicates", there really is no problem with allowing
iteration as well as case statements.

Also note that a "static predicate" effectively has the value of an aspect
changing legality of a program. That is something that we've tried to avoid in
the past (but not always). In this case, it is even worse, as the *form* of the
expression used in the predicate is significant. As I understand it, you are
proposing that
      subtype S is T with predicate => S in (1 .. 3 | 8 .. 10); is a static predicate, while
      subtype R is T with predicate => S in 1 .. 3 or S in 8 .. 10; is not. This is nasty given that the vertical bar might be read "or" in this case. (Note that we can change the rules around in various ways to eliminate cases exactly like this one, but w
e'll always be able to have such cases by just making the expression more complex.) This is a (somewhat minor) maintenance hazard.

With all of these factors included, I still think predicates and whatever you
call this (static predicates; set constraints; or muggywomps ;-) are really
different (but possibly related) features. Probably there ought to be some
syntactic indication that you want a static predicate, such that if the
expression doesn't have the appropriate magic form, the subtype immediately gets
rejected rather than some use of the subtype in some other unit far away. Such
an indication doesn't need to be as radical as the set constraint syntax
(indeed, it doesn't make sense to do that if we are going to have predicates
anyway).

One easy option would be to have two related aspects, "predicate" and
"static_predicate" (or, as I would prefer, "set_predicate"); with
"set_predicate" defining a predicate *and* a bunch of additional capabilities
(case statements and for loops), but requiring some restricted form of the
predicate expression.

One would presume to extend Tucker's "with predicate" idea for generic formals
to handle the different kinds as well, reducing the need for Program_Error.

P.S. Is anyone planning to propose an alternative AI that allows for static
predicates? (I think we want the version that we mostly agreed upon in Valencia
as a baseline without these bells and whistles.)

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

From: Robert Dewar
Date: Friday, October 1, 2010  7:12 AM

...
> As I'm sure you know, there is no correlation between the effort
> needed to describe something in Standard wording, the implementation
> effort, and the "big picture" description.

*No* correlation is too strong, but often it is the case that a formal
description can be simple, and the implementation hard, or vice versa, and
sometimes the formal description can be hard and the informal one simple, or the
other way round (own variables in algol-60 are a good example, the formal
description is trivial, but the informal description and implementation are a
mess).

> In order for the case statement to work, we need to change the set of
> values that "belongs" to a subtype, or some other change to the
> semantics of the subtype. (Given that the currrent wording in
> 3.8.1(9-13) includes subtypes in "discrete_range", the easiest fix is
> to change the rules for "belongs to a discrete_range" to take into
> account set values. That's what I did for the set constraint. But
> given the bizarreness of calling a static set predicate a
> "discrete_range" when 'First and 'Last aren't even being allowed to be
> referenced explicitly, maybe we need to do more major surgery).

Well remember that the fact that we don't allow explicit 'First and 'Last has
nothing to do with the formal description, you can perfectly well maintain the
existence of these as a formal description device.

After all suppose you decide after all this discussing to allow 'First and 'Last
after all, what's the impact on the implementation. Minimal, you make the
message a warning message, and remove the raise of PE for generics. What's the
impact on the user? Not much, most users pay attention to warnings much as they
do to errors, in fact a lot of users use -gnatwe in gnat to convert all warnings
to errors anyway.

> Set constraints also allow iteration (unlike non-static predicates). I
> think this is important, as it is common to use a loop to try all
> possible values when unit testing. For a predicate (not allowed to
> directly iterate), the intent was that you would write something like:
>
>            for P in S'First .. S'Last loop
>                if P in S then -- Call tested subprogram
>                -- else not in set, ignore.
>                end if;
>            end loop;

I am dubious about the value of this, I have never ever needed to iterate
through such a beast, but I understand the point you are making about unit
testing.

> However, since you don't want to allow the use of 'First and 'Last
> here, you'll have to use S'Base'First and S'Base'Last instead. (That
> will be a disaster if the base type is 64-bit integers; indeed, this
> is a reason that we don't allow iteration of subtypes with predicate
> as the compiler would have to generate some similar thing for many
> predicates, and that is not going to work well for 64-bit types.)

There is no difficulty in using 'First and 'Last in the description of the loop.
And given that we have an "as if" implementation rule always, the formal
description could use 'Base'First and 'Base'Last with the understanding that an
implementation might do things more efficiently.

(there is nothing to stop implementations from using a recursive definition of
multiplication using addition, but it's not a very good idea, but if it
simplified the description we could describe multiplication that way, and if
that seems far fetched, just remember that the definition of exponentiation is
EXACTLY like that, the description uses repeated multiplication, but the
implementation presumably and hopefully does not)

Indeed I would say the better approach for implementation in the static case
would be to build a static array of the values in the subtype, and then iterate
through that array. This is the same "as if" result. Iterating from 'First to
'Last can still be quite painful if you have a giant enumeration type, and a
subtype with a couple of values at big distance from one another.

> Now, for a "static predicate", it is necessary that the compiler to be
> able to enumerate all of the members of the set (else it is impossible
> to check the coverage for case statements). So the reason to disallow
> iteration does not exist. Also note that since we're already allowing
> more uses for "static predicates" than normal "predicates", there
> really is no problem with allowing iteration as well as case statements.

I tend to agree, not that hard to allow loops in this case, so why not?
Note by the way that "enumerating all members of the set" is not something that
is needed in the case statement, since once you have verified that a set
predicate is static, you just plop it into the case statement, and normal
analysis does everything else.

But one minor thought, what about

     type Vowel is (A, E, I, O, U);

     subtype V is Vowel range I .. U with
       V in A or U;

do we want to allow that and eliminate A from the value set? I find that ugly. I
would either say that an explicit range makes predicate non-static, or that if
you have an explicit range it must be static and all the values in the static
predicate have to be within that range. I would go for allowing this, but
deciding that staticness requires no explicit range to be present. It's nonsense
to have a range anyway when you are defining these predicates that are really
set constraints at some informal conceptual level.

> Also note that a "static predicate" effectively has the value of an
> aspect changing legality of a program. That is something that we've
> tried to avoid in the past (but not always).

Well lots of aspects affect legality

     type X is new Integer with
       Size => 3;

is illegal

> In this case, it is even worse, as the *form* of the expression used
> in the predicate is significant. As I understand it, you are proposing
> that
>        subtype S is T with predicate =>  S in (1 .. 3 | 8 .. 10); is a
> static predicate, while
>        subtype R is T with predicate =>  S in 1 .. 3 or S in 8 .. 10;
> is not. This is nasty given that the vertical bar might be read "or"
> in this case. (Note that we can change the rules around in various
> ways to eliminate cases exactly like this one, but we'll always be
> able to have such cases by just making the expression more complex.)
> This is a (somewhat minor) maintenance hazard.

I don't really mind allowing OR (and OR ELSE?) as well as the set notation, but
was trying to keep things simple. The nice thing about the set notation is that
it is exactly what you would write in the case statement.

Note that it is already the case that the legality of a program depends on
expressions being static, and the rules of whether expressions are static are
not are as you know quite delicate!

> With all of these factors included, I still think predicates and
> whatever you call this (static predicates; set constraints; or
> muggywomps ;-) are really different (but possibly related) features.
> Probably there ought to be some syntactic indication that you want a
> static predicate, such that if the expression doesn't have the
> appropriate magic form, the subtype immediately gets rejected rather
> than some use of the subtype in some other unit far away. Such an
> indication doesn't need to be as radical as the set constraint syntax
> (indeed, it doesn't make sense to do that if we are going to have predicates anyway).

A concern

> One easy option would be to have two related aspects, "predicate" and
> "static_predicate" (or, as I would prefer, "set_predicate"); with
> "set_predicate" defining a predicate *and* a bunch of additional
> capabilities (case statements and for loops), but requiring some
> restricted form of the predicate expression.

I think I prefer to call this Set_Predicate if we go that way, because it also
emphasizes the syntactic form. On the other hand this business of delicate rules
on form of expressions affecting legality far away are common in the language
now with static expressions, so what's the big deal:

     type Wrapped_Integer is record
        Int : Integer;
     end record;
     for Wrapped_Integer'Size
       use Integer'Size;

     B : constant Integer := ... + ref to Wrapped_Integer'Size;
     C : constant Integer := ... + ref to Integer'Size;

Now B and C have identical values and will generate identical code wherever they
are used EXCEPT that you can't use B in a context requiring a static expression
(like a case statement in a remote unit).

The proposal to add a separate aspect Set_Predicate would be analogous to
getting worried about this and proposing something like

     C : static constant Integer := ...

which is not a terrible idea at all (and we sort of have this a bit with number
declarations), but we have lived without it for 20 years, is it really worth
getting worried about in the relatively minor context of predicates? I think
not.

> One would presume to extend Tucker's "with predicate" idea for generic
> formals to handle the different kinds as well, reducing the need for
> Program_Error.

Seems like an unnecessary complication to me in practice, and I think it will
just be a nuisance. Is it really worth this nuisance just to deal with the
marginal obscure case of using 'First and 'Last?

> P.S. Is anyone planning to propose an alternative AI that allows for
> static predicates? (I think we want the version that we mostly agreed
> upon in Valencia as a baseline without these bells and whistles.)

Bob is working actively on this (to catch up with the implementation effort :-))
I would include the bells and whistles from the start, we really must have the
case statements working right.

By the way, I have predicates fully working except for the staticness aspect.
For now 'Size etc are illegal (and raise PE in generics). Of course that is easy
to change later, and better at the implementation level to go from illegal to
legal than the other way round.

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

From: Bob Duff
Date: Friday, October 1, 2010  9:30 AM

> P.S. Is anyone planning to propose an alternative AI that allows for
> static predicates? (I think we want the version that we mostly agreed
> upon in Valencia as a baseline without these bells and whistles.)

I am planning to produce a new version of this AI as soon as possible.
(It's turning out to be a lot harder than I thought, surprise,
surprise.)  I would prefer it to be a new version of the same AI -- we can
always see what the old one was (that's what CVS is for).

As I've said many times, for Ada 2012 (as opposed to 95 and 2005) I'm more
concerned about implementation difficulty than wording difficulty (etc). But
since Robert is steaming ahead with the implementation, and it's turned out
not-so-hard, I should try to update the proposed RM wording.

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

From: Randy Brukardt
Date: Friday, October 1, 2010  3:46 PM

The whole idea of static predicates is a new one and not really related to the
existing proposal (it's much more related to set constraints than predicates).
The only reason we didn't approve AI05-0153-1 in Valencia was that we didn't
agree on the wording for the new restrictions on composite predicate
expressions. So I would prefer to have the existing AI reflect the decisions of
that meeting exactly; and have a separate alternative with the more complex
proposal that is now being crafted here. That way, we can see if it really is
more complex or not.

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

From: Randy Brukardt
Date: Friday, October 1, 2010  4:07 PM

...
> But one minor thought, what about
>
>      type Vowel is (A, E, I, O, U);
>
>      subtype V is Vowel range I .. U with
>        V in A or U;
>
> do we want to allow that and eliminate A from the value set?

The set constraint proposal includes a check that each value of the set belongs
to the range of the parent subtype. (It also doesn't syntactically allow the
above, of course.) So the intent there is that something like this would raise
Constraint_Error (and in this case [and most other cases], the compiler would
issue a warning that it would always do that).

That has the advantage that we don't have to worry about non-static ranges, in
that they get checked and then can be ignored (either the subtype raises
Constraint_Error, in which case no one can use it, or it is OK to treat it as
static). This is similar to what happens for ranges themselves: only the "last"
range has any semantic effect other than in the declaration of the subtype.

That way, if we have:

      subtype W is range Function_returning_I .. U;
      subtype V is W with Set_Predicate => V in (A | U);

V also raises Constraint_Error or works as if it is static.

...
> > Also note that a "static predicate" effectively has the value of an
> > aspect changing legality of a program. That is something that we've
> > tried to avoid in the past (but not always).
>
> Well lots of aspects affect legality
>
>      type X is new Integer with
>        Size => 3;
>
> is illegal

That's not what I meant; I'm talking about things other than aspects. There are
few aspects that affect the legality of anything other than other aspects.

...
> Note that it is already the case that the legality of a program
> depends on expressions being static, and the rules of whether
> expressions are static are not are as you know quite delicate!

This is definitely true. But just because Ada does something wrong currently
doesn't mean we have to repeat that in new features. (For instance, we require
matching rather than ignore the subtypes for new features in renaming.)

...
> > One easy option would be to have two related aspects, "predicate"
> > and "static_predicate" (or, as I would prefer, "set_predicate");
> > with "set_predicate" defining a predicate *and* a bunch of
> > additional capabilities (case statements and for loops), but
> > requiring some restricted form of the predicate expression.
>
> I think I prefer to call this Set_Predicate if we go that way, because
> it also emphasizes the syntactic form. On the other hand this business
> of delicate rules on form of expressions affecting legality far away
> are common in the language now with static expressions, so what's the
> big deal:
>
>      type Wrapped_Integer is record
>         Int : Integer;
>      end record;
>      for Wrapped_Integer'Size
>        use Integer'Size;
>
>      B : constant Integer := ... + ref to Wrapped_Integer'Size;
>      C : constant Integer := ... + ref to Integer'Size;
>
> Now B and C have identical values and will generate identical code
> wherever they are used EXCEPT that you can't use B in a context
> requiring a static expression (like a case statement in a remote
> unit).

Correct of course; but this particular case is a major problem (at least it has
been in my code) because it is common to want to write a representation clause
like R'Size use Component_1'Size + Component_2'Size, and this is illegal if
either of the components are composite. I usually end up using the literal value
and adding some code somewhere "if <literal> /= Component_1'Size +
Component_2'Size then raise Program_Error; end if;". This is silly at the best.

So I don't have much sympathy for using the existence of a major annoyance in
Ada to justify introducing another similar maintenance hazard.

> > One would presume to extend Tucker's "with predicate" idea for
> > generic formals to handle the different kinds as well, reducing the
> > need for Program_Error.
>
> Seems like an unnecessary complication to me in practice, and I think
> it will just be a nuisance. Is it really worth this nuisance just to
> deal with the marginal obscure case of using 'First and 'Last?

Don't know. I've been presuming that Tucker is proposing a general feature that
could be used on a variety of aspects. As Steve suggested, it could be used to
require types to have available stream attributes. If that's the case, there
isn't much additional cost to supporting one additional aspect.

OTOH, if it is just a special-case gizmo, I think I'd oppose it on the grounds
of adding more complexity to the language for little gain.

But note that the major use isn't for 'First and 'Last, it is in (dis)allowing
the use of the subtype in array indexes and slices. That's both more likely to
happen and much more important to disallow (since we've determined that it is
unimplementable in general - 'First and 'Last are just confusing).

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

From: Robert Dewar
Date: Friday, October 1, 2010  6:30 PM

> The set constraint proposal includes a check that each value of the
> set belongs to the range of the parent subtype. (It also doesn't
> syntactically allow the above, of course.) So the intent there is that
> something like this would raise Constraint_Error (and in this case
> [and most other cases], the compiler would issue a warning that it would always do that).

I think the right thing to do here is simply to say that for a predicate to be
static, it has to have a static range, and all elements of the set expression
have to be static and lie in this range, otherwise it is not static.

> That has the advantage that we don't have to worry about non-static
> ranges, in that they get checked and then can be ignored (either the
> subtype raises Constraint_Error, in which case no one can use it, or
> it is OK to treat it as static). This is similar to what happens for
> ranges themselves: only the "last" range has any semantic effect other
> than in the declaration of the subtype.

I do not like the idea of a separate aspect name for the static stuff.
It is unnecessary, adds complication, and is inconsistent with the treatment of
static expressions elsewhere in the language.

> That way, if we have:
>
>        subtype W is range Function_returning_I .. U;
>        subtype V is W with Set_Predicate =>  V in (A | U);
>
> V also raises Constraint_Error or works as if it is static.

I really think this is unnecessary

>> Well lots of aspects affect legality
>>
>>       type X is new Integer with
>>         Size =>  3;
>>
>> is illegal
>
> That's not what I meant; I'm talking about things other than aspects.
> There are few aspects that affect the legality of anything other than
> other aspects.

But there are cases, and I really think this is not a big deal, and not worth
adding more complexity of it.

> This is definitely true. But just because Ada does something wrong
> currently doesn't mean we have to repeat that in new features. (For
> instance, we require matching rather than ignore the subtypes for new
> features in
> renaming.)

I disagree this is done wrong now, and I think consistency is more important
than a dubious claim that you are inconsistenyt because the language is wrong
now.

> Correct of course; but this particular case is a major problem (at
> least it has been in my code) because it is common to want to write a
> representation clause like R'Size use Component_1'Size +
> Component_2'Size, and this is illegal if either of the components are
> composite. I usually end up using the literal value and adding some
> code somewhere "if<literal>  /= Component_1'Size + Component_2'Size
> then raise Program_Error; end if;". This is silly at the best.

I can't see this as a major problem (in 15 years, no one has complained about
this in the context of GNAT, and people complain about anything they don't like
very freely). I certainly don't find it an issue in my code. I don't find that a
common way to write a rep clause at all, on the contrary it seems to be assuming
all kinds of target dependent stuff about alignment etc.

> So I don't have much sympathy for using the existence of a major
> annoyance in Ada to justify introducing another similar maintenance hazard.

For me this does not even rise to the level of minor annoyance, let alone major
annoyance.

>>> One would presume to extend Tucker's "with predicate" idea for
>>> generic formals to handle the different kinds as well, reducing the
>>> need for Program_Error.
>>
>> Seems like an unnecessary complication to me in practice, and I think
>> it will just be a nuisance. Is it really worth this nuisance just to
>> deal with the marginal obscure case of using 'First and 'Last?

> But note that the major use isn't for 'First and 'Last, it is in
> (dis)allowing the use of the subtype in array indexes and slices.
> That's both more likely to happen and much more important to disallow
> (since we've determined that it is unimplementable in general - 'First
> and 'Last are just confusing).

I see the use of enumeration subtypes as array indexes as being pretty unusual
in the context of predicates. I don't see this again as worth worrying about.

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

From: Robert Dewar
Date: Friday, October 1, 2010  6:36 PM

> The whole idea of static predicates is a new one and not really
> related to the existing proposal (it's much more related to set
> constraints than predicates). The only reason we didn't approve
> AI05-0153-1 in Valencia was that we didn't agree on the wording for
> the new restrictions on composite predicate expressions. So I would
> prefer to have the existing AI reflect the decisions of that meeting
> exactly; and have a separate alternative with the more complex
> proposal that is now being crafted here. That way, we can see if it really is more complex or not.

I see no point in this extra level of work, we know we need the static
predicates here (for GNAT, the approval or non-approval of this feature in the
official standard will only affect the operation of the equivalent of the
-pedantic mode, so it is not a big deal). To me the proposal is useless without
the static predicates for cases, so why bother preparing or discussing a useless
proposal. Is there anyone who thinks that we do NOT need these things to work in
case statements?

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

From: Randy Brukardt
Date: Friday, October 1, 2010  10:07 PM

> > The whole idea of static predicates is a new one and not really
> > related to the existing proposal (it's much more related to set
> > constraints than predicates). The only reason we didn't approve
> > AI05-0153-1 in Valencia was that we didn't agree on the wording for
> > the new restrictions on composite predicate expressions. So I would
> > prefer to have the existing AI reflect the decisions of that meeting
> > exactly; and have a separate alternative with the more complex
> > proposal that is now being crafted here. That way, we can see if it
> > really is more complex or not.
>
> I see no point in this extra level of work,

There is no significant extra work involved in this, given that all I'm asking
is to keep the Valencia proposal around intact.

> ... we know we need
> the static predicates here (for GNAT, the approval or non-approval of
> this feature in the official standard will only affect the operation
> of the equivalent of the -pedantic mode, so it is not a big deal). To
> me the proposal is useless without the static predicates for cases, so
> why bother preparing or discussing a useless proposal. Is there anyone
> who thinks that we do NOT need these things to work in case
> statements?

The entire predicate proposal has been very controversial, just barely getting
enough support to survive. Almost everybody has seriously considered voting
against it at some point or other (including me and Bob). If the only version in
existence is one loaded down with bells and whistles, it is much more likely to
be rejected.

And yes, I do view the set constraint as a bell/whistle on this proposal. (I
realize that your mileage differs greatly on that.) The problems that I have
that I hope to solve all have to do with subtypes of a composite types, which
this added feature has little to do with. The only reason that predicates are
allowed on scalar types at all is because we would otherwise have generic
contract problems with formal private types (they cause a lot of problems where
reasonable things can't be implemented).

So while I agree with you that if set constraints are allowed in some form, they
have to work in case statements and for loops, I don't agree with you that they
have to be included in a predicate proposal.

In any case, the entire ARG will need to weigh in on this at the next meeting;
there is little point in just two of us discussing and completely disagreeing on
this point.

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

From: Robert Dewar
Date: Friday, October 1, 2010  10:19 PM

> And yes, I do view the set constraint as a bell/whistle on this
> proposal. (I realize that your mileage differs greatly on that.) The
> problems that I have that I hope to solve all have to do with subtypes
> of a composite types, which this added feature has little to do with.
> The only reason that predicates are allowed on scalar types at all is
> because we would otherwise have generic contract problems with formal
> private types (they cause a lot of problems where reasonable things can't be
> implemented).

I don't believe that for one moment, having talked to several people involved in
the diccussion. It is clear that there is considerable value in having
predicates for scalars. Indeed, virtually all the uses I see for predicates are
for scalars (though by no means are all in the static category). I do see some
utility for composites, but far less than for scalars.

> So while I agree with you that if set constraints are allowed in some
> form, they have to work in case statements and for loops, I don't
> agree with you that they have to be included in a predicate proposal.

You still are transparently following an agenda of trying to get in your set
constraint proposal, but to me that has no hope at this stage, and I think it is
a bad idea to try to cripple the predicate proposal in a continuing attempt to
push the idea of the set constraint proposal.

> In any case, the entire ARG will need to weigh in on this at the next
> meeting; there is little point in just two of us discussing and
> completely disagreeing on this point.

Well by the time the ARG discusses this, we will have some more experience with
actual use, since the Ada 2012 prevue that we will release shortly will have a
full implementation of predicates including an implementation of static
predicates. The main purpose of the static predicates is surely for case
statements, but I will try to get in the loops if I can (note you can always do
the loops yourself, but you can't do the case statements yourself without losing
the essential completeness check.

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

From: Randy Brukardt
Date: Friday, October 1, 2010  10:33 PM

...
> > That has the advantage that we don't have to worry about non-static
> > ranges, in that they get checked and then can be ignored (either the
> > subtype raises Constraint_Error, in which case no one can use it, or
> > it is OK to treat it as static). This is similar to what happens for
> > ranges themselves: only the "last" range has any semantic effect
> > other than in the declaration of the subtype.
>
> I do not like the idea of a separate aspect name for the static stuff.
> It is unnecessary, adds complication, and is inconsistent with the
> treatment of static expressions elsewhere in the language.

So what? This concept has nothing whatsoever to do with static expressions.

In particular, the expression of a predicate can never be static, because the
current instance of the subtype is never static. (How could it be? The actual
object being tested might be a variable.)

I can imagine defining this somehow in terms of static expressions, but in any
case it is a new concept. That being said, there is nothing in particular to be
consistent with.

...
> > That's not what I meant; I'm talking about things other than aspects.
> > There are few aspects that affect the legality of anything other
> > than other aspects.
>
> But there are cases, and I really think this is not a big deal, and
> not worth adding more complexity of it.

It used to be a huge deal to Pascal with the Rational compiler. Whether it would
be in this case (or whether we ought to care), I can't say. But I'm still
sensitive to those concerns.

> > This is definitely true. But just because Ada does something wrong
> > currently doesn't mean we have to repeat that in new features. (For
> > instance, we require matching rather than ignore the subtypes for
> > new features in
> > renaming.)
>
> I disagree this is done wrong now, and I think consistency is more
> important than a dubious claim that you are inconsistenyt because the
> language is wrong now.

But there is nothing here to be consistent with. The closest thing to be
consistent with here is range constraints; in order to do that you need the
entire set constraint mechanism as outlined in AI05-0153-2. I think we all agree
we are not going to do that; therefore any solution that we chose is going to be
very inconsistent with the rest of the language. That's especially true if we
insist on your "all static ancestors" rule - which is nothing like any other
subtype in the language. (BTW, such a rule would mean that you couldn't define a
set of any generic formal discrete type. Maybe not a big deal, but that to me
seems inconsistent with the rest of the language.) Predicates in general are
also very inconsistent with the rest of language in that they don't allow use as
array indexes and the like.

So I view this as an entirely new feature that pretty much has to be very
inconsistent with the rest of the language. At this point, why add in
maintenance hazards and readability problems??

> > Correct of course; but this particular case is a major problem (at
> > least it has been in my code) because it is common to want to write
> > a representation clause like R'Size use Component_1'Size +
> > Component_2'Size, and this is illegal if either of the components
> > are composite. I usually end up using the literal value and adding
> > some code somewhere "if<literal>  /= Component_1'Size +
> > Component_2'Size then raise Program_Error; end if;". This is silly at the best.
>
> I can't see this as a major problem (in 15 years, no one has
> complained about this in the context of GNAT, and people complain
> about anything they don't like very freely). I certainly don't find it
> an issue in my code. I don't find that a common way to write a rep
> clause at all, on the contrary it seems to be assuming all kinds of
> target dependent stuff about alignment etc.

It's probably more correct that it is assuming all kinds of
*compiler-dependent* stuff, in that this works fine on all existing (and the
uncompleted ones as well) Janus/Ada compilers. I can easily believe that other
compilers do something different. I *know* that I have tried to write code like
this many times (including in Claw) and it gets rejected. I'm quite surprised
that you've never had anyone complain about it, given I've seen others gripe
about this problem on comp.lang.ada. I suspect, though, that most everybody
gripes and then replaces the code by something less portable; it's not the sort
of thing that a compiler writer can do anything about.

...
> > But note that the major use isn't for 'First and 'Last, it is in
> > (dis)allowing the use of the subtype in array indexes and slices.
> > That's both more likely to happen and much more importantto disallow
> > (since we've determined that it is unimplementable in general - 'First
> > and 'Last are just confusing).
>
> I see the use of enumeration subtypes as array indexes as being pretty
> unusual in the context of predicates. I don't see this again as worth
> worrying about.

It *has* to be worried about. It's not possible to implement slices in general
for such subtypes (consider an array of limited components; there is no way to
"close up the gaps" in the slice, and distributed overhead to handle such cases
in unacceptable - remember that the slice can be passed as a parameter to some
other routine). For direct uses of such subtypes, the uses are statically
illegal in slices, array indexing, and the like. The problem is, what if someone
uses a formal discrete subtype in a slice? If it has a predicate, it has to be
disallowed somehow. But using an assume-the-worst rule would be wildly
incompatible (that would disallow all such slices in generic bodies). Thus we
proposed a runtime check that raises Program_Error; all Tucker is proposing is
to add some additional syntax to allow us to use the assume-the-worst rule
without introducing compatibility problems.

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

From: Randy Brukardt
Date: Friday, October 1, 2010  11:53 PM

...
> > So while I agree with you that if set constraints are allowed in
> > some form, they have to work in case statements and for loops, I
> > don't agree with you that they have to be included in a predicate
> proposal.
>
> You still are transparently following an agenda of trying to get in
> your set constraint proposal, but to me that has no hope at this
> stage, and I think it is a bad idea to try to cripple the predicate
> proposal in a continuing attempt to push the idea of the set
> constraint proposal.

I'm certainly not doing that on purpose. But what you are asking to be added to
the language is semantically very similar to the set constraint proposal. I
certainly don't care that much about the exact name it is given or the exact
syntax. But it is going to need to have essentially the same set of rules as was
given in that old proposal; some of those can be shared with "regular"
predicates, but not all can (because of the added capabilities, of course). It
isn't possible to add "static predicates" to the language and have them work in
case statements and for loops without adding a lot of new text and probably a
few new concepts as well.

If that is "trying to get in your set constraint proposal", I suppose then I am
guilty. But I don't believe that there is any sane way to add "set predicates"
or "static predicates" or whatever you want to call it to the language without
adopting many of the rules from the "set constraint" proposal. I had long since
given up on set constraints, except that you keep insisting that we have to have
them (in a different syntax, but with essentially the same semantics as
originally proposed). I'm not "trying to get" anything in here -- I can surely
live without this by any name. But if we do have it, then we're going to be
adding some form of set constraints (although surely not by that name) -- no
other result is possible. That's simply a pragmatic judgment, not some attempt
at a hidden agenda.

What I *can't* live with is adding maintenance and readability problems to the
language in the guise of consistency, particularly when this is an entirely new
feature that is already very inconsistent with existing subtypes. (And some of
the rules you are proposing makes it more inconsistent with existing subtypes.)
Perhaps I'm alone on that, perhaps not; we'll need to get input from other ARG
members to figure that out. Nothing that you or I say here is going to be the
last word on this subject -- and I'm not sure that further discussion here is
going to add anything.

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

From: Tucker Taft
Date: Saturday, October 2, 2010  2:25 PM

I suspect that some people are more friendly to the (potentially-static) subtype
predicate approach in that it piggy backs on the aspect specification syntax.
You are almost certainly right that getting static predicates to work may
require many of the same things that the set constraints would have required,
but avoiding adding a new constraint syntax is a significant advantage for
predicates, at least to some people (including me, for what it is worth), even
if it ends up requiring more words in the RM. Somehow additional syntax "weighs"
a lot more than additional RM words for most people.

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

From: Robert Dewar
Date: Saturday, October 2, 2010  3:19 PM

Absolutely true! And indeed having several different syntaxes for what is to the
programmer essentially the same thing is a mistake, even if in the language
lawyer realm it makes things "cleaner". to me even distinguishing between
invariants and predicates is a big mistake which will cause a lot of confusion.
I would have used the same syntax for both, and simply made whether or not they
were applied to private types be the distinguishing factor (and having the
inside/outside semantics for predicates on private types, which makes perfect
sense given that private types look quite different inside, and then you would
put a predicate/whatever on the full type if you want checking inside.

Anyway, I guess the unfortunate duplication of concepts in invariants/predicates
is a done deal, too bad, but let's not exacerbate this by trying to have
different syntax for static and non-static predicates.

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

From: Robert Dewar
Date: Saturday, October 2, 2010  3:47 PM

In this note, I want to specifically address the issue of looping through
predicated types with a syntax like

     for J in subtype loop

It seems like we have three possibilities

a) allow it always, the official semantics could be

    for J in subtype'base'range loop
       if predicate(J) then
           body
       end if
    end loop

    with specific implementation permission allowing optimization.

    probably general implementation permission apply to predicates
    and invariants in general should be something like

    If the compiler can determine the returned result of evaluating
    a predicate at compile time, or if the result is not needed,
    then the evaluation of the predicate or invariant expression
    can be omitted (even if there are side effects).

b) forbid it entirely

c) allow it only for static predicates

I can live with b, since I don't think it's terribly useful, but on the other
hand there are some who think it IS really useful (Randy in particular), and it
does help ease generic contract concerns (one less case for warnings/exception
when an error message wold violate the generic contract model).

I don't really like c), since it is weird to have staticness control the
validity of a loop (case statements are a different matter, there we are used to
staticness being an issue).

Furthermore, defining the semantics so it just covers case
c) is a peculiar excercise. The easiest way of defining the semantics is with
the bogus loop that you would use if you choose approach a).

So I would go with a), it's no harder to implement than c.

Note that an efficient implementation of c) is not easy!
I can think of three approaches

a) do the loop through subtype'first .. subtype'last. This can obviously be
   disastrously inefficient in time for a case like

     subtype Integer_Extremes is Integer with
       predicate => Integer_Extremes in Integer'First | Integer'Last;

b) build an array of valid values and iterate through that. This can be
   disastrously inefficient in space in a case like:

     subtype Integer_Non_Zero is Integer with
       predicate => Integer_Non_Zero in
         Integer'First .. -1 | +1 .. Integer'Last;

c) use a case statement like

     subtype Consonants is Character with
       predicate => Consonants in
         'B' .. 'D' |
         'F' .. 'H' |
         'J' .. 'N' |
         'P' .. 'T' |
         'V' .. 'Z';

     for J in Consonants loop
        body
     end loop

   translates to

     declare
        J : Character := 'B';
     begin
        loop
           body

           case J  is
              when 'D' => J := 'F';
              when 'H' => J := 'J';
              when 'N' => J := 'P';
              when 'T' => J := 'V';
              when 'Z' => exit;
              when others
                 J := Character'Succ (J);
           end case;
        end loop;
     end;

A bit complicated, but takes advantage of the (presumably complex and efficient
and there already) code for generating efficient code for case statements.

One final thought, it is odd to allow

    subtype R is Character with
       predicate => R in 'A' | 'F' | 'W' .. 'Y';

    for J in R loop ...

and to disallow

    for J in 'A' | 'F' | 'W' .. 'Y' loop ...

Obviously it is no harder to implement one than the other, so perhaps we should
give some thought to allowing the more general loop format?
        when Z => I := Q;

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

From: Robert Dewar
Date: Saturday, October 2, 2010  3:50 PM

Randy notes that the predicate expression is not static since it is of the form

    type in bla bla

yes, well of course type is not static but that's language lawyerism running
amok if you make too much of it, and certainly claiming this makes things
completely different from normal static expressions is unnecessary at an
informal level.

At an informal level, if you just said that a predicate to be static must be of
the form of a set expression and must be static to use in a case statement, then
informally anyone would know what you meant. Yes, you need to work harder in the
RM to describe this, but simplicity of description in the RM is a very
unimportant criterion in the total scheme of things, and we certainly don't want
to complicate the informal understanding of the language in an attempt to
simplify the (already unreadable in any case for most programmers) RM.

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

From: Robert Dewar
Date: Saturday, October 2, 2010  3:54 PM

So far I have thought of limiting it to set membership form, but the example I
wrote in previous mail definitely lead one to at least wonder about
generalizing:

>      subtype Integer_Non_Zero is Integer with
>        predicate => Integer_Non_Zero in
>          Integer'First .. -1 | +1 .. Integer'Last;

Contrast with

>      subtype Integer_Non_Zero is Integer with
>        predicate => Integer_Non_Zero /= 0;

The latter is easier to write, but would not qualify as static if we insist on
set forms

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

From: Robert Dewar
Date: Saturday, October 2, 2010  4:00 PM

Do we allow

    subtype X is Integer with
      predicate =>
        x in 1 .. 10 | 7 .. 15;

I think the easy answer is yes, here is an example showing it is helpful to
allow this kind of overlap

    type European_Countries is (England, Monaco, France, ...

    subtype Eurozone is European_Countries with
      predicate => Eurozone in Monaco | France ...

    subtype EEC is European_Countries with Predicate
      predicate => EEC in England | France ....

It seems nice to be able to write

    subtype Euro_Friendly is European_Countries with
      predicate Euro_Friendly in Eurozone | EEC;

to disallow this would make maintenance painful, since one would have to know
about the overlap in writing the declaration for Euro_Friendly.

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

From: Tucker Taft
Date: Sunday, October 3, 2010  12:53 PM

> So far I have thought of limiting it to set membership form, but the example I
> wrote in previous mail definitely lead one to at least wonder about
> generalizing:
...

We went around already on the problem with staticness and full coverage when you
have general static predicates such as:

    subtype Very_Even is Long_Long_Integer with predicate
       Very_Even mod 2**60 = 0;

Use of such subtypes, either as case statement labels, or as the subtype of the
case expression, would be painful, since determining full coverage would be
difficult.  We could require an "others" alternative, as we do for universal
integer case expressions now, but that would lose the benefit of full coverage.
We could require the "others" in just some cases, e.g. whenever it is a form we
consider too "hard" to enumerate.

Use of "mod" or "rem" by a large number is painful, unless you recognize it
specially, as would be use of "and" and "xor" with a large modular type,
probably.  Use of exponentiation with a modular type would also be painful.  I
suppose we could set some arbitrary limit on the underlying contiguous range of
the subtype, and if it is larger than 1000, say, then we require the "others."
Sounds pretty groddy, though the limit of 200 for static strings is a bit of a
precedent for groddy rules relating to staticness.

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

From: Tucker Taft
Date: Sunday, October 3, 2010  1:04 PM

> Do we allow
>
> subtype X is Integer with
> predicate =>
> x in 1 .. 10 | 7 .. 15;
>
> I think the easy answer is yes, here is an example showing it is
> helpful to allow this kind of overlap
...

I certainly agree with this.  I can't see any benefit in requiring
non-overlapping ranges for predicates, if the corresponding membership test
would be static. In general, I hope the rule would be based on the staticness of
the corresponding membership test, if the subtype name were replaced with a
static expression.

I suppose one generalization that would still avoid the complexities of "x mod
2**60 = 0" would be to require that the subtype name must appear directly as the
operand of a membership or relational/equality operator, i.e. with no arithmetic
operator applied to it first.  Hence "X mod 2**60 /= 0" as a predicate would
*not* result in a static subtype, but "X /= (3**25 mod 2**60)" would be fine. We
will have to make some statement about the subtype name appearing alone in the
membership test (since "(X mod 2**60) in 0..0" isn't better than "X mod 2**60 =
0"), so having said the subtype name must appear alone, we can perhaps
generalize to allow other relational/equality operators as well.

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

From: Robert Dewar
Date: Sunday, October 3, 2010  1:27 PM

> We went around already on the problem with staticness and full
> coverage when you have general static predicates such as:
>
>      subtype Very_Even is Long_Long_Integer with predicate
>         Very_Even mod 2**60 = 0;
...

OK, let's limit it to set membership, that gets the common cases, and is easy to
describe and reasonably easy to implement (just wrote many hundreds of lines of
code for static predicates, but it's all straight forward, and I included the
loop case :-))

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

From: Robert Dewar
Date: Sunday, October 3, 2010  1:35 PM

> I suppose one generalization that would still avoid the complexities
...

I would not generalize, I would say that static predicates must be of the form

    subtype_IDENTIFIER in static_MEMBERSHIP_CHOICE_LIST

    MEMBERSHIP_CHOICE_LIST ::=
      MEMBERSHIP_CHOICE {'|' MEMBERSHIP CHOICE}

    MEMBERSHIP_CHOICE ::=
      CHOICE_EXPRESSION | RANGE | SUBTYPE_MARK

where static here means that

CHOICE_EXPRESSION is a static epression
RANGE is a static range
SUBTYPE_MARK references a static subtype without a predicate, or a subtype with
a static predicate.

In addition, I would require that to be static

None of the static_MEMBERSHIP_CHOICE_LIST entries an be outside the range of the
subtype

The subtype itself must have a static range

The subtype must not inherit predicates (or have more than one predicate, since
we allow multiple predicate pragmas in GNAT) (*)

This limited form covers the important case of what Randy calls set predicates,
which is 99% of the useful usage of static predicates. Let's not over-complicate
the proposal to death by trying to go from implementable reasonable good, to
unimplemented over-complex best.

After all lost of things are not static that should be (my favorite peeve in
this area is that 'Size should always be static if you have given an explicit
static Size value in the program).

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

From: Robert Dewar
Date: Sunday, October 3, 2010  2:34 PM

> I certainly agree with this.  I can't see any benefit in requiring
> non-overlapping ranges for predicates, if the corresponding membership
> test would be static.
> In general, I hope the rule would be based on the staticness of the
> corresponding membership test, if the subtype name were replaced with
> a static expression.

I think that criterion is met with the
exception of checking the range against
the range of the subtype. We could of
course just ignore it this is cases like

    type C is (A,B,C,D,E,F,G,H,I,J);

    subtype K is C range C .. H with
      Predicate => K in B .. D | H | J;

I consider this non-static because of the B that is outside the raneg C .. H. Of
course giving a range as well as a list in the predicate is unusual anyway (I
considered the possibility of the mere presence of a range making the predicate
non-static, but decided to allow a static range).

I would not mind just excluding B in a case like this, not a big deal.

Of course it would be nice to have warnings as to why this is non-static.

I am thinking of implementing a pragma Static which would check staticness and
be usable in the other cases randy worries about, it's main purpose would be to
check at the point of definition, and allow you to give clearer diagnostics as
to why something is not static, but no big deal.

Back to overlap, allowing overlap here makes good sense to me, but note we don't
allow it in case statements themselves:

>      1. procedure w is
>      2.    type R is (A, B, C, D, E, F);
>      3.    RV : R;
>      4. begin
>      5.    case RV is
>      6.       when A => null;
>      7.       when B .. D | C .. E => null;
>                               |
>         >>> duplication of choice value at line 7
>
>      8.       when F => null;
>      9.    end case;
>     10. end w;

So it's a little odd that now if I define a static predicate with this exact
same set of values spelled the same way, the case statement WILL work with the
name of that static predicate.

Note that my argument in favor of allowing overlap is just as applicable to case
statements

       when Eurozone | EEC =>

would be illegal now, and for the same reasons I gave before this would be
annoying.

Still giving names to such choices, which is now possible, is SUCH an
improvement that I don't mind too much pushing people in that direction :-)

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

From: Tucker Taft
Date: Sunday, October 3, 2010  3:05 PM

I'm curious why you don't think it is reasonable to generalize to all equality/
relational operators.  X in 1..10 is essentially equivalent to X >= 1 and X <=
10, so it seems odd to specifically require membership. And as you pointed out,
"X /= 0" is certainly more readable and natural than X in blah'first .. -1 | +1
.. blah'last.  There are already restrictions in the use of discriminants that
they must be used alone rather than as part of a larger expression, so this
feels somewhat in the same ballpark.

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

From: Robert Dewar
Date: Sunday, October 3, 2010  4:36 PM

I just think that fully analyzing expressions like

     (X >= 1 and  <= 10) or ... and ..

will be a bit of an implementation nightmare ...
I think it is a good idea to keep things simple at first, and then if we find we
really need more generalization, OK, we can look at the effort required.

Just doing the set predicates is a LOT of work!

I am also concerned that endless arguments and discussion over what should or
should not be static will be a messy discussion that will drag things out at a
stage where such dragging is not welcome. If you burden the predicates feature
with too many bells and whistles you risk killing it.

BTW here is some output from GNAT in its current state:

>      1. procedure Static_Predicates is
>>      2.    type a is (B, C, D, E, F, G, H, I, J, K, L, M, N);
>>      3.    subtype s1 is a with
>>      4.      Predicate => S1 in C | J | E .. F;
>>      5.    subtype s2 is a with
>>      6.      Predicate => S2 in L .. N | S1 | D | L .. M;
>>      7.
>>      8.    V1 : s1;
>>      9.    V2 : s2;
>>     10.
>>     11. begin
>>     12.    case V1 is
>>     13.       when B => null;
>>     14.       when D => null;
>>     15.       when G .. I  => null;
>>     16.       when K .. N  => null;
>>     17.       when S1 => null;
>>     18.    end case;
>>     19.
>>     20.    case V2 is  -- I missing
>>            |
>>         >>> missing case value: "I"
>>
>>     21.       when B      => null;
>>     22.       when S2     => null;
>>     23.       when G .. H => null;
>>     24.       when J .. K => null; -- duplicates J
>>                      |
>>         >>> duplication of choice value at line 22
>>
>>     25.    end case;
>>     26. end;

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

From: Robert Dewar
Date: Monday, October 4, 2010  11:27 PM

I just found an interesting infinite loop in my implementation, and I wondered
whether it might also be an infinite loop in the description?

    type R is new Integer with
       R in 1 .. 7 | 10 .. 12;

The implementation (and description?) of membership tests for predicated types
includes a call to the predicate, so that sounds a bit recursive :-) Easily
fixed with a special kludge test in the implementation of course!

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

From: Steve Baird
Date: Monday, October 4, 2010  12:09 PM

I don't think there is any problem here with recursion.

Recall that the predicate for a base subtype is always True, and that a
predicate is associated with a subtype, not with a type.

In this example,
     R in 1..7 | 10..17
is (if you don't mind some fast-and-loose name resolution) semantically
equivalent to

     R in R'Base range 1.. 7 | R'Base range 10 .. 17

, where the "R" in the two occurrences of R'Base refers to R, not to he current
instance of R (as opposed to the first use of "R" in the predicate expression,
which refers to the current instance).

There is a freezing issue here (the predicate expression includes static
expressions of type R'Base). This has been discussed internally at AdaCore.

Steve Baird wrote:
> I think we need a freezing rule something like
>
>    At the place where a subtype is frozen, its predicate expression
>    (including any inherited predicate expressions) causes freezing.
>
> and then we also need to add predicate expressions to the
> special-exemption list of 13.14(8/1):
>
>   An object name or nonstatic expression causes freezing where it
>   occurs, unless the name or expression is part of a default_expression,
>   a default_name, or a per-object expression of a component's
>   constraint, in which case, the freezing occurs later as part of
>   another construct.

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

From: Robert Dewar
Date: Monday, October 4, 2010  12:48 PM

> In this example,
>       R in 1..7 | 10..17
> is (if you don't mind some fast-and-loose name resolution)
> semantically equivalent to
>
>       R in R'Base range 1.. 7 | R'Base range 10 .. 17

Are you saying that if I have a predicated subtype R then

        RX : R;

        if RX in R ..

checks the subtype predicate

but    if RX in 1 .. 7

does not check the subtype predicate (right now I check the range and the
predicate in such a case, but of course that is easily changed).

what about

       if RX in R'(1) .. R'(7);

same thing?

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

From: Steve Baird
Date: Monday, October 4, 2010  12:59 PM

> Are you saying that if I have a predicated subtype R then
>
>        RX : R;
>
>        if RX in R ..
>
> checks the subtype predicate
>
> but    if RX in 1 .. 7
>
> does not check the subtype predicate (right now I check the range and
> the predicate in such a case, but of course that is easily changed).
>

Yes, that is my reading of the AI.

> what about
>
>       if RX in R'(1) .. R'(7);
>
> same thing?

Here you get predicate checks on 1 and 7 (because of
the subtype conversion associated with a qualified
expression) =, but not on Rx.

You might get a predicate check on

    if Rx in R range 1 ,, 7

, or you might not because of the implementation
permission (which applies because the subtype of Rx is R).

At least that's my interpretation.

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

From: Robert Dewar
Date: Monday, October 4, 2010  1:00 PM

OK, well I can easily fix this, anyone else care to comment on this?

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

From: Tucker Taft
Date: Monday, October 4, 2010  1:25 PM

I agree with Steve's interpretation.
A range by itself has a *type* but does not have a *subtype*, or at least the RM
never mentions the subtype of a range.

A subtype_indication with a range constraint clearly is defining a subrange of a
subtype, but a "range" by itself is defining a subrange of a type, independent
of the "subtype" of the bounds of the range.  Actually, *values* of an
elementary type don't really have subtypes, only types.  An *object* of an
elementary type has a subtype, but the bounds of a range are values, not
objects.

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

From: Steve Baird
Date: Monday, October 4, 2010  2:53 PM

> You might get a predicate check on'
>
>    if Rx in R range 1 ,, 7
>
> , or you might not because of the implementation permission (which
> applies because the subtype of Rx is R).
>

I said this, and I believe that this is the intent, but (upon looking at the AI
again) I don't think the wording of the AI correctly captures this.

The AI says:
   A predicate check may be omitted if the subtype with the predicate
   statically matches the nominal subtype of the value being checked.

The subtypes R and "R range 1 .. 7" have the same predicate, but they certainly
do not statically match.

The definition of "statically compatible" needs to take predicates into account.
I don't remember whether this point has already been discussed, but the point
holds in any case.

Assuming that "statically compatible" is defined correctly, one approach would
be to use that notion instead of "statically matches" in the implementation
permission.

This would be an improvement, but it still wouldn't work in the case in
question. The compatibility relationship holds in the wrong direction in this
particular example.

I think we need to capture the notion of two subtypes having the same predicate
and of one having a predicate which implies the predicate of the other (because
the predicate of the former is a conjunction of the predicate of the latter with
other predicates).

The general idea is that in deciding whether it is ok to eliminate a predicate
check, an implementation should be allowed to assume that
    1) the predicate is "well-behaved" - given the same argument twice,
       it returns the same answer and it returns a Boolean result
       (as opposed to raising an exception, failing to terminate, etc.)
       for any argument in the basetype.
    2) the value of an object satisfies the predicate of the subtype
       of the object (this assumption may be false for, say,
       uninitialized variables, but that's ok).

One approach would be to define the implementation permission explicitly in
these terms.

The current wording is not permissive enough and requires checks in cases which
meet these criteria. A performance hit associated with requiring unneeded
predicate checks would not be good.

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

From: Randy Brukardt
Date: Monday, October 4, 2010  3:25 PM

...
>    1) the predicate is "well-behaved" - given the same argument twice,
>       it returns the same answer and it returns a Boolean result
>       (as opposed to raising an exception, failing to terminate, etc.)
>       for any argument in the basetype.
>     2) the value of an object satisfies the predicate of the subtype
>        of the object (this assumption may be false for, say,
>        uninitialized variables, but that's ok).

I don't think this is OK as you have written it. It would be OK to say "if the
value of an object is known to be valid, then the value of the object satisfies
the predicate".

Another way to put it is that "if the compiler can assume the value is in range,
it can also assume that the value satisifies the predicate". We definitely don't
want to be able to elimimate predicate changes in cases where it is not allowed
to eliminate range checks.

I suspect that both of the above are the same as assuming (1) [since a valid
value must have been checked at some previous point], so my feeling is that it
is the only assumption needed.

Or to put it another way: We really do not want to restart all of the arguments
about when you can assume validity of objects! (Or rework those rules, either.)

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

From: Randy Brukardt
Date: Monday, October 4, 2010  4:01 PM

> a) allow it always, the official semantics could be
...
> b) forbid it entirely
>
> c) allow it only for static predicates
>
> I can live with b, since I don't think it's terribly useful, but on
> the other hand there are some who think it IS really useful (Randy in
> particular), and it does help ease generic contract concerns (one less
> case for warnings/exception when an error message wold violate the
> generic contract model).
>
> I don't really like c), since it is weird to have staticness control
> the validity of a loop (case statements are a different matter, there
> we are used to staticness being an issue).

I don't think (a) is practical in the general case, because such a loop could
take literally forever to run, even for a small set of values. In particular,
consider the 64-bit type (based on the one Tucker noted):

    function Is_Very_Even (V : in Long_Long_Integer) return Boolean is
    begin
        return V mod 2**60 = 0;
    end Is_Very_Even;

    subtype Very_Even is Long_Long_Integer with predicate
       Is_Very_Even (Very_Even);

If the function is declared such that the body isn't known to the compiler, then
there is no way to avoid iterating over the entire 64 bit integer space, calling
the function each time. That's going to take a long time!

Even if this somehow worked on a particular compiler, it probably wouldn't be
portable (as there could be no requirement for other compilers to come up with
such an optimization). As such, using loops other than in the static set
predicate case is dangerous (even though the definition is clear, practically it
may not work). This was the reason that we made loops illegal for the general
case of predicates.

> Furthermore, defining the semantics so it just covers case
> c) is a peculiar excercise. The easiest way of defining the semantics
> is with the bogus loop that you would use if you choose approach a).

Huh? For a static set predicate, the solution is to define the set of values of
the subtype. This also properly defines the set of values for the case statement
(and probably other cases that I can't remember right now). That of course isn't
possible for the general predicate case.

But in order for that to work, there needs to be a clear distinction between the
two cases. I would prefer that distinction is one that programmers are required
to declare in some way (as it avoids maintenance headaches caused by the
semantics changing).

> So I would go with a), it's no harder to implement than c.
>
> Note that an efficient implementation of c) is not easy!
> I can think of three approaches

I think you would have to use both of the latter approaches; it's much the same
as a case statement (where you need to use both table and sequence of if
approaches). It shouldn't be possible to write something that would be
inefficient for both approaches. I don't think the supposedly general approach
is ever acceptable for static set predicates - it should be possible to iterate
on small sets quickly, no matter what the base type is (including 128-bit
integer).

...
> One final thought, it is odd to allow
>
>     subtype R is Character with
>        predicate => R in 'A' | 'F' | 'W' .. 'Y';
>
>     for J in R loop ...
>
> and to disallow
>
>     for J in 'A' | 'F' | 'W' .. 'Y' loop ...

We have to disallow this, it is ambiguous (Character, Wide_Character, etc.).
It would be nice to be able to say:

     for J in Character when 'A' | 'F' | 'W' .. 'Y' loop

but of course there was a proposal like this which everyone agrees was an
unnecessary idea. :-)

> Obviously it is no harder to implement one than the other, so perhaps
> we should give some thought to allowing the more general loop format?
>         when Z => I := Q;

I suspect that doing this syntactically would be hard without running into
ambiguities with the other new iterator forms. In addition, it would be a
significant pain to give types to these things (as shown above), and they don't
work without the types.

It's interesting that you are rediscovering all of the features of the set
constraint proposal, one by one, and finding them valuable. I just hope this
doesn't result in a hodge-podge of rules rather than in integrated whole.

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

From: Tucker Taft
Date: Monday, October 4, 2010  4:30 PM

> In this note, I want to specifically address the issue of looping
> through predicated types with a syntax like
>
>       for J in subtype loop
>
> It seems like we have three possibilities
>
> a) allow it always, the official semantics could be
> b) forbid it entirely
> c) allow it only for static predicates
>
> I can live with b, since I don't think it's terribly useful,...

I vote for (b): disallow it entirely.
It doesn't seem worth the trouble.
There is the possibility of too much trickery happening behind the scenes.  It
reminds me of iterating over holey enumeration types, only worse.

I also hope we are disallowing such subtypes for array index subtypes.

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

From: Randy Brukardt
Date: Monday, October 4, 2010  4:17 PM

> I suspect that some people are more friendly to the
> (potentially-static) subtype predicate approach in that it piggy backs
> on the aspect specification syntax.  You are almost certainly right
> that getting static predicates to work may require many of the same
> things that the set constraints would have required, but avoiding
> adding a new constraint syntax is a significant advantage for
> predicates, at least to some people (including me, for what it is
> worth), even if it ends up requiring more words in the RM.
> Somehow additional syntax "weighs" a lot more than additional RM words
> for most people.

I sometimes think I'm drawing diagrams on a whiteboard in a room full of blind
people. :-)

I've said several times that the syntax itself doesn't bother me. What worries
me is grafting on a boatload of special case rules to predicates and subtype
declarations and case statements and for loops and gosh knows what else in order
to support these things, and creating a maintenance headache in the meantime.
You're the one who always is telling us that new features ought not look bolted
on -- this seems like it has the potential to do that in the worst way possible.

All I want is some (required) way to declare that you are defining one of these
things. I suggested a separate aspect Set_Predicate which would allow case
statements and loops and require the predicate expression to have a certain
form, as well as trigger the needed semantic rules to make the case statements
and loops work. That meets everything you say here, and I proposed it last week.

I suspect part of the problem here is the mistaken emphasis on "static".
That is not the important property here; the property that matter is a subtype
that describes some set of values that aren't necessarily contiguous. Once this
was described as "static", then a whole bunch of additional (and sometimes
unnecessary) considerations arise. (For instance, Robert's suggested rules mean
that you could not use a set predicate on a generic formal type, including on a
formal integer type. That seems inconsistent and limiting to me. It seems
perfectly possible to have set predicates on non-static subtypes - but don't
worry Robert, I too am concerned about overgeneralization so I am not about to
propose fully dynamic set predicates!)

Anyway, since it is obvious that I'm not doing that well describing what I
already know about this problem (and remember that Steve and I spent a lot of
time last year in developing the set constraint proposal), and
because it is becoming obvious that Robert is rediscovering the issues anyway,
I'm going to try to hold my tongue on any further discussion here (I need to do
work which will help progress the language).

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

From: Randy Brukardt
Date: Monday, October 4, 2010  4:27 PM

...
> After all lost of things are not static that should be (my favorite
> peeve in this area is that 'Size should always be static if you have
> given an explicit static Size value in the program).

Arggghhh!!! I complained about this problem last week and you said that "in
15 years, no one has complained about this in the context of GNAT" (Friday,
6:31 PM). (Any specified Size value has to be static by language rule, so this
applies to essentially all  uses of 'Size.) No wonder I feel like I am losing my
mind...

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

From: Robert Dewar
Date: Monday, October 4, 2010  6:40 PM

But you were using it in a very odd context, and my comment was about that odd
context. But yes, I understand what you mean. Your example was trying to set the
size of a record by summing the size of its components, but that seems plain odd
and wrong, and really I was reacting to that :-)

But I agree I was being inconsistent

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

From: Robert Dewar
Date: Monday, October 4, 2010  6:29 PM

> Huh? For a static set predicate, the solution is to define the set of
> values of the subtype. This also properly defines the set of values
> for the case statement (and probably other cases that I can't remember
> right now). That of course isn't possible for the general predicate case.

Yes, you can do that if you like, and if it is not too much trouble, and that
does make more sense if you intend to ban the non-static ase

> But in order for that to work, there needs to be a clear distinction
> between the two cases. I would prefer that distinction is one that
> programmers are required to declare in some way (as it avoids
> maintenance headaches caused by the semantics changing).

I know you want that distinction, but so far I see *no one* else in favor of it. I certainly very much dislike making this distinction, and Bob Duff, who holds the pen also dislikes making this distinction.
Unless there is some glimmer of support on this for your position, I don't consider it worth discussing further.

> I think you would have to use both of the latter approaches; it's much
> the same as a case statement (where you need to use both table and
> sequence of if approaches). It shouldn't be possible to write
> something that would be inefficient for both approaches. I don't think
> the supposedly general approach is ever acceptable for static set
> predicates - it should be possible to iterate on small sets quickly,
> no matter what the base type is (including 128-bit integer).

The case statement is the way to go on this, because it automatically takes
advantage of the extensive optimization of case statements for various degrees
of sparseness that is in all sophisticated code generators anyway.

However, I am inclined to go with disallowing this completely now, given that
you regard it as only viable if we make a clear syntactic distinction for static
predicates, and Tuck doesn't like it anyway.

I have implemented it, was fairly easy to do, but of course unimplementing it
would be even quicker!

>>      for J in 'A' | 'F' | 'W' .. 'Y' loop ...
>
> We have to disallow this, it is ambiguous (Character, Wide_Character, etc.).

Randy, don't you ever turn the excessive language lawyering off? :-=) Yes, of
course this particular one is ambiguous (easily cured with qualification), but
it is the general idea that needs to be addressed,

Anyway, I think we should probably kill the entire loop business completely,
then this becomes a dead issue as well.

> I suspect that doing this syntactically would be hard without running
> into ambiguities with the other new iterator forms. In addition, it
> would be a significant pain to give types to these things (as shown
> above), and they don't work without the types.

I don't see particular pain here

> It's interesting that you are rediscovering all of the features of the
> set constraint proposal, one by one, and finding them valuable. I just
> hope this doesn't result in a hodge-podge of rules rather than in integrated whole.

I never said I found the loops valuable, I said I found them useless but
harmless and that you liked them :-) :-)

The critical capability of static predicates is to use them in case statements.
I have that all working just fine.

****************************************************************
From: Steve Baird
Date: Monday, October 4, 2010  4:33 PM

> We
> definitely don't want to be able to eliminate predicate changes in
> cases where it is not allowed to eliminate range checks.
>

I completely agree. This is the idea that I was trying to capture.

The question here concerns the rules regarding the elimination of range checks.
However this question is resolved, I agree with your point that the rules for
predicate check elimination should be consistent with those for range check
elimination.

So what about range checks?

Given
    type R is record F1, F2 : Natural; end record;

    procedure P (X : in out R; Y : R) is
    begin
        X.F1 := Y.F1;
    end P;

are you arguing that a constraint check is required if it is possible that Y.F1
might be uninitialized?

13.9.1 says

   If the representation of a scalar object does not represent a value of
   the object's subtype (perhaps because the object was not initialized),
   the object is said to have an invalid representation. It is a bounded
   error to evaluate the value of such an object If the error is
   detected, ...
   Otherwise, execution continues using the invalid representation.
   ...

   The semantics of operations on invalid representations are as follows:

     ...

     If the representation of the object does not represent a value of
     the  object's type, the semantics of operations on such
     representations is implementation-defined, but does not by itself
     lead to erroneous or unpredictable execution, or to other objects
     becoming abnormal

As has been noted previously, an implementation always has the option of
defining the representation -> value mapping for a given object to be such that
no invalid representation ever represents a value of the object's type. So an
implementation always has permission to go with implementation-defined behavior
for an operation on such a value, subject only to the requirement that this must
not "by itself" lead to war, death, pestilence, and famine. Nobody has ever come
close to nailing down the meaning of "by itself", so the meaning of this
requirement is unclear.

Paragraph 13/3 certainly spells out one case in which scalar assignment with an
invalid rhs is not required to raise an exception.

I take all this to mean that an assignment statement whose rhs is the value of
an invalid scalar object is not *required* to fail a range check.

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

From: Randy Brukardt
Date: Monday, October 4, 2010  6:58 PM

...
> The question here concerns the rules regarding the elimination of
> range checks. However this question is resolved, I agree with your
> point that the rules for predicate check elimination should be
> consistent with those for range check elimination.
>
> So what about range checks?
>
> Given
>     type R is record F1, F2 : Natural; end record;
>
>     procedure P (X : in out R; Y : R) is
>     begin
>         X.F1 := Y.F1;
>     end P;
>
> are you arguing that a constraint check is required if it is possible
> that Y.F1 might be uninitialized?

Oh, please do not bring this up again. You asked essentially the same question
last year, it was turned into AI05-0195-1, and this was discussed and voted No
Action in Valencia: the language was felt to be clear enough.

All I've said is that the rules are the same. The rules for a particular
compiler depend on what it allows for invalid and valid values, and there is no
requirement anywhere for those to be the same for different compilers, or even
different switches in the same compiler (Janus/Ada has three different checking
elimination models, depending on whether the user wants errors detected early or
the fastest legal code).

This is a case where compilers can move the "bump" all over the place.

And this is all I'm going to say on this topic.

P.S. Whatever gave you the idea that I had any intention whatsoever in meddling
in this can of worms??

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

From: Randy Brukardt
Date: Monday, October 4, 2010  7:35 PM

...
> > But in order for that to work, there needs to be a clear distinction
> > between the two cases. I would prefer that distinction is one that
> > programmers are required to declare in some way (as it avoids
> > maintenance headaches caused by the semantics changing).
>
> I know you want that distinction, but so far I see *no one* else in
> favor of it. I certainly very much dislike making this distinction,
> and Bob Duff, who holds the pen also dislikes making this distinction.
> Unless there is some glimmer of support on this for your position, I
> don't consider it worth discussing further.

Obviously I'd like to see some support for this position, but so far I've seen
hardly any support for *your* position, either. Indeed, hardly anyone has
participated in this discussion, so I wouldn't draw any conclusions from it.

In any case, I will never be able to support mixing these two distinct things in
a way which will prevent improving either of them in the future. And I will do
everything I can to stop such an addition to the language.

...
> >>      for J in 'A' | 'F' | 'W' .. 'Y' loop ...
> >
> > We have to disallow this, it is ambiguous (Character,
> Wide_Character, etc.).
>
> Randy, don't you ever turn the excessive language lawyering off? :-=)
> Yes, of course this particular one is ambiguous (easily cured with
> qualification), but it is the general idea that needs to be addressed,

My entire point was that you need a way to get the type name into such things.
Qualification is a really ugly way to do it and also unfamilar (I don't think
I've ever used qualification other than to debug expressions that were
mysteriously not resolving, and I don't think I've ever left the qualifications
around after fixing the error - I always name the type).

...
> > It's interesting that you are rediscovering all of the features of
> > the set constraint proposal, one by one, and finding them valuable.
> > I just hope this doesn't result in a hodge-podge of rules rather
> > than in integrated whole.
>
> I never said I found the loops valuable, I said I found them useless
> but harmless and that you liked them :-) :-)

The loops was not what I was referring to...I was referring to your "pragma
Static" (which appears to be providing some of the distinction that I was
discussing before), to the idea that you might want to declare an anonymous set
predicate, etc.

> The critical capability of static predicates is to use them in case
> statements. I have that all working just fine.

I find case statements and for loops to be closely related. But apparently,
anything I think on this topic is way out in left field. I have to wonder why I
bother at all...(Note: I rarely charge e-mail discussions to my ARG work; all of
these have been on my own time.)

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

From: Robert Dewar
Date: Monday, October 4, 2010  8:09 PM

> In any case, I will never be able to support mixing these two distinct
> things in a way which will prevent improving either of them in the future.
> And I will do everything I can to stop such an addition to the language.

Hmmm, reminds me of JDI, who would furiously hold on to his position even when
it was N-1 against :-). I always figure that if I can't convince others, then I
am either wrong, or presenting the argument incompetently.

Of course it is true that others need to weigh in on this, I know Bob Duff is
very opposed to distinguishing the two, and I know I am, and I know you are very
interested in having this distinction.

To me they are not distinct at all, no more distinct than the normal case of

     X : constant Integer := expression;

where X can be used in very different contexts (including the case we are
discussing here, case statements), depending on the nature of expression. I
don't find this case any different (you may find them the same too, in one
message you claimed this was a mistake in the language in this case as well),
but to me if something is in Ada for two decades and we live with it, claiming
it is a big mistake is kind of besides the point.

So anyway, how about it other-arg-folks .. cast your vote :-) Do you want to see
two separate aspects here one for general predicates one for what Randy likes to
call set predicates, and I like to call static predicates, or one single aspect
as now, with a recognition of static forms corresponding to a very set and
simple syntax (set membership with static right sides).

Looks like it is lining up that the ONLY distinction is whether they can be used
in case statements, since I think we are likely to lose the loops given cool
support for looping over these beasts. And no Tuck, no one is suggesting them as
array indexes :-)

> My entire point was that you need a way to get the type name into such
> things. Qualification is a really ugly way to do it and also unfamilar
> (I don't think I've ever used qualification other than to debug
> expressions that were mysteriously not resolving, and I don't think
> I've ever left the qualifications around after fixing the error - I always name the type).

I use qualification ALL the time to disambiguate, that's what it's there for, I
find it amazing you haven't -- another way in which I think the RB Ada coding
style is a bit different from most of the world. Of course aren't you one of
these anti-use fanatics. If you are in that camp, then of course you need to
disambiguate less.

>> I never said I found the loops valuable, I said I found them useless
>> but harmless and that you liked them :-) :-)
>
> The loops was not what I was referring to...I was referring to your
> "pragma Static" (which appears to be providing some of the distinction
> that I was discussing before), to the idea that you might want to
> declare an anonymous set predicate, etc.

These are just passing ideas, the pragma Static is just a passing idea, often it
is appropriate to do with pragmas that which you do NOT want to burden the
language with, and I think this is a good example. But in any case, I would
prefer to keep things simple in the actual proposal and limit it to case
statements, which is really the critical need.

>> The critical capability of static predicates is to use them in case
>> statements. I have that all working just fine.
>
> I fine case statements and for loops to be closely related. But
> apparently, anything I think on this topic is way out in left field. I
> have to wonder why I bother at all...(Note: I rarely charge e-mail
> discussions to my ARG work; all of these have been on my own time.)

Case statements and for loops *are* closely related from a language lawyer point
of view, I agree, but in usage, there is a world of difference. I have never
ever needed to iterate through an enumeration subtype! I use case statements on
enumeration subtypes all the time. So it is for me a differentiation of usage.

For entertainment, here is a test program (as I told you I did implement
iteration over predicated types :-)

>      1. with Text_IO; use Text_IO;
>      2. procedure Predicate_Loops is
>      3.    type Int is range 1 .. 10;
>      4.
>      5.    procedure Q (X : Int) is
>      6.       subtype NonStaticP is Int with
>      7.          predicate => NonStaticP > X;
>      8.    begin
>      9.       for J in NonStaticP loop
>     10.          Put_Line ("non static:" & J'Img);
>     11.       end loop;
>     12.    end;
>     13.
>     14.    subtype StaticP is Int with
>     15.       predicate => StaticP in 3 | 5 .. 7 | 10;
>     16.
>     17. begin
>     18.    for J in StaticP loop
>     19.       Put_Line ("static:" & J'Img);
>     20.    end loop;
>     21.
>     22.    Q (5);
>     23. end;

And the output is:

> static: 3
> static: 5
> static: 6
> static: 7
> static: 10
> non static: 6
> non static: 7
> non static: 8
> non static: 9
> non static: 10

The expanded code for the non-static case loop looks like:

> -- 9:       for J in NonStaticP loop
>       L_1 : for j in 1 .. 10 loop
>          if predicate_loops__q__nonstaticpPredicate (j) then
>
> -- 10:          Put_Line ("non static:" & J'Img);
 >
>             ... lots of concatenation and IO crud
>
> -- 11:       end loop;
>             end L_1__B3b;
>          end if;
>       end loop L_1;

This is the code that can take a long time, if you concoct an example that is
very sparse over a giant type :-)

The expanded code for the static case loop looks like:

> -- 18:    for J in StaticP loop

>    B29b : declare
>       j : predicate_loops__staticp := 3;
>
>    begin
>       loop

> -- 19:       Put_Line ("static:" & J'Img);

>          ... lots of concatenation and IO crud
>

> -- 20:    end loop;

>          case j is
>             when 3 =>
>                j := 5;
>             when 7 =>
>                j := 10;
>             when 10 =>
>                exit;
>             when others =>
>                j := predicate_loops__staticp'succ(j);
>          end case;
>       end loop;
>    end B29b;

This approach with the case statement guarantees nice linear performance with
minimal overhead, The compiler does a good job of figuring out how to optimize
case statements, and this particular one will likely come out as a jump table,
but might be if's depending on the details of the target architecture.

I think looking at this, I do agree with Tuck, the non-static case is too much
hidden overhead, and after all you can always do this yourself if you want.

On the other hand, the static case, you definitely can't do yourself, not
without knowing exactly what literals are present, and the whole point is to
avoid this.

BTW, in GNAT we have the (optional) concept of unordered enumeration types. If
you select this option, then all enumeration types are considered unordered
unless you apply a pragma Ordered to them. Then for unordered enum types, you
get warnings on stuff like comparisons or First/Last.

I think I probably will arrange for gnat to always consider these predicated
enumeration types to be unordered, that really seems to make sense to me (it's
just a matter of what warnings are generated by default, nothing to do with
legality).

--  The following is off-topic, read only
--  if you have nothing better to do!

P.P.S I mused the other day about reintroducing under the GNAT extensions
switch, the old Ada 79 notion of unordered enums

   type X is (A | B | C | D | E | F);

then of course the subtypes could be written

   subtype Y is X with
     predicate Y in (B | D | F);

(I *keep* wanting to put parens around the the set :-))

and if you are in a really naughty mood you could allow

   type X is {A | B | C | D | E | F};

   subtype Y is X with
     predicate Y in {B | D | F};

since W is not around to complain about his keypunch.

Note that this would be VERY easy to implement in GNAT, since as I say, at the
level of warning messages we already have the full mechanism for unordered enum
types :-)

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

From: Randy Brukardt
Date: Monday, October 4, 2010  11:37 PM

...
> > My entire point was that you need a way to get the type name into
> > such things. Qualification is a really ugly way to do it and also
> > unfamiliar (I don't think I've ever used qualification other than to
> > debug expressions that were mysteriously not resolving, and I don't
> > think I've ever left the qualifications around after fixing the
> > error - I always name the type).
>
> I use qualification ALL the time to disambiguate, that's what it's
> there for, I find it amazing you haven't -- another way in which I
> think the RB Ada coding style is a bit different from most of the
> world. Of course aren't you one of these anti-use fanatics. If you are
> in that camp, then of course you need to disambiguate less.

I don't quite think I'm a "fanatic" on "use clauses"; I just limit their use to
very common packages. I do use "use type" frequently, and probably will use "use
all type" as well. I'll probably use the package use even less as "use all type"
is more limited and covers the main reason that I use "use" at all: enumeration
literals. (Full qualification on the instruction names in the Janus/Ada
intermediate code would cause one's head to explode. :-) Anyway, you are
probably right, this is a contributing factor in my not using qualification that
often.

...
> Case statements and for loops *are* closely related from a language
> lawyer point of view, I agree, but in usage, there is a world of
> difference. I have never ever needed to iterate through an enumeration
> subtype! I use case statements on enumeration subtypes all the time.
> So it is for me a differentiation of usage.

I find that odd; I've definitely iterated through enumeration subtypes (and not
just for testing). For instance, I often have arrays indexed by an enumeration
and it is not usual that some operation on that array or part of it needs
iteration. It's not something that comes up everyday, but it's not that rare,
either. I only used testing for an example because it was the simplest case I
could think of that didn't involve arrays (as those won't work for sets).

...
> I think looking at this, I do agree with Tuck, the non-static case is
> too much hidden overhead, and after all you can always do this
> yourself if you want.

I agree completely.

> On the other hand, the static case, you definitely can't do yourself,
> not without knowing exactly what literals are present, and the whole
> point is to avoid this.

Right. I probably would have implemented this with a table of items (if the
number of items is small), or something like you have otherwise (the case table
jump table and set up code would be at lot larger than the item table for
typical enumeration types - which usually get 8-bit representations in our
compiler).

...
> --  The following is off-topic, read only
> --  if you have nothing better to do!

That is always intriguing. ;-)

> P.P.S I mused the other day about reintroducing under the GNAT
> extensions switch, the old Ada 79 notion of unordered enums
>
>    type X is (A | B | C | D | E | F);
>
> then of course the subtypes could be written
>
>    subtype Y is X with
>      predicate Y in (B | D | F);
>
> (I *keep* wanting to put parens around the the set :-))

I thought that the syntax of memberships required it. Or did we change our minds
on that one? I forget. (I would much rather that it did have the parens, as that
would make it clear that
    Y in (1 .. 10)
is a set membership. Depending on what resolution rules we end up with, that
could be helpful.)

> and if you are in a really naughty mood you could allow
>
>    type X is {A | B | C | D | E | F};
>
>    subtype Y is X with
>      predicate Y in {B | D | F};
>
> since W is not around to complain about his keypunch.

I actually semi-seriously had suggested something like this for set constraints.
But it got even less traction than the constraints themselves. :-(

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

From: Gary Dismukes
Date: Tuesday, October 5, 2010  1:16 AM

> So anyway, how about it other-arg-folks .. cast your vote :-) Do you
> want to see two separate aspects here one for general predicates one
> for what Randy likes to call set predicates, and I like to call static
> predicates, or one single aspect as now, with a recognition of static
> forms corresponding to a very set and simple syntax (set membership
> with static right sides).

I think they should be part of one predicate aspect.  I can't see splitting them
into two separate aspects, to me that would seem confusing and just complicate
things unnecessarily, plus being inconsistent with how staticness is normally
handled.

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

From: Randy Brukardt
Date: Tuesday, October 5, 2010  1:49 AM

Sigh. As I've repeatedly said, this has nothing to do with staticness (its about
sets). Simply by calling it that, Robert has managed to get everyone confused.

Robert said earlier:

>I always figure that if I can't convince others, then I am either
>wrong, or presenting the argument incompetently.

Unfortunately, with me, it seems that unless the issue is clear cut, I am
*always* doing that latter. I can't always be wrong...(and if I am, I should be
replaced).

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

From: Robert Dewar
Date: Tuesday, October 5, 2010  3:58 AM

> Sigh. As I've repeatedly said, this has nothing to do with staticness
> (its about sets). Simply by calling it that, Robert has managed to get
> everyone confused.

Couldn't disagree more

sets have nothing to do with staticness

staticness has everything to do with being able to use things in case statements

a predicate defines a subset of an enumeration type (or other discrete type).

the issue is whether this set of values is statically known at compile time.

if so, then it can be used in a case statement

this is *EXACTLY* the same general concept that underlies the issue of normal
static constants

  X : constant Integer := expression;

you can use X in some remote case statement if and only if expression is known
at compile time. Known at compile time is to amorphous a predicate, so we
introduce a clearly defined syntax rule that defines that certain expressions
must be known at compile time.

   subtype x is enum with
      predicate => expression;

(cut and paste!)
you can use X in some remote case statement if and only if expression is known
at compile time. Known at compile time is to amorphous a predicate, so we
introduce a clearly defined syntax rule that defines that certain expressions
must be known at compile time.

The analogy is to me 100% appropriate, hence the reuse of the term static.

On the other hand, "set" has no implication of staticness to me, so does not
capture this distinction. the subtype declaration above ALWAYS defines a subset
of enum, whether or not it is static. So the issue is not sets per se, it is
statically known subsets.

I think it is Randy who creates the confusion by insisting on the term "set" to
describe static sets.

you can use X in some remote

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

From: Robert Dewar
Date: Tuesday, October 5, 2010  4:11 AM

> Right. I probably would have implemented this with a table of items
> (if the number of items is small), or something like you have
> otherwise (the case table jump table and set up code would be at lot
> larger than the item table for typical enumeration types - which
> usually get 8-bit representations in our compiler).

Well for such a marginal feature, it is hardly worth having yet another special
way of doing things when the case statement always provides reasonable linear
performance. Sure it's a bit better in some cases, but there are MANY other
cases more worthwhile for improving performance. But you can't use the table of
items in all cases

    subtype R is Integer with
       predicate R in -Integer'First .. -1 | +1 .. Integer'Last;

can't be done with a table :-)

>> P.P.S I mused the other day about reintroducing under the GNAT
>> extensions switch, the old Ada 79 notion of unordered enums
>>
>>     type X is (A | B | C | D | E | F);
>>
>> then of course the subtypes could be written
>>
>>     subtype Y is X with
>>       predicate Y in (B | D | F);
>>
>> (I *keep* wanting to put parens around the the set :-))
>
> I thought that the syntax of memberships required it. Or did we change
> our minds on that one? I forget. (I would much rather that it did have
> the parens, as that would make it clear that
>      Y in (1 .. 10)
> is a set membership. Depending on what resolution rules we end up
> with, that could be helpful.)

We do not require parens here (because we don't in other contexts such as case.

>> and if you are in a really naughty mood you could allow
>>
>>     type X is {A | B | C | D | E | F};
>>
>>     subtype Y is X with
>>       predicate Y in {B | D | F};
>>
>> since W is not around to complain about his keypunch.
>
> I actually semi-seriously had suggested something like this for set
> constraints. But it got even less traction than the constraints themselves.
> :-(

Right, this is not a tractiony proposal :-)

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

From: Tucker Taft
Date: Tuesday, October 5, 2010  11:49 AM

>> I'm curious why you don't think it
>> is reasonable to generalize to all equality/ relational operators. X
>> in 1..10 is essentially equivalent to X>= 1 and X<= 10, so it seems
>> odd to specifically require membership.
>> And as you pointed out, "X /= 0" is certainly more readable and
>> natural than X in blah'first .. -1 |
>> +1 .. blah'last. There are already restrictions
>> in the use of discriminants that they must be used alone rather than
>> as part of a larger expression, so this feels somewhat in the same
>> ballpark.
>
> I just think that fully analyzing expressions like
>
> (X >= 1 and <= 10) or ... and ..
>
> will be a bit of an implementation nightmare ...

I don't agree with this.  I believe that if we restrict it to just the form of a
single membership, we are falling into the trap of defining it syntactically,
which is to some extent what Randy has been objecting to.

If we are using the notion of a "static" predicate I believe we should define it
semantically, with some attention to implementability, including representing
the static predicate in the symbol table in finite space. One implementability
criteria might be that a static (discrete) predicate should be representable as
a list of ranges, where the number of ranges in the list is proportional to the
size of the predicate expression.

Clearly you can represent "X >= 1" as a single range (1..Int'Last), and "X <=
10" as a single range. "And" means "intersection" and "or" means union the
ranges.

There are actually very few operators which cause the number of ranges to be
disproportional to the size of the predicate.  "mod"/"rem" in particular are
nasty ones, as are some of the bit-wise and/or/xor operators.  For example,
"mod" is bad because "X mod 10 = 0" produces 2*int'last/10 ranges, roughly.

Not many other operators pose much of a challenge as far as computing the
ranges.  I would disallow the operators that break the proportionality rule, and
allow all the others.

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

From: Tucker Taft
Date: Tuesday, October 5, 2010  1:34 PM

FWIW, in our compiler, we already have a list-of-ranges representation we use
when checking case statements for gaps or overlaps, so perhaps in some
implementations this same list-of-ranges representation could be used to
represent static predicates.  You would need to implement the "and" operation
(intersection). Presumably you already have an "or" operation that operates on
list-of-ranges since that is what happens each time you see another choice list
in the case statement.

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

From: Robert Dewar
Date: Tuesday, October 5, 2010  4:54 PM

Well I just implemented the set membership form for now, I am dubious about
adding more bells and whistles!

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

From: Steve Baird
Date: Tuesday, October 5, 2010  12:53 PM

> So anyway, how about it other-arg-folks .. cast your vote :-) Do you
> want to see two separate aspects here one for general predicates one
> for what Randy likes to call set predicates, and I like to call static
> predicates, or one single aspect as now, with a recognition of static
> forms corresponding to a very set and simple syntax (set membership
> with static right sides).

I agree with Robert.

I'd like to see rules pertaining to garden-variety subtype predicates along the lines (at least in effect) of
    "and if the predicate happens to satisfy the following
     additional conditions, then they can be used in
     the following additional ways".

Randy, reasonably enough, might respond - "of course that would be nice, but the
devil is in the details - we need a specific proposal".

I'll happily leave that to Bob.

Incidentally, I assume that the rules for case statements and variant parts
would remain consistent - I hope we are not talking about allowing these
subtypes in one of these contexts and not the other.

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

From: John Barnes
Date: Wednesday, October 6, 2010  1:55 AM

I have not followed this in intimate detail becuase I have been too busy writing
the Rationale for the bits that are settled (and that I do understand).

However, I am in favour of just one feature with perhaps special features in
some circumstances.Much easier to explaiin than having two quite separate things
which seem to overlap. Could be very confusing for most users.

Back to the Rat.

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

From: Robert Dewar
Date: Wednesday, October 6, 2010  2:11 AM

> Incidentally, I assume that the rules for case statements and variant
> parts would remain consistent - I hope we are not talking about
> allowing these subtypes in one of these contexts and not the other.

Yes, of course, and also for choices in aggregates. In the GNAT implementation,
these are handled by a single common generic, so the changes for predicates
affect all three in an identical manner.

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

From: Robert Dewar
Date: Wednesday, October 6, 2010  2:20 AM

> Not many other operators pose much of a challenge as far as computing
> the ranges.  I would disallow the operators that break the
> proportionality rule, and allow all the others.

Seems like over-complexification to me, compared to a simple syntactic rule. I
find the current set of rules on what is and what is not static for expressions
to be a nasty mess. I hate to replicate the same kind of thing here, and it will
definitely add a considerable burden to the implementation. Is it really worth
it, given that 9x% of the use of static predicates will be for non-contiguous
enumeration subtypes for which the set notation is perfectly adequate.

Why do we always have to take simple things and make them over-complex in an
attempt to over-generalize in ways that are not really useful.

That being said, I think a critical part of the argument above is the 9x% guess.
If it is really the case that the predicate feature will be used extensively for
things like non-zero integer, I certainly agree that

    subtype Non_Zero_Integer is Integer with
      Predicate =>
        Non_Zero_Integer /= 0;

is clearer and neater than

    subtype Non_Zero_Integer is Integer with
      Predicate =>
        Non_Zero_Integer in
          Integer'First .. -1 | +1 .. Integer'Last;

The question is .. how often will this sort of thing occur in practice. I am
guessing very rarely (I have never for example felt any need for this particular
non-zero-integer subtype in an actual program -- it is of course a neat concept
-- but to me it is just that a neat concept, not a useful feature).

And if you do generalize, it seems you still have arbitrary restrictions, if you
for instance exclude mod 2 on the basis of implementation difficulties.

I am *NOT* suggesting we allow

    subtype Even_Integer is Integer with
      Predicate => Even_Integer mod 2 = 0;

to be considered static

since indeed it would be a nightmare to implement.
But if your notion of a static predicate is a subset known at compile time, it
seems arbitrary to say that this particular (obviously known at compile time)
subset is not static.

Given we end up with arbitrary rules anyway, I think that having a very simple
syntactic rule makes things easier for the programmer without significantly
restricting expressive power.

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

From: Robert Dewar
Date: Wednesday, October 6, 2010  2:28 AM

> FWIW, in our compiler, we already have a list-of-ranges representation
> we use when checking case statements for gaps or overlaps, so perhaps
> in some implementations this same list-of-ranges representation could
> be used to represent static predicates.  You would need to implement
> the "and" operation (intersection).
> Presumably you already have an "or" operation that operates on
> list-of-ranges since that is what happens each time you see another
> choice list in the case statement.

We have a list of ranges too, but we don't have even a general OR operation, we
just keep the list of ranges sorted, and that's good enough to check for
overlaps and gaps.

So we would have to implement both a general AND and a general OR on lists of
ranges, plus all the mechanism for multiple lists of ranges. Could be done, but
would be a significant implementation effort, probably considerably more work
than has been done for static predicates in their current restricted form.

Is this extra work worthwhile? I am dubious!

BTW, the initial GNAT implementation for static predicates (that will be in our
Ada 2012 preview release) so far looks like this:

A predicate is static if it has the syntactic form of a set membership with all
static alternatives (obviously we could add to this later if the consensus is to
generalize).

Static predicates can be used in case statements, variant parts, case
expressions and aggregates in the obvious way.

You can loop through a static predicated type (but NOT through a non-static
predicated type). We could forbid this (or more likely put it under control of
the -gnatX extension switch) later if we decide to forbid it, but thinking
things through, I think it does make sense to go this far (but not allow these
things as array indexes).

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

From: Tucker Taft
Date: Wednesday, October 6, 2010  2:38 AM

> The question is .. how often will this sort of thing occur in
> practice. I am guessing very rarely (I have never for example felt any
> need for this particular non-zero-integer subtype in an actual program
> -- it is of course a neat concept -- but to me it is just that a neat
> concept, not a useful feature).

It is pretty common in my experience to have a type with a value that means
"undefined" and it would seem pretty typical to have a predicate that
represented "not undefined."  In the same way we have "not null."

Similarly, there might be a couple of values not handled by a given routine, and
it is most natural to write:

    subtype Simple_Expr_Kind is Expr_Kind
       with Predicate =>
         Simple_Expr_Kind /= Membership_Kind
        and then
         Simple_Expr_Kind /= Relational_Kind;

Having to convert this to three ranges would be a pain, and a maintenance burden
if additional Expr_Kinds are added.

I agree we probably end up with something that seems a bit arbitrary, but I find
only allowing membership is a bit *too* arbitrary for my taste.  Including
relational and equality operators (and "not in"), joined by logical operators,
seems very straightforward to implement.  Perhaps going beyond that is overkill.

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

From: Tucker Taft
Date: Wednesday, October 6, 2010  2:39 AM

> BTW, the initial GNAT implementation for static predicates (that will
> be in our Ada 2012 preview release) so far looks like this:
>
> A predicate is static if it has the syntactic form of a set membership
> with all static alternatives (obviously we could add to this later if
> the consensus is to generalize). ...

Do you allow "not in"?

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

From: Robert Dewar
Date: Wednesday, October 6, 2010  3:01 AM

> Similarly, there might be a couple of values not handled by a given
> routine, and it is most natural to write:
>
>      subtype Simple_Expr_Kind is Expr_Kind
>         with Predicate =>
>           Simple_Expr_Kind /= Membership_Kind
>          and then
>           Simple_Expr_Kind /= Relational_Kind;

Well that would be a bit strange, since the natural way to write this would be

           with Predicate =>
             Simple_Expr_Kind not in
               Membership_Kind | Relational_Kind;

:-) [I know, takes getting used to new features] and of course NOT IN would not
be static either.

> Having to convert this to three ranges would be a pain, and a
> maintenance burden if additional Expr_Kinds are added.

That's a strong argument

> I agree we probably end up with something that seems a bit arbitrary,
> but I find only allowing membership is a bit *too* arbitrary for my
> taste.  Including relational and equality operators (and "not in"),
> joined by logical operators, seems very straightforward to implement.
> Perhaps going beyond that is overkill.

"very straightforward to implement"

Sure, no conceptual problems, but a LOT of rather tricky code with lots of
special cases to do it efficiently (if we could use bit vectors that would be
easier, but not too appetizing for subtypes of 64-bit integer :-))

I am tending in the direction of agreeing that we need this generalization.

I really think we could allow ONLY
AND and OR (and not XOR)

the short circuit forms I would disallow, they make no sense in a static
definition of a subset!

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

From: Robert Dewar
Date: Wednesday, October 6, 2010  3:08 AM

> Do you allow "not in"?

No, not currently, but anything is possible, I agree that NOT IN at least is a
strong argument, having to figure out positive ranges is unacceptable.

Once you allow NOT IN, you have a fairly big generalization anyway, and you
really want to allow something like

     subtype Special_Euro_Zone is European_Countries
       with Predicate =>
         Special_Euro_Zone in Euro_Zone
           and
         Special_Euro_Zone /= Greece;

If you allow general use of OR and AND, then there is no reason to forbid
inheritance from subtypes that themselves have static predicates, allowing the
above to be written

     subtype Special_Euro_Zone is Euro_Zone
       with Predicate =>
         Special_Euro_Zone /= Greece;

I am convinced by Tuck's arguments, I think we have to generalize.

a) allow inheriting from static predicates

b) allow static equality and comparisons (yes, Randy, they aren't really static, since they have an occurrence of the type, but I think everyone knows what we mean here)

c) allow set membership in static list (IN and NOT IN)

d) allow AND/OR

Is that enough?
It's already a huge amount of work, I doubt we will get this into the 2012
preview, but that does not matter.

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

From: Edmond Schonberg
Date: Wednesday, October 6, 2010  9:59 AM

...
> I agree we probably end up with something that seems a bit arbitrary,
> but I find only allowing membership is a bit *too* arbitrary for my
> taste.  Including relational and equality operators (and "not in"),
> joined by logical operators, seems very straightforward to implement.
> Perhaps going beyond that is overkill.

To my mind, membership is not arbitrary at all, it's exactly what is needed.  In
way, leaving aside the fact that we are speaking about finite sets only, this is
like the distinction between recursive sets and recursively enumerable sets. In
one case you have an effective procedure to generate the elements of the set, in
the other you have a predicate that you have to apply to a (much larger) domain
to determine whether an element of that domain belongs in the set.  If the set
is recursive, iterating over it is a natural thing to do.  So my choice would be
to say that a predicate is static if it is given by an extended membership
expression (no negation allowed).  This gives you case statements and loops
without semantic complications, and the description in the RM is one sentence.

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

From: Tucker Taft
Date: Wednesday, October 6, 2010  10:37 AM

> ...  So my choice would be to say that a predicate is static if it is
> given by an extended membership expression (no negation allowed).
> This gives you case statements and loops without semantic
> complications, and the description in the RM is one sentence.

Including "not in" seems very straightforward, and eliminates a maintenance
headache involved with specifying the multiple ranges necessary to leave exactly
the right gaps.  "not in" would at least address the most basic case of "X /=
Blah_Kind and X /= Blur_Kind", via "X not in Blah_Kind | Blur_Kind".

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

From: Edmond Schonberg
Date: Wednesday, October 6, 2010  10:42 AM

I guess the compiler could easily transform this into a sequence of ranges. As
long as it is a single membership test (possibly negated) with no other
operators in sight it would be OK to declare this static. The description is a
few tokens longer....

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

From: Robert Dewar
Date: Wednesday, October 6, 2010  5:13 PM

> To my mind, membership is not arbitrary at all, it's exactly what is
> needed.  In way, leaving aside the fact that we are speaking about
> finite sets only, this is like the distinction between recursive sets
> and recursively enumerable sets. In one case you have an effective
> procedure to generate the elements of the set, in the other you have a
> predicate that you have to apply to a (much larger) domain to
> determine whether an element of that domain belongs in the set.  If
> the set is recursive, iterating over it is a natural thing to do.  So
> my choice would be to say that a predicate is static if it is given by
> an extended membership expression (no negation allowed).  This gives
> you case statements and loops without semantic complications, and the
> description in the RM is one sent

As you know, this is what I was arguing earlier, but I have changed my mind
after reading Tuck's example. The most potent of these is this kind of thing:

     type European_Country is (England, Andorra, .....)

     subtype Signifiant_European_Country is
       European_Country with
         Predicate
           European_Countrg not in Andorra | Lichtenstein;

Requiring you to do this with a positive set membership is a bit painful.

Either you list all the entries, or you work out the three ranges depending on
the order, neither is nearly as clearly self-documenting, and either of these
work arounds is a maintenance headache, because you need to modify the subtype
if you add a new significant european country.

I really think the more general form is desirable, and it is not that hard to
implement (I am currently implementing a full form including

o  inheritance of static predicates (and'ed with new
    predicate)

o  primaries are IN NOT-IN equality and comparisons

o  Allowed connectors are NOT AND OR AND-THEN OR-ELSE XOR

Really not that hard to do and much more general
     Defined_Country is Country with
       Defined_Country /= No_Country;

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

From: Robert Dewar
Date: Wednesday, October 6, 2010  5:14 PM

> Including "not in" seems very straightforward, and eliminates a
> maintenance headache involved with specifying the multiple ranges
> necessary to leave exactly the right gaps.  "not in"
> would at least address the most basic case of "X /= Blah_Kind and X /=
> Blur_Kind", via "X not in Blah_Kind | Blur_Kind".

For me once you have NOT IN, you might as well generalize further, I definitely
agree that NOT IN should be included.

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

From: Jean-Pierre Rosen
Date: Thursday, October 7, 2010  3:56 AM

> Hmmm, reminds me of JDI, who would furiously hold on to his position
> even when it was N-1 against :-).

Yes - but he was right (keeping derived types, IIRC)!

> So anyway, how about it other-arg-folks .. cast your vote :-)

I didn't speak up because I'm uncertain...
On one hand, having things like case statements allowed only for static cases is
familiar. OTOH, having a different notion of staticness just for this case seems
strange (and hard to teach, which is a concern for me).

If the issue is with huge sets, wouldn't it be a pity to restrict the feature
for reasonably sized sets? Allow general syntax for types with less than an
implementation-defined limit (required by the standard to be at least 256,
f.e.)?

> Do you want to see two separate aspects here one for general
> predicates one for what Randy likes to call set predicates, and I like
> to call static predicates, or one single aspect as now, with a
> recognition of static forms corresponding to a very set and simple
> syntax (set membership with static right sides).

If static really means static, I prefer one aspect. If there is a totally
different definition of "static", I'm not so sure.

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

From: Robert Dewar
Date: Thursday, October 7, 2010  5:48 AM

> If the issue is with huge sets, wouldn't it be a pity to restrict the
> feature for reasonably sized sets? Allow general syntax for types with
> less than an implementation-defined limit (required by the standard to
> be at least 256, f.e.)?

I don't think that's a good approach. The mod stuff is really a bit of a red
herring, I don't think it will come up in practice, but the excluded bad value
case is definitely common.

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

From: Robert Dewar
Date: Thursday, October 7, 2010  6:33 AM

> If static really means static, I prefer one aspect. If there is a
> totally different definition of "static", I'm not so sure.

Well even if you try to generalize further you need special rules.
As Randy has pointed out the expression here will never be a static expression
exactly in the 4.9 sense, since it contains references to the type, e.g.

      subtype X is Integer with
        predicate X /= 0;

Obviously X /= 0 is not a static expression, since what the heck is X in 4.9
terms, so special rules are needed in any case (and of course these special
rules are needed whether or not you use a separate aspect name for static
predicates).

Again my set of rules that I proposed is

The expression consists of

   a) set membership (IN or NOT IN), where the left side is
      an instance of the subtype name, and the right side is
      a list of static expressions or static subtype names.

   b) comparison op with one side being an instance of the subtype
      name, and the other side is a static expression.

   c) static expressions (True is at least useful!)

   d) recursively expressions allowed by these rules connected
      by AND or OR or NOT.


Additionally

   a) the range of the subtype itself must be static

   b) any inherited predicate must itself be static

I don't think these rules are hard to teach, they seem very straightforward to
me.

Up in the air

   a) allow AND THEN/OR ELSE with same meaning as AND/OR
      obviously no side effects are in sight. I don't care
      on this one.

   b) allow XOR, useless but someone may argue for completeness

This set of rules is easily implementable, and seems quite general enough. I do
think the inheritance of static predicates is useful, and that means allowing
AND in any case.

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

From: Steve Baird
Date: Thursday, October 7, 2010  11:31 AM

> Again my set of rules that I proposed is
>
> The expression consists of
>
>   a) ...
>
>   b) ...
>
>   c) ...
>
>   d) recursively expressions allowed by these rules connected
>      by AND or OR or NOT.

and presumably recursively an expression allowed by these rules enclosed in
parens. Just a nit, but we wouldn't want this left out of the final wording.

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

From: Randy Brukardt
Date: Thursday, October 7, 2010  12:00 PM

...
> and presumably recursively an expression allowed by these rules
> enclosed in parens. Just a nit, but we wouldn't want this left out of
> the final wording.

and presumably qualified expressions where the expression is allowed by these
rules and the named subtype is static. (Those always go together with
parenthesized expressions in the wording, and we surely wouldn't want to prevent
the use for overloading resolution.)

I presume we're not going to include conditional expressions (both if and
case) here, even if the condition or selector_expression is static. That seems
like too much work. (Recall that conditional expressions usually work like
parenthesizes.)

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

From: Steve Baird
Date: Thursday, October 7, 2010  1:02 PM

> and presumably qualified expressions where the expression is allowed
> by these rules and the named subtype is static. (Those always go
> together with parenthesized expressions in the wording, and we surely
> wouldn't want to prevent the use for overloading resolution.)
>

You may be right, but we need to think about this one (at least briefly).

This introduces the possibility of an exception during the testing of a "static"
predicate. How do we handle exception-raising values in the coverage rules?

Given

    subtype S is Integer with predicate Natural'(S) not in 123 .. 456;

    X : S := ... ;
  begin
    case X is

, what choices need to be covered here? Does the qualified expression play no
role in determining the set of values that need to be covered, so that in this
example, we might have

    when Integer'First .. 122 => Foo;
    when 457 .. Integer'Last => Bar;
   end case;

?

This shouldn't be a big problem, but, as usual, we need to be sure that
    a) we know what we want
    b) the RM wording captures that intent .

> I presume we're not going to include conditional expressions (both if
> and
> case) here, even if the condition or selector_expression is static.
> That seems like too much work. (Recall that conditional expressions
> usually work like parenthesizes.)

Agreed.

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

From: Steve Baird
Date: Thursday, October 7, 2010  1:04 PM

> and presumably qualified expressions where the expression is allowed
> by these rules and the named subtype is static.

Could you provide an example illustrating why we would want to allow this?

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

From: Robert Dewar
Date: Thursday, October 7, 2010  1:18 PM

...
> and presumably recursively an expression allowed by these rules
> enclosed in parens. Just a nit, but we wouldn't want this left out of
> the final wording.

Rignt, and a qualified expression as well I would think, the usual special cases
:-)

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

From: Robert Dewar
Date: Thursday, October 7, 2010  1:19 PM

> I presume we're not going to include conditional expressions (both if
> and
> case) here, even if the condition or selector_expression is static.
> That seems like too much work. (Recall that conditional expressions
> usually work like parenthesizes.)

I would think that's not worth the effort, but no strong feeling

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

From: Robert Dewar
Date: Thursday, October 7, 2010  1:20 PM

> Could you provide an example illustrating why we would want to allow
> this?

It just seems that one should ALWAYS be able to qualify expressions without
affecting things, and that this should be a quite general rule. Probably it is
not possible to provide a useful example :-)

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

From: Randy Brukardt
Date: Thursday, October 7, 2010  1:26 PM

> Could you provide an example illustrating why we would want to allow
> this?

Other than consistency with the rest of the language, you mean?

Aspects generally resolve to "any boolean type". That means there is a
(slight) chance of ambiguity. So I was thinking of something like:

    Boolean'(X /= 10 and X /= 20)

so that the "and" is clearly Boolean and not some other boolean type that might
be defined with a set of user-defined operators.

But my primary concern was consistency. Generally, a qualified expression is
allowed anywhere parens are, and it seems wrong to not preserve that, especially
when ambiguity is possible.

You're right that there is something weird about allowing it directly on the
subtype name. I'd argue the same applies to parens: why would anyone want to
write
    (X) /= 10
??

So perhaps neither should be allowed directly on the subtype name. (After all,
we aren't allowing other operators, either, so there isn't anything to be
consistent with here -- the rules are what we make them.)

What I think is important is that:

    with predicate => X /= 10 and X /= 20

and

    with predicate => (X /= 10) and (X /= 20)

and

    when predicate => Boolean'(X /= 10) and Boolean'(X /= 20)

all work the same, because they *are* the same to any experienced Ada
programmer.

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

From: Tucker Taft
Date: Thursday, October 7, 2010  1:27 PM

I don't think we should allow a qualified expression to be applied to the
subtype name.  That is unnecessary complexity, since the subtype name is not
overloadable. I wouldn't allow it to be applied to any expression containing the
subtype name.  Any expression *not* containing the subtype name should allow
anything at all, so long as it is a static expression.  So I don't agree with
the idea of allowing qualified expressions anywhere we allow parentheses.  Ditto
for conversions, and other complexities.  As soon as the subtype name gets
involved in the expression, there should be very tight limits.

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

From: Robert Dewar
Date: Thursday, October 7, 2010  1:45 PM

> What I think is important is that:
>
>      with predicate =>  X /= 10 and X /= 20
>
> and
>
>      with predicate =>  (X /= 10) and (X /= 20)
>
> and
>
>      when predicate =>  Boolean'(X /= 10) and Boolean'(X /= 20)
>
> all work the same, because they*are*  the same to any experienced Ada
> programmer.

I agree with this, and it is trivial to implement

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

From: Robert Dewar
Date: Thursday, October 7, 2010  1:49 PM

> I don't think we should allow a qualified expression to be applied to
> the subtype name.  That is unnecessary complexity, since the subtype
> name is not overloadable.
> I wouldn't allow it to be applied to any expression containing the
> subtype name.  Any expression *not* containing the subtype name should
> allow anything at all, so long as it is a static expression.  So I
> don't agree with the idea of allowing qualified expressions anywhere
> we allow parentheses.  Ditto for conversions, and other complexities.
> As soon as the subtype name gets involved in the expression, there
> should be very tight limits.

I agree that parenthesizing the subtype name is silly, but I see no reason not
to allow qualification of expressions containing it like Boolean'(S > 12).

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

From: Tucker Taft
Date: Thursday, October 7, 2010  1:50 PM

A useful property of membership, comparison, logical ops, and parentheses, is
that there is no possibility for failure.  Allowing a qualified expression to be
applied to an expression involving the subtype name (current instance) would
introduce the possibility of raising an exception, which definitely adds
unnecessary complexity.  I think we should limit ourselves to never-failing
operations for the parts which are non-static expressions, namely the operations
having the subtype current instance as an operand or sub-operand.

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

From: Robert Dewar
Date: Thursday, October 7, 2010  3:05 PM

I see the point, makes sense, let's eliminate this oddity.

P.S. when discussions get down to this level, one senses some convergence in
agreement on the main ideas, which is good, since I am right now implementing
them. But it's only a preview, we can always change our minds later :-)

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

From: Steve Baird
Date: Thursday, October 7, 2010  2:59 PM

Is it intended that imposing a static predicate on a static subtype results in
another static subtype ?

For example:

    subtype S1 is Integer with (S1 in 1 .. 10) or (S1 = -1);

    Named_Number constant := Boolean'Pos (123 in S1); -- legal?

I think we want this.

Given

    subtype Truth is Boolean range True .. True;

    subtype S2 is Integer with predicate
      (S2 = 0) or Truth'(S2 not in -10 .. 10)

would the following be rejected

     X : constant := Boolean'Pos (0 in S2);

due to 4.9's

   For a static expression that is evaluated:
     The expression is illegal if its evaluation fails a language-defined
     check other than Overflow_Check.
?

I see no definitional problems with this - I just wanted to be clear about the
consequences of allowing exception-raising static predicates.

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

From: Robert Dewar
Date: Thursday, October 7, 2010  3:08 PM

> Is it intended that imposing a static predicate on a static subtype
> results in another static subtype ?
>
> For example:
>
>      subtype S1 is Integer with (S1 in 1 .. 10) or (S1 = -1);
>
>      Named_Number constant := Boolean'Pos (123 in S1); -- legal?
>
> I think we want this.

Seems reasonable ...

> I see no definitional problems with this - I just wanted to be clear
> about the consequences of allowing exception-raising static predicates.

Ugly, let's not allow qualification, as suggested by Tuck, why introduce the
complexity of exception-raising static predicates for a language feature of no
conceivable use, which would be there just for unnecessary consistency reasons
:-)

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

From: Randy Brukardt
Date: Thursday, October 7, 2010  3:17 PM

> A useful property of membership, comparison, logical ops, and
> parentheses, is that there is no possibility for failure.
> Allowing a qualified expression to be applied to an expression
> involving the subtype name (current instance) would introduce the
> possibility of raising an exception, which definitely adds unnecessary
> complexity.  I think we should limit ourselves to never-failing
> operations for the parts which are non-static expressions, namely the
> operations having the subtype current instance as an operand or
> sub-operand.

That was actually my intent - it was why I added "static subtype" to my
description. Qualification is usually used for disambiguation, and there is no
intent to raise an exception. It's not possible for Boolean'(<some expr allowed
by these rules>) to raise an exception, for instance.

So perhaps the rule ought to be "qualified expressions where the expression is
allowed by these rules and the named subtype is static and has the base range of
its type". Or alternatively: "qualified expressions where the expression is
allowed by these rules and the named subtype is static and no subtype check can
fail" (this would allow Natural'(X) if X is a subtype of Natural).

But I do feel pretty strongly that if we are allowing parenthesized expressions
that we should also be allowing qualified expressions that also don't raise
exceptions. Otherwise, we risk a situation where it's hard to eliminate a
resolution problem from a predicate (because doing so makes the predicate
non-static -- that's the maintenance objection that I've had to this particular
concept from the beginning).

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

From: Tucker Taft
Date: Thursday, October 7, 2010  3:38 PM

> But I do feel pretty strongly that if we are allowing parenthesized
> expressions that we should also be allowing qualified expressions that
> also don't raise exceptions. Otherwise, we risk a situation where it's
> hard to eliminate a resolution problem from a predicate (because doing
> so makes the predicate non-static -- that's the maintenance objection
> that I've had to this particular concept from the beginning).

I have trouble imagining how this could happen.  Since you will be able to apply
a qualified expression to all of the static operands, and the subtype name is
not overloadable, why would you need to apply it to the result as well? We
aren't allowing user-defined operators to be called, since they wouldn't be
static.  How can we get into a situation where you need to qualify the result?

If we do feel the need, I would go with allowing qualification by unconstrained
subtypes and constrained scalar subtypes whose range is the base range. But I
have real trouble imagining the need...

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

From: Randy Brukardt
Date: Thursday, October 7, 2010  4:42 PM

...
> How can we get into a situation where you need to qualify the result?

I was imagining that there exists a user-defined boolean type:

    package My_Bool_Pack is
        type My_Bool is new Standard.Boolean;

        function "and" (Left, Right : My_Bool) return My_Bool;
        ... other operators here ...
    end My_Bool_Pack;

    with My_Bool_Pack;
    package Another_Pack is
        type An_Int is range ...;
        function "=" (Left, Right : An_Int) return My_Bool_Pack.My_Bool;
        -- Remember, the "Boolean" versions are declared here automatically.
    end Another_Pack;

    with My_Bool_Pack, Another_Pack; use My_Bool_Pack, Another_Pack;
    procedure Try_It is
        subtype Foo is An_Int with predicate => Foo /= 10 and Foo /= 20; -- !!
    begin
        ...

In this case, the only way to ensure that Standard.Boolean is used is to qualify
it (the expression is ambiguous as written); qualifying the operands doesn't
help. And the result is static if and only if it is qualified as Boolean
(My_Bool would definitely not be static).

I'm not going to claim this is common, but I could imagine it happening (the key
seems to be the overloading on another boolean type). It just seems wrong to say
that if someone did this elsewhere in your system, you can't define a static set
subtype. (I also suspect that there are more likely ways to cause this problem,
this was just the first way that came to mind.)

> If we do feel the need, I would go with allowing qualification by
> unconstrained subtypes and constrained scalar subtypes whose range is
> the base range.
> But I have real trouble imagining the need...

I don't think this is the sort of thing that is going to happen very often.
But one of the important points of Ada is programming in large, where there is
always a way to workaround whatever bizarreness you've inherited from some other
subsystem. I think it would be wrong to not have some way to get a static set
subtype.

This doesn't concern me anywhere near as much as the fact that if we *don't*
allow qualification here,

     subtype Foo is An_Int
        with predicate => Boolean'(Foo /= 10) and Foo /= 20;

would not be a static predicate, but there isn't any indication at the
declaration point that this intent failed. Instead, you'll get an error far away
when you try to use it in a case statement or loop. And the reason that this
isn't static surely isn't obvious!

With the error handling system in Janus/Ada, the error message will be cryptic
at best (we don't have a way to refer to things declared outside of the current
unit). I presume GNAT will do better, but it still looks as if it will be at
least partly mysterious.

That's why I wanted an indication at the point of the declaration that this was
intended to be a static subtype. After all, this seems very similar to the
situation with overriding (where whatever you declare will be legal, but it
might not do what you want or cause problems in far-away code). We ended up
inventing overriding indicators to reduce the problems with that. I don't want
to repeat that with this feature.

Anyway, a mitigation of this problem is to allow more sorts of predicate
expressions to be static. That makes it less likely that that maintenance or
just structuring will cause the predicate to be non-static when the programmer
intended it to be static. (It's impossible to reduce this possibility to zero,
of course, so my concern will always exist.)

I had suggested marking these with a separate aspect name, but that doesn't seem
to fly. I don't have any other good ideas as to how to mark the programmers
intentions here (a pragma is *not* a good idea, sorry), but I'm still convinced
that is necessary.

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

From: Robert Dewar
Date: Thursday, October 7, 2010  4:50 PM

> If we do feel the need, I would go with allowing qualification by
> unconstrained subtypes and constrained scalar subtypes whose range is
> the base range.
> But I have real trouble imagining the need...

But the need must be based on a realistic example, not some abstract notion of
being able to add junk qualifications without causing maintenance problems.

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

From: Robert Dewar
Date: Thursday, October 7, 2010  4:55 PM

> But I do feel pretty strongly that if we are allowing parenthesized
> expressions that we should also be allowing qualified expressions that
> also don't raise exceptions. Otherwise, we risk a situation where it's
> hard to eliminate a resolution problem from a predicate (because doing
> so makes the predicate non-static -- that's the maintenance objection
> that I've had to this particular concept from the beginning).

I see the (minor) point here, but blowing this up into a serious maintenance
problem sees really overdrawn. I am willing to bet that if an implementation
fails to allow qualified expressions here, the bug will never be noticed except
by an over-enthusastic ACATS test.

That being said, I have no problem with any of the possible resolutions of this
issue, since I don't think it matters at all, and any of the possibilities is
trivial to implement :-)

For now, I will allow qualifications :-)

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

From: Jean-Pierre Rosen
Date: Monday, October 11, 2010  8:26 AM

> I had suggested marking these with a separate aspect name, but that
> doesn't seem to fly. I don't have any other good ideas as to how to
> mark the programmers intentions here (a pragma is *not* a good idea,
> sorry), but I'm still convinced that is necessary.

Inventing new syntax is such fun! What about:

with predicate => [constant] <expression>; or
with predicate => constant'(<expression>);

Note that there are certainly other places where the ability to specify that an
expression is expected to be static might be useful.

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

From: Bob Duff
Date: Monday, October 11, 2010  9:05 AM

> > I had suggested marking these with a separate aspect name, but that
> > doesn't seem to fly. I don't have any other good ideas as to how to
> > mark the programmers intentions here (a pragma is *not* a good idea,
> > sorry), but I'm still convinced that is necessary.

For what it's worth, if you want to say, "I'd like to place a restriction on
myself (or on my team), dear compiler please complain (at compile time if
possible) if I violate that restriction", a pragma is the perfect tool.

This is one of those "good taste" pragmas that can be removed or ignored without
causing any trouble.

I don't think it's worth the trouble, but I have no objection if some compiler
wants to implement such a pragma, and if it gets heavily used, we can
standardize in 2015 or 2099 or whenever.

> Inventing new syntax is such fun! What about:
>
> with predicate => [constant] <expression>; or with predicate =>
> constant'(<expression>);
>
> Note that there are certainly other places where the ability to
> specify that an expression is expected to be static might be useful.

Well, I'd prefer not to add to the confusion between "constant" and "static".

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

From: Robert Dewar
Date: Monday, October 11, 2010  4:48 PM

> Inventing new syntax is such fun! What about:
>
> with predicate =>  [constant]<expression>; or with predicate =>
> constant'(<expression>);
>
> Note that there are certainly other places where the ability to
> specify that an expression is expected to be static might be useful.

I really find the confusion this would cause between constant and static
horrible. I further find it unnecessary to have any syntax here.

As I have said before, we already have

    X : constant Integer := complicated expression;

and in some remote package

    case B is

       ...
       when X => ...

legal? Depends on whether complicated expression meets the (very complex and
non-intuitive IMO) rules in 4.9.

So what's the big deal about

    subtype X is Integer with
      predicate X = complicated expression;

and we use the subtype name in a case

The situation to me is identical. If the second upsets you, then the first one
should upset you equally.

That's why a pragma is a nice solution, because you can't fix the first case
with syntax here, but you could have a pragma that fixed it

    pragma Static (local_NAME);

if name is a constant object, checks its value is static

if name is a subtype, checks its bounds are static, and if it has a predicate,
that the predicate is static.

I don't think we should add such a pragma to the definition right now, but it
seems a reasonable thing for an implementation to add (we are planning to add
this in GNAT).

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

From: Bob Duff
Date: Monday, October 11, 2010  5:36 PM

> I don't think we should add such a pragma to the definition right now,
> but it seems a reasonable thing for an implementation to add (we are
> planning to add this in GNAT).

I don't see the point of such a pragma.  Sure, changing from static to nonstatic
can cause faraway errors.  But changing the VALUE of a static expression can
cause faraway errors, too. Same for changing the range (or predicate!) of a
static subtype.

If somebody says "when X =>" in a case statement, then changing X to nonstatic
is no more likely to cause trouble than changing the value (or range, or static
predicate) of X!

Basically, if you change the visible part of a package, you can affect clients.
If you don't like that, then use deferred constants, private types, etc.

P.S. Don't forget that these faraway errors are a GOOD thing.  It's what the
full-coverage/no-overlap rules are all about.

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

From: Bob Duff
Date: Friday, October  8, 2010  4:58 PM

> The whole idea of static predicates is a new one and not really
> related to the existing proposal (it's much more related to set
> constraints than predicates).

FWIW, I disagree with that assessment (from a technical point of view).

>... The only reason we didn't approve AI05-0153-1 in Valencia was  that
>we didn't agree on the wording for the new restrictions on composite
>predicate expressions. So I would prefer to have the existing AI
>reflect the  decisions of that meeting exactly; and have a separate
>alternative with the  more complex proposal that is now being crafted
>here. That way, we can see  if it really is more complex or not.

Well, the restrictions imposed in Valencia are a radical departure from what I
originally wrote.  If anything, THAT version of the AI should be split out as a
separate proposal.

There's nothing in the minutes that gives any hint as to why these restrictions
were approved.  At first I thought they were an attempt to make sure that
predicates are (like constraints) always true for all valid values of the
subtype.  But those restrictions don't even come close to ensuring that, and
"valid" is meaningless for composites, so I ended up thinking, "What were they
thinking?!".

It was only recently that I read the Valencia minutes, and I was surprised to
find that Tucker has an action item for ai05-0153. I thought it was my
responsibility.

I am about to send a new version of ai05-0153, which (1) fixes bugs in
ai05-0153-1, and (2) incorporates the functionality from ai05-0153-2.  I've read
all the emails in those two AI's, plus a whole bunch that came in since emails
were filed.

I'd prefer my new version be a new version of ai05-0153-1, but call it -3 or -4
if you insist.  (With the nonexistent Valencia proposal being -4 or -3.  Do we
really need this proliferation of alternatives?!)

For the record, I am strongly opposed to any proposal that doesn't allow
function calls -- the subprogram is the most important abstraction facility
invented in the history of computer science!

> There is no significant extra work involved in this, given that all
> I'm asking is to keep the Valencia proposal around intact.

There is no Valencia proposal, other than in the minutes!
In any case, all old versions of all AI's are kept in CVS.

Please read my new proposal before commenting on this rant.  ;-)

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

From: Randy Brukardt
Date: Saturday, October  9, 2010 12:23 AM

(You asked me to read your proposal before commenting, but since the questions
in this message are procedural, and about the technical content of the Valencia
meeting, I don't quite know why. Nor do I have time to do that now; quite
possibly I won't read it until right before the meeting. So I answered these
now.)

...
> > The whole idea of static predicates is a new one and not really
> > related to the existing proposal (it's much more related to set
> > constraints than predicates).
>
> FWIW, I disagree with that assessment (from a technical point of view).

I'd like to know why (without reading a 77K document and trying to guess from
that). But let's discuss that off-line; I don't want to have to record the
ensuing discussion and it isn't likely to be relevant to the alternative
proposal.

> >... The only reason we didn't approve AI05-0153-1 in Valencia was
> >that we didn't agree on the wording for the new restrictions on
> >composite predicate expressions. So I would prefer to have the
> >existing AI reflect the  decisions of that meeting exactly; and have
> >a separate alternative with the  more complex proposal that is now
> >being crafted here. That way, we can see  if it really is more complex or not.
>
> Well, the restrictions imposed in Valencia are a radical departure
> from what I originally wrote.  If anything, THAT version of the AI
> should be split out as a separate proposal.

Sorry, but this originally was my idea, and I wrote the first couple versions of
this AI. It later got assigned to you, and you surely did work on it, but you
have no claim of originality.

But that's irrelevant in any case. I want this alternative to reflect what the
ARG decided, not what Bob Duff by himself wants. That's no more relevant that
what any other individual ARG members want.

> There's nothing in the minutes that gives any hint as to why these
> restrictions were approved.  At first I thought they were an attempt
> to make sure that predicates are (like
> constraints) always true for all valid values of the subtype.
>  But those restrictions don't even come close to ensuring that, and
> "valid" is meaningless for composites, so I ended up thinking, "What
> were they thinking?!".

Yes, that is exactly what we were thinking: it's an attempt to ensure that
predicates don't become False for composite values (so that they work more like
constraints). Essentially, if the predicate check succeeds for a composite
value, it will continue to succeed until the entire value is changed.

But I don't understand why you say they don't come close to accomplishing that.
It is possible with these restrictions to build a predicate that depends on
global state that changes, and that would cause trouble. But I can't imagine
anyone thinking that would be a good idea in any case. It is *not* possible with
these restrictions to build a predicate that depends on any part of the
composite value other than the bounds and discriminants, so there is no
possibility of a component change making the predicate turn False.

Note that there was a lot of discomfort with the idea that predicates don't hold
for composites except at the instant of checking. These rules were an attempt to
deal with that discomfort. It did not appear that the proposal was going to
reach a consensus without some attempt to deal with this problem (if you have
*not* dealt with this problem, your proposal is already on shaky ground).

I suspect that we have much more support for static scalar predicates (which are
really set constraints with a different syntax :-) than we do for the dynamic
composite predicates. So this whole proposal might be in trouble. OTOH, people
may have changed their minds since Valencia. Or maybe there is some other way to
make this work. You were not at the meeting, and I can't record everything that
is said (or record the looks on people's faces, etc.), so I realize that you are
surely missing some of the nuances (and maybe the entire idea if I did a lousy
job of explaining it in the minutes). But let me say that I think your previous
positions on this proposal are pretty much at the fringe of the net ARG
position, and a consensus is likely to require a more moderate position.

> It was only recently that I read the Valencia minutes, and I was
> surprised to find that Tucker has an action item for ai05-0153.
> I thought it was my responsibility.

I don't know why. We're trying to finish these AIs, and when they are done, I
usually get them to make the final editorial revisions. In this case, we
believed that AI05-0153-1 was finished except for a paragraph of wording to be
provided by Tucker. One solitary paragraph. So it was assigned to Tucker for
that paragraph, and then I was going to make the other minor changes.

Now, I agree that subsequent discussion has changed that assumption of it being
finished, but of course that doesn't retroactively change the work assignments!

Keep in mind that we assigned you two other critical AIs in part because we
didn't think you had any other homework. By not figuring out what you were
supposed to be working on, you've doubled your workload (or potentially delayed
the completion of the Amendment). So please read the minutes next time!

> I'd prefer my new version be a new version of ai05-0153-1, but call it
> -3 or -4 if you insist.  (With the nonexistent Valencia proposal being
> -4 or -3.  Do we really need this proliferation of alternatives?!)

The Valencia proposal will be -1, and yours will be -3. We surely don't need any
other version, because those versions were discussed and considered not
acceptable in meetings. What would the point be? But we surely need to keep a
version with some attempt to make them more like constraints -- a number of
members deemed this important.

...
> > There is no significant extra work involved in this, given that all
> > I'm asking is to keep the Valencia proposal around intact.
>
> There is no Valencia proposal, other than in the minutes!
> In any case, all old versions of all AI's are kept in CVS.

Where else would it be? People in this group are notorious for doing their
homework at the last minute. There surely will be a proposal, because I'll get
wording from Tucker (or do it myself) and then update the AI.

I consider this very important because it is an attempt (maybe not one that will
work out) to address a concern that has prevented a number of members from
supporting the predicate proposal. Even if it ultimately is rejected, we need to
keep track of the attempt. And, as you are noting, there is not yet any proposal
in the CVS -- so that has to be done. And I'm not going to try to somehow do it
on top of your new proposal, that way lies madness (mine, at least). Thus, it is
better to make your unrequested proposal a separate alternative (even if it more
nearly reflects the thinking of the recent e-mail threads).

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

From: Bob Duff
Date: Saturday, October  9, 2010 11:32 AM

> > FWIW, I disagree with that assessment (from a technical point of view).
>
> I'd like to know why (without reading a 77K document and trying to
> guess from that). But let's discuss that off-line; I don't want to
> have to record the ensuing discussion and it isn't likely to be
> relevant to the alternative proposal.

I don't think there's anything to discuss.  I've stated my opinion.  You're the
editor, and you plan to overrule me. Nothing wrong with that, and nothing
further to discuss.

> > Well, the restrictions imposed in Valencia are a radical departure
> > from what I originally wrote.  If anything, THAT version of the AI
> > should be split out as a separate proposal.
>
> Sorry, but this originally was my idea, ...

Heh?  I certainly didn't intend to imply any originality!
In fact, I don't think I put ANYTHING original in this AI -- I got it all from
the existing AI versions, and the emails, from you and many others.

My "what I originally wrote" above is simply a reference to version 6 of the AI
(that is, AI05-0153-1/06, dated 10-05-19), which I wrote, based heavily on your
previous work.

I realize that your version was an attempt to show what a stupid idea "general
predicate expressions" is.  Nothing wrong with that, but I obviously disagree
with the conclusion.  The Valencia restrictions are a radical departure, in the
sense that it's no longer "general".

>...and I wrote the first couple
> versions of this AI. It later got assigned to you, and you surely did
>work  on it, but you have no claim of originality.

Right, I have no claim of originality.

> But that's irrelevant in any case. I want this alternative to reflect
> what the ARG decided, not what Bob Duff by himself wants. That's no
> more relevant that what any other individual ARG members want.
>
> > There's nothing in the minutes that gives any hint as to why these
> > restrictions were approved.  At first I thought they were an attempt
> > to make sure that predicates are (like
> > constraints) always true for all valid values of the subtype.
> >  But those restrictions don't even come close to ensuring that, and
> > "valid" is meaningless for composites, so I ended up thinking, "What
> > were they thinking?!".
>
> Yes, that is exactly what we were thinking: it's an attempt to ensure
> that predicates don't become False for composite values (so that they
> work more like constraints).

OK, I think I see.  The ARG is understandably nervous about the fact that
predicates can surprisingly become false.  ARG attempted to pass some laws
fixing that, but failed, and then passed those laws anyway.

Note that predicates can surprisingly become false for scalars, too, and access
types, so it's not clear to me why the nervousness is focused on composites.

>...Essentially, if the predicate check succeeds for a  composite value,
>it will continue to succeed until the entire value is  changed.

That's false, as you note below (global variables!).

> But I don't understand why you say they don't come close to
> accomplishing that. It is possible with these restrictions to build a
> predicate that depends on global state that changes, and that would cause
> trouble.

Right, that's why.  Forbidding refs to components, while allowing refs to
globals doesn't accomplish the goal.

Also, "Predicate => Is_Good(Cur_Inst)" can depend on components.
It's not clear to me whether that was intended to be forbidden in Valencia.

>... But I
> can't imagine anyone thinking that would be a good idea in any case.

But it makes no sense to say "we need rules to prevent X", and then turn around
and say "we don't really need to prevent X, because nobody would think it's a
good idea to do X".

Either you trust programmers to write sensible predicates, or you FORCE
programmers to write sensible predicates -- no middle ground can make sense.

So...  You're sucking me into a technical discussion without having read my new
proposal, which I was hoping to avoid.  I think I'd better wrap this up, until
you and others have had a chance to read it. I know it's long -- sorry about
that!

> OTOH, people may have changed their minds since Valencia. Or maybe
> there is some other way to make this work. You were not at the
> meeting, and I can't record everything that is said (or record the
> looks on people's faces, etc.), so I realize that you are surely
> missing some of the nuances (and maybe the entire idea if I did a lousy job of
> explaining it in the minutes).

I'm NOT saying you did a lousy job!  Your note-taking skills are far superior to
mine, for what that's worth.  (I never took notes in school, other than to write
down the homework assignment. I can't seem to pay attention to what's being said
if I also try to write it down.)

But the fact remains that I can't understand exactly what restrictions were
approved at Valencia.  All I can say is I'm strongly opposed to having
restrictions that attempt to forbid Bad Things if the restrictions don't
actually prevent those Bad Things.  Any restrictions that allow Steve's favorite
example ("subtype T is ... with Predicate => T > Calendar.Clock") are misguided.

> But let me say that I think your previous positions on this proposal
> are pretty much at the fringe of the net ARG position, and a consensus
> is likely to require a more moderate position.

I tried to produce a more moderate position, but in recent days, people (Robert,
Ed, Tucker, you, perhaps others) have been pushing for more and more
functionality (more cases when predicates are considered static, more allowed
uses of static ones ('for' loops), so I put all that in).

> > It was only recently that I read the Valencia minutes, and I was
> > surprised to find that Tucker has an action item for ai05-0153.
> > I thought it was my responsibility.
>
> I don't know why. We're trying to finish these AIs, and when they are
> done, I usually get them to make the final editorial revisions. In
> this case, we believed that AI05-0153-1 was finished except for a
> paragraph of wording to be provided by Tucker. One solitary paragraph.
> So it was assigned to Tucker for that paragraph, and then I was going to make the other minor changes.

Surely that paragraph requires some rationale -- a statement of what the
restrictions try to prevent, along with an argument that they do, in fact,
prevent such things.

> Now, I agree that subsequent discussion has changed that assumption of
> it being finished, but of course that doesn't retroactively change the
> work assignments!
>
> Keep in mind that we assigned you two other critical AIs in part
> because we didn't think you had any other homework. By not figuring
> out what you were supposed to be working on, you've doubled your
> workload (or potentially delayed the completion of the Amendment). So
> please read the minutes next time!

Consider me suitably admonished.

I plan to do the rest of my ARG homework next week, unless some AdaCore
customers cause emergencies I need to deal with.

> The Valencia proposal will be -1, and yours will be -3.

You're the editor -- it's your decision.

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

From: Robert Dewar
Date: Saturday, October  9, 2010 12:00 PM

For me, the only really important parts of the predicate proposal are for scalar
types (both static and dynamic predicates are useful), and for composites, only
restrictions on the set of possible discriminants seem useful.

I don't mind them supporting more general stuff, providing the attempt at
generality does not compromise what to me are the useful uses :-)

BTW, here is the current general implementation of static predicates in the GNAT
Ada 2012 preview edition. Note that GNAT now only allows loops over static
predicates, so the fact that this program compiles means it recognized all
predicates as static. The loops sure are useful for testing purposes :-)

Test program:

> with Text_IO; use Text_IO;
> procedure Predicate_Loops is
>    type Int is range 1 .. 10;
>
>    subtype P1 is Int with
>      predicate =>
>        P1 in 3 | 5 .. 7 | 10 | 5 .. 6 | 6;
>
>    subtype P2 is Int with
>      predicate => P2 > 6;
>
>    subtype P3 is Int with
>      predicate => ((P3 > 6) and P3 < 9);
>
>    subtype P4 is Int with
>      predicate => (P4 in P2) and P4 < 10;
>
>    subtype P5 is Int with
>      predicate => P5 < 3 or P5 > 8;
>
>    subtype P6 is P5 with
>      predicate => P6 /= 1 and P6 /= 10;
>
>    subtype P7 is Int with
>      predicate => Boolean'(P7 not in 1 .. 7);
>
>    subtype P8 is Int with
>      predicate => not (P8 = 3 or else P8 in 5 .. 10);
>
>    subtype P9 is Int with
>      predicate => P9 in 1 .. 8 xor P9 in 3 .. 10;
>
>    type Enum is (A,B,C,D,E,F,G,H,I,J);
>
>    subtype E1 is Enum with
>      predicate =>
>        E1 in C | E .. G | J | E .. F | F;
>
>    subtype E2 is Enum with
>      predicate => E2 > F;
>
>    subtype E3 is Enum with
>      predicate => ((E3 > F) and E3 < I);
>
>    subtype E4 is Enum with
>      predicate => (E4 in E2) and E4 < J;
>
>    subtype E5 is Enum with
>      predicate => E5 < C or E5 > H;
>
>    subtype E6 is E5 with
>      predicate => E6 /= A and E6 /= J;
>
>    subtype E7 is Enum with
>      predicate => Boolean'(E7 not in A .. G);
>
>    subtype E8 is Enum with
>      predicate => not (E8 = C or else E8 in E .. J);
>
>    subtype E9 is Enum with
>      predicate => E9 in A .. H xor E9 in C .. J;
>
>    subtype T1 is Int with
>      predicate => True;
>
>    subtype T2 is Int with
>      predicate => False;
>
>    subtype T3 is Enum with
>      predicate => True;
>
>    subtype T4 is Enum with
>      predicate => False;
>
>
> begin
>    for J in P1 loop
>       Put_Line ("P1:" & J'Img);
>    end loop;
>
>    for J in P2 loop
>       Put_Line ("P2:" & J'Img);
>    end loop;
>
>    for J in P3 loop
>       Put_Line ("P3:" & J'Img);
>    end loop;
>
>    for J in P4 loop
>       Put_Line ("P4:" & J'Img);
>    end loop;
>
>    for J in P5 loop
>       Put_Line ("P5:" & J'Img);
>    end loop;
>
>    for J in P6 loop
>       Put_Line ("P6:" & J'Img);
>    end loop;
>
>    for J in P7 loop
>       Put_Line ("P7:" & J'Img);
>    end loop;
>
>    for J in P8 loop
>       Put_Line ("P8:" & J'Img);
>    end loop;
>
>    for J in P9 loop
>       Put_Line ("P9:" & J'Img);
>    end loop;
>
>    for J in E1 loop
>       Put_Line ("E1:" & J'Img);
>    end loop;
>
>    for J in E2 loop
>       Put_Line ("E2:" & J'Img);
>    end loop;
>
>    for J in E3 loop
>       Put_Line ("E3:" & J'Img);
>    end loop;
>
>    for J in E4 loop
>       Put_Line ("E4:" & J'Img);
>    end loop;
>
>    for J in E5 loop
>       Put_Line ("E5:" & J'Img);
>    end loop;
>
>    for J in E6 loop
>       Put_Line ("E6:" & J'Img);
>    end loop;
>
>    for J in E7 loop
>       Put_Line ("E7:" & J'Img);
>    end loop;
>
>    for J in E8 loop
>       Put_Line ("E8:" & J'Img);
>    end loop;
>
>    for J in E9 loop
>       Put_Line ("E9:" & J'Img);
>    end loop;
>
>    for J in T1 loop
>       Put_Line ("T1:" & J'Img);
>    end loop;
>
>    for J in T2 loop
>       Put_Line ("T2:" & J'Img);
>    end loop;
>
>    for J in T3 loop
>       Put_Line ("T3:" & J'Img);
>    end loop;
>
>    for J in T4 loop
>       Put_Line ("T4:" & J'Img);
>    end loop;
>
> end Predicate_Loops;

Output:

> P1: 3
> P1: 5
> P1: 6
> P1: 7
> P1: 10
> P2: 7
> P2: 8
> P2: 9
> P2: 10
> P3: 7
> P3: 8
> P4: 7
> P4: 8
> P4: 9
> P5: 1
> P5: 2
> P5: 9
> P5: 10
> P6: 2
> P6: 9
> P7: 8
> P7: 9
> P7: 10
> P8: 1
> P8: 2
> P8: 4
> P9: 1
> P9: 2
> P9: 9
> P9: 10
> E1:C
> E1:E
> E1:F
> E1:G
> E1:J
> E2:G
> E2:H
> E2:I
> E2:J
> E3:G
> E3:H
> E4:G
> E4:H
> E4:I
> E5:A
> E5:B
> E5:I
> E5:J
> E6:B
> E6:I
> E7:H
> E7:I
> E7:J
> E8:A
> E8:B
> E8:D
> E9:A
> E9:B
> E9:I
> E9:J
> T1: 1
> T1: 2
> T1: 3
> T1: 4
> T1: 5
> T1: 6
> T1: 7
> T1: 8
> T1: 9
> T1: 10
> T3:A
> T3:B
> T3:C
> T3:D
> T3:E
> T3:F
> T3:G
> T3:H
> T3:I
> T3:J

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

From: Randy Brukardt
Date: Sunday, October 10, 2010  10:19 PM

(I'm going to engage in some meta-technical discussion, but I'm going to try to avoid talking about a specific proposal, to avoid "sucking" Bob into a
discussion...)

Bob Duff writes:
...
> >...Essentially, if the predicate check succeeds for a composite
> >value, it will continue to succeed until the entire value is  changed.
>
> That's false, as you note below (global variables!).

I don't think we care about global variables. (More below.)

> > But I don't understand why you say they don't come close to
> > accomplishing that. It is possible with these restrictions to build
> > a predicate that depends on global state that changes, and
> that would cause trouble.
>
> Right, that's why.  Forbidding refs to components, while allowing refs
> to globals doesn't accomplish the goal.
>
> Also, "Predicate => Is_Good(Cur_Inst)" can depend on components.
> It's not clear to me whether that was intended to be forbidden in
> Valencia.

My view of the rule (not necessarily the same as what everyone else was thinking; the rule was only partially worded) is that it banned any reference to the current instance other than discriminants and bounds. Thus the above would not be allowed.

> >... But I
> > can't imagine anyone thinking that would be a good idea in any case.
>
> But it makes no sense to say "we need rules to prevent X", and then
> turn around and say "we don't really need to prevent X, because nobody
> would think it's a good idea to do X".
>
> Either you trust programmers to write sensible predicates, or you
> FORCE programmers to write sensible predicates -- no middle ground can
> make sense.

I think you are setting up a straw man here.

It is unlikely that there are any rules (for anything) which can prevent all
intentional misuse. (For example, the container reference tampering checks won't
work if someone does an unchecked deallocation of the reference object.) What
rules can prevent is accidental misuse, and are especially important if there is
some reason to expect users to want to do something that won't work.

In the case of predicates (in the abstract, not referring to any particular
proposal), there are a number of things that can be done that "won't work" (i.e.
cause a change in result of a predicate without a recheck):

(1) Dependence on the current instance of an elementary type (not counting
    dereference) and dependence on bounds and discriminants of a current
    instance (or a dereference). Always OK.

(2) Dependence on non-discriminant component values of a current instance (or a
    dereference). This is a dangerous case, because it is likely to be written
    either on purpose by a misquided programmer or by accident. And this is
    exactly the case where the programmer would expect the predicate to either
    still hold or be rechecked (neither of which is possible for single
    component assignments).

(3) Dependence on globals that don't change such that the function calls (or
    direct object references) always return the same value for the same
    arguments. Memo functions are in this category. Obviously, these don't
    present any problem, so we don't need to prevent such references.

(4) Dependence on globals that are expected to change frequently. Function calls
    to Clock and Random are in this category. I can't imagine any useful use for
    such a predicate; these are clearly pathologies and thus are "intentional
    misuse". We don't need to try to prevent such cases (if we can, great).

(5) Finally, dependence on globals that change rarely. This category includes
    program bugs (functions/objects that are supposed to be in category 3 but
    are broken), but I suppose it might include something done intentionally.
    These also might be dangerous, but whether this category is important
    depends on whether you think that much other than bugs will fall into it. In
    my programs at least, there aren't many globals that would fall into this
    category (it's a good thing to avoid these whether or not predicates are
    involved), so the number of non-pathological cases possible would be quite
    small.

Thus, evaluation of possible rules comes down to two things: how serious you
think the problem is, and how likely you think category (5) is. It certainly is
consistent to think that category (5) is a very small set [thinking that most
globals falling into category (3) or (4)], and thus concentrate only on category
(2). Moreover, there is a pragmatic issue here: we don't have the tools to
detect category (5) problems, but that doesn't necessarily mean that we should
just throw up our hands about the much more common category (2) problems.

Now, if *I* were running the circus (to steal someone's turn of phrase), we'd
have checked global in/global out annotations, and calling functions or using
global variables would be prohibited in predicates (and
preconditions/postconditions/invariants). And then the checking would be pretty
airtight (and we still could allow function calls in many cases). But we don't
have that, so we are going to have to settle for a less airtight solution (which
might be no solution at all).

End of meta-technical discussion...

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

From: Bob Duff
Date: Monday, October 11, 2010  8:53 AM

> (I'm going to engage in some meta-technical discussion, but I'm going
> to try to avoid talking about a specific proposal, to avoid "sucking"
> Bob into a
> discussion...)

I'm puzzled -- you seem to talk pure "technical" below.
I can't resist getting sucked in.  ;-)

> I don't think we care about global variables. (More below.)

Well, my evil twin, who is a rude and sarcastic kind of guy, says:

    I lock my front door when I go out, to keep people from
    sneaking into my house and stealing my stuff.  I keep
    the back door open, though, because I don't want the
    hassle of unlocking it when I return.

    In fact, I squirted epoxy glue into the front door keyhole,
    which prevents even my own legitimate use of the front door,
    but it sure does prevent burglars from using the front door!

Seriously, this reminds me of the "functions can't have 'inout'
params, but they can do what they like with globals" idea.
I even understand that point of view, while disagreeing with it.

> > Also, "Predicate => Is_Good(Cur_Inst)" can depend on components.
> > It's not clear to me whether that was intended to be forbidden in
> > Valencia.
>
> My view of the rule (not necessarily the same as what everyone else
> was thinking; the rule was only partially worded) is that it banned
> any reference to the current instance other than discriminants and
> bounds. Thus the above would not be allowed.

I've been playing around with the predicate feature in GNAT for the past few
days, partly to test Robert's work, and partly just for the fun of it. I've
tried to write "typical" uses of the feature. FWIW, I've written quite a few
functions of the Is_Good nature above.  Some refer to discrims, some to other
components.

Hence, if this restriction exists, it seems like "glue in the keyhole".

> > Either you trust programmers to write sensible predicates, or you
> > FORCE programmers to write sensible predicates -- no middle ground
> > can make sense.
>
> I think you are setting up a straw man here.

Not deliberately.

I really want a feature that can express general properties of things.
I often want to refer to components, I often want to call functions, and I
sometimes want to refer to globals.

The closer we get to forbidding the "bad" cases, the more "good" cases get
forbidden.  And any in-between position is doubly frustrating: I can't do what I
want, and I *still* don't get the "predicates always true" assurance.

> It is unlikely that there are any rules (for anything) which can
> prevent all intentional misuse. (For example, the container reference
> tampering checks won't work if someone does an unchecked deallocation
> of the reference object.)

Now THAT part seems like a strawman.  We all know that chap-13-ish features mean
"all bets are off", for predicates or any other proposed feature.  So of course
I understand the "predicates always true" goal as "predicates always true unless
you use unchechked_blah and the like".

I snipped the rest (but I did read it carefully).  Two points:

1. I can think of a few cases where using global vars in predicates
   is reasonable.

2. I object to the term "dangerous" when referring to predicates
   that might not ALWAYS be true.  These aren't like array bounds checks,
   where missing one case can trash memory.  Nothing particularly
   bad happens, and the predicate will likely get checked soon -- as
   soon as you pass the thing as a parameter.

3. The alternative is putting in pragmas Assert.  Those are no better;
   they'll become false sometimes, but eventually (you hope) you'll
   trip over another Assert.  In other words, the predicate feature
   is useful despite loopholes.

4. Make that three points.

5. Constraints have a giant loophole, too: uninitialized variables.
   Note that even discriminants can contain uninitialized junk.

> Now, if *I* were running the circus (to steal someone's turn of
> phrase),

I stole it from Steve, who stole it from Dr. Seuss.

> we'd have checked global in/global out annotations,...

I agree we could do a much better job if we had globals annotations.
Keep in mind that we might have them someday, either as a language feature, or
as a proof-tool-specific feature (see SPARK).

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

From: Bob Duff
Date: Monday, October 11, 2010  3:36 PM

In Valencia, one concern was that if you say "X.Comp := ...;", then the
predicate of X is not checked.  The predicate of Comp is, but not the object as
a whole.

One way to address this would be to say it *is* checked, if the left-hand side
of an assignment is a selected or indexed component.  Also if an actual for an
[in]out param is such a component.

There would still be loopholes, but plugging this obvious one might make some
people (including me, I admit!) happier.

I'm not sure it's a good idea, but it's far better than the baby/bathwater ideas
of Valencia (which of course ALSO imply that no predicate can be checked on
"X.Comp := ...;", because there can be none!).

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

From: Robert Dewar
Date: Monday, October 11, 2010  4:40 PM

To me it seems horrible to allow predicates to mention components and then not
check on component assignment. Makes no sense at all.

I am quite happy with a solution that forbids predicates from mentioning
components, or otherwise restricts things. Really to me the ONLY useful use of
predicates for composites is in checking discriminants. Otherwise I think
invariants handle things just fine.

But if this is allowed, it seems obvious to me it should be checked on a
component assignment.

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

From: Bob Duff
Date: Monday, October 11, 2010  4:47 PM

> To me it seems horrible to allow predicates to mention components and
> then not check on component assignment.
> Makes no sense at all.

I tend to write a lot of code where record and array components are "mostly
constant".  They get initialized, and change little or none afterward.

So why don't I make all the components into discriminants?
Well, discriminants are restricted in various ways so it won't usually work.

Anyway, in this scenario, predicates referencing components makes a lot of sense
(and checking on assignment into components could perhaps make sense as well).

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

From: Robert Dewar
Date: Monday, October 11, 2010  5:22 PM

That's the thing, IF you allow this, then I think you have to check on
assignment into components, and this seems a LOT of overhead if you only use the
predicate to check the discriminants.

Ah, missing language feature (constant components) :-)

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

From: Bob Duff
Date: Monday, October 11, 2010  5:48 PM

> That's the thing, IF you allow this, then I think you have to check on
> assignment into components, and this seems a LOT of overhead if you
> only use the predicate to check the discriminants.

Heh?

There's no overhead for checking on assignment into components in my scenario --
I don't do that (or only rarely).

OK, I suppose this depends on a not-yet-implemented optimization:
don't check predicates when they depend on things that can't change.

Anyway, I don't care much about efficiency of assertion checks in general --
there's always the fallback of turning them off.

I say, "If your assertions are efficient enough for production mode, then you
don't have enough assertions."

> Ah, missing language feature (constant components) :-)

Look at Ada (circa) 1979!  There were constant components, which were
more-or-less like discriminants.  Or maybe it was still Green then.  ;-)

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

From: Robert Dewar
Date: Tuesday, October 12, 2010  11:29 AM

> OK, I suppose this depends on a not-yet-implemented optimization:
> don't check predicates when they depend on things that can't change.

HUGELY hard to do, since predicates call bodies that you cannot see, so this has
to be done at link time!

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

From: Bob Duff
Date: Tuesday, October 12, 2010  11:39 AM

No, no, no.  My idea is to optimize at compile time, and only if the relevant
information is available. E.g. here's my favorite predicate of all (which I wish
applied to String itself!):

    subtype Good_String is String with
        Predicate => Good_String'First = 1;

'First can never change.

    procedure P(S: in out Good_String) is
    begin
        if S /= "" then
            S(1) := To_Upper(S(1)); -- (*)
        end if;
    end P;

If we require predicate checks on component assignments, the predicate check at
(*) can be optimized away, because the predicate does not depend on any
component.

That may or may not be hard, but it doesn't require any link-time work!

If you change "Good_String'First = 1" to "Is_Good(Good_String)", then you don't
get any optimization (unless perhaps if Is_Good is inlined?).

And as I said, if we don't implement such optimizations, that's OK because I can
always turn off the checks.  Optimizing checks is IMHO merely a "nice to have".

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

From: Robert Dewar
Date: Tuesday, October 12, 2010  11:47 AM

...
> 'First can never change.

Right, but we are talking here about the composite case with record components,
most likely such invariants would be written as function calls I would think.

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

From: Bob Duff
Date: Tuesday, October 12, 2010  12:02 PM

Then I'm not sure what your point is.

Efficiency doesn't matter -- only distributed overhead matters.

That is, this sub-discussion is about whether we should restrict predicates on
composite types as discussed in Valencia.  We don't have a precise definition of
what those restrictions are, but they probably forbid refs to non-discrim
components (perhaps forbidding function calls).

It makes no sense to say "checks on predicates that refer to non-discrim
components are inefficient, so we should forbid such predicates".

It MIGHT make sense to say "checks on predicates that refer only to
bounds/discrims will be slower if we allow more general predicates".  This is a
distributed overhead argument.

Maybe an example would clarify your point.

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

From: Robert Dewar
Date: Tuesday, October 12, 2010  12:21 PM

...
> It MIGHT make sense to say "checks on predicates that refer only to
> bounds/discrims will be slower if we allow more general predicates".
> This is a distributed overhead argument.

Yes, that's my point, if we insist on checking on component assignment, that's
totally useless in the bounds/discrims case, but will add overhead.

> Maybe an example would clarify your point.

     Predicate => Typ.Discrim = Red;

Now any assignment to a component will check the discriminant which is silly. Of
course in this case you MAY be able to optimize some cases away, but if the
tests on the discriminants are hidden in a function you won't be able to!

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

From: Bob Duff
Date: Tuesday, October 12, 2010  7:08 PM

OK, I understand your point now, and it's correct.

However, note that Randy thinks the Valencia proposal forbids such "hiding in a
function".

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

From: Randy Brukardt
Date: Tuesday, October 12, 2010   9:37 PM

> > (I'm going to engage in some meta-technical discussion, but I'm
> > going to try to avoid talking about a specific proposal, to avoid "sucking"
> > Bob into a
> > discussion...)
>
> I'm puzzled -- you seem to talk pure "technical" below.
> I can't resist getting sucked in.  ;-)

I was trying to avoid any discussion of any particular proposal, and just look
at the general issues. Even the general issues have some technical content...

...
> Seriously, this reminds me of the "functions can't have 'inout'
> params, but they can do what they like with globals" idea.
> I even understand that point of view, while disagreeing with it.

I personally would prefer to ban access to globals as well. Tucker actually
suggested something on that line (only allow references to constants), but it
seemed too holey (there are many constants that aren't really constant, plus we
couldn't do anything useful about the function case).

...
> > > Either you trust programmers to write sensible predicates, or you
> > > FORCE programmers to write sensible predicates -- no middle ground
> > > can make sense.
> >
> > I think you are setting up a straw man here.
>
> Not deliberately.
>
> I really want a feature that can express general properties of things.
> I often want to refer to components, I often want to call functions,
> and I sometimes want to refer to globals.
>
> The closer we get to forbidding the "bad" cases, the more "good" cases
> get forbidden.  And any in-between position is doubly frustrating:
> I can't do what I want, and I *still* don't get the "predicates always
> true" assurance.

If there was a rule that would in fact be implementable and ensured the
"predicates are always true", I'd be strongly in favor of it.

But part of the problem is most of what you say you want to do is *exactly* what
I don't think you ought to be doing (more below). So I'm not surprised there is
some tension...

...
> > It is unlikely that there are any rules (for anything) which can
> > prevent all intentional misuse. (For example, the container
> > reference tampering checks won't work if someone does an unchecked
> > deallocation of the reference
> > object.)
>
> Now THAT part seems like a strawman.  We all know that chap-13-ish
> features mean "all bets are off", for predicates or any other proposed
> feature.  So of course I understand the "predicates always true" goal
> as "predicates always true unless you use unchechked_blah and the
> like".

I don't agree in the sense that I don't think it is very valuable to treat
chapter 13 as some sort of second-class citizen. And I suspect that if you asked
6 Ada lawyers what features were included in this all-bets are off category,
you'd get 9 answers.

I much prefer to think of this in terms of pathologies. Doing an unchecked
deallocation of a reference to defeat the protection is a pathology -- no one is
going to do it by accident or expect it to work. However, doing an
unchecked_conversion to get the representation of an enumeration value is not a
pathology, and saying that "all bets are off" just because there is a safe
occurrence of Unchecked_Conversion around does not seem helpful. And the same is
true for address clauses and AtoA and a lot of other chapter 13 things.

In this case, I'm certain that calling a random number generator in a predicate
is a pathology. Only Ada lawyers would think to do so, and it has no possible
use. I don't see this case as different than abusing Unchecked_Conversion.

> I snipped the rest (but I did read it carefully).  Two points:
>
> 1. I can think of a few cases where using global vars in predicates
>    is reasonable.

Please give one or more examples. I cannot think of any such cases, and some
compelling examples would be a big help in moving the discussion along.

> 2. I object to the term "dangerous" when referring to predicates
>    that might not ALWAYS be true.  These aren't like array bounds checks,
>    where missing one case can trash memory.  Nothing particularly
>    bad happens, and the predicate will likely get checked soon -- as
>    soon as you pass the thing as a parameter.
>
> 3. The alternative is putting in pragmas Assert.  Those are no better;
>    they'll become false sometimes, but eventually (you hope) you'll
>    trip over another Assert.  In other words, the predicate feature
>    is useful despite loopholes.

I don't think these things are at all equivalent. I think everyone knows that
Assert means "check this here". And that they aren't going to provide any sort
of protection. But when putting predicates on subtypes, I think there is an
expectation that they work much more like a constraint (that is, they hold so
long as the object has the subtype). Subtypes *do* provide that sort of
protection in Ada (modulo validity and erroneousness), and it is unfortunate to
lose that property.

That was *surely* my original intent; I wanted these to be checked in the same
places as constraints, to be optimizable the same as constraints, and so on.
It's the fact that this hasn't worked out that makes me so uncomfortable.

As such, I think referencing anything but the current instance and true
constants (and pure functions) in a predicate is dubious. Especially because
doing so invalidates optimizations (in general, you have to keep rechecking a
predicate even if you've already proved it true).

> 4. Make that three points.

Or 5. :-)

> 5. Constraints have a giant loophole, too: uninitialized variables.
>    Note that even discriminants can contain uninitialized junk.

Only in erroneous programs. All bets are off really does mean that.

I'm not very interested in programs that are using pragma Suppress, or use
broken address clauses, or whatever. There isn't much that can be done to help
those. But I surely worry about programmers who wear their seatbelts (constraint
checking, predicate checking, etc.) all the time -- I don't want them getting
bogus results. Especially as those are likely to turn into support calls...

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

From: Randy Brukardt
Date: Tuesday, October 12, 2010  10:01 PM

> In Valencia, one concern was that if you say "X.Comp := ...;", then
> the predicate of X is not checked.  The predicate of Comp is, but not
> the object as a whole.
>
> One way to address this would be to say it *is* checked, if the
> left-hand side of an assignment is a selected or indexed component.
> Also if an actual for an [in]out param is such a component.
>
> There would still be loopholes, but plugging this obvious one might
> make some people (including me, I admit!) happier.
>
> I'm not sure it's a good idea, but it's far better than the
> baby/bathwater ideas of Valencia (which of course ALSO imply that no
> predicate can be checked on "X.Comp := ...;", because there can be
> none!).

I'm pretty sure it is not a good idea, at least from an implementation
perspective.

The issue here is that this would be the first check in Ada that would occur
*after* assignment. That would force the compiler to generate/keep a lot of
information that it currently does not. That happens because the compiler cannot
in general evaluate an Ada "Name" twice (I'm referring to the syntactic entity
"Name" here).

To give a concrete example of what I mean:

Consider the following call:

        P (F1(1).Arr(F2).Int);

where P is procedure P (O : in out Integer);
      F1 is function F1 (O : Integer) return Rec1;
      F2 is function F2 return Integer;
      Arr_Type is type Arr_Type (1..10) of Rec2;
      Arr is a component of the type of Rec1, of Arr_Type;
      Int is an Integer component of the type of Rec2;
      And both Rec1 and Rec2 are subtypes that have a predicate.

Currently, the only check done after the call is the back assignment check,
which is done before assigning the component. In order to do that back
assignment, the address of the object passed to the parameter has to be saved
somewhere during the call. That's important as the body of P might change the
result of F1 or F2, and we have to assign back into the same object that was
passed as a parameter. (Note that the same effect can occur if F1 and F2 are
replaced by global variables.)

Now, to do predicate checks here, we would also have to save the addresses of
the prefixes F1(1) and F1(1).Arr(F2) before the call for use afterward. This is
necessary for the same reason: F1 and F2 might be different after the call. This
is definitely a new mechanism; I'm pretty sure that Janus/Ada doesn't have any
way to evaluate part of a Name. (I've had to do it for dereferences, and the
solution was to chop off the .all part of the Name and then evaluate normally.
But that requires generating the rest of the name with new code; that's not
tough for .all alone, but gets to be a major mess if the rest is
".Arr(F2).Int".)

[I believe that invariants escape this problem by only checking at the package
interface, so only the passed object need be checked, not some prefix.]

A similar problem arises to evaluate assignments:

    F1(1).Arr(F2).Int := 10;

would require saving the addresses of the prefixes F1(1) and F1(1).Arr(F2) when
the LHS is evaluated so that predicate checks can be done after the assignment
is finished.

This is clearly going to take significant implementation work, especially as the
overhead is occurring in a common place such that a lot of optimization effort
would be justified. (I know we have very complex predicates which we use to
determine when we can dispense with the saving of the address of the object
passed as an "in out" parameter; similar efforts would be needed here.)

This isn't a show-stopper, but it promises to make predicates substantially more
expensive to implement than the current proposals.

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

From: Robert Dewar
Date: Wednesday, October 13, 2010  2:05 AM

> Only in erroneous programs. All bets are off really does mean that.

Uninitialized variables do not make a program erroneous!

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

From: Robert Dewar
Date: Wednesday, October 13, 2010  2:20 AM

They do result in invalid values, which are of course exactly analogous to
values that do not meet their predicates. But this failure of a value to meet
its constraints does NOT of itself make a program erroneous.

The statements about variables not meeting their predicates should be analogous
to the statements in the invalid values section. Basically an invalid value may
not meet its predicate, so what?

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

From: Bob Duff
Date: Wednesday, October 13, 2010  7:53 AM

> I'm pretty sure it is not a good idea, at least from an implementation
> perspective.

What if we allowed, instead of required, implementations to check predicates at
certain additional places?

I can certainly imagine super-checking modes that check more than is required --
whether or not permission for such modes is given by the RM.

> Consider the following call:
>
>         P (F1(1).Arr(F2).Int);

This example is illegal -- function results (and their
subcomponents) are constant.  But I understand what you're saying -- you would
need to save array indices, or addresses of array components.

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

From: Bob Duff
Date: Wednesday, October 13, 2010  9:24 AM

> I personally would prefer to ban access to globals as well.

I agree that might be a useful direction to go in, but only if we have globals
annotations.  Without globals annotations, the only way to forbid accessing
globals is to forbid calls to user-defined functions, and that's way too
restrictive.

> If there was a rule that would in fact be implementable and ensured
> the "predicates are always true", I'd be strongly in favor of it.

I don't believe you.  Surely you have some other criteria for such a rule than
just "implementable"!

> But part of the problem is most of what you say you want to do is
> *exactly* what I don't think you ought to be doing (more below).

Ah, the punitive school of language design.  ;-)

What about my example of records with mostly-constant components?
Why "ought" I avoid asserting properties involving such?

> I don't agree in the sense that I don't think it is very valuable to
> treat chapter 13 as some sort of second-class citizen.

But you HAVE to.  And you DO, below, where you say "I'm not very interested in
programs that are using pragma Suppress, ...".  If you want to design a language
feature that has some useful property, you MUST allow that property to be
violated by chap-13-ish features.

>... And I suspect that if you
> asked 6 Ada lawyers what features were included in this all-bets are
>off  category, you'd get 9 answers.

Well, I think I know what I mean by "chap-13-ish".  Do I need to spell it out?

> > 1. I can think of a few cases where using global vars in predicates
> >    is reasonable.
>
> Please give one or more examples.

I have a bunch of items.  Each item has a current state, which is an enum.  For
each state, there is a collection of items in that state.  Certain state
transitions are valid.  When one item is processed (changes state), other
dependent items get moved to different states.  There are algorithms like "while
there are items in state X, process one of them".  I wish to assert that the
enum matches which collection the item is in (and it's not in two or zero
collections).

Every state transition is done by passing an item to a procedure, so don't worry
that somebody might poke the enum out of the blue, or call "Set_To_Empty" on the
collection (either of which would invalidate the predicate).  No language rule
ensures this -- it's just my design, and there's a comment saying so.

I probably don't give write access to that enum to clients.

This is a real example (simplified) that I've written more than once.
I used pragmas Assert in each procedure to ensure my predicate, and help
document it.  A Predicate saying "this is true every time you pass an item to
one of the state-changing ops" is clearly superior to Asserts, even in the
presence of loopholes.

>...Subtypes *do* provide that
> sort of protection in Ada (modulo validity and erroneousness), and it
>is  unfortunate to lose that property.

In other words, subtypes do NOT provide that protection!  I'll let you get away
with "modulo erroneousness" if you're talking about chap-13-ish features.  But
uninit vars are not a chap-13-ish feature.  I use them all over the place
(taking care not to look at them until initialized).

Uninit vars are a huge loophole in the "constraints always true" property, so I
reject the argument that predicates should have no (non-chap-13) loopholes "just
like constraints".

> That was *surely* my original intent; I wanted these to be checked in
> the same places as constraints, to be optimizable the same as
> constraints, and so on. It's the fact that this hasn't worked out that
> makes me so uncomfortable.

I agree with the discomfort.  In a from-scratch language design, I'd address it.
But Ada just doesn't have the necessary mechanisms to address it, short of
onerous restrictions.

> > 5. Constraints have a giant loophole, too: uninitialized variables.
> >    Note that even discriminants can contain uninitialized junk.
>
> Only in erroneous programs. All bets are off really does mean that.

Robert corrected your misuse of "erroneous" here, so I'll assume you mean "Only
in wrong programs".  The same is true of predicates with loopholes -- no matter
what the loopholes are, if you use them to do wrong things, then your program is
wrong.

> I'm not very interested in programs that are using pragma Suppress, or
> use broken address clauses, or whatever. There isn't much that can be
> done to help those.

So you agree with me about chap-13-ish features.

>... But I surely worry about programmers who wear their seatbelts
>(constraint checking, predicate checking, etc.) all the time -- I don't
>want  them getting bogus results. Especially as those are likely to
>turn into  support calls...

Well, yeah.  We get a lot of support calls about uninit vars.
Too bad there's no language feature to prevent those bugs.
I fixed an uninit var in my own code about a month ago -- the bug had existed
for a year, and showed up only on a certain operating system -- on most systems,
it just happened to have a value that obeyed its constraint.  I felt kind of
stupid.  :-(

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

From: Randy Brukardt
Date: Wednesday, October 13, 2010  1:37 PM

> > I'm pretty sure it is not a good idea, at least from an implementation
> > perspective.
>
> What if we allowed, instead of required, implementations to check
> predicates at certain additional places?
>
> I can certainly imagine super-checking modes that check more than is
> required -- whether or not permission for such modes is given by the
> RM.

Possible, I suppose, but I thought we had discussed that during one of the
meetings and rejected it. (But I may be misremembering.)

> > Consider the following call:
> >
> >         P (F1(1).Arr(F2).Int);
>
> This example is illegal -- function results (and their
> subcomponents) are constant.  But I understand what you're saying --
> you would need to save array indices, or addresses of array
> components.

Not necessarily: dereferences are implicit in Ada. (But I forgot to include it
in my description, so of course you are right.)

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

From: Randy Brukardt
Date: Wednesday, October 13, 2010  1:34 PM

> > Only in erroneous programs. All bets are off really does mean that.
>
> Uninitialized variables do not make a program erroneous!

I was talking about "discriminants containing garbage". That can't happen unless
the program is erroneous (discriminants are always initialized) - possibly from
reading an abnormal value. "Invalid" representations are a different issue, but
the language rules prevent problems with them.

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

From: Randy Brukardt
Date: Wednesday, October 13, 2010  2:04 PM

...
> > > 5. Constraints have a giant loophole, too: uninitialized variables.
> > >    Note that even discriminants can contain uninitialized junk.
> >
> > Only in erroneous programs. All bets are off really does mean that.
>
> Robert corrected your misuse of "erroneous" here, so I'll assume you
> mean "Only in wrong programs".  The same is true of predicates with
> loopholes -- no matter what the loopholes are, if you use them to do
> wrong things, then your program is wrong.

I stand by my original statement, but I should have elaborated on it. First, I
was responding to the "discriminants can contain uninitialized junk" statement,
not the part about uninitialized variables in general. Although I could have
covered the "abnormal" object case as well. (And no, I don't care *why* the
program is erroneous; I absolutely would not limit it to chapter 13 features
unless you take a very expansive view of what those are. Perhaps you are doing
that, I don't know because the concept is not at all well-defined.)

But invalid values (which have to be scalar) are a different kettle of fish, and
I really don't agree with your assertion. While it is obvious that they can be
out of range at a particular point in the program, the language rules ensure
that they will be checked before they can do any harm. So the only effect of
allowing invalid values in an Ada program is to move a check from one point to
another.

While I agree this is a loophole, calling it "giant" seems wrong to me. Ada
usually does operations on the base types, so you have to be prepared for this
behavior in any case (with intermediate rules, for one example). And this
"loophole" is far less troublesome than the "predicate changing values" problem,
because the check (whenever it is made) is always going to return the same
result. Thus the effect is going to be moving a check later in the program,
rather than failing to detect a problem at all.

As I've said, I want to be able to use the same mechanisms for predicates as for
constraints. That clearly includes the entire invalid value machinery, so
anything having to do with that alone is clearly OK. But assigning a component
that changes the predicate is a very different problem from invalid values (a
known valid value cannot become invalid short of erroneous execution).

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

From: Bob Duff
Date: Wednesday, October 13, 2010  4:36 PM

> I stand by my original statement, but I should have elaborated on it.
> First, I was responding to the "discriminants can contain uninitialized junk"
> statement, not the part about uninitialized variables in general.

Oh.  Sorry.  Here's what I mean:

    type Color is (Red, ...);

    type T(Discrim: Color) is ...;

    X : Color; -- uninitialized

    Y : T(Discrim => X);

This gives a possibly-invalid value to Discrim.
I don't THINK that's erroneous.  And I don't think it requires a run-time check,
since the subtype of Discrim is the same as that of X.

Am I wrong?

>...Although I
> could have covered the "abnormal" object case as well. (And no, I don't care
> *why* the program is erroneous; I absolutely would not limit it to chapter
> 13 features unless you take a very expansive view of what those are.
> Perhaps you are doing that, I don't know because the concept is not at
> all well-defined.)

It's pretty well-defined in my brain.  ;-)  I just haven't explained it in
detail.  Basically, it's the features that interface to stuff outside the
pristine Ada world (hardware, or other languages). These features are inherently
dangerous, but you can isolate them in packages.

Uninit vars are not a chap-13-ish feature, because they are not inherently
dangerous, and you can't isolate them.  They are dangerous in Ada, but not
inherently so.  (Java has a pretty-good story for uninit vars, at least in the
local case -- and the bug I mentioned that I caused a year ago and fixed a month
ago was a local uninit var, which would have been caught at compile time in
Java.)

I also include pragma Suppress (or equivalent switches) in "chap-13-ish".
It can't be isolated in the same way, but it can be controlled easily (turn the
checks back on!).

You're not alone in thinking my idea (that chap-13-ish features are "special")
is bogus.  I think Robert disagrees with me -- he says things like "address
clauses are a perfectly good feature of Ada, nothing special about them".

> But invalid values (which have to be scalar) are a different kettle of
> fish, and I really don't agree with your assertion. While it is
> obvious that they can be out of range at a particular point in the
> program, the language rules ensure that they will be checked before
> they can do any harm. So the only effect of allowing invalid values in
> an Ada program is to move a check from one point to another.

That's often the case, but the check might be moved to "never"
in some cases.

    X: Integer range 1..10;

    if ... then
        X := Func(...);
    end if;
    ...
    if ... then -- programmer hopes this condition is implied by earlier one
        if X >= 1 then
            Print(X);
        end if;
    end if;

might print -999.

>... (a known valid value cannot become invalid short of erroneous
>execution).

That could be true or false, depending on what you mean.

    Foo: Color; -- could be invalid;
    Bar: Color := Red; -- Bar is valid
    Bar := Foo; -- Bar becomes invalid (maybe)

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

From: Bob Duff
Date: Wednesday, October 13, 2010  4:44 PM

> > I can certainly imagine super-checking modes that check more than is
> > required -- whether or not permission for such modes is given by the
> > RM.
>
> Possible, I suppose, but I thought we had discussed that during one of
> the meetings and rejected it. (But I may be misremembering.)

I had trouble with doing the wording, so I rejected the idea.
Not sure if we discussed that in a meeting -- maybe one of the sub-ARG phone
meetings.  But maybe I should try harder.

The problem is: you can't just allow the implementation to check predicates any
time they like.  But maybe "any time a component is modified or any time a value
is read", or something like that, might work.

> > > Consider the following call:
> > >
> > >         P (F1(1).Arr(F2).Int);
> >
> > This example is illegal -- function results (and their
> > subcomponents) are constant.  But I understand what you're saying --
> > you would need to save array indices, or addresses of array
> > components.
>
> Not necessarily: dereferences are implicit in Ada. (But I forgot to
> include it in my description, so of course you are right.)

That was my first reaction -- you're missing a ".all" somewhere.
But as soon as you do ".all", you're not talking about a component of the outer
object anymore.

Which reminds me of another case (I've mentioned before) where I want predicates
to refer to globals.  In GNAT, a Node_Id is an index into some table, and I want
to say:

    subtype Expression_Node_Id is Node with
        Predicate => Node_Kind(Expression_Node_Id) in Expression_Node_Kind;

Node_Kind is a function (so I want calls!), and the table is global (so I want
globals!), but it's not evil, because the node kind rarely changes.

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

From: Tucker Taft
Date: Wednesday, October 13, 2010  5:50 PM

>> I stand by my original statement, but I should have elaborated on it.
>> First, I was responding to the "discriminants can contain uninitialized junk"
>> statement, not the part about uninitialized variables in general.
>
> Oh.  Sorry.  Here's what I mean:
>
>      type Color is (Red, ...);
>
>      type T(Discrim: Color) is ...;
>
>      X : Color; -- uninitialized
>
>      Y : T(Discrim =>  X);
>
> This gives a possibly-invalid value to Discrim.
> I don't THINK that's erroneous.  And I don't think it requires a
> run-time check, since the subtype of Discrim is the same as that of X.
>
> Am I wrong?

It depends.  If a compiler doesn't do the constraint check on setting the
discriminant, then it has to worry about this case everywhere, since
uninitialized variables are not allowed to lead to erroneous execution (they are
"bounded" errors, according to the RM).  Compilers differ in how they "bound"
the effects of uninitialized variables.  AdaMagic makes the distinction between
objects whose declared subtype is "trusted" and those for which it isn't.  We
don't allow you to assign the value of an "untrusted" object into a "trusted"
one without a constraint check.

I think pretty much all compilers have to have these two categories, but how
they define the two categories may vary.

In our compiler, all object that are initialized at their declaration point, all
discriminants, all IN and IN-out parameters, and all record components with a
default expression, are considered trusted.  I don't know what is the rule for
other compilers.  I believe Rational at one point trusted everything, by
automatically default-initializing scalars to some in-range value.  I think that
can hide bugs.  An alternative approach is to assign an out-of-range default
value to all untrusted objects, and then you are likely to catch most
inappropriate use.  AdaMagic takes the approach of letting any old stack "junk"
be the default initial value for an untrusted object, and rely on normal
constraint checks to find most cases of uninitialized variables.

In any case, I would be surprised if a compiler put discriminants in the
"untrusted" category, unless it put *everything* in the untrusted category.
Perhaps that is what GNAT does, by doing checks on every use of a scalar that
could lead to something erroneous, like a wild jump or a wild store, unless
there is "local" knowledge about the value being in range.

But if the compiler has at least some scalar objects in the trusted category,
then I would predict that discriminants are "trusted" and would require a check
on initialization from an untrusted scalar object, even if it is of the same
subtype.

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

From: Randy Brukardt
Date: Wednesday, October 13, 2010  6:14 PM

Thanks for the detailed reply. I was just writing a less-detailed one, which
I've round-filed.

Janus/Ada has a similar concept (called "known to be valid"), and discriminants
are always in that category. A discriminant constraint like the one in Bob's
example would always be checked (subtype information is ignored in this case, as
the source is not "known to be valid").

I'm sure it is technically true that discriminants could be allowed to be
invalid, but I'm dubious that it is practical to prevent erroneous execution if
they are. Declaration and use of discriminant-dependent components would be,
umm, interesting to implement without the certainty that the discriminants are
valid for all objects that aren't abnormal. That's especially true for a
compiler that uses a max-size implementation strategy (how would the compiler
prevent access to non-existent memory if the bounds of the component allow it?
Multiple checks? Yuk.)

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

From: Bob Duff
Date: Wednesday, October 13, 2010  7:02 PM

> Thanks for the detailed reply. I was just writing a less-detailed one,
> which I've round-filed.

OK, Tucker and Randy have replied.  So the conclusion is: I'm (technically)
right that discrims can be invalid according to the RM, but it's really not a
problem in practice, and all compilers actually check that discrims are in
range.

Right?

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

From: Randy Brukardt
Date: Wednesday, October 13, 2010  8:35 PM

Right. (At least all Ada 95 and later compilers. Ada 83 didn't have any of the
validity stuff, and allowed uninitialized variables to cause erroneousness. I
think.)

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

From: Randy Brukardt
Date: Wednesday, October 13, 2010  9:05 PM

...
> >... (a known valid value cannot become invalid short of erroneous
> >execution).
>
> That could be true or false, depending on what you mean.
>
>     Foo: Color; -- could be invalid;
>     Bar: Color := Red; -- Bar is valid
>     Bar := Foo; -- Bar becomes invalid (maybe)

I meant in the sense of what Tucker called "trusted" and Janus/Ada calls
internally "known to be valid". There's a compiler bug if non-erroneous
execution causes such a value to become invalid. It's implementation-defined
what values (if any) have that property.  A compiler (absent other local
information) can only remove subtype range checks if the source is known to be
valid or the target is NOT known to be valid, and the subtypes are the same or
the target has a static subtype and the source has a smaller maximum range.
(Array indexing is required by the language to be treated as a target that is
known to be valid: that is, execution cannot be erroneous if an invalid value is
used, and that can only be guaranteed by making the check or having a known to
be valid source).

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

From: Randy Brukardt
Date: Wednesday, October 13, 2010  9:13 PM

> > Not necessarily: dereferences are implicit in Ada. (But I forgot to
> > include it in my description, so of course you are right.)
>
> That was my first reaction -- you're missing a ".all" somewhere.
> But as soon as you do ".all", you're not talking about a component of
> the outer object anymore.

True, but the designated type of the access type could have a predicate. One
would hope that got checked the same way!

That is, change my original explanation of F1 to:

      F1 is function F1 (O : Integer) return access Rec1;

If Rec1 has a predicate, we have the same issue. (And you'd have to save the
returned access value somewhere.)

> Which reminds me of another case (I've mentioned before) where I want
> predicates to refer to globals.  In GNAT, a Node_Id is an index into
> some table, and I want to say:
>
>     subtype Expression_Node_Id is Node with
>         Predicate => Node_Kind(Expression_Node_Id) in
> Expression_Node_Kind;
>
> Node_Kind is a function (so I want calls!), and the table is global
> (so I want globals!), but it's not evil, because the node kind rarely
> changes.

This example seems a bit dubious, since the point of having static predicates is
to completely replace a function like Node_Kind with a subtype like
Expression_Node_Id. Not that you could completely replace the function calls in
existing code, but surely reduce the use.

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

From: Bob Duff
Date: Thursday, October 14, 2010  7:35 AM

> Right. (At least all Ada 95 and later compilers. Ada 83 didn't have
> any of the validity stuff, and allowed uninitialized variables to
> cause erroneousness. I think.)

Actually, it's not right.  The following program prints "zero", and I don't
think that's a GNAT bug.

gnatmake -g -gnata -gnatwa -gnato -f uninit.adb

uninit.adb:12:07: warning: variable "X" is read but never assigned
uninit.adb:15:20: warning: condition can only be True if invalid values present

So my claim is not just theoretical -- discriminants can be invalid in practice.
(I must admit, it took quite a bit of playing around to trick the compiler!)

with Text_IO; use Text_IO;

procedure Uninit is

   procedure Q is
      type Int is range 1..10;
      type T(Discrim: Int) is
         record
            null;
         end record;
      X : Int;
      Y : T(Discrim => X);
   begin
      if Y.Discrim = 0 then
         Put_Line("zero");
      else
         Put_Line("nonzero");
      end if;
   end Q;

begin
   Q;
end Uninit;

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

From: Tucker Taft
Date: Thursday, October 14, 2010  9:46 AM

> So my claim is not just theoretical -- discriminants can be invalid in
> practice.  (I must admit, it took quite a bit of playing around to
> trick the compiler!)

That confirms my guess that *everything* is untrusted in GNAT.  So it would be
interesting to use Y.Discrim as an index into an array indexed by "Int", and see
whether it detects the problem then.  If it doesn't, then I believe there *is* a
GNAT bug.  It needs to do the constraint check *somewhere* before the invalid
value gets used as an array index.

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

From: Bob Duff
Date: Thursday, October 14, 2010  10:01 AM

Right, and it does.  I just tried it, and it gets Constraint_Error when using
that discrim as an array index.  For both reading and modifying the array
element.

I think GNAT may have been too lax years ago, but those bugs have been fixed.

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

From: Edmond Schonberg
Date: Thursday, October 14, 2010  10:15 AM

> That confirms my guess that *everything* is untrusted in GNAT.  So it
> would be interesting to use Y.Discrim as an index into an array
> indexed by "Int", and see whether it detects the problem then.  If it
> doesn't, then I believe there *is* a GNAT bug.  It needs to do the
> constraint check *somewhere* before the invalid value gets used as an
> array index.

GNAT always does a constraint check on an indexed assignment, so there is
fortunately no bug there. It doesn't check on an indexed retrieval.

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

From: Robert Dewar
Date: Thursday, October 14, 2010  5:38 PM

> That confirms my guess that *everything* is untrusted in GNAT.  So it
> would be interesting to use Y.Discrim as an index into an array
> indexed by "Int", and see whether it detects the problem then.  If it
> doesn't, then I believe there *is* a GNAT bug.  It needs to do the
> constraint check *somewhere* before the invalid value gets used as an
> array index.

Why? I thought the only restriction was that you can't have it causing
erroneousness (whatever that exactly means). What GNAT does in a case like this
is check it if it is on the left side (a store), but not check it on a load.
That seems OK to me. On a load, you just propagate some possibly invalid value.
On a store you could do an erroneous store and you can't allow that.

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

From: Robert Dewar
Date: Thursday, October 14, 2010  5:38 PM

> Right, and it does.  I just tried it, and it gets Constraint_Error
> when using that discrim as an array index.  For both reading and
> modifying the array element.

I am surprised you get a CE for the read ...
interesting ...

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

From: Bob Duff
Date: Thursday, October 14, 2010  5:53 PM

I'm not surprised.  I think we need to do that, because otherwise a wild memory
reference (even a read) can cause a segfault.  (We could try to turn that
segfault into a Constraint_Error, but it would be hard, and we don't -- I think
we turn segfaults into Storage_Error always.)

On an embedded machine without memory mapping hardware, reading from a
nonexistent address doesn't fault, but on some hardware it hangs.  So we need
the check on those machines, too.

I think GNAT is RM-conformant in this area, after much discussion of such issues
in years past!  The RM rules are not crystal clear, unfortunately.

I hate bounded errors.  "Erroneous" is a menace, but at least it's clear that it
means "don't do that".  A bounded error requires a full page of RM verbiage to
explain what the bounds are, and nobody understands it.

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

From: Bob Duff
Date: Thursday, October 14, 2010  5:27 PM

> > Node_Kind is a function (so I want calls!), and the table is global
> > (so I want globals!), but it's not evil, because the node kind
> > rarely changes.
>
> This example seems a bit dubious, since the point of having static
> predicates is to completely replace a function like Node_Kind with a
> subtype like Expression_Node_Id.

Heh?  You still want to query Node_Kind when you want to know what sort of
expression it is.  I guess I miss your point -- this example doesn't seem at all
dubious to me.  I can't imagine how one would express the required property of
Expression_Node_Id without calling something like Node_Kind.

>...Not that you could completely replace the function  calls in
>existing code, but surely reduce the use.

I think it's fair to talk about new code.

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

From: Robert Dewar
Date: Thursday, October 14, 2010  6:15 PM

> Heh?  You still want to query Node_Kind when you want to know what
> sort of expression it is.  I guess I miss your point -- this example
> doesn't seem at all dubious to me.  I can't imagine how one would
> express the required property of Expression_Node_Id without calling
> something like Node_Kind.
>
>> ...Not that you could completely replace the function calls in
>> existing code, but surely reduce the use.
>
> I think it's fair to talk about new code.

I agree with BOb on this, seems a perfectly reasonable use (in fact it is the
usage example that persuaded me that dynamic predicates have some value :-))

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

From: Robert Dewar
Date: Thursday, October 14, 2010  6:22 PM

>> I am surprised you get a CE for the read ...
>> interesting ...
>
> I'm not surprised.  I think we need to do that, because otherwise a
> wild memory reference (even a read) can cause a segfault.  (We could
> try to turn that segfault into a Constraint_Error, but it would be
> hard, and we don't -- I think we turn segfaults into Storage_Error
> always.)

For me, it is perfectly fine for the use of an invalid value to cause a
segfault. Yes, language lawyers might mumble, but really I don't care much. All
that you want is an exception, or program termination with an error, or
something else reasonable.

In fact why should language lawyers mumble:

> 11    If the representation of the object does not represent a value of the
>       object's type, the semantics of operations on such representations is
>       implementation-defined, but does not by itself lead to erroneous or
>       unpredictable execution, or to other objects becoming abnormal.

Seems to me that causing a segfault if the invalid value results in an out of
range address is quite within the realm ofr implementation defined stuff that is
not erroneous or unpredictable (obviously you can't exactly predict behavior for
uninitialized values :-))

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

From: Robert Dewar
Date: Thursday, October 14, 2010  6:25 PM

> On an embedded machine without memory mapping hardware, reading from a
> nonexistent address doesn't fault, but on some hardware it hangs.  So
> we need the check on those machines, too.

I don't know what GNAT targets you are talking about here, we get faults on all
machines (that's how we do stack checking after all!) All machines we run on
have memory mapping hardware!

> I hate bounded errors.  "Erroneous" is a menace, but at least it's
> clear that it means "don't do that".  A bounded error requires a full
> page of RM verbiage to explain what the bounds are, and nobody
> understands it.

I think I understand it just fine, and I like the idea! Erroneousness is much
too course!

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

From: Randy Brukardt
Date: Thursday, October 14, 2010  8:28 PM

> Heh?  You still want to query Node_Kind when you want to know what
> sort of expression it is.  I guess I miss your point -- this example
> doesn't seem at all dubious to me.  I can't imagine how one would
> express the required property of Expression_Node_Id without calling
> something like Node_Kind.

I must have misunderstood your example. What kind of thing is Node? What does
Node_Kind do? I had presumed that Node was some sort of enumeration, but looking
at this again, you may have meant something else. From the looks of it, it seems
like Node_Kind is a really strange way to extract the discriminant value from a
some sort of Node record type. I wouldn't do things that way (I leave the
discriminant(s) visible), but I suppose it is reasonable to wrap discriminants
in a function call. OTOH, if the function is referencing non-discriminants, then
they can be changed at will, and the entire thing is iffy (especially as the
only good reason for using non-discriminants is so that they can be changed
easily -- we do that in some cases in our compiler and I surely would not want
to have any predicates depending on that).

You also said something about a table: I can't begin to imagine what the purpose
of that is, so I can't make a judgement on how reasonable it is.

> >...Not that you could completely replace the function  calls in
> >existing code, but surely reduce the use.
>
> I think it's fair to talk about new code.

In new code, don't hide discriminants. :-) Then you don't need any functions (at
least not for what this purpose seems to be).

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

From: Randy Brukardt
Date: Thursday, October 14, 2010  8:44 PM

> In fact why should language lawyers mumble:
>
> > 11    If the representation of the object does not represent a value of the
> >       object's type, the semantics of operations on such representations is
> >       implementation-defined, but does not by itself lead to erroneous or
> >       unpredictable execution, or to other objects becoming abnormal.
>
> Seems to me that causing a segfault if the invalid value results in an
> out of range address is quite within the realm ofr implementation
> defined stuff that is not erroneous or unpredictable (obviously you
> can't exactly predict behavior for uninitialized values :-))

Gosh. I would have said that a segfault is *exactly* the definition of erroneous
behavior, as it is not something that could be described by the normally defined
semantics of an Ada program. (It would be OK if it is turned into an exception,
through, as that is normally defined semantics. In particular, turning it into
Storage_Error would seem to be OK, since that can be raised for any reason
anywhere.)

But I'd also worry about invalid reads that don't segfault. I would be
especially concerned if such an invalid read happened to touch a memory-mapped
device. I recall that there used to be devices that only allowed values to be
read once (our CP/M disk controller was like that - yes I know that was circa
1979). Touching such a device because of a wayward read would cause data loss
and who knows what else. And I can't think of any practical way that the
programmer of such a device could protect themselves if any array read could
cause trouble.

Bob also mentioned hardware that hung on accesses to non-existent memory. I've
also programmed that sort of system (but yes, a long time ago).

Even if the letter of the language allows invalid reads, it seems that you would
be hoping that no customer ever had an unusual system where an extra read would
do damage. That's not a bet I'd want to make.

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

From: Robert Dewar
Date: Thursday, October 14, 2010  8:46 PM

> Especially as the only good reason for using non-discriminants is so
> that they can be changed easily -- we do that in some cases in our
> compiler and I surely would not want to have any predicates depending
> on that.

Nope, the decision in the GNAT sources is NOT to use discriminants because it
makes it too difficult to do untyped traversals of the tree.

> In new code, don't hide discriminants. :-) Then you don't need any
> functions (at least not for what this purpose seems to be).

I don't think we would change the decision in GNAT if we were starting from
scratch, the compiler is full of untyped traversals of the tree.

Never assume that the particular style you choose for writing code is the only
one that has to be catered to!

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

From: Robert Dewar
Date: Thursday, October 14, 2010  8:52 PM

> Gosh. I would have said that a segfault is *exactly* the definition of
> erroneous behavior, as it is not something that could be described by
> the normally defined semantics of an Ada program. (It would be OK if
> it is turned into an exception, through, as that is normally defined
> semantics. In particular, turning it into Storage_Error would seem to
> be OK, since that can be raised for any reason anywhere.)

Well we significantly disagree, and since you can't find the word segfault in
the RM, you are going to have a hard time proving your point. For me erroneous
means ANYTHING can happen, but if you say that your impl-defined behavior for
some bounded error is that any of the following can occur, and include segfault,
that seems reasonable, and is definitely NOT unbounded behavior.

> But I'd also worry about invalid reads that don't segfault. I would be
> especially concerned if such an invalid read happened to touch a
> memory-mapped device. I recall that there used to be devices that only
> allowed values to be read once (our CP/M disk controller was like that
> - yes I know that was circa 1979). Touching such a device because of a
> wayward read would cause data loss and who knows what else. And I
> can't think of any practical way that the programmer of such a device
> could protect themselves if any array read could cause trouble.

Well again, this is not unbounded erroneousness, and would have to be
documented, but most likely would not be a good idea to allow.

> Bob also mentioned hardware that hung on accesses to non-existent memory.
> I've also programmed that sort of system (but yes, a long time ago).

I know of no such hardware, certainly not true of any of the GNAT Pro targets.

> Even if the letter of the language allows invalid reads, it seems that
> you would be hoping that no customer ever had an unusual system where
> an extra read would do damage. That's not a bet I'd want to make.

We don't bet, we know the hardware we run on!

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

From: Randy Brukardt
Date: Thursday, October 14, 2010  9:19 PM

> We don't bet, we know the hardware we run on!

I know this sort of thing happened with our compiler back in the dark ages. (Bob
seems to have such a memory as well, judging from his messages.) But I can
believe that you aren't encountering such hardware anymore, so maybe it's just a
case of continuing to worry about issues that are OBE these days.

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

From: Robert Dewar
Date: Thursday, October 14, 2010  9:03 PM

Actually I will say that I thought the intent of talking about non-erroneous
behavior was precisely to capture the fact that a wild store is unacceptable,
but a wild load may be acceptable. So Randy's viewpoint is a complete surprise
to me.

Similar to an IF possibly not thinking a boolean value is true or false, but
CASE needs to check since a wild jump is unacceptable.

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

From: Tucker Taft
Date: Thursday, October 14, 2010  9:31 PM

A wild load is fine so long as its effect can be "bounded."  I agree that a
segmentation fault is acceptable (or raising Storage_Error I suppose, if that
was a seg fault turns into).

I don't think doing something that hangs indefinitely or causes some hardware
device to start misbehaving would be acceptable. I am familiar with hardware
which (mis)behaved in this way, but as Randy has admitted, this was a long time
ago.  If you are comfortable that for your current targets a wild load has
bounded ill effects, then it seems like you are fine.

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

From: Robert Dewar
Date: Thursday, October 14, 2010  9:39 PM

Right, I agree with the above

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

From: Robert Dewar
Date: Thursday, October 14, 2010  9:06 PM

I have the following question about where predicates are set, consider

     subtype A1 is Integer range 1 .. 10;

     subtype A2 is Integer with
       predicate => A in 1 .. 10;

What are the differences between where the range check is made for A1 and A2.

BTW, I know predicates are not constraints for language lawyers, how about for
users. I suspect that the reasonable thing is for users to think of predicates
as being like constraints?

Accordingly, I wonder whether Constraint_Error should be raised instead of
Assertion_Error when predicates fail.

Basicaly my question is, how important is it to pester the programmer with an
educational message saying "predicates are NOT constraints!"

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

From: Randy Brukardt
Date: Thursday, October 14, 2010  9:21 PM

> Basicaly my question is, how important is it to pester the programmer
> with an educational message saying "predicates are NOT constraints!"

That was my point when I was talking to Bob about expectations. I think it would
be hard to educate people that predicates and constraints are different in
significant ways, and I'm pretty sure that most users will think of them as the
same. Thus I worry about cases where they're *not* the same.

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

From: Robert Dewar
Date: Thursday, October 14, 2010  9:36 PM

Sure, that's reasonable, the question is whether the differences end up being in
the significant or insignificant category. And for sure the fact that
Assertion_Error instead of CE is raised seems significant (for one thing it
seems to mean that optimizations applying to CE do not apply here?)

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

From: Randy Brukardt
Date: Thursday, October 14, 2010  9:28 PM

> > Especially as the only good reason for using non-discriminants is so
> > that they can be changed easily -- we do that in some cases in our
> > compiler and I surely would not want to have any predicates depending
> > on that.
>
> Nope, the decision in the GNAT sources is NOT to use discriminants
> because it makes it too difficult to do untyped traversals of the
> tree.

Not sure at all what this means. Traversals (that is reads) are not likely to be
a problem no matter how the nodes are implemented. I can imagine having problems
if you needed to change the kind of a node when it is already linked in a tree,
but not with traversal.

> > In new code, don't hide discriminants. :-) Then you don't need any
> > functions (at least not for what this purpose seems to be).
>
> I don't think we would change the decision in GNAT if we were starting
> from scratch, the compiler is full of untyped traversals of the tree.
>
> Never assume that the particular style you choose for writing code is
> the only one that has to be catered to!

For the record, the only reason that we used discriminants in tree nodes was for
the purposes of storage minimization. Ada doesn't let you use variants unless
you use discriminants. And there definitely were problems with using
discriminants rather than some sort of components. (We later found that using
the variants helped with debugging, as it made referencing unused fields in
nodes raise Constraint_Error. But that was a lucky side-effect, not something we
planned for.)

But I still don't think I would have used functions.

Anyway, I agree with you about programming style; that's why I asked Bob for
these examples in the first place.

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

From: Robert Dewar
Date: Thursday, October 14, 2010  9:39 PM

> Not sure at all what this means. Traversals (that is reads) are not
> likely to be a problem no matter how the nodes are implemented. I can
> imagine having problems if you needed to change the kind of a node
> when it is already linked in a tree, but not with traversal.

In the Alsys compiler a complex discriminated structure was used for the tree,
covering pages of code (the type definition). Traversing the tree meant a huge
complex case statement where each kind of node had to be separately traversed.
We avoid this in GNAT

> For the record, the only reason that we used discriminants in tree
> nodes was for the purposes of storage minimization. Ada doesn't let
> you use variants unless you use discriminants. And there definitely
> were problems with using discriminants rather than some sort of
> components. (We later found that using the variants helped with
> debugging, as it made referencing unused fields in nodes raise
> Constraint_Error. But that was a lucky side-effect, not something we
> planned for.)

We achieve that debugging effort with assertions.

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

From: Bob Duff
Date: Friday, October 15, 2010  10:25 AM

> I must have misunderstood your example. What kind of thing is Node?
> What does Node_Kind do? I had presumed that Node was some sort of
> enumeration, but looking at this again, you may have meant something else.

Node_Id is conceptually a pointer to a variant record (or a class-wide type).
The Kind field is conceptually the discriminant (or tag).

But it's not implemented that way.  Deep down, Node_Id is an integer, which is
an index into a table (some sort of growable array).  There's a function to
fetch the Kind field (which returns an enum).

So my point is, I want to be able to say:

    Current_Expression: Expression_Node_Id;

where we currently must say:

    Current_Expression: Node_Id;

Whether Node_Id is represented as an access type, or as an index into a table.

>... From the looks
> of it, it seems like Node_Kind is a really strange way to extract the
>discriminant value from a some sort of Node record type. I wouldn't do
>things that way (I leave the discriminant(s) visible), but I suppose it
>is  reasonable to wrap discriminants in a function call.

There's no discriminant at the Ada level.  We think of Kind as a discriminant of
a variant record, but in fact it's not a discriminant, and there's no variant
record.  It's all simulated by accessor functions and whatnot.

The actual record contains things like:

               Field1 : Union_Id;
               Field2 : Union_Id;
               Field3 : Union_Id;
               Field4 : Union_Id;
               Field5 : Union_Id;

but these are all hidden behind accessor functions, such as:

   function Component_List
      (N : Node_Id) return Node_Id is
   begin
      pragma Assert (False
        or else NT (N).Nkind = N_Record_Definition
        or else NT (N).Nkind = N_Variant);
      return Node1 (N);
   end Component_List;

where Node1 fetches Field1:

      function Node1 (N : Node_Id) return Node_Id is
      begin
         pragma Assert (N <= Nodes.Last);
         return Node_Id (Nodes.Table (N).Field1);
      end Node1;

As Robert said, one advantage of this design is that we can write things like
"Walk_Subnodes_Of_Node" in a few lines of code, whereas the variant record
design would require a case statement with thousands of lines, and the tagged
type design would require thousands of lines of overriding "Walk" methods.

Another advantage (over access types pointing to variant records or class-wide
types) is the ease of making the data structures persistent.

>... OTOH, if the function
> is referencing non-discriminants, then they can be changed at will, ...

No, that's the point you keep missing.  Sure, Ada allows non-discrim components
to be changed, but the designer of the program can easily hide those components
so they cannot be changed, or can only be changed in controlled ways.

We could have put those "Field1 : Union_Id;" components in a visible part
somewhere, and let folks change them willy-nilly. But that would be a poor
design.  The fact that poor designs are possible in Ada is irrelevant.  In fact,
that record type is safely buried in a package body.

>...and the
> entire thing is iffy (especially as the only good reason for using
>non-discriminants is so that they can be changed easily -- we do that
>in  some cases in our compiler and I surely would not want to have any
>predicates depending on that).

There are lots of reasons to use discriminants, and lots of other reasons to
avoid discriminants.  Our language design philosophy ought to be that new
features are agnostic with respect to such design choices.  It's really not our
job to encourage or discourage the use of discriminants.

And it's REALLY not our job to prevent people from wrapping whatever they like
in a function, including a pseudo-discriminant accessor!

> You also said something about a table: I can't begin to imagine what
> the purpose of that is, so I can't make a judgement on how reasonable it is.

Well, I don't really think it's our job to pass judgement; as I said, new Ada
features should be agnostic, to the extent possible.

Anyway, representing pointers as indices into arrays is entirely appropriate in
some circumstances.  If you're coding in SPARK, you have to do that (if you need
pointers at all), because SPARK doesn't have "access".

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

From: Bob Duff
Date: Friday, October 15, 2010  10:35 AM

>    function Component_List
>       (N : Node_Id) return Node_Id is
>    begin
>       pragma Assert (False
>         or else NT (N).Nkind = N_Record_Definition
>         or else NT (N).Nkind = N_Variant);
>       return Node1 (N);
>    end Component_List;

Note that both occurrences of Node_Id above could be subtypes, with predicates
indicating what Kind of node is expected and returned.  (And then the Assert
could be removed).

We could do the same with pre/post, but predicates are more convenient (can be
used also for local variables).

Which reminds me: Why are we all hot and bothered by loopholes in predicates,
when all the other new assertion facilities (pre, post, invariant) have similar
loopholes?

You want me to say, "Pre => Kind(N) in (N_Record_Definition | N_Variant)"
but that's no safer -- no language rule prevents Kind from modifying globals or
components.

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

From: Bob Duff
Date: Friday, October 15, 2010  10:52 AM

> > That was my point when I was talking to Bob about expectations. I
> > think it would be hard to educate people that predicates and
> > constraints are different in significant ways, and I'm pretty sure
> > that most users will think of them as the same. Thus I worry about
> > cases where they're *not* the same.

The main way predicates differ from constraints is that predicates have bigger
loopholes (cases where they can be false).  But constraints have loopholes, too
(can be false for uninitialized objects), so I'm not too excited about it.  I
already demonstrated yesterday that a discriminant can be out of bounds, and the
sky didn't fall.

> Sure, that's reasonable, the question is whether the differences end
> up being in the significant or insignificant category. And for sure
> the fact that Assertion_Error instead of CE is raised seems
> significant

I'm puzzled by this comment.  You wouldn't normally handle such exceptions, and
an unhandled CE is pretty-much the same as an unhanlded AE.

I don't really care if we change AE to CE for predicates, but for consistency,
we should then make pre/post/invariant use CE.

> (for one thing it seems to mean that optimizations applying to CE do
> not apply here?)

For sure the rules in that area are different for assertions (pragma Assert,
pre/post, invariants, predicates) than for things like array-bounds checks.  But
I wouldn't call that "significant" from a programmer's point of view.

The programmer's rule is simple: Make sure assertions are true. And make sure
array indexing is in bounds.  The consequences of violating these rules may
differ, but that's secondary.

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

From: Robert Dewar
Date: Friday, October 15, 2010  11:15 AM

> I'm puzzled by this comment.  You wouldn't normally handle such
> exceptions, and an unhandled CE is pretty-much the same as an unhanlded AE.

I thought 11.6 permissions only applied to CE and not AE, if they apply to AE
that's really interesting, but probably wrong!

> I don't really care if we change AE to CE for predicates, but for
> consistency, we should then make pre/post/invariant use CE.

Maybe yes for invariant, but I am not sure, invariants are very obviously
different from constraints to me.

For sure not for pre/post

These are nothing to do with constraints, and furthermore this would be a very
nasty incompatibility with existing implementations.

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

From: Robert Dewar
Date: Friday, October 15, 2010  11:23 AM

Also it seems to me that predicates should turn on and off like constraints
(suppress All_Checks, or -gnatp switch in GNAT) rather than on and off like
assertions (pragma Assertion_Policy, -gnata switch in GNAT).

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

From: Robert Dewar
Date: Friday, October 15, 2010  11:25 AM

Note by the way that in GNAT, using the Check_Policy pragma you can turn
invariants, predicates, postconditions, preconditions on and off separately.
Assertions always turns all of these on, which I am not sure is ideal, but is
needed for conformance.

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

From: Bob Duff
Date: Friday, October 15, 2010  11:59 AM

> I thought 11.6 permissions only applied to CE and not AE, if they
> apply to AE that's really interesting, but probably wrong!

11.6 talks about "checks".  It doesn't mention CE explicitly; it applies to PE
as well, for example.

Assertions (including predicates) are not checks, so 11.6 does not apply to
them, according to the current proposal.

But so what?  11.6 is language-lawyerly.  Regular programmers don't understand
it, and ignore it.  Your query was about the programmer's view -- Are predicates
and constraints pretty much the same thing?  I say, "Yes, they are -- the
differences are details for language lawyers to worry about."

As a user, I would consider:

    subtype T is Integer range 0..Integer'Last;

and:

    subtype T is Integer with Predicate => T >= 0;

to be interchangeable.  Both mean "I believe that property is true, and if I'm
wrong, stop the program and give me some info to help me debug".

> > I don't really care if we change AE to CE for predicates, but for
> > consistency, we should then make pre/post/invariant use CE.
>
> Maybe yes for invariant, but I am not sure, invariants are very
> obviously different from constraints to me.

Not to me.  Constraints, invariants, predicates, and null exclusions all serve
the same purpose: asserting properties of things. If you think otherwise, you
must be wearing your Language Lawyer hat.

> For sure not for pre/post
>
> These are nothing to do with constraints, and furthermore this would
> be a very nasty incompatibility with existing implementations.

That's settles it (the compatibility issue).

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

From: Bob Duff
Date: Friday, October 15, 2010  12:05 PM

> Also it seems to me that predicates should turn on and off like
> constraints (suppress All_Checks, or -gnatp switch in GNAT) rather
> than on and off like assertions (pragma Assertion_Policy, -gnata
> switch in GNAT).

The current proposal is that pragma Suppress does not apply to assertions
(including predicates).

You can turn off predicates with Assertion_Policy, but it has a different
effect: Suppressing wrong checks causes erroneous behavior, turning off wrong
predicates does not.

I'm reasonably happy with this state of affairs, but we can certainly discuss
alternatives.

Note that assertions (including predicates) can be arbitrarily inefficient,
whereas constraint checks cannot.

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

From: Robert Dewar
Date: Friday, October 15, 2010  2:27 PM

> But so what?  11.6 is language-lawyerly.  Regular programmers don't
> understand it, and ignore it.  Your query was about the programmer's
> view -- Are predicates and constraints pretty much the same thing?  I
> say, "Yes, they are -- the differences are details for language
> lawyers to worry about."

They may ignore it, but the compiler doesn't! The permissions to optimize checks
are important, and IMO should apply to predicate checs.

> As a user, I would consider:
>
>      subtype T is Integer range 0..Integer'Last;
>
> and:
>
>      subtype T is Integer with Predicate =>  T>= 0;
>
> to be interchangeable.  Both mean "I believe that property is true,
> and if I'm wrong, stop the program and give me some info to help me
> debug".

That's why I would expect them to raise the same exception

> Not to me.  Constraints, invariants, predicates, and null exclusions
> all serve the same purpose: asserting properties of things.
> If you think otherwise, you must be wearing your Language Lawyer hat.

The reason invariants are so different from constraints is the inside/outside
semantics, so they can be very deliberately "violated" and nothing is wrong.
Programmers DO need to understand this distinction!

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

From: Robert Dewar
Date: Friday, October 15, 2010  2:30 PM

> Note that assertions (including predicates) can be arbitrarily
> inefficient, whereas constraint checks cannot.

I fundamentally disagree with this cavalier attitude to efficiency in this case.
I think it important that predicates be as efficient as possible (after all you
think of using them in the compiler, we can't have things in the compiler that
are arbitrarily inefficient).

 From a formal language lawyer point of view, anything can be arbitrarily
 inefficient, the formal description of the language has nothing to say about
 efficiency. If you want to implement constraint checks by a serial search
 through a table of out of range values, that's bad idea, but not
 non-conforming.

 From a pragmatic point of view, everything should be as efficient as possible,
 and the ability to do things efficiently should inform the language design.

I do not understand the distinction you draw here between constraint checks and
predicates. Makes no sense to me with or without a language lawyer hat on.

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

From: Bob Duff
Date: Friday, October 15, 2010  2:57 PM

> > Note that assertions (including predicates) can be arbitrarily 
> > inefficient, whereas constraint checks cannot.
> 
> I fundamentally disagree with this cavalier attitude to efficiency in 
> this case.

I don't see any "cavalier attitude".  It's simply a fact that predicates can
be arbitrarily slow, whereas constraints are restricted to simple (and
therefore efficient) checks.

A fact isn't an "attitude", cavalier or otherwise.

>... I think it important that
> predicates be as efficient as possible (after all you  think of using 
>them in the compiler, we can't have  things in the compiler that are 
>arbitrarily inefficient).

We turn off pragmas Assert, and constraint checks, in production builds of the
compiler.

>  From a formal language lawyer point of view, anything can be 
> arbitrarily inefficient, the formal description of the language has 
> nothing to say about efficiency.

Of course you know that I know that!  I also know how range checks are typically
implemented, and it's a couple of instructions.  Of course any discussion of
efficiency is totally unrelated to RM conformance!

>...If
> you want to implement constraint checks by a serial  search through a 
>table of out of range values, that's  bad idea, but not non-conforming.
> 
>  From a pragmatic point of view, everything should be as efficient as 
> possible, and the ability to do things efficiently should inform the 
> language design.

I'm pretty sure you don't believe "everything should be as efficient as possible",
because the most efficient thing would be to avoid Assert and Predicate altogether.
You must mean something else...

I don't understand what "informing" you're wanting here.
If we allow arbitrary code in procedures (and we do), then procedures can be
arbitrarily slow.  If we allow arbitrary code in predicates, then those can be
arbitrarily slow, too.

Of course I agree with the general principle that the language design should allow
for efficient implementations. But I don't see what you're applying that to, here
-- it doesn't seem to have much to do with the CE vs. AE question, or the exact
rules for what Assertion_Policy means, or -gnata vs. -gnatp, or anything else we've
been discussing lately.

> I do not understand the distinction you draw here between constraint 
> checks and predicates. Makes no sense to me with or without a language 
> lawyer hat on.

I'm puzzled that you don't understand.  It seems quite
obvious:  If somebody writes Predicate => F(Cur_Inst), and the body of F loops
through a million array elements, and checks whether each one is a prime number, that's
going to take a long time, and there's nothing the language designer or compiler writer
can or should do about it.

It just means that you might want to turn off Predicates and other assertions, while
keeping constraint checks turned on.

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

From: Robert Dewar
Date: Friday, October 15, 2010  3:07 PM

> I don't see any "cavalier attitude".  It's simply a fact that 
> predicates can be arbitrarily slow, whereas constraints are restricted 
> to simple (and therefore efficient) checks.
>
> A fact isn't an "attitude", cavalier or otherwise.

Ah, OK, I thought you were saying that the implementation could be arbitrarily
slow, yes of course I understadn you can write inefficient predicate checks.


> I'm puzzled that you don't understand.  It seems quite
> obvious:  If somebody writes Predicate =>  F(Cur_Inst), and the body 
> of F loops through a million array elements, and checks whether each 
> one is a prime number, that's going to take a long time, and there's 
> nothing the language designer or compiler writer can or should do 
> about it.

I thought you were saying something completely different so now that I
understand I agree.

> It just means that you might want to turn off Predicates and other 
> assertions, while keeping constraint checks

OK, I see what you are saying (indeed constraint checks vs assertion checks in the gnat
front end is similar to this).

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

From: Bob Duff
Date: Friday, October 15, 2010  4:22 PM

> Ah, OK, I thought you were saying that the implementation could be 
> arbitrarily slow, ...

Ah, now I understand the misunderstanding.
Glad that's cleared up -- I was pretty mystified by your earlier message!

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

From: Robert Dewar
Date: Friday, October 15, 2010  4:37 PM

Funny sometimes how two people talk past one another and neither can understand 
the idiotic position of the other, and it is all just a misunderstanding.

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

From: Bob Duff
Date: Friday, October 15, 2010  2:31 PM

> The reason invariants are so different from constraints is the 
> inside/outside semantics, so they can be very deliberately "violated"
> and nothing is wrong. Programmers DO need to understand this 
> distinction!

Agreed (that programmers need to understand this).

Predicates can be temporarily and deliberately violated too, by a different
mechanism: Use the 'Base attribute.  Only works for scalars, unfortunately.

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

From: Robert Dewar
Date: Friday, October 15, 2010  3:04 PM

> Predicates can be temporarily and deliberately violated too, by a 
> different
> mechanism: Use the 'Base attribute.  Only works for scalars, unfortunately.

I don't see that as a way of violating the predicate, what do you have in mind (and
how is it different from the situation with constraints).

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

From: Randy Brukardt
Date: Friday, October 15, 2010  3:23 PM

...
> Assertions (including predicates) are not checks, so 11.6 does not 
> apply to them, according to the current proposal.

Like Robert, I don't view predicates as assertions; I think that is a lousy model. I
personally don't think of any of the others as assertions, either. Tying them to the
assertion mechanism was a matter of expediency (don't have to have a separate
mechanism for turning them on and off) more than one of intent.

If/when these ever get implemented in Janus/Ada, they'll come with a real global
in/global out mechanism, and there'll be warnings if it is violated.
(Possibly even errors unless in what GNAT calls "pedantic" mode.)

In another message, Bob says:
...
>You want me to say, "Pre => Kind(N) in (N_Record_Definition | N_Variant)"
>but that's no safer -- no language rule prevents Kind from modifying
globals or components.

No, I don't *want* to you to write this at all - not in Pre, Post, Invariant,
Predicate, or anything else that gets invented - not without proper, checked
contracts on Kind. You can write it in Assert pragmas if you want, anything goes
there.

The Ada language doesn't have the tools to enforce that, so it can't be a
language rule. Unfortunately. But that doesn't mean that you should write
dangerous things just because you can.

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

From: Robert Dewar
Date: Friday, October 15, 2010  3:40 PM

> If/when these ever get implemented in Janus/Ada, they'll come with a 
> real global in/global out mechanism, and there'll be warnings if it is
> violated. (Possibly even errors unless in what GNAT calls "pedantic" mode.)

Don't get confused --pedantic means "folow the standard even if it is silly", it
does not mean be more strict than the standard (just a terminology issue, but if
we start using pedantic other than in the well known gcc sense, we will confuse
ourselves).

> In another message, Bob says:
> ...
>> You want me to say, "Pre =>  Kind(N) in (N_Record_Definition | N_Variant)"
>> but that's no safer -- no language rule prevents Kind from modifying
> globals or components.
>
> No, I don't *want* to you to write this at all - not in Pre, Post, 
> Invariant, Predicate, or anything else that gets invented - not 
> without proper, checked contracts on Kind. You can write it in Assert 
> pragmas if you want, anything goes there.

This is absurd to me. It is a perfect use of predicates, and the use of assertions
wouldn't work at all, because we need the checks to be everywhere, and the whole
point is NOT to scatter assertions around the place.

> The Ada language doesn't have the tools to enforce that, so it can't 
> be a language rule. Unfortunately. But that doesn't mean that you 
> should write dangerous things just because you can.

What Bob proposes is not dangerous at all, it is just adding automated checking
to rules that now exist only in comments. If you don't undertstand this, then you
just have a wrong model in your head of what Bob proposes.

There is nothing dangerous about checking something which is now not checked at
all. It's not checked at all now, because it's not practical syntactically to do
it with assertions. Now with predicates we can do the checks in the places where
they will be helpful with a minimal syntactic overhead. This will be potentially
useful in detecting coding errors that might otherwise escape early detection.

If your coding philosophy says we shouldn't be doing this, I haven't the foggiest
idea why you think that, and frankly don't care much, but I AM sure you should not
be trying to impose these peculiar ideas on other programmers :-)

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

From: Bob Duff
Date: Friday, October 15, 2010  4:10 PM

> I don't see that as a way of violating the predicate, what do you have 
> in mind
    
Using the silly Even subtype:

    procedure Q(X: in out Even) is

        procedure P(X: in out S'Base) is ... end;
    begin
        ... Here X is even.
        declare
            Y : S'Base := X + 1;
            -- Y is not even.
        begin
            Y := Y + 1;
            -- Now Y is even again, so we can safely assign it back to X.
            X := Y;
        end;

        -- Or we could call a procedure:
        P(X); -- P can make X odd, but restores its evenness
                 before returning.
    end Q;

This isn't a big deal.  I'm just saying that (for scalars) the programmer has
full control of when the predicate applies and when it doesn't.

>... (and how is it different from the situation with constraints).

It's not different for constraints.  I'm trying to design predicates to be as
similar as possible to constraints (but no more so ;-)).

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

From: Robert Dewar
Date: Friday, October 15, 2010  4:32 PM

>> I don't see that as a way of violating the predicate, what do you 
>> have in mind

This doesn't even vaguely violate the predicate as far as I am concerned, you
have two subtypes S and S'Base, one has a predicate the other doesn't. You can
assign between them, but if you assign from S'Base to S, the predicate is
checked. This is nothing like having a value *of the subtype with the invariant*
having a value that does not meet the invariant inside the abstraction.

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

From: Bob Duff
Date: Friday, October 15, 2010  4:40 PM

> ...
> > Assertions (including predicates) are not checks, so 11.6 does not 
> > apply to them, according to the current proposal.
> 
> Like Robert, I don't view predicates as assertions; I think that is a 
> lousy model. I personally don't think of any of the others as assertions, either.

Saying "a predicate is an assertion" is not a "model".  It's just a term.
A rose by any other name...

> Tying them to the assertion mechanism was a matter of expediency 
> (don't have to have a separate mechanism for turning them on and off) 
> more than one of intent.

For what it's worth, Bertrand Meyer, who should be considered an authority in
this area for obvious reasons, uses the term "assertion" to apply to
pre/postconditions, and to invariants, and to whatever the Eiffel equivalent of
pragma Assert is.  At least, that's my memory -- I'm too lazy to look it up and
verify that statement.

Note that I've been using the term "assertion" inconsistently.
With my programmer hat on, "assertion" usually includes "constraint".
With my language lawyer hat on, it usually/sometimes does not.
Sorry about that.  ;-)

Eiffel doesn't have constraints.  Meyer recently invented something like
null_exclusions.

> If/when these ever get implemented in Janus/Ada, they'll come with a 
> real global in/global out mechanism, and there'll be warnings if it is violated.

Sounds good to me!

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

From: Rnady Brukardt
Date: Friday, October 15, 2010  4:41 PM

> > If/when these ever get implemented in Janus/Ada, they'll come with a 
> > real global in/global out mechanism, and there'll be warnings if it 
> > is violated.
> > (Possibly even errors unless in what GNAT calls "pedantic" mode.)
> 
> Don't get confused --pedantic means "folow the standard even if it is 
> silly", it does not mean be more strict than the standard (just a 
> terminology issue, but if we start using pedantic other than in the 
> well known gcc sense, we will confuse ourselves).

I think I used it correctly. If in "pedantic" mode, the language rules would be
followed exactly (in this case, allowing dangerous function calls). If not in
"pedantic" mode, warnings or even errors would be produced for dangerous
function calls in these contexts.

...
> > The Ada language doesn't have the tools to enforce that, so it can't 
> > be a language rule. Unfortunately. But that doesn't mean that you 
> > should write dangerous things just because you can.
> 
> What Bob proposes is not
> dangerous at all, it is just adding automated checking to rules that 
> now exist only in comments. If you don't undertstand this, then you 
> just have a wrong model in your head of what Bob proposes.

It's dangerous because any function call is dangerous in these contexts unless
it is declared and checked to not have conflicting side-effects. I don't *want*
people to write dangerous code -- period. Now, there isn't going to be any way
to stop someone from writing such code, but that doesn't mean that you should
do so.

> If your coding philosophy says we shouldn't be doing this, I haven't 
> the foggiest idea why you think that, and frankly don't care much, but 
> I AM sure you should not be trying to impose these peculiar ideas on 
> other programmers :-)

We've had this discussion before. Predicates, preconditions, et. al. should
only contain "pure" functions (for some reasonable definition of "pure",
admittedly hard to do). Anything else is dangerous because it doesn't meet the
expectations of the use. To take a concrete example, using the Is_Valid
function of Claw in a predicate or precondition is dangerous because it can
change values because of some user action that is asynchronous in relation to
the executing code. Just because the precondition or predicate succeeds tells
you nothing about the current state of the object - and this leads to dangerous
assumptions in the code (that you don't have to worry about Not_Valid_Error
exceptions being raised).

I believe that there is an obligation of the compiler to reduce/eliminate the
creation of dangerous predicates/preconditions. This can only be done if the
contract of functions includes some sort of enforced side-effect information,
so it isn't possible to do it in Ada 2012 as proposed -- but this does not mean
that it shouldn't be done.

I know that we totally disagree on what and how dangerous side-effect in
functions are identified (yours is operational and thus cannot be enforced;
mine is functional and can be enforced) -- I think everything else flows from
that. We can only agree to disagree on this topic.

Anyway, this isn't going to be a language rule in any case, so it's probably
irrevelant to this group.

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

From: Robert Dewar
Date: Friday, October 15, 2010  5:25 PM

> I think I used it correctly. If in "pedantic" mode, the language rules 
> would be followed exactly (in this case, allowing dangerous function 
> calls). If not in "pedantic" mode, warnings or even errors would be 
> produced for dangerous function calls in these contexts.

OK, right!

> It's dangerous because any function call is dangerous in these 
> contexts unless it is declared and checked to not have conflicting 
> side-effects. I don't *want* people to write dangerous code -- period. 
> Now, there isn't going to be any way to stop someone from writing such 
> code, but that doesn't mean that you should do so.

Well I think this is a legitimate point of view, but NOT one that should
influecne the language design much, and certainly we don't want to insist on
others following this point of view (with which i strongly disagree). The
language should only to a certain extent enforce particular styles. By trying
to outlaw dangersous stuff, you will almost always outlaw useful stuff as
well, and thus decrease safety of some programs.

>> If your coding philosophy says we shouldn't be doing this, I haven't 
>> the foggiest idea why you think that, and frankly don't care much, 
>> but I AM sure you should not be trying to impose these peculiar ideas 
>> on other programmers :-)
>
> We've had this discussion before. Predicates, preconditions, et. al. 
> should only contain "pure" functions (for some reasonable definition 
> of "pure", admittedly hard to do). Anything else is dangerous because 
> it doesn't meet the expectations of the use. To take a concrete 
> example, using the Is_Valid function of Claw in a predicate or 
> precondition is dangerous because it can change values because of some 
> user action that is asychronous in relation to the executing code. 
> Just because the precondition or predicate succeeds tells you nothing 
> about the current state of the object - and this leads to dangerous 
> assumptions in the code (that you don't have to worry about Not_Valid_Error
> exceptions being raised).

That's fine, but any attempt to define exactly what you mean by pure here and
to outlaw functions you don't think are pure is misguided, and will most
certainly result in forbidding useless stuff.

> I believe that there is an obligation of the compiler to 
> reduce/eliminate the creation of dangerous predicates/preconditions. 
> This can only be done if the contract of functions includes some sort 
> of enforced side-effect information, so it isn't possible to do it in 
> Ada 2012 as proposed -- but this does not meant that it shouldn't be done.

I disagree with pursuing this idea, I think it will be a big mistake to try
to do anything in the language in this direction.

> I know that we totally disagree on what and how dangerous side-effect 
> in functions are identified (yours is operational and thus cannot be 
> enforced; mine is function and can be enforced) -- I think everything 
> else flows from that. We can only agree to disagree on this topic.

The issue is not whether to agree with your view or my view on this, the issue
is whether the language should try to enforce one viewpoint. To me the ONLY
useful definition of pure is operational (e.g. a counter kept to count calls
for performance reasons does not make a function impure, and any function is
impure given it takes time to execute at one extreme point of view). Any
language feature will be a menace. We discussed this at great lengths in the
Ada 83 design, several people wanted functions to be required to be pure, but
were eventually convinced that the attempt was misguided (by hundreds of
examples).

> Anyway, this isn't going to be a language rule in any case, so it's 
> probably irrevelant to this group.

One language thing that would be useful is what gnat calls pragma Pure_Function
to label (and check) individual functions for being pure in the sense of
functions in a pure unit.

Then if you want the restricted view, it is easier for a programmer to achieve
that.

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

From: Randy Brukardt
Date: Friday, October 15, 2010  5:39 PM

...
> One language thing that would be useful is what gnat calls pragma 
> Pure_Function to label (and check) individual functions for being pure 
> in the sense of functions in a pure unit.
> 
> Then if you want the restricted view, it is easier for a programmer to 
> achieve that.

We seriously considered that pragma for Ada 2005 (see AI95-00290-1), but we
couldn't get consensus on how it should work. There was a significant
contingent that felt that labeling something Pure means that it should be
enforced as it is for pure packages (else there is a confusion). In the end,
we decided that changing the definition of a pragma from a commonly used
implementation was a bad idea, and there didn't seem to be another name that
made sense. So the idea was dropped.

One of the reasons that I pursued global in/global out annotations was to get
this functionality in a way that wouldn't conflict with existing
implementations. But it was considered too immature (a statement that I can't
disagree with). So we have nothing this go-round.

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

From: Robert Dewar
Date: Friday, October 15, 2010  6:47 PM

> We seriously considered that pragma for Ada 2005 (see AI95-00290-1), 
> but we couldn't get consensus on how it should work. There was a 
> significant contingent that felt that labeling something Pure means 
> that it should be enforced as it is for pure packages (else there is a 
> confusion). In the end, we decided that changing the definition of a 
> pragma from a commonly used implementation was a bad idea, and there 
> didn't seem to be another name that made sense. So the idea was dropped.

Too bad that the "it must enforce our theoretical notion of X or we won't
tolerate it at all" school was allowed to derail this useful feature. Never
mind, all Ada 2005 users in the real world do have access to this useful
pragma. And a style rule that all functions in predicates and variants have
to be pure at least in this sense is probably a useful style rule.

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

From: Bob Duff
Date: Friday, October 15, 2010  5:26 PM	

> It's dangerous because any function call is dangerous in these 
> contexts unless it is declared and checked to not have conflicting side-effects.

"Dangerous" means "potentially harmful", so I think it's fair to use "dangerous"
here -- the Kind function might read the clock and do crazy things based on that.

But there are degrees of "danger".  If I happen to know that Kind doesn't read
the clock (etc), then it's really not very dangerous.

I guess my most important point in all this discussion is:
Consider the alternatives.  Their relative danger.  I mean, comments are dangerous.
"-- This thing is never zero." is not checked by the compiler (of course!).  It
might lead someone to divide by that thing.  But I don't want to outlaw comments
just in case they might be false.

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

From: Yannick Moy
Date: Monday, October 18, 2010  3:51 AM	

Plus we can build tools (different from the compiler) which analyze your
pre/post/predicate aspects and issue errors/warnings, while we will never be
able to do so for comments. Including warnings that there are writes to some
data.

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


Questions? Ask the ACAA Technical Agent