Version 1.11 of ai12s/ai12-0054-2.txt

Unformatted version of ai12s/ai12-0054-2.txt version 1.11
Other versions for file ai12s/ai12-0054-2.txt

!standard 3.2.4(14/3)          13-10-07 AI12-0054-2/05
!standard 3.2.4(31/3)
!standard 3.2.4(35/3)
!class binding interpretation 13-05-31
!status Corrigendum 1-2012 13-07-05
!status WG9 Approved 13-11-15
!status ARG Approved 8-0-0 13-06-15
!status work item 13-05-31
!status received 13-05-02
!priority High
!difficulty Medium
!qualifier Omission
!subject Aspect Predicate_Failure
!summary
The aspect Predicate_Failure defines what happens when a predicate check fails. It does NOT apply when evaluating memberships and the Valid attribute.
!question
AI12-0022-1, "Raise expressions for specifying the exception raised for an assertion" added the ability to control which exception is raised by failure of various assertions. This is an important capability, because it allows one to change interfaces to use assertions while preserving compatibility.
However, this feature doesn't work properly for predicates, because predicates are evaluated by membership tests, so we would get spurious failures in such membership tests if the predicate expression included a raise expression. Should these spurious failures be removed from the language, so that there is a mechanism for specifying the exception raised by a predicate failure? (Yes.)
!recommendation
(See !summary.)
!wording
Add after 3.2.4(14/3):
For a subtype with a directly-specified predicate aspect, the following additional language-defined aspect may be specified with an aspect_specification (see 13.1.1):
Predicate_Failure
This aspect shall be specified by an expression, which determines the action to be performed when a predicate check fails because a directly-specified predicate aspect of the subtype evaluates to False, as explained below.
Name Resolution Rules
The expected type for the Predicate_Failure expression is String.
Remove "Assertion_Error is raised if any of these checks fail." from 3.2.4(31/3).
Add after 3.2.4(31/3):
If any of the predicate checks fail, Assertion_Error is raised, unless the subtype whose directly-specified predicate aspect evaluated to False also has a directly-specified Predicate_Failure aspect. In that case, the specified Predicate_Failure expression is evaluated; if the evaluation of the Predicate_Failure expression propagates an exception occurrence, then this occurrence is propagated for the failure of the predicate check; otherwise, Assertion_Error is raised, with an associated message string defined by the value of the Predicate_Failure expression. In the absence of such a Predicate_Failure aspect, an implementation-defined message string is associated with the Assertion_Error exception.
[Editor's Note: The above will be changed again by AI12-0071-1, which defines the order of checks when a predicate is evaluated.]
Add additional notes and examples after 3.2.4(35/3):
Predicate_Failure expressions are never evaluated during the evaluation of a membership test (see 4.5.2) or Valid attribute (see 13.9.2).
A Predicate_Failure expression can be a raise_expression (see 11.3).
Examples
subtype Basic_Letter is Character -- See A.3.2 for "basic letter". with Static_Predicate => Basic_Letter in 'A'..'Z' | 'a'..'z' | '' | '' | 'D' | 'd' | '_' | '_' | '';
subtype Even_Integer is Integer with Dynamic_Predicate => Even_Integer mod 2 = 0, Predicate_Failure => "Even_Integer must be a multiple of 2";
*Text_IO (see A.10.1) could have used predicates to describe some common exceptional conditions as follows:*
with Ada.IO_Exceptions; package Ada.Text_IO is
type File_Type is limited private;
subtype Open_File_Type is File_Type with Dynamic_Predicate => Is_Open (Open_File_Type), Predicate_Failure => raise Status_Error with "File not open"; subtype Input_File_Type is Open_File_Type with Dynamic_Predicate => Mode (Input_File_Type) = In_File, Predicate_Failure => raise Mode_Error with "Cannot read file: " & Name (Input_File_Type); subtype Output_File_Type is Open_File_Type with Dynamic_Predicate => Mode (Output_File_Type) /= In_File, Predicate_Failure => raise Mode_Error with "Cannot write file: " & Name (Output_File_Type);
...
function Mode (File : in Open_File_Type) return File_Mode; function Name (File : in Open_File_Type) return String; function Form (File : in Open_File_Type) return String;
...
procedure Get(File : in Input_File_Type; Item : out Character);
procedure Put(File : in Output_File_Type; Item : in Character);
...
-- Similarly for all of the other input and output subprograms.
[Editor's note: We can't do this for Ada 202x, because of compatibility concerns (these subprograms would not be subtype conformant with existing access-to-subprogram types, so 'Access would fail). We could do something similar with preconditions, but that would look like a lot more change.]
!discussion
Note that Predicate_Failure is not involved when a predicate is evaluated in a membership or Valid attribute. This is how we get our cake and eat it too in this case.
Originally the Predicate_Failure was suggested as a way to be able to specify a different exception to be raised when failing a predicate check, without causing an exception to be raised when the predicate expression were evaluated as part of a membership test or Valid attribute reference. But then it was realized that the Predicate_Failure expression could be something other than simply a raise expression. In particular, it was suggested that it could specify the string to be associated with the predicate failure, even when still raising Assertion_Error. The first example in the wording above is a simple example, using Predicate_Failure to specify the message string to be associated with the Assertion_Error.
The expression of the Predicate_Failure aspect can be as simple as a string literal if all that is desired is to change the message; note that the current instance of the subtype is visible in the Predicate_Failure expression -- this can be useful in the exception message. On the other hand, the expression could be a complicated function call to log the bad value, for example.
If the evaluation of the expression raises an exception (an especially if it is a raise expression), the exception will propagate normally, thus achieving the effect of the predicate raising an expression different from Assertion_Error.
If it is necessary to raise multiple exceptions for different failures, or have distinct messages depending on which predicate fails, it is necessary to define multiple subtypes. For an example, see the Text_IO example in the wording above.
One could imagine defining similar aspects for the other kinds of contract assertions such as Pre and Post. However, there is no counterpart to the membership test or Valid attribute that causes the trouble with predicates. But defining such aspects would not be hard.
A simpler alternative would be to simply have a Predicate_Failure_Exception aspect, which just specifies the exception. In that case, however, we've lost the ability to specify an exception message. Thus we prefer the given solution.
NOTE: The example in AI12-0022-1 should not use a predicate expression as an example of where raise_expression should be used. That part of AI12-0022-1 should be replaced by the following.
Example: Imagine the following routine in a GUI library:
procedure Show_Window (Window : in out Root_Window); -- Shows the window. -- Raises Not_Valid_Error if Window is not valid.
We would like to be able to use a predicate to check the comment. With the "raise_expression" (and the Predicate_Failure aspect, see AI12-0054-2), we can do this without changing the semantics:
subtype Valid_Root_Window is Root_Window with Dynamic_Predicate => Is_Valid (Valid_Root_Window), Predicate_Failure => raise Not_Valid_Error;
procedure Show_Window (Window : in out Valid_Root_Window); -- Shows the window.
If we didn't include the Predicate_Failure aspect with a raise_expression here, using the predicate would change the exception raised on this failure. That could cause the exception to fall into a different handler than currently, which is unlikely to be acceptable.
We could have used a precondition on Show_Window instead of defining a predicate. In that case, we'd use the raise_expression directly in the precondition to raise the correct exception:
procedure Show_Window (Window : in out Valid_Root_Window) with Pre => Is_Valid (Window) or else raise Not_Valid_Error; -- Shows the window.
or perhaps a conditional expression is preferable:
procedure Show_Window (Window : in out Valid_Root_Window) with Pre => (if not Is_Valid (Window) then raise Not_Valid_Error); -- Shows the window.
Similarly, the various Containers packages in Ada could use predicates or preconditions in this way to make some of the needed checks; but that can only be done if the semantics remains unchanged (raising Program_Error and Constraint_Error, not Assertion_Error). (The !proposal also shows how this could be used in Text_IO and other I/O packages.)
End replacement for AI12-0022-1.
!corrigendum 3.2.4(14/3)
Insert after the paragraph:
the new paragraphs:
For a subtype with a directly-specified predicate aspect, the following additional language-defined aspect may be specified with an aspect_specification (see 13.1.1):
Predicate_Failure
This aspect shall be specified by an expression, which determines the action to be performed when a predicate check fails because a directly-specified predicate aspect of the subtype evaluates to False, as explained below.
Name Resolution Rules
The expected type for the Predicate_Failure expression is String.
!corrigendum 3.2.4(31/3)
Replace the paragraph:
On every subtype conversion, the predicate of the target subtype is evaluated, and a check is performed that the predicate is True. 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 performed 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 default_expressions, the predicate of the nominal subtype of the created object is evaluated, and a check is performed that the predicate is True. Assertions.Assertion_Error is raised if any of these checks fail.
by:
On every subtype conversion, the predicate of the target subtype is evaluated, and a check is performed that the predicate is True. 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 performed 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 default_expressions, the predicate of the nominal subtype of the created object is evaluated, and a check is performed that the predicate is True.
If any of the predicate checks fail, Assertion_Error is raised, unless the subtype whose directly-specified predicate aspect evaluated to False also has a directly-specified Predicate_Failure aspect. In that case, the specified Predicate_Failure expression is evaluated; if the evaluation of the Predicate_Failure expression propagates an exception occurrence, then this occurrence is propagated for the failure of the predicate check; otherwise, Assertion_Error is raised, with an associated message string defined by the value of the Predicate_Failure expression. In the absence of such a Predicate_Failure aspect, an implementation-defined message string is associated with the Assertion_Error exception.
!corrigendum 3.2.4(35/3)
Insert after the paragraph:
6 A Static_Predicate, like a constraint, always remains True for all objects of the subtype, except in the case of uninitialized variables and other invalid values. A Dynamic_Predicate, on the other hand, is checked as specified above, but can become False at other times. For example, the predicate of a record subtype is not checked when a subcomponent is modified.
the new paragraphs:
7 Predicate_Failure expressions are never evaluated during the evaluation of a membership test (see 4.5.2) or Valid attribute (see 13.9.2).
8 A Predicate_Failure expression can be a raise_expression (see 11.3).
Examples
subtype Basic_Letter is Character -- See A.3.2 for "basic letter". with Static_Predicate => Basic_Letter in 'A'..'Z' | 'a'..'z' | 'Æ' | 'æ' | 'Ð' | 'ð' | 'Þ' | 'þ' | 'ß';
subtype Even_Integer is Integer with Dynamic_Predicate => Even_Integer mod 2 = 0, Predicate_Failure => "Even_Integer must be a multiple of 2";
Text_IO (see A.10.1) could have used predicates to describe some common exceptional conditions as follows:
with Ada.IO_Exceptions; package Ada.Text_IO is
type File_Type is limited private;
subtype Open_File_Type is File_Type with Dynamic_Predicate => Is_Open (Open_File_Type), Predicate_Failure => raise Status_Error with "File not open"; subtype Input_File_Type is Open_File_Type with Dynamic_Predicate => Mode (Input_File_Type) = In_File, Predicate_Failure => raise Mode_Error with "Cannot read file: " & Name (Input_File_Type); subtype Output_File_Type is Open_File_Type with Dynamic_Predicate => Mode (Output_File_Type) /= In_File, Predicate_Failure => raise Mode_Error with "Cannot write file: " & Name (Output_File_Type);
...
function Mode (File : in Open_File_Type) return File_Mode; function Name (File : in Open_File_Type) return String; function Form (File : in Open_File_Type) return String;
...
procedure Get (File : in Input_File_Type; Item : out Character);
procedure Put (File : in Output_File_Type; Item : in Character);
...
-- Similarly for all of the other input and output subprograms.
!ACATS Test
An extra C-Test is needed to test that this aspect works as specified.
!ASIS
No ASIS effect.
!appendix

From: Jean-Pierre Rosen
Sent: Friday, February  8, 2013  8:49 AM

...
> AI12-0054-1/04   2013-01-30 --  A raise_expression does not cause membership failure
...
> Approve __________   Oppose _X__           Abstain _________
>
Justification:

I think this whole business is simply going out of control. I find the idea of
having "raise" not raising an exception, depending on context, "disgusting" (as
John would put it). Not counting the endless discussions about what happens if
an exception is propagated for other reasons during the evaluation of a
condition.

The !discussion section in AI12-0022-1/03 proposes alternative possibilities,
and concludes "Thus the selected alternative seems clearly to be the best
option." However, this was stated at a time where we didn't consider the
consequences for membership tests and the like. Now that we have discovered more
issues, I think we should reconsider alternative solutions before commiting to
this one.

For example, the only counter argument I see for alternatives #1 and #2 is the
case of complicated preconditions that would raise different exceptions. I have
a feeling that there will always be conditions that cannot be expressed as
formal pre-conditions, and that it might be better to accept this. This would be
cleaner than the raise-which-does-not-raise-it-depends expression.

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

From: Randy Brukardt
Sent: Friday, February  8, 2013  1:55 PM

...
> > Approve __________   Oppose _X__           Abstain _________

Just to make sure everyone is clear on this, with an Oppose vote during a letter
ballot, our procedures require that the AI is discussed and revoted at the next
meeting. So, unless J-P can be convinced to change his vote, the ballot on
AI12-0054-1 has effectively failed.

J-P does not say what he intends to vote on AI12-0022-1 at WG 9, but his
justification suggests that he would reject that, too. I think that would be a
mistake (see below), but it might make sense to pull it as well.

> Justification:
> I think this whole business is simply going out of control. I find the
> idea of having "raise" not raising an exception, depending on context,
> "disgusting" (as John would put it).
> Not counting the endless discussions about what happens if an
> exception is propagated for other reasons during the evaluation of a
> condition.
>
> The !discussion section in AI12-0022-1/03 proposes alternative
> possibilities, and concludes "Thus the selected alternative seems
> clearly to be the best option." However, this was stated at a time
> where we didn't consider the consequences for membership tests and the
> like.
> Now that we have discovered more issues, I think we should reconsider
> alternative solutions before commiting to this one.
>
> For example, the only counter argument I see for alternatives
> #1 and #2 is the case of complicated preconditions that would raise
> different exceptions. I have a feeling that there will always be
> conditions that cannot be expressed as formal pre-conditions, and that
> it might be better to accept this.
> This would be cleaner than the
> raise-which-does-not-raise-it-depends expression.

This is throwing out the baby with the bathwater: we have a small problem with
one contract item, so we shouldn't solve a major problem for all contract items.
That's insane!

You are essentially saying that we should never try to use preconditions and/or
predicates with well-designed reusable libraries (including ones like Text_IO
and Ada.Containers.Vectors). A well-designed library will raise different
exceptions in response to different classes of major errors. That's critical for
debugging, especially in the absence of static analysis tools. I know Robert
says that they have some sort of clever message that identifies the error in a
predicate without having to raise different exceptions, but truly reusable,
portable code cannot depend on the features of a single implementation.

So, in my view at least, using proposals #1 or #2 alone would essentially make
contracts useless except in toy programs. (Alternatively, we would just have to
resort to Geert's trick everywhere, and there would be no use of proposals #1 or
#2, plus the membership bug would be back.)

It also should be noted that AI12-0022-1 also solves other problems (most
notably, the problem of the required return statement). I don't think that we
should make any change to AI12-0022-1 and it should be approved as is. I feel
*very* strongly about this.

So let's look at your objection to AI12-0054-1 *by itself*. The problem we have
is that predicates are fundamentally (but subtly) different than other
contracts. Perhaps the solution lies in admitting that and treating them
differently. That sort of was the idea behind AI12-0054-1, but I can agree that
it is a screwy solution, and one that we didn't try very hard to look at
alternatives to.

One obvious alternative is to forget AI12-0054-1, and adopt a new aspect for
predicates only:

     Predicate_Exception => <exception_name>

which of course would change the exception raised by a predicate.

This also would have eliminated most of the need for the discussion about which
exception to raise by default, as it would be easy to change it without changing
memberships.

Note that this would increase implementation complexity a bit, because multiple
predicates can apply to a subtype, and they all could raise different
exceptions:

    subtype Open_File_Type is File_Type
       with Dynamic_Predicate => Is_Open (Open_File_Type), Predicate_Exception => Status_Error;
    subtype Read_File_Type is Open_File_Type
       with Dynamic_Predicate => Mode(Read_File_Type) /= Out_File, Predicate_Exception => Mode_Error;

Also, this solution is suboptimal because there is no way to add an exception
message in the latter case (to display the file name along with the exception).
This is one important reason why the raise_expression is much preferred as a
solution for the other contracts. (Another is that other contracts cannot be
combined, and in particular we don't want to get into automatic combination of
preconditions, so only one exception could be raised for it.)

The advantage here of course is that memberships would work right without
unusual rules.

I could see adding a requirement that all predicates for a particular subtype
raise the same exception (which would make the above code illegal), if the added
implementation complexity is considered too much.

I could also see adding similar aspects for the other contracts just for
consistency, although we would not expect them to be used.

To summarize, I think that we definitely need raise_expressions (they work best
in complicated preconditions), but perhaps we need to have two solutions so that
simple predicates also work properly. Rather than having a bizarre and
incomplete exception. (And if someone doesn't care that memberships work, they
can use raise_expressions in their predicates as well.)

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

From: Bob Duff
Sent: Friday, February  8, 2013  2:14 PM

> Also, this solution is suboptimal because there is no way to add an
> exception message in the latter case (to display the file name along
> with the exception). This is one important reason why the
> raise_expression is much preferred as a solution for the other
> contracts. (Another is that other contracts cannot be combined, and in
> particular we don't want to get into automatic combination of
> preconditions, so only one exception could be raised for it.)

I think it's important that predicates and preconditions be interchangeable in
the case where the precondition is a property of one parameter.  For example, I
often have something like:

    function F(X: T) ... with Pre => not Is_Empty(X);
    function G(X: T) ... with Pre => not Is_Empty(X);
    ...

which I realize would be better expressed as:

    subtype Non_Empty_T is T with Dynamic_Predicate => not Is_Empty(X);
    function F(X: Non_Empty_T) ...;
    function G(X: Non_Empty_T) ...;
    ...

And then I end up declaring local variables of subtype Non_Empty_T.
I want any raise expressions to be moved into the predicate when such
refactoring is done.

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

From: Randy Brukardt
Sent: Friday, February  8, 2013  7:49 PM

...
> I think it's important that predicates and preconditions be
> interchangeable in the case where the precondition is a property of
> one parameter.
...

There is nothing particularly optimal about the solution I suggested.

But I'm very worried that we're about to get into a situation similar to that
which we have had repeatedly for "unreserved keywords". The ARG (most of whose
members are in the US delagation) vote for a solution, and WG 9 (where the US
only gets one vote) rejects it. The effect is that we'll end up with no solution
to this problem -- and I can easily imagine that we would quickly reach a point
where further work on the Ada standard has become pointless. (I won't care about
an Ada standard that can't solve this problem, that's for sure.)

I'm trying to find a way to cut the gordian knot here. I'm almost certain that
the solution we have is the best one possible for preconditions, postconditions,
and invariants (not to mention function returns and probably a variety of other
instances). It's not optimal for predicates, but I don't want to lose the
solution for all of those other uses just because we can't agree on how to fix
it for a marginal use in predicates.

Rather than gunking up the language as in AI12-0054-1, perhaps providing an
alternative mechanism for the problematic case would be better.

And please note that nothing about AI12-0054-1 really meets your premise: if the
exception is raised by a function, a precondition will work fine, but a
predicate will fail. AI12-0054-1 only applies to raise_expressions directly in
the predicate, and that surely isn't the only way that preconditions will be
structured.

After all, early Ada 2012 code using Geert's trick will *not* be interchangeable
in this way. And that will be true no matter *what* solution is invented to
solve this problem or the original one. (The closest we could get is adding
No_Return functions and applying AI12-0054-1-like rules to them, but of course
that isn't in Ada 2012, so existing Geert trick code will not have the needed
aspect.)

So while this principle seems important, I don't think there will ever be a rule
which would make it true all of the time. I wouldn't want to lose *any* solution
over a principle that can't be absolute in any event.

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

From: Jean-Pierre Rosen
Sent: Sunday, February 10, 2013  3:58 AM

> Just to make sure everyone is clear on this, with an Oppose vote
> during a letter ballot, our procedures require that the AI is
> discussed and revoted at the next meeting. So, unless J-P can be
> convinced to change his vote, the ballot on AI12-0054-1 has effectively failed.

If everybody else is perfectly happy with AI12-0054, I can change my vote to
avoid blocking the process. But I want to raise a flag that further
consideration is still needed (see below).

> J-P does not say what he intends to vote on AI12-0022-1 at WG 9, but
> his justification suggests that he would reject that, too.

Not necessarilly. I have nothing against raise expressions, just to the weird
notion that their effect depends on the context in which they are used -
including the effect that sometimes writing "raise" means "return false". If the
/only/ justification for raise expressions is for chosing the exception raised
by a precondition (as the title indicates - but that can be changed), they would
logically go away. But if it can be showed (as I think) that they are useful in
other contexts, why not?

----

Back to AI12-0054. The root of the problem is that it mixes the detection of a
failure with the choice of the exception being raised. If these can be separated
(as in alternatives #1-3), the problem would go away. So here is an outline for
an alternative#4. If I can get a seconder, I'll turn it into an AI.

A "secondary exception" can be attached to the raising of an exception, similar
to the way a message is attached. Precise syntax TBD, but the following would be
added to package Ada.Exceptions:

procedure Raise_Exception(E : in Exception_Id;
                          Message : in String := "";
                          Secondary : in Exception_ID)
        with No_Return;

and/or:

function Raise_Exception(E : in Exception_Id;
                         Message : in String := "";
                         Secondary : in Exception_ID)
         return Boolean
        with No_Return;

Any code that wants the failure of an assertion to raise something different
would raise Assertion_Error, with the desired exception as a secondary
exception.

Then the rules would become (just the intent, wording would need
improvements):
- For a predicate: if the predicate evaluates to false, Assertion_Error is
  raised. If Assertion_Error is propagated by the evaluation of the predicate
  and there is a secondary exception, the secondary exception is raised with the
  same message as associated to Assertion_Error. Any other exception (as well as
  Assertion_Error without a secondary exception) propagates normally.

- For a membership test: if evaluation of the predicate raises Assertion_Error,
  then the membership test evaluates to False (and the exception is not
  propagated). Any other exception is propagated normally.

----
Much less magic to my taste, and presumably easier to implement...

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

From: Bob Duff
Sent: Sunday, February 10, 2013  5:35 AM

> If everybody else is perfectly happy with AI12-0054, I can change my
> vote to avoid blocking the process.

I think that would be best.  It's not that everybody is "perfectly happy".  I
think it's the best solution, not perfect.

>...But I want to raise a flag that
> further consideration is still needed (see below).

OK.

> > J-P does not say what he intends to vote on AI12-0022-1 at WG 9, but
> > his justification suggests that he would reject that, too.
> Not necessarilly. I have nothing against raise expressions, just to
> the weird notion that their effect depends on the context in which
> they are used - including the effect that sometimes writing "raise"
> means "return false".

I agree it's a little bit weird.  But I still think it's the best solution (see
below).

>...If the /only/ justification for raise expressions is for chosing
>the exception raised by a precondition

That's the main use, but not the only one.  It could also be useful for
assertions other than preconditions.  And it could be useful to specify 'with
"useful message"'.

>...(as the title indicates - but
> that can be changed), they would logically go away. But if it can be
>showed (as I think) that they are useful in other contexts, why not?

Another context in which they are useful is a function that always raises an
exception:

    function F (...) return ... is
    begin
        raise Not_Yet_Implemented; -- Illegal!
    end F;

You have to write a bogus return statement:

    function F (...) return ... is
    begin
        raise Not_Yet_Implemented;
        return F (...); -- Can't get here
    end F;

We just had a customer complaining about this, and he didn't know about the
bogus-recursion trick, which makes it worse (e.g., if the return subtype is
limited or indefinite, then there's no handy value to return).

Anyway, with raise expressions, you can say:

    function F (...) return ... is
    begin
        return raise Not_Yet_Implemented;
    end F;

I can't think of any other use for raise expressions, but that doesn't mean
there are none.

> ----
>
> Back to AI12-0054. The root of the problem is that it mixes the
> detection of a failure with the choice of the exception being raised.
> If these can be separated (as in alternatives #1-3), the problem would
> go away. So here is an outline for an alternative#4. If I can get a
> seconder, I'll turn it into an AI.

Sorry, no "second" from me.  ;-)

For one thing, I don't fully understand it.  Currently you say, "Pre => not
End_Of_File (File)".  With AI12-0022-1, you can say

    Pre => not End_Of_File (File) or else raise End_Error

You seem to be suggesting something like:

    Pre => not End_Of_File (File) or else
        raise Assertion_Error with Secondary_Exception => End_Error

I'm puzzled by that.  I'm unclear how that connects up with the Raise_Exception
subprograms below.

> A "secondary exception" can be attached to the raising of an
> exception, similar to the way a message is attached. Precise syntax
> TBD, but the following would be added to package Ada.Exceptions:
>
> procedure Raise_Exception(E : in Exception_Id;
>                           Message : in String := "";
>                           Secondary : in Exception_ID)
>         with No_Return;
>
> and/or:
>
> function Raise_Exception(E : in Exception_Id;
>                          Message : in String := "";
>                          Secondary : in Exception_ID)
>          return Boolean
>         with No_Return;
>
> Any code that wants the failure of an assertion to raise something
> different would raise Assertion_Error, with the desired exception as a
> secondary exception.

So to raise End_Error, you have to raise Assertion_Error with End_Error as a
"secondary exception"?  I don't see how that works.

> Then the rules would become (just the intent, wording would need
> improvements):
> - For a predicate: if the predicate evaluates to false,
> Assertion_Error is raised. If Assertion_Error is propagated by the
> evaluation of the predicate and there is a secondary exception, the
> secondary exception is raised with the same message as associated to
> Assertion_Error. Any other exception (as well as Assertion_Error
> without a secondary exception) propagates normally.
>
> - For a membership test: if evaluation of the predicate raises
> Assertion_Error, then the membership test evaluates to False (and the
> exception is not propagated). Any other exception is propagated normally.

That has the problem we discussed at the Boston meeting: it hides bugs.  If I
say "Static_Predicate => F(...)", and F calls procedure P, and P says "Assert
(something);", and 'something' is False, then that's a bug, and I want an
exception even when we're dynamically within a membership test.

The only time you want an exception to be turned into "return False"
is when the exception is syntactically within the predicate expression -- where
you can see it.  Not within some callee.

> ----
> Much less magic to my taste, and presumably easier to implement...

Well, for AdaCore, the easiest to implement at this point is AI12-0054-1 as
written, because Robert already implemented it a week or two ago.

Anyway, I'm not sure what your implementation model is.
Does it involve wrapping things with exception handlers?

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

From: Robert Dewar
Sent: Sunday, February 10, 2013  6:15 AM

> Not necessarilly. I have nothing against raise expressions, just to
> the weird notion that their effect depends on the context in which
> they are used - including the effect that sometimes writing "raise"
> means "return false". If the /only/ justification for raise
> expressions is for chosing the exception raised by a precondition (as
> the title indicates - but that can be changed), they would logically
> go away. But if it can be showed (as I think) that they are useful in other
> contexts, why not?

I think they are generally useful, it seems quite useful to be able to translate

    if x = 4 then
       return 42;
    elsif x > y then
       return 84;
    else
       raise constraint_error;
    end if;

as

    return
     (if x = 4 then 42 elsif x > y then 84 else raise constraint_error);

I am certainly finding this applicable now that it is available in GNAT.

as for the special behavior in membership, I think this is reasonable

basically the feeling is

    if expression in B then

means if the value of the expression meets all the criteria for being in B, i.e.
that the test for membership is true, then the IN returns True, otherwise the IN
returns False.

If you write a predicate where the only use of the raise expression is to change
the exception that is issued, then in the case of IN, no exception would be
issued in the normal case (you don't get an assert failure), so it seems
reasonable not to get one for the raise.

Or another way of looking at it. Normally if a predicate fails, we get an
exception raised. But not in the case where the predicate fails in an IN. In
other words, we already in a sense special case predicate behavior in an IN
context.

> weird notion that their effect depends on the context in which they
> are used - including the effect that sometimes writing "raise" means
> "return false"

So this is not a weird notion. The appearence of raise bla in a predicate means
the predicate fails. Normally a failing predicate raises an exception (and if
use raise to give the failure, rather than False, you are simply saying what
exception should be raised). But in an IN, a failing predicate does not raise an
exception.

I think you could better argue that raising the exception in the IN context
would be the wierd behavior.

> Back to AI12-0054. The root of the problem is that it mixes the
> detection of a failure with the choice of the exception being raised.
> If these can be separated (as in alternatives #1-3), the problem would
> go away. So here is an outline for an alternative#4. If I can get a
> seconder, I'll turn it into an AI.

I don't like this at all, I think it is clutter that provides no advantages over
the raise exception approach, which is generally useful, unlike this very
specialized gizmo.

> A "secondary exception" can be attached to the raising of an
> exception, similar to the way a message is attached. Precise syntax
> TBD, but the following would be added to package Ada.Exceptions:
>
> procedure Raise_Exception(E : in Exception_Id;
>                            Message : in String := "";
>                            Secondary : in Exception_ID)
>          with No_Return;
>
> and/or:
>
> function Raise_Exception(E : in Exception_Id;
>                           Message : in String := "";
>                           Secondary : in Exception_ID)
>           return Boolean
>          with No_Return;
>
> Any code that wants the failure of an assertion to raise something
> different would raise Assertion_Error, with the desired exception as a
> secondary exception.

> Much less magic to my taste, and presumably easier to implement...

I don't think it is any easier to implement, anyway, it was only two days work
to implement both the AI's as they stand in GNAT. In the unlikely event that the
ARG changed its mind and did in raise expressions, we would definitely keep them
in GNAT (presumably under the extension flag). Actually I think the above would
be much harder to implement. Adding gizmos to Raise_Exception would be really
rather tricky to implement. And unlike JPR, I find this much *more* magic :-)

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

From: Bob Duff
Sent: Sunday, February 10, 2013  7:29 AM

>     return
>      (if x = 4 then 42 elsif x > y then 84 else raise constraint_error);

Agreed.  And this reminds me of another case: the case expression.
In a case statement, if you're unable to obey the full coverage rules, you can
say "when others => raise ...;".  Raise expressions allow this to be done in
case expressions.  I have already run into this in my own coding (using case
exprs, but before raise exprs were implemented).

Which reminds me of the meta-reminder:  If I say, "I can't think of any reason
to do X other than Y", that doesn't prove that "there is no reason to do X other
than Y".  It could just as well be that I lack imagination.

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

From: Jean-Pierre Rosen
Sent: Monday, February 11, 2013  7:57 AM

> I think they are generally useful, it seems quite useful to be able to
> translate
[...]
So do I. I have nothing against raise expressions, it's Randy who assumed that. And I don't see anything in alternative#4 that contradicts your view of how predicates and membership tests relate.

> as for the special behavior in membership, I think this is reasonable
> basically the feeling is
>
> if expression in B then
>
> means if the value of the expression meets all the criteria for being
> in B, i.e. that the test for membership is true, then the IN returns
> True, otherwise the IN returns False.

Yes, sure, but that's where the devil is in the details. If an exception is
raised anywhere else in the evaluation of the predicate (or even in the
evaluation of the message of the raise exception), then it is propagated, it
does not make the predicate false. That's what I find weird: the AI says "well,
when we say "raise" in that context, we don't really mean "raise", we just
indicate the exception associated with the failure of the predicate.

Moreover, if you have a complicated expression in your predicate and want to
make it simpler by moving parts of it into an expression function, the "raise"
will return to its regular meaning, and the predicate won't be usable in
membership tests. I'm afraid this will cause a lot of surprises for casual
users.

Here is my line of reasoning:
The need is to indicate the failing of a predicate, and this is different from a
regular exception (I considered adding a "failed" expression, but it didn't seem
to go anywhere). The less disruptive solution I found was to have a special
exception to indicate failure, and Assert_Error seemed perfectly fit for that.
That made a simple rule: in a predicate, if Assert_Error is raised, the
predicate fails; if any other exception is raised, it is propagated normally.

The only drawback with this approach is that you still can't chose the
exception. Hence, by attaching an (extra) Exception_ID to the occurrence, you
can  fix that. It requires two Exception_ID in the exception occurrence
structure instead of one, which does not look like a big deal.

A typical call sequence would be (assuming you can have an exception handler for
an expression, just to make it simpler to read):

For a predicate:
begin
   if not <predicate expression> then
      raise Assert_Error;
   end if;
exception
   when Occur: Assert_Error =>
      raise Secondary_Exception (Occur) with Exception_Message (Occur); end;

For a membership test:
begin
   <predicate expression>
exception
   when Assert_Error =>
      False;
end;

(Yes, I forgot to mention the function Secondary_Exception in my previous
message)

This way, the decision of what to make with a failed predicate is at the proper
place: on the caller's side.

--

Note that I can see other cases where this feature can be useful.
Imagine you have a rule to always report the raising of an exception by having a
call to a logging procedure in every handler in every subprogram (Eurocontrol
has something like that). You get a lot of cascading messages as you go down the
call stack. But the first logging procedure can raise a "Failure" exception
(which is not logged) with the original exception attached. This way, only the
first subprogram logs the exception, and the bottom of stack of calls can
resurect the original exception.

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

From: Robert Dewar
Sent: Monday, February 11, 2013  8:54 AM

> Moreover, if you have a complicated expression in your predicate and
> want to make it simpler by moving parts of it into an expression
> function, the "raise" will return to its regular meaning, and the
> predicate won't be usable in membership tests. I'm afraid this will
> cause a lot of surprises for casual users.

I agree this is odd (see my earlier messages). I doubt it will cause a lot of
surprises for casual users in practice, since I expect most predicates to be
pretty simple!

> Here is my line of reasoning:
> The need is to indicate the failing of a predicate, and this is
> different from a regular exception (I considered adding a "failed"
> expression, but it didn't seem to go anywhere). The less disruptive
> solution I found was to have a special exception to indicate failure,
> and Assert_Error seemed perfectly fit for that. That made a simple rule:
> in a predicate, if Assert_Error is raised, the predicate fails; if any
> other exception is raised, it is propagated normally.
>
> The only drawback with this approach is that you still can't chose the
> exception. Hence, by attaching an (extra) Exception_ID to the
> occurrence, you can  fix that. It requires two Exception_ID in the
> exception occurrence structure instead of one, which does not look
> like a big deal.

A fairly big deal, because this structure is pervasive across tools (e.g. the
debugger), but doable (certainly more work than the current AI!)

But an interesting idea indeed!

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

From: Bob Duff
Sent: Monday, February 11, 2013  11:30 AM

> Yes, sure, but that's where the devil is in the details. If an
> exception is raised anywhere else in the evaluation of the predicate
> (or even in the evaluation of the message of the raise exception),
> then it is propagated, it does not make the predicate false. That's
> what I find
> weird: the AI says "well, when we say "raise" in that context, we
> don't really mean "raise", we just indicate the exception associated
> with the failure of the predicate.

Yes, it's a little weird, but it's the least error prone of all the
alternatives.  If "raise" always raises, then there's a gotcha -- "in" and
'Valid go around raising exceptions for no good reason, which is a surprise.

The other alternative is that exceptions propagating from callees get turned
into "return False".  I'm opposed to that because it hides bugs. Also, inserting
implicit exception handlers seems heavy, and confuses debuggers.

> Moreover, if you have a complicated expression in your predicate and
> want to make it simpler by moving parts of it into an expression
> function, the "raise" will return to its regular meaning, and the
> predicate won't be usable in membership tests. I'm afraid this will
> cause a lot of surprises for casual users.

Yes, if you want to encapsulate a predicate in a function, you must learn to NOT
encapsulate the "or else raise ..." part.

> Here is my line of reasoning:
> The need is to indicate the failing of a predicate, ...

Well, there are two ways a predicate can fail:  It can be False, or some
constituent can raise an exception.  The former case should return False for
'in'.  The latter case is likely a bug, and should always raise.

I consider "Predicate => X > 0 or else raise Something"
to be the FORMER case, conceptually.  It's exactly the same as "Predicate => X >
0", except to override Assertion_Error.

"Predicate => X / Y" (where Y happens to be zero) is cleaerly the LATTER case --
divide by zero shouldn't get turned into "return False". The same applies to
"Predicate => F(X)", where F fails a pre/postcondition or some internal pragma
Assert, or calls a procedure that fails a postcondition, or...

>...and this is
> different from a regular exception (I considered adding a "failed"
> expression, but it didn't seem to go anywhere). The less disruptive
>solution I found was to have a special exception to indicate failure,
>and Assert_Error seemed perfectly fit for that. That made a simple rule:
> in a predicate, if Assert_Error is raised, the predicate fails; if any
>other exception is raised, it is propagated normally.
>
> The only drawback with this approach is that you still can't chose the
> exception. Hence, by attaching an (extra) Exception_ID to the
> occurrence, you can  fix that. It requires two Exception_ID in the
> exception occurrence structure instead of one, which does not look
> like a big deal.
>
> A typical call sequence would be (assuming you can have an exception
> handler for an expression, just to make it simpler to read):
>
> For a predicate:
> begin
>    if not <predicate expression> then
>       raise Assert_Error;
>    end if;
> exception
>    when Occur: Assert_Error =>
>       raise Secondary_Exception (Occur) with Exception_Message
> (Occur); end;
>
> For a membership test:
> begin
>    <predicate expression>
> exception
>    when Assert_Error =>
>       False;
> end;
>
> (Yes, I forgot to mention the function Secondary_Exception in my
> previous message)

Without wording, I'm not sure I fully understand the above, but it sure seems
like a lot of mechanism -- rather more complicated than the solution agreed upon
in Boston.

And it is a variation on the schemes that hide bugs; that seems unreasonable to
me.

> This way, the decision of what to make with a failed predicate is at
> the proper place: on the caller's side.

I don't understand what you mean by that.  The decision of what to do about a
failed predicate lies with us, the language designers. The programmer has no
control over these rules, neither at caller's or callee's side.

> Note that I can see other cases where this feature can be useful.
> Imagine you have a rule to always report the raising of an exception
> by having a call to a logging procedure in every handler in every
> subprogram (Eurocontrol has something like that). You get a lot of
> cascading messages as you go down the call stack. But the first
> logging procedure can raise a "Failure" exception (which is not
> logged) with the original exception attached. This way, only the first
> subprogram logs the exception, and the bottom of stack of calls can
> resurect the original exception.

Well, that's one of the many things one could do if exceptions were objects, as
they should be, and as they are in most other languages that have exceptions.
But we lost that battle in Ada 9X.

J.P., I really think you're letting "perfect" be the enemy of "good enough".  I
understand why you're uncomfortable with the solution you called "weird".  It is
indeed imperfect. But now we're going around in circles searching for a better
solution that does not exist.

Other solutions either hide bugs, or have even worse "weirdness"
for "in" and 'Valid.

Therefore, my suggestion is to withdraw your "No" vote (change it to "abstain").

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

From: Bob Duff
Sent: Monday, February 11, 2013  11:41 AM

> Therefore, my suggestion is to withdraw your "No" vote (change it to
> "abstain").

By the way, I don't much like the rule that says a single "No"
vote has veto power in letter ballots.  (I'd prefer "two No's" or "three No's".)

But anyway, there is no such veto power in meetings, so this rule only serves to
delay things.  Unless of course you can convince someone to change their mind;
in that case it does make sense to defer the decision to the next meeting.

But I haven't seen that, yet.  If anybody is convinced by J.P. that there is a
better solution than the one in the AI, I suggest they should speak up!

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

From: Robert Dewar
Sent: Monday, February 11, 2013  11:50 AM

> But I haven't seen that, yet.  If anybody is convinced by J.P. that
> there is a better solution than the one in the AI, I suggest they
> should speak up!

I do like the fact that JPR's solution avoids this nasty abstraction glitch
(raise expression means something different if abstracted into a function).

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

From: Bob Duff
Sent: Monday, February 11, 2013 12:19 PM

Yes, but it introduces the "hide bugs" problem (among other things), which seems
far worse to me.

If you're not prepared to argue that the "abstraction glitch" is so bad that
hiding bugs (and other things) is acceptable, then I don't think you can support
JP's proposal.

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

From: Jean-Pierre Rosen
Sent: Monday, February 11, 2013 12:32 PM

My proposal doesn't "hide bugs". If you raise Assertion_Error, you signal a
failure of the predicate. If you raise anything else, it propagates. The
difference looks clear to me.

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

From: Bob Duff
Sent: Monday, February 11, 2013 12:45 PM

> > Yes, but it introduces the "hide bugs" problem (among other things),
> > which seems far worse to me.
> My proposal doesn't "hide bugs".

It does hide bugs.  Consider a type T with a predicate that calls function F.  F
calls procedure P, which calls Q, which calls R.  Now suppose R fails its
postcondition.  That's a bug.  Failing a postcondition is (almost?) always a
bug.  Your proposal, if I understand it correctly, hides that bug -- that is,
when somebody says "if X in T then", instead of raising Assertion_Error as it
should, it silently returns False from the 'in'.

(Sorry if I have misunderstood your proposal!)

>... If you raise Assertion_Error, you
> signal a failure of the predicate. If you raise anything else, it
>propagates. The difference looks clear to me.

Not at all.  Assertion_Error almost always indicates a bug.

In the above example, R doesn't know it was called (indirectly!) from a
predicate.  A failure in R does not and should not mean "If I was called from an
'in', then I want that 'in' to be False." That would be a huge hole in the
abstraction.

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

From: Robert Dewar
Sent: Monday, February 11, 2013  1:10 PM

>> Yes, but it introduces the "hide bugs" problem (among other things),
>> which seems far worse to me.
> My proposal doesn't "hide bugs". If you raise Assertion_Error, you
> signal a failure of the predicate. If you raise anything else, it
> propagates. The difference looks clear to me.

Well I think the hide bugs thing comes from the scenario where you call a
screwed up function, which fails an assertion, and the failure is hidden by a
membership test failing, swalling up the assert_error.

This is a real issue I would say!

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

From: Robert Dewar
Sent: Monday, February 11, 2013  1:12 PM

I am convinced by Bob's reargument of his position, and withdraw any feeling of
support for the JPR alternative.

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

From: Steve Baird
Sent: Monday, February 11, 2013  1:26 PM

> I have nothing against raise expressions, just to the weird notion
> that their effect depends on the context in which they are used -
> including the effect that sometimes writing "raise" means "return
> false".

I am very sympathetic. I don't think anyone ever argued that this is elegant;
only that it was the best of the alternatives that were identified.

In particular, there is a consensus (which I agree with) that implicitly
wrapping a "when others" exception handler around a membership test (or around
the predicate-evaluation part of a membership test) is a bad idea because of
bug-hiding problems.

If I understand your proposal, its merit depends (at least in a
necessary-but-not-sufficient way) on whether the reduction in bug hiding
associated with wrapping only a "when Assertion_Error" exception handler
(instead of a "when others =>") reduces the bug hiding enough to make this
proposal more palatable than the current "raise sometimes means return false"
wart.

I agree with Bob that this is still too much bug hiding; it is better than the
"when others ="" proposal, but still not good enough.

This is a strong statement because (like you) I strongly dislike changing the
meaning of a raise expression by moving it from a predicate expression into a
function.

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

From: Robert Dewar
Sent: Monday, February 11, 2013  1:34 PM

Add a new exception Predicate_Error
used to indicate that a predicate is failing.

The exception gets changed to

   Assert_Error if it is signalled within a normal predicate test.

   return False if it is signalled within a membership

And then we rig up some way of signalling some other exception (if this is
really needed for predicates, I am not convinced, we do need it for
preconditions and postconditions, but I am not that convinced we need it for
predicates).

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

From: Steve Baird
Sent: Monday, February 11, 2013  1:52 PM

What if we treated them as two unrelated exceptions except for a rule that a
handler for Assertion_Error which lacks an explicit handler for Predicate_Error
also handles Predicate_Error?

Membership tests could then have an implicit "when Predicate_Error"
handler.

One could still construct examples where this leads to bug hiding, but this
becomes less likely.

One could think of Predicate_Error as a "flavor" of Assertion_Error (I think the
Apex compiler supports this idea of "flavored" exceptions - we could go
whole-hog and add Apex-style flavored exceptions to the language, but that seems
like overkill).

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

From: Bob Duff
Sent: Monday, February 11, 2013  5:06 PM

> > If everybody else is perfectly happy with AI12-0054, I can change my
> > vote to avoid blocking the process.
>
> I think that would be best.  It's not that everybody is "perfectly
> happy".  I think it's the best solution, not perfect.
> ...

The above got delivered way late, which explains why you didn't understand what I was talking about when I said "hide bugs".
I hope it's clear now what I meant.

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

From: Bob Duff
Sent: Monday, February 11, 2013  5:26 PM

> So here is a refeinment of JPR's idea
>
> Add a new exception Predicate_Error
> used to indicate that a predicate is failing.
>
> The exception gets changed to
>
>    Assert_Error if it is signalled within a normal predicate test.
>
>    return False if it is signalled within a membership

OK, that fixes my "hide bugs" complaint, I think.

> And then we rig up some way of signalling some other exception

There's the rub.  "Rig up some way?"  Well, we already have a way, which is
raise expressions.  I wouldn't want to have a different way for preconditions
versus predicates.

I think the idea that a raise expression that is sitting right there
(statically) in the text of a predicate is different from a raise exp in some
callee,, is reasonable.  I admit this idea takes some getting used to.  ;-)

> (if this is really needed for predicates, I am not convinced, we do
> need it for preconditions and postconditions, but I am not that
> convinced we need it for predicates).

It's most important for preconditions, where the client might care what
exception is raised.

But (as I've said before) I've found that it's very useful to encode what might
be preconditions as predicates:

    procedure P(X : T);
    procedure Q(X : T);
    procedure R(X : T);

P, Q, and R all share the same "precondition" -- that X must be in subtype T,
which includes the predicate of T.

Therefore, I think it's important that predicates, as well as preconditions,
have the capability of controlling which exception gets raised on failure.

Think about:

    function First_Element (X : Non_Empty_List) return Element;

We might want that to raise Empty_List_Error if X is empty.
But we want "X in Non_Empty_List" to return True or False (absent some bugs in
the body of Is_Empty).

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

From: Randy Brukardt
Sent: Monday, February 11, 2013  5:35 PM

>> I think they are generally useful, it seems quite useful to be able
>> to translate
[...]
>So do I. I have nothing against raise expressions, it's Randy who
>assumed that.
>And I don't see anything in alternative#4 that contradicts your view of
>how predicates and membership tests relate.

You said that we needed to reconsider how we change the exception in assertion
expressions, suggesting using a different alternative than raise_exceptions. It
was hard for me to imagine how you could support raise_expressions without using
them for their defining purpose. I wouldn't think we'd want two ways to do this.

...
> A typical call sequence would be (assuming you can have an exception
> handler for an expression, just to make it simpler to read):
>
> For a predicate:
> begin
>    if not <predicate expression> then
>       raise Assert_Error;
>    end if;
> exception
>    when Occur: Assert_Error =>
>       raise Secondary_Exception (Occur) with Exception_Message (Occur);
> end;

This would be far too expensive for any implementation that does not have
zero-cost exeception handling. (Including implementations that use target
exceptions, as Windows has.) I think it is critical that predicate checking be
fast enough (for reasonable expressions) that there is little performance need
to Ignore them (in the same way that there is little performance need to
Suppress constraint checks).

Perhaps there is some way to work around this expense without changing the
overall exception mechanism (the ideas that I have are limited and require
wide-spread use of classification aspects on functions), but in the absense of
such an idea this is not acceptable.

(The "hide bugs" issue of course is related to this; any time you have a general
exception handler, you have the risk of handling an exception not intended for
this purpose.)

> For a membership test:
> begin
>    <predicate expression>
> exception
>    when Assert_Error =>
>       False;
> end;

I'm somewhat less concerned about the cost here (memberships are rather rare in
my code), but the bug hiding remains.

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

From: Bob Duff
Sent: Monday, February 11, 2013  5:38 PM

> What if we treated them as two unrelated exceptions except for a rule
> that a handler for Assertion_Error which lacks an explicit handler for
> Predicate_Error also handles Predicate_Error?
>
> Membership tests could then have an implicit "when Predicate_Error"
> handler.

The above seems at least as kludgy as the rules agreed to in Boston.
I mean, that's a pretty "magical" exception you just invented.

> One could still construct examples where this leads to bug hiding, but
> this becomes less likely.

I'm not convinced it's less likely enough.  There are predicate checks all over
the place.  Any one of those might raise Predicate_Error, which would be a bug,
and would be hidden in subtle cases.

> One could think of Predicate_Error as a "flavor" of Assertion_Error (I
> think the Apex compiler supports this idea of "flavored"
> exceptions - we could go whole-hog and add Apex-style flavored
> exceptions to the language, but that seems like overkill).

Well, if we did exceptions right, then an exception would just be an object of
type Exception'Class, whose 'Tag indicates which particular type of exception it
is.  I've got no problem with "type Predicate_Error is new Assertion_Error".
But that ain't gonna happen, and wouldn't really solve the current issue anyway,
IMHO.

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

From: Randy Brukardt
Sent: Monday, February 11, 2013  5:42 PM

...
> And then we rig up some way of signalling some other exception (if
> this is really needed for predicates, I am not convinced, we do need
> it for preconditions and postconditions, but I am not that convinced
> we need it for predicates).

We surely need this for predicates, otherwise one has to duplicate the predicate
as a precondition on many common subprograms.

As I suggested the other day:

    subtype Read_File_Type is File_Type
       with Dynamic_Predicate =>
          (Is_Open (Read_File_Type) or else raise Status_Error) and then
          (Mode(Read_File_Type) /= Out_File or else raise Mode_Error with "Cannot read " & Name(Read_File_Type);

for Text_IO; then use this subtype as the parameter subtype for all of the Get
and Get_Line routines. This eliminates having to duplicate this complex
condition on every Get subprogram.

Some of us (like Bob) are going to use this sort of construction a lot. We don't
want Predicates to be a second-class citizen in this regard.

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

From: Randy Brukardt
Sent: Monday, February 11, 2013  5:48 PM

> > So here is a refeinment of JPR's idea
> >
> > Add a new exception Predicate_Error
> > used to indicate that a predicate is failing.
> >
> > The exception gets changed to
> >
> >    Assert_Error if it is signalled within a normal predicate test.
> >
> >    return False if it is signalled within a membership
>
> OK, that fixes my "hide bugs" complaint, I think.

Does it? If you write a lot of your preconditions as predicates, predicate
failures could happen inside of nested calls in a predicate expression. And
those failures still represent bugs, not "failures". And we'd still have to have
extra exception handlers in predicate checks, which is too expensive for a
marginal case.

OTOH, if you *don't* handle propogations from nested calls (which would be more
efficient), then this is just a more complex description of the standard
semantics. What have we gained??

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

From: Bob Duff
Sent: Monday, February 11, 2013  5:57 PM

> Does it?

No, I guess you're right -- it doesn't quite solve the "hide bugs"
problem, as you explain below (and I think I said something similar in my
response to Steve).

>... If you write a lot of your preconditions as predicates, predicate
>failures could happen inside of nested calls in a predicate expression.
>And  those failures still represent bugs, not "failures". And we'd
>still have to  have extra exception handlers in predicate checks, which
>is too expensive  for a marginal case.
>
> OTOH, if you *don't* handle propogations from nested calls (which
> would be more efficient), then this is just a more complex description
> of the standard semantics. What have we gained??

I'm thinking that any approach that involves automatic wrapping with exception
handlers is a wrong approach.  As always, I could be wrong.

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

From: Randy Brukardt
Sent: Monday, February 11, 2013  6:00 PM

> So here is a refeinment of JPR's idea
>
> Add a new exception Predicate_Error
> used to indicate that a predicate is failing.

It strikes me here that we're going about this the wrong way. This is not about
an exception at all. The issue is to specify when a predicate fails and what
happens. The use of a raise_expression for that is uncomfortable, because it
doesn't mean raise an exception when it appears in a predicate.

So perhaps we should consider directly saying that an assertion expression has
failed.

One way to do that would be to have an Ada.Exceptions function to that effect:

     function Failed (E : Exception_Id := Assertion_Error'Identity;
                      Msg : String := "") return Boolean;

Then, the semantics of a call of this routine would be as described in
AI12-0054-1. Raise_expressions then would only raise.

Even better would be to have a "failed_expression" (which would need a new
keyword, I think):

     failed_expression ::= failed exception_name [with string_expression];

A failed_expression would have the semantics described in AI12-0054-1 for a
raise_expression. Moreover, it would be illegal to write it outside of an
assertion expression. So if someone mistakenly wrapped it in a function, the
function would be illegal. That would eliminate most of the risk of accidentally
raising exceptions. (And giving a raise_expression in a predicate would give a
warning, because it almost certainly is not what you meant, perhaps we could
even require that a-la pragmas.)

The problem with this idea is that it needs a new keyword -- which prevents us
from using it as a fix to Ada 2012 (new syntax is uncomfortable enough, a new
keyword is over the top). (I thought about proposing this reusing "abort" or
"terminate", but I think that would have the same problem of confusion that we
have with "raise".)

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

From: Randy Brukardt
Sent: Monday, February 11, 2013  6:05 PM

...
> That would eliminate most of the risk of accidentally raising
> exceptions.

"when you meant to fail the predicate".

I noticed that we keep talking about the predicate "failing". What if we
elevated that idea to first-class semantics? There seems to be a solution there,
not sure if it is worth the complexity.

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

From: Steve Baird
Sent: Monday, February 11, 2013  6:07 PM

> But that ain't gonna
> happen, and wouldn't really solve the current issue anyway, IMHO.

Agreed on both points.

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

From: Randy Brukardt
Sent: Monday, February 11, 2013  6:22 PM

A bit too little context here, methinks. Usually I complain about quoting too
much, but it's possible to quote too little, as well, and this is a classic
example. What isn't gonna happen?? No clue from the message.

The PST timestamp didn't help (I looked at a bunch of other messages Bob sent
around 3:40 CST). I had to search my entire ARG mail repository (now approaching
17000 messages) to find the phrase in question.

In case any one else cares, the full quote above would be:

> Well, if we did exceptions right, then an exception would just be an object
> of type Exception'Class, whose 'Tag indicates which particular type of exception
> it is.  I've got no problem with "type Predicate_Error is new Assertion_Error".
> But that ain't gonna happen, and wouldn't really solve the current issue
> anyway, IMHO.

And now I know what Steve is agreeing with...

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

From: Robert Dewar
Sent: Monday, February 11, 2013  7:25 PM

>> So here is a refeinment of JPR's idea
>>
>> Add a new exception Predicate_Error
>> used to indicate that a predicate is failing.
>>
>> The exception gets changed to
>>
>>     Assert_Error if it is signalled within a normal predicate test.
>>
>>     return False if it is signalled within a membership
>>
>> And then we rig up some way of signalling some other exception (if
>> this is really needed for predicates, I am not convinced, we do need
>> it for preconditions and postconditions, but I am not that convinced
>> we need it for predicates).

No, that doesn't work! You have to make sure that an inner predicate failure (in
the middle of evaluating an outer one) is properly signalled and not swallowed.
The below proposal does not achieve this, my form does.

> What if we treated them as two unrelated exceptions except for a rule
> that a handler for Assertion_Error which lacks an explicit handler for
> Predicate_Error also handles Predicate_Error?
>
> Membership tests could then have an implicit "when Predicate_Error"
> handler.
>
> One could still construct examples where this leads to bug hiding, but
> this becomes less likely.
>
> One could think of Predicate_Error as a "flavor" of Assertion_Error (I
> think the Apex compiler supports this idea of "flavored"
> exceptions - we could go whole-hog and add Apex-style flavored
> exceptions to the language, but that seems like overkill).

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

From: Robert Dewar
Sent: Monday, February 11, 2013  7:43 PM

> You said that we needed to reconsider how we change the exception in
> assertion expressions, suggesting using a different alternative than
> raise_exceptions. It was hard for me to imagine how you could support
> raise_expressions without using them for their defining purpose. I
> wouldn't think we'd want two ways to do this.

Please improve your imagination :-)

I find raise expressions a very nice feature, quite independent of the "defining
purpose", and if that "defining purpose" goes away, I would still support the
presence of raise expression in the language. After all the "defining purpose"
of conditional expressions is for assertions, but I have used them FAR more
often outside assertions.

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

From: Robert Dewar
Sent: Monday, February 11, 2013  7:48 PM

> This would be far too expensive for any implementation that does not
> have zero-cost exeception handling. (Including implementations that
> use target exceptions, as Windows has.) I think it is critical that
> predicate checking be fast enough (for reasonable expressions) that
> there is little performance need to Ignore them (in the same way that
> there is little performance need to Suppress constraint checks).

I don't see this! For the case where the exception is raised locally (remember
that's the ONLY case the AI allows currently), the local exception is a simple
goto.

And as for implementations which do not have zero cost exception handling (in
the sense of incurring overhead even if the exception is not raised), that's a
broken implementation IMO (or one that simply does not care that much about
efficiency!)

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

From: Robert Dewar
Sent: Monday, February 11, 2013  7:56 PM

OK, making my thought a bit more concrete

We introduce the exception Predicate_Error

We add an aspect Predicate_Exception which says what exception to raise if the
predicate fails in other than a membership situation.

For the normal ca,se where no exception is explicitly raised, we can just write
e.g.

     with Predicate => Val > 4,
          Predicate_Exception => Constraint_Error;

that's equivalent to what we have now as

     with Predicate => Val > 4 or else raise Constraint_Error;

If you write an explicit raise that you want treated as a predicate failure, you
use Predicate_Exception

     with Predicate => Gunk (Val),
          Predicate_Exception => Constraint_Error,

where Gunk (Val) either returns False or raises Predicate_Error to make the
predicate fail, and either of these results in Constraint_Error being raised.

Then if a predicate fails in a membership test (returns False or raises
Predicate_Exception) then you get no exception, instead the membership test is
False.

I think this works, allows the flexibility of defining what exception is needed
with the same granularity, and avoids the bug hiding.

I am not sure I like it :-)

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

From: Robert Dewar
Sent: Monday, February 11, 2013  8:00 PM

>> OK, that fixes my "hide bugs" complaint, I think.
>
> Does it? If you write a lot of your preconditions as predicates,
> predicate failures could happen inside of nested calls in a predicate
> expression. And those failures still represent bugs, not "failures".
> And we'd still have to have extra exception handlers in predicate
> checks, which is too expensive for a marginal case.

No, you don't hide bugs, if a Predicate_Exception is raised which causes an
inner predicate check to fail (the bug), then it is converted to and propagated
as Assertion_Error, which does not cause the outer predicate to fail, but just
gets propagated, that's the whole point!

The expense argument seems bogus. It's only expensive in the cases which the
current proposal does not provdie anyway of handling!

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

From: Randy Brukardt
Sent: Monday, February 11, 2013  8:03 PM

> > This would be far too expensive for any implementation that does not
> > have zero-cost exeception handling. (Including implementations that
> > use target exceptions, as Windows has.) I think it is critical that
> > predicate checking be fast enough (for reasonable expressions) that
> > there is little performance need to Ignore them (in the same way
> > that there is little performance need to Suppress constraint checks).
>
> I don't see this! For the case where the exception is raised locally
> (remember that's the ONLY case the AI allows currently), the local
> exception is a simple goto.

But the compiler cannot know this. The semantics as J-P proposed (and you seemed
to be advocating) involved changing any Assertion_Error that propagates (no
matter from where). Unless the predicate has no function calls at all (rare with
dynamic predicates), the compiler cannot know whether any such exception will
propagate, thus it has to provide a handler (and probably one that will never be
triggered). In the case where there are no function calls, the compiler ought to
generate the code as described in AI12-0054-1 and never use any exceptions at
all. So I don't see any benefit to this proposal, just complications.

> And as for implementations which do not have zero cost exception
> handling (in the sense of incurring overhead even if the exception is
> not raised), that's a broken implementation IMO (or one that simply
> does not care that much about efficiency!)

I don't see this at all; matching your environment is important, and the
efficiency of handlers doesn't matter much because modern code has very few of
them. (Mostly last chance handlers and debugging aids.) I would think you would
have a much better case if we were discussing finalization. Anyway, this is
getting way off topic.

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

From: Robert Dewar
Sent: Monday, February 11, 2013  8:07 PM

Regarding Randy's complaint about too little context, don't people use
threading? I find far too much context quoted in most ARG messages!

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

From: Randy Brukardt
Sent: Monday, February 11, 2013  8:16 PM

> Regarding Randy's complaint about too little context, don't people use
> threading? I find far too much context quoted in most ARG messages!

I have to post these more-or-less linearly in the AIs (because threads always
cross-fertilize, we can't have people talking about something that a reader
doesn't see for 5 pages). Thus I read the mail linearly as well.

I agree that most people quote way too much. But it is possible to quote too
little, as in this example. And others have complained about too little context
as well.

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

From: Robert Dewar
Sent: Monday, February 11, 2013  9:53 PM

> I agree that most people quote way too much. But it is possible to
> quote too little, as in this example. And others have complained about
> too little context as well.

But if you ever DO have trouble figuring out what something is replying to,
learn how to use threading to easily answer this question (you complained that
it was difficult to figure out!)

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

From: Randy Brukardt
Sent: Monday, February 11, 2013  8:11 PM

> OK, making my thought a bit more concrete
>
> We introduce the exception Predicate_Error

What does this buy us?

> We add an aspect Predicate_Exception which says what exception to
> raise if the predicate fails in other than a membership situation.

I proposed that on Friday, without the extra exception. I don't see any value to
the extra exception in this case, because you will simply return False to
represent failure from any nested routines. And that's enough to get the
exception you want raised, so long as all failures raise the same exception (or
you use multiple subtypes, as I explained on Friday).

> For the normal ca,se where no exception is explicitly raised, we can
> just write e.g.
>
>      with Predicate => Val > 4,
>           Predicate_Exception => Constraint_Error;
>
> that's equivalent to what we have now as
>
>      with Predicate => Val > 4 or else raise Constraint_Error;
>
> If you write an explicit raise that you want treated as a predicate
> failure, you use Predicate_Exception
>
>      with Predicate => Gunk (Val),
>           Predicate_Exception => Constraint_Error,
>
> where Gunk (Val) either returns False or raises Predicate_Error to
> make the predicate fail, and either of these results in
> Constraint_Error being raised.

Why would you want a *raise* to be treated as predicate failure? All you have to
do is return False to get that to happen! It's a lot simpler and doesn't require
any changes at all.

> Then if a predicate fails in a membership test (returns False or
> raises Predicate_Exception) then you get no exception, instead the
> membership test is False.
>
> I think this works, allows the flexibility of defining what exception
> is needed with the same granularity, and avoids the bug hiding.
>
> I am not sure I like it :-)

I think the Predicate_Error part is pointless, at least unless you add J-P
"secondary exception" idea into the mix.

And the other part is what I suggested on Friday, which was rejected (although
mostly by Bob).

I think this way has been thoroughly explored and nothing new is going to be
found here. And I now think we're addressing the wrong problem anyway. I think
we need to directly specify that the (predicate) assertion failed, but so far
that idea hasn't gained any interest (or brickbats, for that matter).

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

From: Robert Dewar
Sent: Monday, February 11, 2013  8:20 PM

>> I don't see this! For the case where the exception is raised locally
>> (remember that's the ONLY case the AI allows currently), the local
>> exception is a simple goto.
>
> But the compiler cannot know this. The semantics as J-P proposed (and
> you seemed to be advocating) involved changing any Assertion_Error
> that propagates (no matter from where). Unless the predicate has no
> function calls at all (rare with dynamic predicates), the compiler
> cannot know whether any such exception will propagate, thus it has to
> provide a handler (and probably one that will never be triggered). In
> the case where there are no function calls, the compiler ought to
> generate the code as described in
> AI12-0054-1 and never use any exceptions at all. So I don't see any
> benefit to this proposal, just complications.

There are two cases

a) it's an error. I don't think we worry too much about efficiency in this case,
   where an exception is being propagated anyway.

b) it's in a membership test. Here, if it is local (the only case that the
   current proposal allows), you can still convert it into raise false, though
   note with this new proposal, the local use of raise expression in predicates
   will be rarer, since it is not needed,

    e.g. if we now write a predicate

       X > 4 or else raise constraint_error;

    we would now write simply

       X > 4

    and the membership test would just fail without any
    issue of exceptions.

If the exception is not local (a case we can't handle at all in the current
proposal), then yes, there is an expense in handling the exception int he
membership case, but that's compared to not allowing it at all.

Note that if we reduce the use of raise expression in predicates, the issue of
abstraction does not involve exceptions anyway.

As I think about this more, I think it might be just fine to just implement the

    Predicate_Exception => ...

aspect and leave it at that, and abandone the idea of using raise expressions in
predicates as a way of making predicates fail.

The approach of raise expression is still fine for other assertions, but
abandoning it for predicates makes a lot of sense to me, and eliminates 90% of
this discussion!

>> And as for implementations which do not have zero cost exception
>> handling (in the sense of incurring overhead even if the exception is
>> not raised), that's a broken implementation IMO (or one that simply
>> does not care that much about efficiency!)
>
> I don't see this at all; matching your environment is important, and
> the efficiency of handlers doesn't matter much because modern code has
> very few of them. (Mostly last chance handlers and debugging aids.)

Randy you make all kinds of claims about coding style which strike me as plain
bizarre. You are cerrtainly entitled to choose your own style, however weird,
but PLEASE do not try to impose this on the world, or on the language design.
The claim that exception handlers is rare in modern code is complete nonsense. I
say this with some certainty since I read code written by lots of different
people.

So if you choose to implement a compiler which is only acceptably efficient for
your coding style in which exceptions are not used, fine, it will be fine for
your use, but I can assure you that it will not be generally fine. We initially
used non-zero-cost exception handling for all GNAT implementations, and were
forced to change (we nearly always use ZCX now) by lost of instances of customer
code that was just too slow with non-ZCX.

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

From: Randy Brukardt
Sent: Monday, February 11, 2013  9:35 PM

...
> Randy you make all kinds of claims about coding style which strike me
> as plain bizarre. You are cerrtainly entitled to choose your own
> style, however weird, but PLEASE do not try to impose this on the
> world, or on the language design. The claim that exception handlers is
> rare in modern code is complete nonsense. I say this with some
> certainty since I read code written by lots of different people.

There are lots of people out there that write badly structured code. I don't
think the language or implementations ought to get bent into knots to make their
life easier for them.

> So if you choose to implement a compiler which is only acceptably
> efficient for your coding style in which exceptions are not used,
> fine, it will be fine for your use, but I can assure you that it will
> not be generally fine. We initially used non-zero-cost exception
> handling for all GNAT implementations, and were forced to change (we
> nearly always use ZCX now) by lost of instances of customer code that
> was just too slow with non-ZCX.

Obviously, our experiences vary wildly here. Even with our decidedly non-ZCX
implementation, I can only recall a couple instances where anyone had a
performance issue with exception handling. And every such case was easily
fixable by a minor change in the code (usually, moving the handler to a slightly
wider scope).

Moreover, the majority of these handlers involved last-chance handling that
would be better dealt with using finalization (which is impossible to forget,
unlike last-chance handlers), or even something like the At_End that GNAT has.
Exception handling would be the last choice in these cases in new code.

It's possible that other performance concerns vis-a-vis Janus/Ada masked
exception performance. Janus/Ada was never designed to have the absolute best
runtime performance anyway; it was designed primarily to save space, time
optimization has always been a distant second. So that difference in philosophy
may have changed the details.

In any case, your experience is wildly different than mine, this is wildly
off-topic for this list, and it's irrelevant to any reasonable rule that we're
going to adopt here, so we should either drop the topic or take it private.

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

From: Robert Dewar
Sent: Monday, February 11, 2013  10:20 PM

> There are lots of people out there that write badly structured code. I
> don't think the language or implementations ought to get bent into
> knots to make their life easier for them.

But your ideas of well structured code are far too odd for me to let you use
them as a guiding principle in the language design.

> Obviously, our experiences vary wildly here. Even with our decidedly
> non-ZCX implementation, I can only recall a couple instances where
> anyone had a performance issue with exception handling. And every such
> case was easily fixable by a minor change in the code (usually, moving
> the handler to a slightly wider scope).

Well making such "minor changes" in systems with millions of lines of code is
hardly practical.

> Moreover, the majority of these handlers involved last-chance handling
> that would be better dealt with using finalization (which is
> impossible to forget, unlike last-chance handlers), or even something
> like the At_End that GNAT has. Exception handling would be the last
> choice in these cases in new code.

That's just wrong, the majority of handlers are NOT in this category.
A grep for raise in the Ada hierarchy of GNAT yields well over 3000 raise
statements, which are almost all in the business of generating required
exceptions from these routines, and have nothing to do with "last chance
handling".

> In any case, your experience is wildly different than mine, this is
> wildly off-topic for this list, and it's irrelevant to any reasonable
> rule that we're going to adopt here, so we should either drop the
> topic or take it private.

Not so wildly off-topic, because you were using your experience to guide the
language design, and I object to this even if the experience were IMO valid, and
in this case I don't think the experience is valid. I do not think efficiency
concerns (and certainly not misguided efficiency concerns) should be our primary
focus or even *A* primary focus, in addressing this problem

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

From: Randy Brukardt
Sent: Monday, February 11, 2013  10:37 PM

...
> > In any case, your experience is wildly different than mine, this is
> > wildly off-topic for this list, and it's irrelevant to any
> > reasonable rule that we're going to adopt here, so we should either
> > drop the topic or take it private.
>
> Not so wildly off-topic, because you were using your experience to
> guide the language design, and I object to this even if the experience
> were IMO valid, and in this case I don't think the experience is
> valid.

Huh? What else would I use to guide my opinion of the language design? A dart
board? Experience in using the language has to be one of the most important
criteria. It doesn't always get the same answer for different people, which is
expected, but to deny it altogether makes no sense. Especially as you are using
your experience to claim mine is misguided.

> I do not think
> efficiency concerns (and certainly not misguided efficiency
> concerns) should be our primary focus or even *A* primary focus, in
> addressing this problem

I was mostly concerned about adding overhead in the case where no exception will
ultimately be raised (especially in assertion checks). Beyond that, I agree with
you. But in any case it's irrelevant here because any proposal that has this
performance issue would necessarily also have the "hide-a-bug" property, and I
can't see us adopting a rule that hides bugs. And none of the serious proposals
at this point have this property (sorry, J-P).

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

From: Robert Dewar
Sent: Tuesday, February 12, 2013  7:53 AM

> Huh? What else would I use to guide my opinion of the language design?
> A dart board? Experience in using the language has to be one of the
> most important criteria. It doesn't always get the same answer for
> different people, which is expected, but to deny it altogether makes no sense.
> Especially as you are using your experience to claim mine is misguided.

I disagree when it comes to style issues, and declarations that such and such a
feature is rarely used, or used only for some purpose. Next you will be saying
that no one uses USE clauses!

> I was mostly concerned about adding overhead in the case where no
> exception will ultimately be raised (especially in assertion checks).
> Beyond that, I agree with you. But in any case it's irrelevant here
> because any proposal that has this performance issue would necessarily also have the "hide-a-bug"
> property, and I can't see us adopting a rule that hides bugs. And none
> of the serious proposals at this point have this property (sorry, J-P).

Well that's false, the proposal I made for the special exception would
completely avoid the hide-a-bug issue (because in the case of an inner predicate
failure Predicate_Error would get transformed at that inner level to
Assertion_Error, and thus be propagated through a membership test at a higher
level.

But if we follow the line of thinking that I have, and which I think you mostly
share, we don't need the capability of dealing with exceptions in membership
tests at all!

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

From: Geert Bosch
Sent: Monday, February 11, 2013  8:55 PM

>> So here is a refeinment of JPR's idea
>>
>> Add a new exception Predicate_Error
>> used to indicate that a predicate is failing.
>
> It strikes me here that we're going about this the wrong way. This is
> not about an exception at all. The issue is to specify when a
> predicate fails and what happens. The use of a raise_expression for
> that is uncomfortable, because it doesn't mean raise an exception when it appears in a predicate.

I agree with Randy. In essence, we'd like the return type of predicates to be
Boolean_Or_Exception_Id, with values True, False, Assertion_Error, xxx_Error.
When evaluating the predicate in an iterator, the expected type would be the
same BOEI, and we would check for Predicate = True. In other cases, where the
expected type is Boolean, the conversion of Boolean_or_Exception_Id would lead
to raising the exception for values other than True or False.

This would be very similar to normal expression semantics, where intermediate
values can be of a larger base type. The only difference is that a value other
than True or False would not raise Constraint_Error but rather the exception
indicated by the value of the expression.

In a way, one could argue that exceptions resulting from language defined checks
are always part of the expression value. The same issue that shows up with raise
expressions is present with ordinary arithmetic. A compiler may compute (X + Y)
/ 2 without overflow, even when X + Y would overflow.

Similarly Boolean'Pos (Condition) * (X + Y) is not required to exceptions
resulting from language defined checks if Condition is False. I don't see raise
expressions as being fundamentally different. They are just exceptional values
that will propagate if they occur in a context that isn't ready to handle them.

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

From: Robert Dewar
Sent: Monday, February 11, 2013  9:52 PM

>> We add an aspect Predicate_Exception which says what exception to
>> raise if the predicate fails in other than a membership situation.
>
> I proposed that on Friday, without the extra exception. I don't see
> any value to the extra exception in this case, because you will simply
> return False to represent failure from any nested routines. And that's
> enough to get the exception you want raised, so long as all failures
> raise the same exception (or you use multiple subtypes, as I explained on Friday).

I agree that the extra exception is not worth the effort and I agree with your
Friday proposal. See my next message.

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

From: Robert Dewar
Sent: Monday, February 11, 2013  10:15 PM

I hereby change my vote to negative on this AI. I have decided that it's just
too odd to be tolerable. For example, I find it really awkward that with checks
on:

      if A = 0 then raise Constraint_Error else (10 / A) = 0 end if;

is not equivalent to

      (10 / A) = 0

which of course raises Constraint_Error if A is zero. And we have the nasty
abstraction problem that a call to a function that raises an exception (e.g.
Raise_Exception itself) is not equivalent to a raise expression that raises the
same exception).

Here is my thinking about what to do

We motivate the use of Raise_Expression's in assertions to change the exception
raised.

Now we point out that this approach has a problem in the case of a predicate.

We point out that if a predicate is used sometimes in a context where we want an
exception if the predicate fails, and sometimes in membership tests, the raise
expression approach to changing the assertion doesn't work because we would get
an exception in the membership case which is almost certainly not what is
wanted.

So what's the solution?

Well we have two paths, either is acceptable to me.

Approach A: Do nothing special

   In this case, we note that it really is not a good
   idea to try to use the same predicated subtype in
   both these situations:

     a) if the predicate fails, you want an exception
     other than an assertion_error.

     b) the predicate is used in membership tests

   The solution is to use separate predicated subtypes
   for the two cases. The one for case a) can use
   raise expressions as would be done for preconditions.
   The one for case b) just evaluates to false if the
   condition is not met.

   [note: I don't think this is so terrible, after
    all, if you have different subprograms that take
    a subtype which needs tests and the tests are the
    same, but you want different exceptions, you will
    have to declare separate subtypes anyway. I suspect
    that it is more subprogram specific than subtype
    specific what exception you want raised]

