!standard 03.04 (02) 03-09-16 AI95-00349/01 !standard 04.05.02 (14) !class ramification 03-09-16 !status ARG Approved 9-0-0 04-06-14 !status work item 03-09-16 !status received 03-08-01 !qualifier Clarification !priority Low !difficulty Easy !subject Equality on private extensions !summary Equality of a private extension is always that of the full type. !question Consider the following example: package Pak1 is type Typ1 is tagged record F1 : Integer; end record; function "=" (Left, Right : Typ1) return Boolean; -- equality function 1 type Typ2 is new Typ1 with record F2 : Integer; end record; function "=" (Left, Right : Typ2) return Boolean; -- equality function 2 end Pak1; with Pak1; package Pak2 is type Typ3 is private; type Typ4 is new Pak1.Typ1 with private; ... private type Typ3 is new Pak1.Typ2 with null record; type Typ4 is new Pak1.Typ2 with null record; end Pak2; with Pak2; procedure Proc3 is use type Pak2.Typ3; use type Pak2.Typ4; X1, Y1 : Pak2.Typ3; X2, Y2 : Pak2.Typ4; B : Boolean; begin B := X1 = Y1; -- calls equality function 2 B := X2 = Y2; -- Which equality function is called? (2) end Proc3; For the private type Typ3, 4.5.2(15) says that the predefined equality is defined in terms of the primitive equality operator of the full type. There is no user-defined primitive equality operator, hence the predefined equality operator would be used. The full type is a type extension of Pak1.Typ2; thus, 4.5.2(14) says that the predefined equality for Typ3 will call the user-defined equality function for Typ2. Equality on Typ4 should work the same way, since the full type definition is exactly the same. But that doesn't appear to be the case. A private extension is not a private type, so 4.5.2(14) applies rather than 4.5.2(15). This means that to compare X2 and Y2, the predefined equality for Typ4 will call the user-defined equality function for Typ1, not Typ2, since Typ1 is the parent type of the private extension, and then do a regular comparison on the F2 components. This means that although the full type definitions are the same, the predefined equality functions will work differently. It also means that "=" on objects of type Typ4 will behave differently depending on whether the full view of Typ4 is visible or not---inside the body of Pak2, if there is one, equality on Typ4 objects will call the user-defined equality function for Typ2. This couldn't be intended. What is happening here? !response The concept of "parent type" is only defined for derived_type_definitions ( which include type extensions) (see 3.4(2)). In particular, a private_type_extension does not define a parent type (even though it looks like it does). The questioner is correct that 4.5.2(14), rather than 4.5.2(15), applies to the case in question. However, since a private_type_extension does not have a parent type, the 'parent type' mentioned in that paragraph must be the one of the full type. This isn't a problem as this is a dynamic semantics rule, for which there is no distinction between the partial and full views. Therefore equality function 2 is called in the example, as would be expected by the model of the Ada 95 designers. They intended that tagged types always 'work right'. For equality, that means if a user defines an equality operator, it is used in all contexts (at run-time, that is), and never gets superseded by the "reemergence" of some other predefined equality operator. !ACATS test Check if a C-Test similar to the one in the example exists in the ACATS, if not, consider adding one. !appendix !topic Equality on private extensions !reference 4.5.2(14-15), 3.9(2), 3.4(3), 7.3(2-3), 7.3(20) !from Adam Beneschan 08-01-03 !discussion package Pak1 is type Typ1 is tagged record F1 : Integer; end record; function "=" (Left, Right : Typ1) return Boolean; -- equality function 1 type Typ2 is new Typ1 with record F2 : Integer; end record; function "=" (Left, Right : Typ2) return Boolean; -- equality function 2 end Pak1; with Pak1; package Pak2 is type Typ3 is private; type Typ4 is new Pak1.Typ1 with private; ... private type Typ3 is new Pak1.Typ2 with null record; type Typ4 is new Pak1.Typ2 with null record; end Pak2; with Pak2; procedure Proc3 is use type Pak2.Typ3; use type Pak2.Typ4; X1, Y1 : Pak2.Typ3; X2, Y2 : Pak2.Typ4; B : Boolean; begin B := X1 = Y1; -- calls equality function 2 B := X2 = Y2; -- calls equality function 1? end Proc3; There seems to be an inconsistency in how "=" is defined for private types and private extensions. For the private type Typ3, 4.5.2(15) says that the predefined equality is defined in terms of the primitive equality operator of the full type. There is no user-defined primitive equality operator, hence the predefined equality operator would be used. The full type is a type extension of Pak1.Typ2; thus, 4.5.2(14) says that the predefined equality for Typ3 will call the user-defined equality function for Typ2. Offhand, I'd expect the equality on Typ4 to work the same way, since the full type definition is exactly the same. But I'm not sure that's what happens. 3.9(2) implies that a private extension is a type extension, and 7.3 implies that a private extension is not a private type; therefore, 4.5.2(14) applies and not 4.5.2(15). This means that to compare X2 and Y2, the predefined equality for Typ4 will call the user-defined equality function for Typ1, not Typ2, since Typ1 is the parent type of the private extension [or is it?---see below], and then do a regular comparison on the F2 components. This means that although the full type definitions are the same, the predefined equality functions will work differently. It also means that "=" on objects of type Typ4 will behave differently depending on whether the full view of Typ4 is visible or not---inside the body of Pak2, if there is one, equality on Typ4 objects will call the user-defined equality function for Typ2. In addition, this seems to be inconsistent with 7.3(20), which says that for a private extensions, if a primitive subprogram is inherited from the ancestor type, the *body* of the subprogram (if not overridden) comes from the parent type of the full view rather than from the ancestor type of the private extension. Here, when Typ4 objects are compared, the body of "=" comes from the ancestor type of the partial view, not from the parent type of the full view. Actually, after digging into this further, I'm not even sure what the "parent type" of a private extension is! Since a private extension is a type extension, we need to know what it is in order to apply 4.5.2(14). However, "parent type" is defined by 3.4(2-3), and it's defined only for derived_type_definitions; a private_extension_declaration is not a derived_type_definition, and a private_extension_declaration is careful to use the term "ancestor_subtype_indication" instead of "parent_subtype_indication". So just what *is* the parent type of a private extension? It appears to me that the RM never actually defines this---unless it's intended that the "parent type" of a private extension is implicitly supposed to be the "parent type" of the full view, which seems a little odd in that (from the programmer's point of view) it requires a characteristic of the full view to be known at places where the full view isn't visible. So I need to ask: Is it the intent that equality on Typ4 calls the user-defined equality on Typ2? If so, I think the RM wording needs to be fixed, and my suggestion is to fix 4.5.2(14-15) so that 4.5.2(14) starts with "For a type extension other than a private extension", and 4.5.2(15) starts with "For a private type or private extension". (Or would that cause undesirable results in the case where a user-defined "=" for Typ4 appears in the private part of Pak2?) Or I have missed something? By the way, when this code is compiled with GNAT, it calls "equality function 2", not #1, when X2 and Y2 are compared inside Proc3. **************************************************************** From: Tucker Taft Sent: Friday, August 1, 2003 10:03 PM > B := X2 = Y2; -- calls equality function 1? I certainly hope not. One of our "mantras" during the Ada 9X design process was "tagged types work right". In this case, it means that if a user defines an equality operator, it is used in all contexts (at run-time, that is), and never gets superseded by the "reemergence" of some ("junk") predefined equality operator ("junk" is a registered trademark of Robert Dewar ;-). So now all we need to do is interpret the RM carefully to make sure it agrees with this... ... > Offhand, I'd expect the equality on Typ4 to work the same way, since > the full type definition is exactly the same. But I'm not sure that's > what happens. 3.9(2) implies that a private extension is a type > extension, and 7.3 implies that a private extension is not a private > type; therefore, 4.5.2(14) applies and not 4.5.2(15). This means that > to compare X2 and Y2, the predefined equality for Typ4 will call the > user-defined equality function for Typ1, not Typ2, since Typ1 is the > parent type of the private extension [or is it?---see below] No, Typ1 is not the parent of Typ4. Typ1 is an ancestor of Typ4, but it is not the parent. Remember that the partial view and the full view are two views of the same type. A type has only one parent. Typ4's parent is Typ2, as you can see by looking at the full view. > ... > Actually, after digging into this further, I'm not even sure what the > "parent type" of a private extension is! Since a private extension is > a type extension, we need to know what it is in order to apply > 4.5.2(14). 4.5.2(14) is dynamic semantics, and there is no distinction at run-time between the partial view and the full view of the type. There is only one type at run-time, and it has only one parent type, namely that given in its full type definition. > ... > So just what *is* the parent type of a private extension? It appears > to me that the RM never actually defines this---unless it's intended > that the "parent type" of a private extension is implicitly supposed > to be the "parent type" of the full view, which seems a little odd in > that (from the programmer's point of view) it requires a > characteristic of the full view to be known at places where the full > view isn't visible. When it comes to run-time semantics, there is only one type. Privacy is a compile-time thing. Privacy *would* be relevant if you asked what parameter names you can use in a call on "=" using named notation (e.g. "="(blah => x, blurf => y)). The answer to that question must depend only on the partial view, if that is all that the caller can "see." It turns out for most primitive subprograms, it could make a big difference. Within the scope of the full type, you would use the parameter names of the primitive of the parent type; when you can only see the partial view, you would use the parameter names of the primitive of the ancestor type. However, at run-time (we are talking tagged types here, remember), you would execute the same subprogram body in either case. See 3.9.2(20) for the gory details. However, since we are talking about a definition of "=" that conforms to the predefined equality, no equality operation is inherited, but rather a new one is crafted out of thin air for a type extension, and this one definitely has parameter names "Left" and "Right." [Note that for untagged types, where the only corresponding case is formal untagged derived types, the view you see actually *does* determine what operation you invoke at run-time. That is a way in which untagged types don't quite "work right" in my view, but alas, we had to deal with the fact that for untagged types, overridings don't even have to have the same parameter modes! Though of course, "=" necessarily has parameter mode "IN".] > ... Or I have missed something? You missed the fact that 4.5.2(14) is dynamic semantics, and privacy is pretty much out the window by that time. There is only one Typ4 at run-time, and it has only one parent, namely Typ2. > By the way, when this code is compiled with GNAT, it calls "equality > function 2", not #1, when X2 and Y2 are compared inside Proc3. That's good to hear! **************************************************************** From: Adam Beneschan Sent: Friday, August 1, 2003 10:55 PM Tucker wrote: > You missed the fact that 4.5.2(14) is dynamic semantics, > and privacy is pretty much out the window by that time. > There is only one Typ4 at run-time, and it has only > one parent, namely Typ2. Well, if there's no privacy by the time we get to Dynamic Semantics, how can 4.5.2(15) make any sense at all? :) :) Come to think of it, doesn't 4.5.2(15) imply that for an untagged type, the Dynamic Semantics of "=" are different depending on whether the full or partial view of the type is visible when "=" is used (if a user-defined "=" for the type appears in the private part)? I have no problem accepting your interpretation, but I'm still not convinced that the RM unambiguously says this. The general notion that "there is no distinction at run-time between the partial view and the full view of the type" doesn't seem to be completely true, given what 4.5.2(15) says about untagged types (which you acknowledged in your bracketed comment). The concept that a type only has one parent type, and that there can't be one parent type for the full view and a different one for the partial view, doesn't seem to be explicit anywhere; in fact, it's not even explicitly said that a *type* has a parent type (as opposed to a "view of a type" or a "derived_type_definition" having a parent type). The place where "parent type" is defined, 3.4(2), pretty much says the parent type is "there" without really saying of *what* the parent type is a characteristic. I still think that the RM's wording ought to be cleaned up somewhere in order to avoid the kind of confusion I just went through. ****************************************************************