!standard 3.09.03 (04) 04-06-29 AI95-00334/02 !standard 3.09.03 (05) !class binding interpretation 03-07-30 !status Amendment 200Y 04-09-23 !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 op 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) @drepl For a derived type, if the parent or ancestor type has an abstract primitive subprogram, or a primitive function with a controlling result, then: @dby 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) @drepl @xbullet @dby @xbullet !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. ****************************************************************