Approach B, new aspect

   In approach B, we introduce a new aspect
   Predicate_Exception, applicable to subtypes
   or to subprograms, which says what exception
   is to be raised if a predicate fails.

   This Predicate_Exception aspect of course has
   no role to play in the case of a membership
   test since no exception is raised when the
   predicate fails (yields False).

   I think it makes sense to be allowed to apply
   this to subprograms, since as I said above,
   I suspect the need for exception substitution
   really arises more at the subprogram level
   than the type level.

   This still does not handle the case where
   different arguments raise different exceptions
   under different conditions. In such cases, if
   you want to use predicates, you will have to
   define special subtypes for the occasion with
   special predicates.

So in short, I think this needs more thought. I spent quite a bit of effort
implementing AI-0054 but I still don't like it. In fact the implementation is
very odd, involving a traversal looking for raise expressions, excluding any
that are generated by the code generator and not in the original source, and
replacing them with return Fale. Very odd ....

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

From: Bob Duff
Sent: Tuesday, February 12, 2013  8:33 AM

> I hereby change my vote to negative on this AI. I have decided that
> it's just too odd to be tolerable. For example, I find it really
> awkward that with checks on:
>
>       if A = 0 then raise Constraint_Error else (10 / A) = 0 end if;
>
> is not equivalent to
>
>       (10 / A) = 0
>
> which of course raises Constraint_Error if A is zero.

