Version 1.1 of ai05s/ai05-0198-1.txt
!standard 3.9.3(4/3) 10-02-09 AI05-0198-1/01
!class binding interpretation 10-02-09
!status work item 10-02-09
!status received 09-07-01
!subject Inheriting abstract operators for untagged types
!summary
If the corresponding operator of the parent type is abstract, a predefined
operator for an untagged derived type is abstract.
!question
Consider:
package Pack is
type Unit is new Float;
function "*" (L, R : Unit) return Unit is abstract;
type Unit_Squared is new Unit;
--
--
--
end Pack;
When the derived type Unit_Squared is declared, the implicit declaration of
two homographs occurs. The first one is inherited from the parent type, and
like the parent subprogram is abstract (3.9.3(5)). The second one is defined
because Unit_Squared is a floating-point type and 4.5.5(11) says that this
operation is defined for every floating-point type.
These two implicitly declared functions are homographs, and they're implicitly
declared at the same place.
Now, 8.3(11) says "the implicit declaration of an inherited operator overrides
that of a predefined operator" [when they're homographs]. So the first implicit
function overrides the second.
8.3(12/2) says that when two homographs are implicitly declared at the same
place, then if at least one is a subprogram that is neither null nor abstract,
and does not require overriding, then they override all those that are null or
abstract. So the second implicit function overrides the first.
Something is wrong here.
!recommendation
(See Summary.)
!wording
Modify 3.9.3(4/3) (as previously modified by AI05-0097-1):
If a type has an implicitly declared primitive subprogram that is inherited or
is {a}[the] predefined [equality] operator, and the corresponding primitive
subprogram of the parent or ancestor type is abstract or is a function with a
controlling access result, or if a type other than a non-abstract null extension
inherits a primitive function with a controlling result, then:
!discussion
There are four possibilities as to what happens here:
(1) The predefined operation overrides the abstract one;
(2) The abstract operation overrides the predefined one;
(3) Both override each other;
(4) Neither override the other.
(1) would mean that the predefined operation would "reemerge" for a type where
the operation was "hidden". (4) would have the same meaning, as the abstract
operation is ignored for the purposes of resolution. That would most likely
be exactly what the programmer doesn't want, so we reject (1) and (4).
Both (2) and (3) have the behavior we want, which is for the operator to be
not considered for resolution purposes. (2) has that effect because the
declared operator is abstract, 6.4(8) says it is not considered for resolution.
(3) has that effect as both operators are hidden from all visibility because
they are overridden.
Since (3) is the current state, we wouldn't have to make a language change.
But the semantics of two operators overriding each other is just too weird;
it looks like a mistake. And an easy solution is available.
This problem does not occur for predefined "=", because 3.9.3(4-5) makes it
abstract. Thus both operators are abstract and there is not a problem. (Well,
there is a still a tiny glitch in that 8.3(12.3/2) says that one of the
homographs is chosen arbitrarily; 8.3(11) choses a particular one. Those aren't
necessarily inconsistent, however, so this isn't worth fixing.) "=" was
made abstract to avoid problems with tagged "=", but it appears to have fixed
another problem as well.
And it only makes sense that an operator is abstract if the corresponding
operator of the parent is abstract. So we just change 3.9.3(4) to apply to
all predefined operators.
This change is compatible (presuming that the implementation handles this
case as (2) or (3) as intended, and not as (1) or (4)), since the only
abstract operators that require overriding are those for tagged types. And
the only predefined operator for a tagged type is "=", which was already
covered by this wording.
--!corrigendum 3.9.3(4/2)
!ACATS Test
An ACATS B-Test should be created that is similar to the example in the
question.
!appendix
!topic Possible ambiguity with overriding rules
!reference 8.3(11,12.2)
!from Adam Beneschan 09-07-01
!discussion
Yet another possible candidate for "least important AI ever":
package Pack is
type Unit is new Float;
function "*" (L, R : Unit) return Unit is abstract;
type Unit_Squared is new Unit;
-- function "*" (L, R : Unit_Squared) return Unit_Squared is abstract;
-- function "*" (L, R : Unit_Squared) return Unit_Squared is
-- <predefined>;
end Pack;
From what I can tell, when the derived type Unit_Squared is declared, this
causes the implicit declaration of two homographs. The first one is inherited
from the parent type, and like the parent subprogram is abstract (3.9.3(5)).
The second one is defined because Unit_Squared is a floating-point type and
4.5.5(11) says that this operation is defined for every floating-point type.
These two implicitly declared functions are homographs, and they're implicitly
declared at the same place.
Now, 8.3(11) says "the implicit declaration of an inherited operator overrides
that of a predefined operator" [when they're homographs]. So the first implicit
function overrides the second.
8.3(12/2) says that when two homographs are implicitly declared at the same
place, then if at least one is a subprogram that is neither null nor abstract,
and does not require overriding, then they override all those that are null or
abstract. So the second implicit function overrides the first.
Is this really what happens---that both implicit functions override each other?
Or does one of them win, and if so, which one? If it's supposed to be the
latter, do the rules in 8.3 need to be tweaked to make one of the rules not
apply?
****************************************************************
From: Edmond Schonberg
Sent: Wednesday, July 1, 2009 4:20 PM
This is addressed by AI95-0310 : the abstract non-dispatching subprogram does
not participate in overloading resolution. This is stated in 6.4 (8) : the name
or prefix shall not resolve to denote an abstract subprogram unless it is also a
dispatching subprogram.
Unfortunately this reference to Abstract Subprogram does not appear in the
index.
****************************************************************
From: Adam Beneschan
Sent: Wednesday, July 1, 2009 4:37 PM
> This is addressed by AI95-0310 : the abstract non-dispatching
> subprogram does not participate in overloading resolution.
No, that doesn't address the problem, which has to do with visibility, not
overload resolution.
In the case I'm describing, there are two implicitly declared homographs. They
cannot both be visible since they are homographs. That leaves three
possibilities:
(1) The predefined operation overrides the abstract one;
(2) The abstract operation overrides the predefined one;
(3) Both override each other, which might mean that neither one is
visible, or it might be just plain nonsensical.
I'm not sure the rules are clear on which one is correct.
AI95-310, by contrast, deals with a case where subprograms are *not*
homographs:
function "*" (L, R : Unit) return Unit is abstract;
function "*" (L, R : Unit) return Unit_Squared;
Since they are not homographs, both are visible. AI95-310 is just there to make
sure this fact doesn't cause any construct to be considered ambiguous. But it's
a totally different problem. (Yeah, I extracted the example from AI95-310, so I
can understand why it might have confused you. I'll try not to do that next
time.)
****************************************************************
From: Edmond Schonberg
Sent: Wednesday, July 1, 2009 5:09 PM
You are correct, AI95-0310 is not directly relevant, but at least it suggests
that the desired reading is in fact (2) in your list below, because the purpose
of defining the abstract operation is to "undefine" the primitive one. I would
thing that this "undefinition" is preserved in the derivation. Certainly the
call USQ * USQ should be illegal, so we definitely want to exclude (1). In
any case, the rules could use some clarification.
****************************************************************
From: Randy Brukardt
Sent: Wednesday, July 1, 2009 5:11 PM
> No, that doesn't address the problem, which has to do with visibility,
> not overload resolution.
Right. For the record, I agree with Adam that there is a problem.
> In the case I'm describing, there are two implicitly declared
> homographs. They cannot both be visible since they are homographs.
Strictly speaking, that's not true; there are ways for homographs to be both
visible (from an instance, for example). But in any such case, any call is
ambiguous. Note that the requirement for no homographs only applies to
"non-overriddable" declarations. Not particularly relevant to the point.
> That leaves three possibilities:
>
> (1) The predefined operation overrides the abstract one;
> (2) The abstract operation overrides the predefined one;
> (3) Both override each other, which might mean that neither one is
> visible, or it might be just plain nonsensical.
Well, there are four, really:
(4) Neither override the other and all calls are ambiguous.
> I'm not sure the rules are clear on which one is correct.
Not clear seems to be an understatement. The rules seem to say that both (1) and
(2) are true, which is impossible.
Now, I know there is a principle that inherited operations always override
predefined operations, so I'm pretty sure that the intent is (2). But the
wording doesn't say that.
It is interesting that this problem doesn't happen with "=". (You should have
mentioned that, because it seems highly significant; surely if it happened with
"=", it would be much more likely to happen, and this would actually be
important.)
The reason for that is an interaction with a rule adopted solely for tagged
types, the special case for predefined equality in 3.9.3(4-6). Recall that
"abstract" isn't actually inherited, but rather it is recalculated each time a
subprogram is inherited.
package Pack1 is
type Unit is private;
function "=" (L, R : Unit) return Boolean is abstract;
private
...
end Pack1;
with Pack1;
package Pack2 is
type New_Unit is new Pack1.Unit;
-- function "=" (L, R : New_Unit) return Boolean; -- Predefined (11)
-- function "=" (L, R : New_Unit) return Boolean; -- Inherited (12)
end Pack;
3.9.3(4-6) says that (12) is abstract (since New_Unit is untagged, 3.9.3(5)
applies). It also says that (11) is abstract, since this is a predefined
equality operator, and thus 3.9.3(5) applies to it as well. That means the
8.3(12.3/2) applies (all are abstract and all are fully conformant), we don't
care which one is visible, but one is.
(This rule was adopted for tagged types because predefined equality is not
inherited for those; it is reconstructed, but we surely want the child type's
equality to be abstract if the parent type's is abstract.)
The obvious fix here is to make this rule apply to all predefined operators (not
just equality). If a predefined operator is overridden as abstract, it seems
weird for a derived type to allow the predefined operator to reemerge and not
continue to be abstract. Probably that wasn't supposed to happen because it was
supposed to always be overridden.
The other fix would be to give a preference to the bullets of 8.3(9-13), so that
once 8.3(11) is applied, we don't look further. But that might cause problems
when there are three homographs involved (easy to construct with multiple
derivations). I think it is easier to say that the predefined operators are
abstract - but perhaps there is some implication of that that I am forgetting.
****************************************************************
From: Jean-Pierre Rosen
Sent: Thursday, July 2, 2009 1:17 AM
> Well, there are four, really:
> (4) Neither override the other and all calls are ambiguous.
Hmmm... Since one of them is abstract, AI95-0310 would apply (this time), so the
call would not be ambiguous.
****************************************************************
From: Edmond Schonberg
Sent: Thursday, July 2, 2009 7:58 AM
That can't possibly be the outcome, it would be a re-emergence of an operation
that was "undefined" in the parent. So the intent (which we are trying to intuit
here) is that the user-defined (abstract) operation overrides the predefined
one, and the call is to the abstract operation, which must be rejected by the
compiler to indicate that an explicit overriding is needed.
****************************************************************
Questions? Ask the ACAA Technical Agent