Version 1.2 of acs/ac-00248.txt

Unformatted version of acs/ac-00248.txt version 1.2
Other versions for file acs/ac-00248.txt

!standard 3.4(27/2)          13-05-09 AC95-00248/00
!standard 3.2.4(31/3)
!class confirmation 13-05-09
!status received no action 13-05-09
!status received 13-02-25
!subject Predicate checking and inherited subprograms
!summary
!appendix

From: Steve Baird
Sent: Monday, February 25, 2013   7:09 PM

3.4(27/2) says:

   For the execution of a call on an inherited subprogram, a call on the
   corresponding primitive subprogram of the parent or progenitor type
   is performed; the normal conversion of each actual parameter to the
   subtype of the corresponding formal parameter (see 6.4.1) performs
   any necessary type conversion as well.

It doesn't say whether a new (wrapper) subprogram is introduced because it isn't
supposed to be possible for a user to tell the difference.

In particular, given the following context

   type T is ... ;
   type D is new T with ... ;
   subtype S is D with ...;
   S_Var : S;

   procedure Op (X : in out T) is ... ;

   procedure Inherited_Op_Wrapper (X : in out D) is
   begin
     Op (T (X));
   end;

one can think of a call to an inherited op as being more like either

    Op (T (S_Var));
or
    Inherited_Op_Wrapper (S_Var);

The two models (the non-wrapper implementation model for inherited subprograms
and the wrapper model) are semantically equivalent, or at least they are
supposed to be.

With the introduction of subtype predicate checking, however, the equivalence
breaks down (at least slightly) in some cases; this seems undesirable. Either
the equivalence should be restored or one one of the two implementation models
should be designated as the correct model or it should be explicitly stated that
the program behavior associated with either model is permitted.

Consider the case where type T is passed by reference.