I think you are confused.  The first line is syntactically wrong.
I'm guessing you mean it to be an if_expression:

    (if A = 0 then raise Constraint_Error else (10 / A) = 0)

If so, the two are equivalent (with checks on).
If you meant something else, please clarify.

This AI has no effect on the semantics of any features other than membership
tests and 'Valid, neither of which appears above.

So if that's your main reason for changing your vote, I suggest you think again.

> Approach A: Do nothing special

> Approach B, new aspect

I could live with either of those.  But I still prefer the AI as written -- I
guess I've gotten used to the fact that a "raise" that is sitting right there
textually within a predicate is special.

I'm pretty strongly opposed to anything that hides bugs by swallowing exceptions
that are NOT "right there textually within...". Ada has a few such cases
(interrupt handlers and task bodies swallow exceptions), and I consider every
one of them to be a language design flaw.  I don't want to add more such cases,
especially when there's no workaround (you can get in the habit of wrapping all
your interrupt handlers and tasks with "when others" handlers, but you can't
very well put a "when others" in every procedure that might be called directly
or indirectly from a predicate!).

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

From: Bob Duff
Sent: Tuesday, February 12, 2013  9:53 PM

> I think you are confused.

As discussed on the phone, it was I who was confused.

> ...The first line is syntactically wrong.
> I'm guessing you mean it to be an if_expression:
>
>     (if A = 0 then raise Constraint_Error else (10 / A) = 0)

