Version 1.3 of ais/ai-00349.txt

Unformatted version of ais/ai-00349.txt version 1.3
Other versions for file ais/ai-00349.txt

!standard 03.04 (02)          03-11-07 AI95-00349/02
!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.

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

Questions? Ask the ACAA Technical Agent