Version 1.6 of ais/ai-00334.txt

Unformatted version of ais/ai-00334.txt version 1.6
Other versions for file ais/ai-00334.txt

!standard 3.09.03 (04)          04-11-08 AI95-00334/03
!standard 3.09.03 (05)
!class binding interpretation 03-07-30
!status Amendment 200Y 04-09-23
!status WG9 approved 04-11-18
!status ARG Approved 8-0-0 04-09-18
!status work item 03-07-30
!status received 03-04-23
!qualifier Omission
!priority Low
!difficulty Easy
!subject Is overriding of abstract equality required?
!summary
Predefined equality shall be overridden if the parent primitive equality is abstract for a non-limited type extension.
!question
Consider the following:
type Root_Key is abstract tagged null record; function "="(Left, Right : Root_Key) return Boolean is abstract; -- "=" is used to determine equivalence of keys
...
type My_Key is new Root_Key with record X : Integer := 0; end record;
Should it be required to override "=" explicitly in this case? (Yes.)
4.5.2(14) (and 3.4(17)) says that the predefined equality operator for a type extension is not inherited, but rather "incorporates" the primitive equality operator of the parent type. However, the rules for abstract subprograms (3.9.3(4-6)) don't mention this case -- they only talk about inherited subprograms.
!recommendation
(See Wording.)
!wording
Change 3.9.3(4-5): If a derived type has an implicitly declared primitive subprogram that is inherited or is the predefined equality operator, and the corresponding primitive subprogram of the parent or ancestor type is abstract or is a function with a controlling result, then:
* If the derived type is abstract or untagged, the implicitly declared subprogram is abstract.
!discussion
Clearly, the predefined equality for My_Key cannot call the predefined equality for Root_Key: it has no body.
There are practically three choices for handling this:
1) The predefined equality for My_Key shall be overridden; thus the example is illegal. This is like the inheritance of other abstract subprograms.
2) The predefined equality for Root_Key is ignored. This is like the handling of Initialize for extension aggregates.
3) Declaring "=" abstract is illegal for a tagged type.
(3) would cause contract model problems in generic units.
(2) can be accomplished if desired by returning True for equality of Root_Key. Thus, there is little value to making this the default semantics.
Thus we choose (1). This is most like the inheritance of other abstract subprograms, so is likely to be least surprising to users.
Notes on the wording change.
The existing 3.9.3(4-6) is:
For a derived type, if the parent or ancestor type has an abstract primitive subprogram, or a primitive function with a controlling result, then: * If the derived type is abstract or untagged, the inherited subprogram is
abstract.
* Otherwise, the subprogram shall be overridden with a nonabstract subprogram;
for a type declared in the visible part of a package, the overriding may be either in the visible or the private part. However, if the type is a generic formal type, the subprogram need not be overridden for the formal type itself; a nonabstract version will necessarily be provided by the actual type.
The interesting thing about this is that neither 3.9.3(4) nor 3.9.3(6) talk about inherited subprograms. 3.9.3(4) says "has an abstract primitive subprogram", without saying anything about the case of a routine which is not inherited. Thus, an abstract equality operator is included by this text, and a overriding is required. But, so are abstract primitive subprograms declared after the derived type which are also not inherited. And it fails to make predefined equality abstract for abstract types. So the existing text is no good.
The solution is to have 3.9.3(4) talk about "implicitly declared primitive subprograms", and then have 3.9.3(5) use this wording, rather than inherited. It's not necessary to change 3.9.3(6), as it refers to "the subprogram", meaning the subprogram mentioned in 3.9.3(4).
!corrigendum 03.09.03(04)
Replace the paragraph:
For a derived type, if the parent or ancestor type has an abstract primitive subprogram, or a primitive function with a controlling result, then:
by:
If a derived type has an implicitly declared primitive subprogram that is inherited or is the predefined equality operator, and the corresponding primitive subprogram of the parent or ancestor type is abstract or is a function with a controlling result, then:
!corrigendum 03.09.03(05)
Replace the paragraph:
by:
!ACATS test
An ACATS test should be constructed with the case from the example.
!appendix

From: Tucker Taft
Sent: Wednesday, April 23, 2003  2:01 PM

4.5.2(14) (and 3.4(17)) says that the predefined equality operator
for a type extension is *not* inherited, but rather "incorporates"
the primitive equality op of the parent type.  What if this
primitive equality is abstract?  There seem to be two choices:

  1) The predefined equality operator of the type extension must be overridden
     if the type extension is non-abstract.  It is abstract if the
     type extension is abstract.
 or:
  2) The primitive equality of the parent type is ignored, and
     equality is checked only for the components of the extension part.

There is some precedent for (2) in 7.6(11), where when
performing default initialization of the ancestor part of
an extension aggregate, we don't call the Initialize
procedure if it is abstract.