What Robert meant was that if you write:

    ... with Dynamic_Predicate =>
        (if A = 0 then raise Constraint_Error else (10 / A) = 0)

or:

    ... with Dynamic_Predicate => (10 / A) = 0

and then use 'in', the above two things are not equivalent according to this AI.
That's true.

> > Approach B, new aspect

Regarding approach B, we could allow something like:

    with Predicate_Failure => raise Some_Error with "some message";

to allow one to give a message.  We would need to figure out what the expected
type of that thing is, and figure out what, if anything, is meant by:

    with Predicate_Failure => 2 + 2;

The following could also be useful:

    with Predicate_Failure => Note_Error (123, "hello, world);

where Note_Error is a No_Return procedure.

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

From: Robert Dewar
Sent: Tuesday, February 12, 2013  9:57 AM

>> I hereby change my vote to negative on this AI. I have decided that
>> it's just too odd to be tolerable. For example, I find it really
>> awkward that with checks on:
>>
>>        if A = 0 then raise Constraint_Error else (10 / A) = 0 end if;
>>
>> is not equivalent to
>>
>>        (10 / A) = 0
>>
>> which of course raises Constraint_Error if A is zero.
>
> I think you are confused.  The first line is syntactically wrong.
> I'm guessing you mean it to be an if_expression:
>
>      (if A = 0 then raise Constraint_Error else (10 / A) = 0)

Yes of course I mean it to be an IF expression :-)