3.2.4(31/3) says:
   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.

   [The phrase "subtype of the actual" is never defined explicitly, but
    6.4.1(17) refers to "the subtype of the variable given as the
    actual parameter" so presumably that's the subtype in question].

Upon returning from the call to Op in
   Op (T (S_Var));
the predicate associated with subtype S is checked.
Only one predicate check is performed.

In the case of
    Inherited_Op_Wrapper (S_Var);
the predicate of D is called upon returning from the call to Op inside of the
wrapper subprogram and then the predicate of S is called upon returning from the
wrapper.

With the wrapper, we get two predicate checks instead of one.
True, one of the two checks might be thought of as implying the other, but this
would require incorrectly making unjustified assumptions about the
implementation of the predicate.

One way to get the two models back in synch would be to give them both the
double-predicate-check definition that is currently implemented only via the
wrapper model. This could be accomplished by changing rules not directly related
to inherited subprograms:

   If a view conversion is passed an actual parameter, the mode is
   not IN, and the parameter is passed by reference, then upon return
   from the call the predicate checking associated with the subtype
   of the conversion is performed followed by that of the subtype of
   the operand of the type conversion (and repeating if that operand
   is itself a type conversion).

Another way to resolve the portability issue would be to impose a rule that only
one check is performed in the above scenario (i.e. the non-wrapper model is
right and the wrapper model is wrong).

A third solution would be to grant an implementation permission allowing either
behavior.

Opinions?

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

From: Randy Brukardt
Sent: Monday, February 25, 2013   7:33 PM

> 3.4(27/2) says:
>
>    For the execution of a call on an inherited subprogram, a call on the
>    corresponding primitive subprogram of the parent or progenitor type
>    is performed; the normal conversion of each actual parameter to the
>    subtype of the corresponding formal parameter (see 6.4.1) performs
>    any necessary type conversion as well.
>
> It doesn't say whether a new (wrapper) subprogram is introduced
> because it isn't supposed to be possible for a user to tell the
> difference.
...
> With the wrapper, we get two predicate checks instead of one.
> True, one of the two checks might be thought of as implying the other,
> but this would require incorrectly making unjustified assumptions
> about the implementation of the predicate.

I agree with your analysis, but I don't see the problem.

S is a subtype of D. Therefore, the predicate of S includes all of the
predicates of D. Thus, the single predicate check of S includes the check for D.

Ergo, the only way for the "extra" check of D to be visible is for the predicate
of D to depend on some global or have some side-effect. But such a predicate
expression violates 11.4.2(27/3) (unless of course the value can never be
different, as in a memo function - but then in that case you couldn't detect the
difference caused by the redundant evaluations).

As such, only non-portable code could possibly depend on the extra check of D.
Moreover, there could not be an ACATS test for such as check (the error is not
required to be detected, and having a C-test that includes an intentional error
is problematical. And we're not in the business of writing tests for
pathological cases anyway.

> One way to get the two models back in synch would be to give them both
> the double-predicate-check definition that is currently implemented
> only via the wrapper model. This could be accomplished by changing
> rules not directly related to inherited subprograms:
>
>    If a view conversion is passed an actual parameter, the mode is
>    not IN, and the parameter is passed by reference, then upon return
>    from the call the predicate checking associated with the subtype
>    of the conversion is performed followed by that of the subtype of
>    the operand of the type conversion (and repeating if that operand
>    is itself a type conversion).
>
> Another way to resolve the portability issue would be to impose a rule
> that only one check is performed in the above scenario (i.e. the
> non-wrapper model is right and the wrapper model is wrong).
>
> A third solution would be to grant an implementation permission
> allowing either behavior.
>
> Opinions?

This seems like a tempest in a thimble (it's not even up to teapot standards). A
change here looks complex and it surely violates the Duff rule: it's not going
to make the slightest difference in any implementation. You would have to have a
permission to skip the check on D even in the wrapper case in order for this to
have any effect at all (the main cost here being a redundant check), and that
seems going too far to me.

If there is a problem, it seems to be with 3.2.4(31/3), as you note, it doesn't
define "subtype of the actual" and it probably ought to have a clearer
definition in the case where the actual is a view conversion and the parameter
is by-reference. I suspect that it would be best to mean the subtype of the
conversion, and have an additional rule for checking the subtype of the operand
of view conversions in this case (this would give your preferred semantics). But
as noted above, it doesn't really matter, so I don't see much point to a "fix".

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

From: Tucker Taft
Sent: Monday, February 25, 2013   7:59 PM

I'm not sure there is a real issue here.

The predicate of "S" includes the predicate of "D"
(or equivalently, the predicate of "D" applies to "S"
as well).

So whenever you check the predicate of S you are checking both.  We should
certainly allow implementations to avoid redundant checks of the predicate of D,
which is presumably already allowed since evaluating a predicate a second time
is supposed to produce the same value as evaluating it only once.

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

From: Randy Brukardt
Sent: Monday, February 25, 2013   9:18 PM

> I'm not sure there is a real issue here.
>
> The predicate of "S" includes the predicate of "D"
> (or equivalently, the predicate of "D" applies to "S"
> as well).

Right. The shorter version of what I said.

> So whenever you check the predicate of S you are checking both.  We
> should certainly allow implementations to avoid redundant checks of
> the predicate of D, which is presumably already allowed since
> evaluating a predicate a second time is supposed to produce the same
> value as evaluating it only once.

Good point. The Implementation Permission 11.4.2(26/3) already allows omitting
redundant evaluations of parts of assertion expressions (even if they have
side-effects).

Combining that with 11.4.2(27/3) means that no portable program can depend on
how many times a part of an assertion expression is evaluated; thus any program
that does depend on that is a pathology. And we generally don't mess with the
wording of the standard for pathologies.

Note that this isn't quite the same as being able to tell. If an assertion
causes some logging, one can tell how many times the assertion is evaluated --
but that number is not guaranteed and could change with compiler versions or
even optimization settings.

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

From: Steve Baird
Sent: Tuesday, February 26, 2013  12:45 PM

> This seems like a tempest in a thimble (it's not even up to teapot
> standards). A change here looks complex and it surely violates the Duff
> rule: it's not going to make the slightest difference in any implementation.

If we assume that folks only write well behaved predicates (or that we don't
care what happens if they don't) then I agree.

To the extent that a poorly-behaved predicate is a corner case, I concede that
we are discussing a corner case.

> I'm not sure there is a real issue here.
>
> The predicate of "S" includes the predicate of "D"
> (or equivalently, the predicate of "D" applies to "S"
> as well).
>
> So whenever you check the predicate of S you are checking both.  We
> should certainly allow implementations to avoid redundant checks of
> the predicate of D, which is presumably already allowed since
> evaluating a predicate a second time is supposed to produce the same
> value as evaluating it only once.

Your argument rests on the premise that "evaluating a predicate a second time is
supposed to produce the same value as evaluating it only once", as though this
is an RM rule of some kind.

Let's look more closely at the rules about poorly behaved subtype predicates.

Randy cited 11.4.2(27/3) as being relevant; it's not.

This Implementation Permission says that implementations "need not allow"
certain poorly behaved predicates. In any discussion of the runtime behavior of
a program (which therefore must have been successfully compiled and linked), any
such permission is clearly irrelevant; if the permitted option had been
exercised, we wouldn't have gotten to the point of executing the program.

Randy then cited 11.4.2(26/3):
   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 pertains primarily to failing assertions: if we are asserting the
conjunction of several conditions and one of them evaluates to False, we don't
have to evaluate the rest.

To claim that it applies in the case of an assertion which passes is a circular
argument. We assume without justification that evaluating the predicate twice
with the same argument yields the same answer. Then, based on that assumption,
we can apply 11.4.2(26/3) to retroactively provide the missing justification.

By the same logic, I could make the preposterous assumption that evaluating any
predicate twice always yields False the second time and then apply this
permission to retroactively justify this assumption and therefore justify
replacing any such repeated predicate evaluations with False.

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

From: Randy Brukardt
Sent: Tuesday, February 26, 2013   5:02 PM

...
> Randy cited 11.4.2(27/3) as being relevant; it's not.
>
> This Implementation Permission says that implementations "need not
> allow" certain poorly behaved predicates. In any discussion of the
> runtime behavior of a program (which therefore must have been
> successfully compiled and linked), any such permission is clearly
> irrelevant; if the permitted option had been exercised, we wouldn't
> have gotten to the point of executing the program.

It's relevant because writing such a program is necessarily not portable, even
to different versions of the same compiler (as it might start taking advantage
of the permission). Our intent (as best as I understood it) was that any program
that violated this rule is pathological and bad -- we couldn't find the right
words to make that true without throwing out important cases, so we don't have a
formal rule to that effect.

> Randy then cited 11.4.2(26/3):
>    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 pertains primarily to failing assertions: if we are asserting the
> conjunction of several conditions and one of them evaluates to False,
> we don't have to evaluate the rest.
>
> To claim that it applies in the case of an assertion which passes is a
> circular argument. We assume without justification that evaluating the
> predicate twice with the same argument yields the same answer. Then,
> based on that assumption, we can apply 11.4.2(26/3) to retroactively
> provide the missing justification.

You might be right that "we assume without justification", but that was the
entire point of 11.4.2(27/3) - to insure that no evaluation of an assertion
expression ever gets a different answer when reevaluated. Perhaps we need some
different wording to capture this rule clearly, but it should NEVER be the case
that the compiler has to evaluate the same assertion subexpression twice as part
of evaluating a single set of assertion checks.

> By the same logic, I could make the preposterous assumption that
> evaluating any predicate twice always yields False the second time and
> then apply this permission to retroactively justify this assumption
> and therefore justify replacing any such repeated predicate
> evaluations with False.

I don't see this logic at all. The initial "assumption" is supported by
11.4.2(27/3) (and often directly with knowledge about the purity of the
functions involved); I can't imagine any rules of Ada that would let your assume
a second evaluation would return false (short of body inspection).

To make this issue even worth the time I've already spent on it, you need to
come up with an example that does not violate 11.4.2(27/3) where the difference
between evaluating the predicate once or twice actually makes some sort of
relevant difference. Otherwise, it seems like a completely pointless argument
(how many assertion checks fit on the head of a pin?).

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

From: Tucker Taft
Sent: Tuesday, February 26, 2013   5:10 PM

I agree with both of you.  The wording as given is inadequate, but the intent is
as Randy stated: you may assume that if you evaluate an assertion expression
once and it is True, you don't need to evaluate it again if all you are doing in
the mean time is evaluating assertion expressions.

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

From: Steve Baird
Sent: Tuesday, February 26, 2013   5:21 PM

I'm happy to leave it at that.

Let's either take no further action or, if anyone thinks it is worth the bother,
add a brief AARM note based on Tuck's words above.

[Editor's note: The AARM note is mentioned in AI12-0005-1.]

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

From: Bob Duff
Sent: Tuesday, February 26, 2013   6:20 PM

> > I agree with both of you.  The wording as given is inadequate, but
> > the intent is as Randy stated: you may assume that if you evaluate
> > an assertion expression once and it is True, you don't need to
> > evaluate it again if all you are doing in the mean time is
> > evaluating assertion expressions.
>
> I'm happy to leave it at that.

Wow.  I was about to reply to Tuck, and I wasn't sure whether to say, "I'd be
happy to leave it at that." or "So let's leave it at that." Then I thought I'd
better read ahead first.

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

Questions? Ask the ACAA Technical Agent