This came up in the following context:

   type Root_Key is abstract tagged null record;
   function "="(Left, Right : Root_Key) return Boolean is abstract;
     -- "=" is used to determine equivalence of keys

   ...

   type My_Key is new Root_Key with record
      X : Integer := 0;
   end record;
   -- Predefined "=" does the right thing for equivalence of My_Key;
   -- no need to define it explicitly.

Should it be required to override "=" explicitly in this case?

The rules for abstract subprograms (3.9.3(4-6)) don't mention
this case explicitly -- they talk about inherited subprograms
but "=" is not inherited by non-limited type extensions if it
matches the profile of predefined "=".

Personally, I tend to favor (mildly) there being a non-abstract predefined
"=" automatically.  Declaring "=" abstract on the root type can
be seen as a statement that there is nothing in the root type
relevant to equality checking in any derivatives.  Furthermore,
even if the derivatives are forced to define their own "=", it
is unclear how they would know how to factor in any components
from the root type anyway, presuming the root type is null
or private (which seems fairly typical).

Other opinions?

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

From: Randy Brukardt
Sent: Wednesday, April 23, 2003  2:26 PM

This does look like a hole.

There is a third option:

3) Declaring "=" abstract is illegal for a tagged type.

Since this doesn't seem to be useful functionality.

...

> Personally, I tend to favor (mildly) there being a non-abstract predefined
> "=" automatically.  Declaring "=" abstract on the root type can
> be seen as a statement that there is nothing in the root type
> relevant to equality checking in any derivatives.  Furthermore,
> even if the derivatives are forced to define their own "=", it
> is unclear how they would know how to factor in any components
> from the root type anyway, presuming the root type is null
> or private (which seems fairly typical).
>
> Other opinions?

I'm in favor of (3) first, then (1). I see no reason to use "abstract" to
mean something completely different for "=" than it does for all other
subprograms, which is what (2) does.

Besides:

  function "="(Left, Right : Root_Key) return Boolean;

  function "="(Left, Right : Root_Key) return Boolean is
  begin
    return True;
  end "=";

Has the effect of declaring that "=" doesn't depend on the root components,
without requiring any new language interpretations. That's how we handle
Finalization.Controlled, for instance. It would be nice if that could be
done in the spec, but if that matters, you can always put a pragma Inline on
it.

However, the choice really should be driven by what compilers currently do.
Would it be possible to write up a small compilable example program to test
on existing compilers??

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

From: Tucker Taft
Sent: Wednesday, April 23, 2003  3:01 PM

Here is a simple test case.
It dies at link time on our compiler!

----------
procedure abstract_eq is
   package pkg is
     type T is abstract tagged record
         F : Integer := 7;
     end record;
     function "="(Left, Right : T) return Boolean is abstract;

     type NT is new T with record
         G : Integer := 99;
     end record;
     X, Y : NT;
     Q : T'Class := X;
     R : T'Class := Y;

     Z0 : Boolean := (X = Y);
     Z : Boolean := (Q = R);
   end;
   use pkg;
begin
     Z := Boolean'Value("True") and then Z0;
end;

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

From: Tucker Taft
Sent: Wednesday, April 23, 2003  3:05 PM

I hadn't thought of your choice (3).
There are probably some generic contract model
issues lurking here, and/or privacy breaking issues.

I suppose choice (1) is probably most consistent with
how abstract works for everything else.  The fact
that "=" is not "inherited" but is rather "incorporated"
is pretty subtle, and probably shouldn't make as
big a difference as would be implied by (2).

It also has the advantage of eliminating any possibility
of confusion or unintended consequences.

So I guess I now (still mildly) favor choice (1).

As I mentioned in my other reply, our compiler
currently dies at link-time on this one, so we
really don't care which choice is made.  We have
to fix it in any case.

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

From: Gary Dismukes
Sent: Wednesday, April 23, 2003  3:08 PM

Tuck wrote:
> Personally, I tend to favor (mildly) there being a non-abstract predefined
> "=" automatically.  Declaring "=" abstract on the root type can
> be seen as a statement that there is nothing in the root type
> relevant to equality checking in any derivatives.  Furthermore,
> even if the derivatives are forced to define their own "=", it
> is unclear how they would know how to factor in any components
> from the root type anyway, presuming the root type is null
> or private (which seems fairly typical).

I lean that direction as well (alternative 2), but...

Randy wrote:
> However, the choice really should be driven by what compilers currently do.
> Would it be possible to write up a small compilable example program to test
> on existing compilers??

GNAT gives an error on the type extension, requiring either that the type
be declared abstract or that "=" be overridden.

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

From: Randy Brukardt
Sent: Wednesday, April 23, 2003  7:51 PM

Tuck said:
> Here is a simple test case.
> It dies at link time on our compiler!

Janus/Ada gets "JCode has undefined label", which is an internal error. It
appears to be calling the non-existent predefined "=".

As Gary reported, GNAT says that "="  must be overridden. 3.15a also reports
that the equality of T'Class is illegal, because it "must be dispatching"
(but that might be an error cascade).

I tried to run it on Rational Apex, but the stupid license manager has
decided to not let me run it. Sigh. I'd hoped for something reasonable
definitive...

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