> If so, the two are equivalent (with checks on).
> If you meant something else, please clarify.

No they are NOT! Not in the context of a predicate expression!
If you use (10 / A) = 0 as a predicate and A is zero this will raise constraint
error if used in a membership test.

So it is interesting that you say "the two are equivalent", it shows that you
can be fooled by the odd semantics of AI-0054. What you meant to say was "the
two are equivalent except when used as a predicate expression for a predicate
used in a membership test" UGH

> This AI has no effect on the semantics of any features other than
> membership tests and 'Valid, neither of which appears above.

But could appear above

> So if that's your main reason for changing your vote, I suggest you
> think again.

No need to think again, your response with the misstatement that those two
expressions are equivalent, just acts to strengthen my impression that AI-0054
is a mal-feature.

>> Approach A: Do nothing special
>
>> Approach B, new aspect
>
> I could live with either of those.  But I still prefer the AI as
> written -- I guess I've gotten used to the fact that a "raise" that is
> sitting right there textually within a predicate is special.
>
> I'm pretty strongly opposed to anything that hides bugs by swallowing
> exceptions that are NOT "right there textually within...".
> Ada has a few such cases (interrupt handlers and task bodies swallow
> exceptions), and I consider every one of them to be a language design
> flaw.  I don't want to add more such cases, especially when there's no
> workaround (you can get in the habit of wrapping all your interrupt
> handlers and tasks with "when others"
> handlers, but you can't very well put a "when others" in every
> procedure that might be called directly or indirectly from a
> predicate!).

