Version 1.1 of acs/ac-00232.txt

Unformatted version of acs/ac-00232.txt version 1.1
Other versions for file acs/ac-00232.txt

!standard 11.4.2(27/3)          12-05-02 AC95-00232/00
!class Amendment 12-05-02
!status received no action 12-05-02
!status received 12-03-27
!subject Side effects in assertions
!summary
!appendix

From: Robert Dewar
Sent: Tuesday, March 27, 2012  2:25 AM

I must say that the whole business of trying to prevent side effects in
assertions/preconditions/postconditions etc seems very misguided to me.
Fortunately it is very unlikely that any real implementations will pay attention
to this!

It is just likely to preclude useful stuff and cause theoretical uncertainty.

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

From: Randy Brukardt
Sent: Tuesday, March 27, 2012  6:11 PM

I should know better than to wade into this one (especially as I'm not
completely convinced of the approach and because its essentially irrelevant at
this point, we're not going to change anything in the Ada 2012 Standard as the
result of a discussion), but:

(1) This is really an attempt to define what users should not expect to work.
"Significant" side-effects should not be expected to work in assertions, since
they violate all of the principles of assertions (most importantly: adding or
ignoring an assertion should never make a program less safe).

For example, in:

     procedure Foo
        with Pre => A is
        pragma Assert (A);
     begin

If evaluating A changes something such that the pragma Assert fails, there is a
significant problem with "A". Such an expression is necessarily tricky (if
useful) or downright dangerous, and it harms the understandability of the code
(the predicate doesn't hold inside the subprogram). It potentially harms
analysis and optimization of the code as well.

We really do not want people writing such assertion expressions.

Similarly, in:

    subtype S
       with Dynamic_Predicate => F(S);
    procedure Bar (P : S)
       with Pre => G(S);

Here, it is important that the order of evaluation of the expressions does not
matter. In particular, if F(S) and then G(S) gets a different result from G(S)
and then F(S), then whether or not Bar can be called is indeterminate. That is
bad, and again we want to strongly discourage such assertion expressions.

Any time either of these situations are violated, the effect of the code is hard
to predict. In particular, many bad things can happen: X'Old does not reflect
the value of X at the start of the subprogram; predicates and preconditions
don't hold inside of a subprogram; postconditions and invariants don't hold
after a subprogram call, and so on.

All of these things are bad, and should be discouraged (or even banned).

If you think any of these cases can be "useful stuff", then you can stop reading
now, because there is nothing really to discuss. I can believe that someone
could somehow use these effects for some interesting effect, but since any such
code is unlikely to be portable (because of the permissions to evaluate
assertions in any order, to short-circuit assertions, and to ignore side-effects
if the result can be determined), such techniques are precisely what we want to
discourage. Ada is not about tricky code!

(Also remember that such tricks can be performed without involving any
assertions. It's only the content of assertions that is restricted.)

(2) How can we discourage these things? We looked at a number of options, but
any choice other than compile-time rejection makes the program less safe in the
margins (if you do something bad, the last thing you want is an exception being
raised randomly).

We also agree that there are "benign" side-effects, like memo functions, call
logging, and the like. We do not want to disallow these.

We'd liked to have crafted a real Legality Rule, but that would require real
global in/out contracts (which we don't have) and probably would have also
prevented some "benign" and useful side-effects. It's just beyond the
state-of-the-art.

Thus, we settled on an implementation-permission. This takes the form of a
narrow rule that allows a compiler to reject only "significant" side-effects.
The intent is that *only* cases like the two described above can be rejected;
other side-effects (even if they affect later calls) *cannot* be rejected.

(3) The effect of the rule should be a stern warning to anyone caring about
portable code to avoid tricky assertions. One hopes that John manages to explain
this more clearly than I can in the Rationale. (He has the handicap of having
missed the meetings where this was discussed at length.)

(4) OTOH, we expected that it would be impossible for a compiler to take
advantage of the permission in all but the most obvious of circumstances. That
is, we're not really expecting compilers to reject anything here -- just having
the threat should help programmers stay on the safe side of assertion
expressions.

If I thought that it was possible to enforce this rule in Janus/Ada, I would
surely do so. But I don't think it will be practical and most likely will fall
back to warnings (and better categorization) instead.

(5) I would not be surprised if the rule will need tweaking. If there are any
truly useful cases that it bans, we'll certainly consider modifying it to allow
them. That should be easier as an Implementation Permission than it would have
been as a Legality Rule (as compatibility is unlikely to be impacted). The point
is that this is a narrow rule, only banning clearly bad behavior, not anything
that could be useful.

I personally would have preferred to see the rule go somewhat further, such that
evaluating an assertion should not change the effects of following code. (That
way, ignoring an assertion never can cause a problem.) But that is too hard to
define while still allowing "benign" side-effects.

(6) No rule at all would mean that (some) users would expect examples like the
ones above to have well-defined meanings. That would be counter to the intent of
assertions, and also would leave us having to explain the detailed semantics of
'Old, ordering for predicates and precondition checks, and other things we
really don't want to deal with. That way leads to madness.


Anyway, 'nuff said. I'm sure you'll disagree with something irrelevant that I
mentioned in passing. :-)

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

From: Tucker Taft
Sent: Tuesday, March 27, 2012  7:21 PM

Well put, Randy.

Just for the record, this is what is left after all our discussions about side
effects in assertion expressions:

       If the result of a function call in an assertion is not needed to
       determine the value of the assertion expression, an implementation
       is permitted to omit the function call. This permission applies
       even if the function has side effects.

       An implementation need not allow the specification of an assertion
       expression if the evaluation of the expression has a side effect
       such that an immediate reevaluation of the expression could produce
       a different value. Similarly, an implementation need not allow the
       specification of an assertion expression that is checked as part
       of a call on or return from a callable entity C, if the evaluation
       of the expression has a side effect such that the evaluation of
       some other assertion expression associated with the same call of
       (or return from) C could produce a different value than it would
       if the first expression had not been evaluated.

This seems eminently reasonable, though as you both seem to agree, most
implementations won't take much if any advantage of the permission.  But I could
see it being a reasonable basis for warnings generated by a decent static
analysis tool (like CodePeer ;-).

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

From: Robert Dewar
Sent: Tuesday, March 27, 2012  11:13 PM

Of course the warnings would be appropriate even if the above language were not
present!

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


Questions? Ask the ACAA Technical Agent