From: Pascal Leroy
Sent: Thursday, April 24, 2003  3:55 AM

Randy and Tuck proposed:
>   1) The predefined equality operator of the type extension must be overridden
>      if the type extension is non-abstract.  It is abstract if the
>      type extension is abstract.
>
>   2) The primitive equality of the parent type is ignored, and
>      equality is checked only for the components of the extension part.
>
>   3) Declaring "=" abstract is illegal for a tagged type.

(Waving the Privacy flag.)  As Tuck pointed out, (3) doesn't work
because it breaks the contract model: a tagged type can be passed to a
generic formal private type, and in the generic you wouldn't know if
declaring an abstract "=" is legal or not.  As usual, a similar problem
exists with normal private types.

I am in favor of (1) because I find that (2) would be too subtle a
solution: most users wouldn't understand the distinction between
"inherited" and "incorporated".  Besides, although I realize that "=" is
already special in many ways, I don't think there is a compelling reason
here to make it even more special.

Randy reported:
> I tried to run it on Rational Apex, but the stupid license manager has
> decided to not let me run it. Sigh. I'd hoped for something reasonable
> definitive...

Apex does essentially the same thing as GNAT, i.e. implements
alternative (1):

10:30:48 >>> Line 9: type Nt is new T with record G : Integer := 99; end record
10:30:48 *** Implicitly declared subprogram Abstract_Eq.Pkg.Nt."=" of
derived type Nt must be overridden [RM_95 3.9.3(6)]

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

From: Tucker Taft
Sent: Thursday, April 24, 2003  9:47 AM

I've swung over to the (1) choice too, now.

It seems clear that the normal interpretation
for "abstract" in OOP is that you *must* override
in non-abstract extensions.  I see no compelling
case for breaking that for "=", even though we
have the subtle "inherited" vs. "incorporated" distinction.

So I will change AdaMagic to conform to GNAT and Rational.

I think we still need a binding AI on the topic.

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

From: Robert I. Eachus
Sent: Thursday, April 24, 2003  3:09 PM

I thought when I saw the first post on this subject that it was at least
a "two pipe problem." (See Sherlock Holmes for the reference.  Now that
I have seen the discussion, I am sure of it.

I actually don't care much how the issue is resolved, but there is one
case we have to be sure not to break:

package A is
   type Foo is abstract tagged limited private;
   -- Is that the right order?
   ...
end A;

package A.B is
   type Bar is new Foo with...;
   function "=" (L,R: Foo) return Boolean;
   ...
end A.B;

Note that at the point where the "=" function is defined, the full
declaration of Foo, plus any definitions in the private part of A will
be visible.  This may include an implicit or explicit definition of
equality for Foo.

Right now, I wouldn't even consider there to be any problem, if there
was an equality for the full definition of Foo.  So, I propose that
Tuck's options be changed to:

   1) The predefined equality operator of the type extension must be
overridden if the type extension is non-abstract, and there is no
definition of equality for the parent type.  It is abstract if the type
extension is abstract.

   2) The primitive equality of the parent type is ignored, and equality
is checked only for the components of the extension part, if there is no
  equality operator defined for the parent type.

Having done that, I am a little uncomfortable with 2.  It fits with the
way other subprogram definitions work for tagged types, but it is a
little more uncomfortable.  The implementation would be easy, tagged
types would always have an equality operator.  Sometimes it could not be
called by a non-dispatching call.  But even then there might be some
magic needed in declarations of equality for child types.  Not being
able to call a parent operation that does nothing anyway is not, as
such, a problem. Dispatching calls can never see an object of the parent
type so that is not a problem either.  Maybe it works.

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

From: Gary Dismukes
Sent: Thursday, April 24, 2003  3:43 PM

But this issue only relates to types with predefined equality,
and the limited tagged type Foo doesn't have a predefined equality,
nor will its extensions, since they must be limited as well.

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

From: Robert I. Eachus
Sent: Thursday, April 24, 2003  8:01 PM

My point was that right now the case I pointed out works fine.  A user
can define a (visible) equality operator for a child type.  And in the
body of the child package call the equality operator for the complete
parent type which is visible there.

My problem is not with Tuck's two solutions to the original problem.  It
is a concern that whichever one is adopted needs to be stated so that it
doesn't break what is currently not broken.  Either of Tuck's solutions
can be so modified, and I stated the consequences of doing so.

Solution number two as I reworded it probably satisfies the principle of
least surprise for ordinary users, but certainly leaves me queasy.  I
suspect that other ARG members feel the same way.  If we adopt my
version of option 2, true limited types have an equality operation that
cannot be named, and does nothing.  The original problem allows a user
to implicitly call it--and it still does nothing.  Private types whose
full declarations are not limited have an equality operation that does
something, can be called in the scope of the full declaration. Like
other operations of tagged types it may be called (by dispatching calls)
outside the scope of the (full) declaration of the type.

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


Questions? Ask the ACAA Technical Agent