Note that both options a) and b) are completely immune to the swallowing bugs
option. And AI-0054 is not completely immune in its current form.

Suppose I write a predicate expression

   (10 / A) = 0

Then I notice that if things are really screwed up, A which is never supposed to
be zero could be zero, so I rewrite the predicate expression as

   (if A = 0 then raise Program_Error
                    with "Fatal error, A is zero",
    else (10 / A) = 0)

confident that now if A is ever zero I will get my Program_Error raised.

And lo and behold, AI-0054 results in cancelling out the effect of this check
and silently hiding the fatal error.

Of course I can fix this by rewriting this as

   (if A = 0 then Fatal ("Fatal error, A is zero"),
    else (10 / A) = 0)

where I write the appropriate function Fatal that is a No_Return function that
raises Program_Error. (yes I know we can't give No_Return for a function, but
that's just a gap!)

That works, but it is odd that I have to do it, and to me this example negates
claims that AI-0054 ensures against the problem of hiding bugs by swallowing
exceptions, since AI-0054 is all about swallowing exceptions.

An exception is an error, that's really a fundamental invariant of the Ada
design, AI-0054 violates this intention.

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

From: Jean-Pierre Rosen
Sent: Tuesday, February 12, 2013 10:11 AM

> The following could also be useful:
>
>     with Predicate_Failure => Note_Error (123, "hello, world);
>
> where Note_Error is a No_Return procedure.

<Asbestos suit on>

At some time while thinking about this, I envisionned allowing:

   function F (...) return exception;

It's a No_Return function, whose return type would be any type (since it does
not return anyway). Could be useful if we go this route.

</Asbestos suit on>

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

From: Robert Dewar
Sent: Tuesday, February 12, 2013  10:16 AM

>>> Approach B, new aspect
>
> Regarding approach B, we could allow something like:
>
>      with Predicate_Failure => raise Some_Error with "some message";
>
> to allow one to give a message.  We would need to figure out what the
> expected type of that thing is

I would think the expected type is Boolean.
And the value would be ignored???

I like this idea, since I realize an interesting use of raise expressions in
assertions is something like

    (M > 0 else raise Assertion_Error with msg)

where we use the raise just to add a msg, not to change the exception.

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

From: Tucker Taft
Sent: Tuesday, February 12, 2013  11:20 AM

> ... I think the idea that a raise expression that is sitting right
> there (statically) in the text of a predicate is different from a
> raise exp in some callee,, is reasonable.  I admit this idea takes
> some getting used to.  ;-)

I might point out that predicates are tricky already in that even when your
assertion policy is "ignore" they are still relevant to membership and 'Valid.
Clearly the use of a predicate in these two contexts is quite special, and is
not about raising an exception.

In fact one would expect that if your assertion policy is "ignore" you can be
confident that the annotations won't be the source of raising of
Assertion_Error.  That seems like a pretty important guarantee to be able to
make, especially to those operating in a mission critical environment.  Hence
for that reason alone, we want the exception associated with a predicate to be
eliminated in some way when used in a membership or 'Valid test.  The approach
we have proposed seems like the most reasonable one, namely the predicate
effectively returns False rather than raising an exception in these contexts.

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

From: Robert Dewar
Sent: Tuesday, February 12, 2013  12:04 PM

>> ... I think the idea that a raise expression that is sitting right
>> there (statically) in the text of a predicate is different from a
>> raise exp in some callee,, is reasonable.  I admit this idea takes
>> some getting used to.  ;-)
>
> I might point out that predicates are tricky already in that even when
> your assertion policy is "ignore" they are still relevant to
> membership and 'Valid.  Clearly the use of a predicate in these two
> contexts is quite special, and is not about raising an exception.

This guarantee does not hold even with AI-0054, since a function called by a
predicate expression could explicitly raise Assertion_Error, a perfectly
reasonable thing if what you want is to add a message to the assertion under
some circumstances but not others.

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

From: Tucker Taft
Sent: Tuesday, February 12, 2013  1:09 PM

But that would be a bad idea, given the use in membership and 'Valid if your
goal was to communicate that the predicate failed.  If your goal was to
communicate the predicate expression itself had a bug, then that would be the
right thing to do, and you would want membership and 'Valid to propagate an
exception as well.

And of course if you have a policy that turns off assertions, you still want
Predicates to work properly in membership and 'Valid, and if you were using an
explicit, but buried, raise those would not work properly.

An explicit raise in the predicate expression itself really seems like the right
thing given their special significance to Membership and 'Valid.

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

From: Randy Brukardt
Sent: Tuesday, February 12, 2013  2:37 PM

> > The following could also be useful:
> >
> >     with Predicate_Failure => Note_Error (123, "hello, world);
> >
> > where Note_Error is a No_Return procedure.
>
> <Asbestos suit on>
>
> At some time while thinking about this, I envisionned allowing:
>
>    function F (...) return exception;
>
> It's a No_Return function, whose return type would be any type (since
> it does not return anyway). Could be useful if we go this route.
>
> </Asbestos suit on>

Note sure why an asbestos suit is needed. We already have an AI for No_Return
functions (AI12-0063-1), so there is nothing controversial about that. IMHO, we
don't need a function that matches any type (98% of these will return Boolean),
but certainly we can discuss that in the context of that AI.

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

From: Randy Brukardt
Sent: Tuesday, February 12, 2013  2:49 PM

...
> >>> Approach B, new aspect
> >
> > Regarding approach B, we could allow something like:
> >
> >      with Predicate_Failure => raise Some_Error with "some message";
> >
> > to allow one to give a message.  We would need to figure out what
> > the expected type of that thing is

I think this just reintroduces the problem. The primary thing that people are
uncomfortable with is a "raise" that doesn't raise. We still have that here (the
predicate still fails when used in a membership, only the result is False, not a
raise).

The other thing people are unconfortable with is the notion that moving some
text into a function changes the semantics. That is fixed here, because you
can't move this into a function.

Luckily, we don't need to allow multiple exceptions from a single predicate,
because using multiple subtypes has no negative effect. So you can split a
predicate such that one exception is used for each subtype, and still get the
effect of multiple exceptions being raised.

So this helps one problem but leaves the weird semantics in the other case.
Definitely an improvement.

I had suggested being more explicit about "failed", but nobody ever responded to
that idea, even to say they hated it. The basic idea was to using something in
place of "raise" that clearly indicates failure (which means "raise" in most
cases, and False in memberships).

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

From: Robert Dewar
Sent: Tuesday, February 12, 2013  3:01 PM

> I think this just reintroduces the problem. The primary thing that
> people are uncomfortable with is a "raise" that doesn't raise. We
> still have that here (the predicate still fails when used in a
> membership, only the result is False, not a raise).

Oh, I don't think so, the point of the Predicate_Failure is that it is only
called when the predicate fails and would otherwise raise Assertion_Error. I
think that's a VERY different matter from AI-0054. For instance there is no
issue of hiding an exception (we are getting an exception anyway), there is no
issue of abstraction (you can put the raise in a function if you like).

This is really just identical to the

          with Predicate_Exception => Some_Error

it just allows you to add a message

> I had suggested being more explicit about "failed", but nobody ever
> responded to that idea, even to say they hated it. The basic idea was
> to using something in place of "raise" that clearly indicates failure
> (which means "raise" in most cases, and False in memberships).

That's similar to what JPR suggested, it seems a bit of an over-complication to
me.

Anyway, clearly this should be discussed at the next meetinG!

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

From: Randy Brukardt
Sent: Tuesday, February 12, 2013  3:11 PM

...
>That's similar to what JPR suggested, it seems a bit of an over-complication
>to me.

J-P's suggestion involved multiple exceptions, which makes no sense as there is
only one (or no) exception involved. I was suggesting indicating failure and an
optional exception to raise if an exception would be raised. There are
similarities, I grant, but this problem is all about appearances, not semantics.
The semantics of what I'm proposing would be exactly as in AI12-0054-1, with the
exception that a failure would not be allowed outside of an assertion expression
- so there would be no extra run-time cost.

> Anyway, clearly this should be discussed at the next meetinG!

Agreed. But we need a worked out alternative proposal to discuss - I don't think
a "blank sheet of paper" would accomplish much. Perhaps Bob would write
something up based on the Predicate_Failure aspect??

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

From: Robert Dewar
Sent: Tuesday, February 12, 2013  3:27 PM

I don 't think anyone is arguing for the Predicate_Failure aspectt at this stage
(certainly not me, it is absent from my latest proposal).

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

From: Randy Brukardt
Sent: Tuesday, February 12, 2013  3:48 PM

...
> > Agreed. But we need a worked out alternative proposal to discuss - I
> > don't think a "blank sheet of paper" would accomplish much. Perhaps
> > Bob would write something up based on the Predicate_Failure aspect??
>
> I don 't think anyone is arguing for the Predicate_Failure aspectt at
> this stage (certainly not me, it is absent from my latest proposal).

Huh? That's the latest idea I saw:

> Regarding approach B, we could allow something like:
>
>       with Predicate_Failure => raise Some_Error with "some message";

To which you replied:

"I like this idea, ..."

And that was what I was responding to. Note the name of the aspect in this
quote!

I don't think that a solution that can't support a custom message will fly; I
has suggested using two aspects for this purpose "Predicate_Exception" and
"Predicate_Message", but Bob's idea is simpler and thus preferred.

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

From: Robert Dewar
Sent: Tuesday, February 12, 2013  3:54 PM

>> I don 't think anyone is arguing for the Predicate_Failure aspectt at
>> this stage (certainly not me, it is absent from my latest proposal).
>
> Huh? That's the latest idea I saw:

OK, I thought you were referring to the idea of having a eparate Predicate
exception

>> Regarding approach B, we could allow something like:
>>
>>        with Predicate_Failure => raise Some_Error with "some
>> message";
>
> To which you replied:
>
> "I like this idea, ..."
>
> And that was what I was responding to. Note the name of the aspect in
> this quote!

OK, fine, no problem!

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

Questions? Ask the ACAA Technical Agent