CVS difference for ais/ai-00158.txt

Differences between 1.1 and version 1.2
Log of other versions for file ais/ai-00158.txt

--- ais/ai-00158.txt	1998/09/30 00:17:28	1.1
+++ ais/ai-00158.txt	2003/06/17 21:10:29	1.2
@@ -1,37 +1,266 @@
-!standard 12.05    (06)                               97-08-19  AI95-00158/01
+!standard 12.05    (06)                               03-06-23  AI95-00158/02
 !class binding interpretation 96-09-04
 !status received 96-09-04
-!priority High
+!priority Medium
 !difficulty Hard
-!subject T'Class as generic actual type
+!subject Renamings of primitives of a class-wide generic actual type
+
+!summary
+
+A renaming of a primitive subprogram of a generic formal type is allowed
+when the actual type is class-wide, even though formal parameters of the
+renaming are class-wide in the instance while the corresponding formals of
+the renamed primitive are of the specific type.  A call to such a renaming
+has the same dynamic semantics as a call to a renaming of the primitive
+subprogram which has formal parameters of the specific type, and such a
+call is always a nondispatching call.
+
+!question
+
+Although it is not explicitly stated, a class-wide type may be
+passed as a generic actual type, so long as the formal is
+indefinite, and either private, or a private extension of some
+ancestor of the root of the class-wide type.
 
-!summary 96-11-18
+Inside the generic, you get a set of primitive operations determined
+by the formal type declaration, which in the case of a nonlimited
+private, consists of "=" and "/=", and for a formal private extension,
+a full set of user-defined primitive operations.
 
-????From the minutes of the Vermont ARG meeting:
+The question is -- what happens if we rename one of these implicitly
+declared operations?
 
-Most members at the ARG see lots of value in the proposed capability,
-yet now the question is to determine whether there is sufficient need
-that justifies this extension.  It was also noted that an AI phrased as
-a general overhaul of the dispatching model has very little chance to
-get by the ARG, let alone WG-9.
+For example:
 
-Bob will complete the AI with (or without) the assistance of Tuck.  Any
-further consideration or votes await the completion of the AI.
+    package Pkg is
+        type Root is tagged ...
+        procedure P(R : Root);
+        function Empty return Root;
+    end Pkg;
 
-!question 96-09-04
+    with Pkg;
+    generic
+        type T(<>) is new Pkg.Root with private;
+        -- primitives are implicitly declared here:
+        --   function "="(Left, Right : T) return Boolean;
+        --   procedure P(R : T);
+        --   function Empty return T;
+    package Gen is
+        -- Now what happens if we rename these ops?
+        procedure My_P(R : T) renames P;
+        function Equals(L, R : T) return Boolean renames "=";
+        function My_Empty return T renames Empty;
+    end Gen;
 
+    with Pkg, Gen;
+    package Inst is new Gen(Pkg.Root'Class);
 
-!recommendation 96-09-04
+What exactly are the semantics of Inst.My_P, Inst.Equals, and Inst.My_Empty?
+Are calls on them dispatching? (No.)
 
+!recommendation
 
-!wording 96-09-04
+A generic unit with a generic formal type parameter can be instantiated
+with a class-wide actual type when the formal type is an indefinite formal
+private type or an indefinite formal private extension.  The primitive
+subprograms of such a formal type can be renamed within the generic
+unit.  In the instance, the copies of any such renamings will have the
+class-wide type as the nominal subtype of each controlling formal or
+result subtype of the renamed primitive subprogram.  Such renamings are
+legal, notwithstanding the type conformance requirements for subprogram
+renaming declarations (8.5.4(4,5/1)).  Within the generic instance, a call
+to such a subprogram renaming is allowed to have class-wide actual parameters,
+but the semantics of the call are as for a nondispatching call of the
+primitive subprogram.  In particular, the actuals in such a call are
+converted to the specific tagged type and the call is made to the
+primitive subprogram of the specific type.  The call is nondispatching
+even in the case of a renaming-as-body, where the semantics indicated
+by 8.5.4(7.1/1) might suggest that a dispatching call would occur.
 
+!wording
 
-!discussion 96-09-04
+TBD
 
+!discussion
 
-!appendix 97-08-19
+The basic issue raised by AI is the renaming anomaly pointed out
+by Tucker Taft (see !appendix).  This arises from being able to pass
+a class-wide type as an actual type that matches what ostensibly
+looks like a specific type from the point of view of the generic
+unit.  The primitive subprograms associated with formal private types
+(just "=") and formal derived types have normal specific types for
+their formals.  However, if the actual type passed in is a class-wide
+type, then a subprogram renaming for a primitive of the formal type
+will end up having formal subtypes (and result subtype) that are
+mapped to the class-wide type in an instance.  A strict reading of
+the reference manual leads to the conclusion that such renamings must
+be illegal, because the renaming's specification is not type-conformant
+with the renamed primitive.  For example, considering the type and
+generic package given in the !question section:
 
+    package Pkg is
+        type Root is tagged ...
+        procedure P(R : Root);
+        function Empty return Root;
+    end Pkg;
+
+    with Pkg;
+    generic
+        type T(<>) is new Pkg.Root with private;
+    package Gen is
+        procedure My_P(R : T) renames P;
+        function Equals(L, R : T) return Boolean renames "=";
+        function My_Empty return T renames Empty;
+    end Gen;
+
+For an instantiation passing Root'Class:
+
+    with Pkg, Gen;
+    package Inst is new Gen(Pkg.Root'Class);
+
+The instance logically contains renamings as follows:
+
+        procedure My_P(R : T'Class) renames P;
+        function Equals(L, R : T'Class) return Boolean renames "=";
+        function My_Empty return T'Class renames Empty;
+
+But it would be illegal to write such a renaming, since the
+controlling formals and result are class-wide, conflicting
+with the specific types of the formals and result of the
+renamed subprograms.
+
+Let's consider several possible ways to address this anomaly:
+
+Alternative 1:
+
+The first possibility for resolving this issue is to say that
+an instance containing such a renaming is illegal.  The problem
+with this approach is that it would create a contract model
+violation for renamings in the generic body.  This could be
+prevented by outlawing potentially problematic renamings in
+the body, but that seems overly restrictive and would clearly
+be incompatible (though such renamings are probably rare).
+
+Alternative 2:
+
+Treat calls to the renaming as statically tagged nondispatching
+calls to the renamed subprogram.  That is, in the instance, the
+fact that a formal or result of the renaming logically has a
+class-wide type would be ignored, and the renaming would be
+effectively be equivalent to a normal renaming using the specific
+type for the formal and result types.
+
+Adopting this alternative would presumably require relaxing the
+conformance rules for subprogram renamings, at least for renamings
+of this particular kind within an instance.  Another approach
+would be to define such renamings as using the class-wide type's
+specific root type for formals and result type.  In any case,
+any approach that allows these renamings will need to make
+semantic changes along these lines.
+
+It's worth noting that if the conformance rules are relaxed in
+this way, this doesn't appear to cause additional anomalies,
+at least for a renaming-as-declaration, because 8.5.4(7) defines
+the profile of the view declared by a renaming-as-declaration
+as taking the subtypes of its formals and result from the
+renamed subprogram.  The rules for a renaming-as-body require
+the stricter subtype conformance, but this could presumably
+be relaxed in a similar manner for the case of these special
+instance renamings.
+
+If these odd "class-wide" renaming declarations are defined to be
+equivalent to a normal "specific" renaming, then calls to these
+renamings would simply  be treated as nondispatching calls, even
+when the actuals in a call within the instance are class-wide.
+This follows from the fact that these renamings will not be
+primitive subprograms of the formal type (more properly, in
+an instance, they aren't primitives of the actual type's root type).
+The normal conversion semantics of calls means that any class-wide
+actuals are simply converted to the specific root type.
+
+This alternative seems to be the simplest solution in terms of
+semantics and implementation.  However, this approach means that
+no new functionality is gained by the availability of these
+special renamings (but that's probably a good thing, since
+this keeps the semantics simple and avoids turning what is
+essentially an accidental artifact of the rules into an extension
+that was never intended).  It could be argued that it's surprising
+that a call from within within the generic body with class-wide
+arguments would simply make a static call without regard to the
+tags of the actuals, but users are unlikely to expect special
+dispatching for such renamings (if they ever even think to try
+them in the first place).
+
+Alternative 3:
+
+Treat calls to the renaming as equivalent to calls to the
+renamed dispatching subprogram, allowing both dispatching
+and nondispatching forms of calls.
+
+This approach falls out of Tucker Taft's proposal involving
+'Class_Only operations (see !appendix for details).  However,
+that proposal involves a rather significant change to the semantic
+model for the primitives of tagged types.  To consider such a
+fundamental change to the description of dispatching semantics
+for the sake of solving this rather limited problem seems
+unjustifiable.  When Tucker originally proposed this, it had
+the merit of leading to a significant semantic extension that
+would provide a Java interface-like capability.  However, now
+that a customized design is under consideration for adding
+interface types to the language (see AI-251), there would be
+no added benefit to revising the tagged type model to provide
+an interface-like feature via these odd renamings.
+
+Alternative 4:
+
+Treat calls to the renaming as functionally equivalent to calling
+a class-wide operation that in turn makes a call to the renamed
+subprogram, passing its arguments on to the corresponding parameters
+of the renamed subprogram.
+
+This is essentially the model proposed by Erhard Ploedereder
+(see !appendix), which defines calls to the renaming in terms
+of a wrapper subprogram that makes a call to the renamed
+subprogram, passing along the actuals (making it a dispatching
+call when the actuals of the renaming are class-wide).  One problem
+with this approach is that it conflicts with the current treatment
+of subprogram renamings as having the profile of the renamed
+subprogram, not that of the specification given in the renaming.
+Also, tag-indeterminate dispatching would not work with this model.
+
+This approach could be worth considering, if it added a significant
+enough functionality benefit.  If the idea is to achieve a
+form of interface capability, then this has been superseded by
+the proposal for interface types being considered in AI-251.
+Simply gaining the ability to make dispatching calls when
+making calls via renamings of this specialized form seems
+too rarefied a need.  Admittedly this could also be used
+to allow dispatching calls via formal subprograms, but that
+also seems a feature of somewhat dubious use, and is unaesthetic
+in that it takes advantage of an odd loophole rather than being
+a properly integrated language feature.  Note also that it would
+merely be shorthand for what can already be done by passing
+in an explicit wrapper subprogram.
+
+Conclusion
+
+Pursuing a solution based on alternatives 3 or 4 might appear
+more attractive in the absence of the interface feature being
+proposed in AI-251.  However, in the absence of any demonstration
+that supporting dispatching operations via these curious renamings
+would address an important user need, it appears to be advisable
+to pursue a conservative solution that simply allows the renamings,
+treats them as normal nonprimitive renamings, and defines them
+to have nondispatching call semantics.  For this reason we propose
+the approach of Alternative 2.  (N.B.: the details of the exact
+rule changes to implement this alternative are not yet worked out.
+Either the renaming conformance rules could be relaxed in the
+context of instances, or a special rule could be added to define
+the formal and result subtype's to be the root type of the
+formal type if the actual has the potential to be class-wide.)
+
+!appendix
+
 !section 12.5(6)
 !subject T'Class as generic actual type
 !reference RM95-12.5(6)
@@ -373,7 +602,7 @@
 consistent with this model. [although the words in 8.5.4 don't use
 this model, but rather appeal to a "view" model]
 
-Using this model, the execution semantics of 
+Using this model, the execution semantics of
    procedure P(X: T'Class) renames Primitive(X: T);
 once allowed by the conformance rules for renaming declarations, come
 out as desired: Calls on P result in a dispatching call on Primitive
@@ -382,7 +611,7 @@
 Similarly, in this model
    procedure P(X: T) renames Primitive(X: T);
 naturally does not result in a dispatching call of Primitive (and that is
-the current semantics of renaming). 
+the current semantics of renaming).
 
 So, by liberalizing the matching rules of renaming, we can accommodate the
 desired functionality without introducing any noticeable incompatibilites.
@@ -428,7 +657,7 @@
             ...
         package Implements is
 
-and this implies that this works only for generics written specifically 
+and this implies that this works only for generics written specifically
 for tagged formal types. I.e., you can't make it work for both tagged and
 untagged types.  But, in a way, this makes sense under the contract
 model. The body really ought to know whether Display is an operation
@@ -437,3 +666,124 @@
 actuals.
 
 ****************************************************************
+
+From: Gary Dismukes
+Sent: Tuesday, June 17, 2003  3:36 AM
+
+Here's a draft write-up for the long-overdue AI-158.  [Editor's note:
+This is version /02.] I've included
+the two appendix messages here, since these are referenced in the
+AI discussion and the details of those messages are important sources
+for understanding two of the considered alternatives.  The AI write-up
+is rather tentative in its current form, and will need discussion
+in any case that may well result in a different decision that the
+proposal, so I didn't attempt to propose specific rule and wording
+changes in this draft.
+
+Pascal, feel free to include this in the agenda or not, as you feel
+appropriate.  It's not a burning issue, but it's been hanging around
+for so long that we should make an effort to get it off the table
+in the near future...
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Tuesday, June 17, 2003  8:25 AM
+
+> Treat calls to the renaming as equivalent to calls to the
+> renamed dispatching subprogram, allowing both dispatching
+> and nondispatching forms of calls.
+>
+> This approach falls out of Tucker Taft's proposal involving
+> 'Class_Only operations (see !appendix for details).  However,
+> that proposal involves a rather significant change to the semantic
+> model for the primitives of tagged types.  To consider such a
+> fundamental change to the description of dispatching semantics
+> for the sake of solving this rather limited problem seems
+> unjustifiable...
+
+I didn't actually intend that we would make major changes in the
+RM to reflect this model.  Instead, it would be more like our
+"design philosophy" annotations.  It would help to explain
+the choices made, but we wouldn't have to "burden" the
+user with the model itself; only the language lawyers
+would be aware of it.
+
+With the current rules for renaming dispatching operations,
+they lose their dispatching-ness if they are no longer
+primitive.  But if you call a subprogram and its renaming
+with exactly the same parameters, it will either be illegal,
+or do the same thing.
+
+What you have proposed here is that if you call a renaming
+with the same parameters (inside the generic), it is legal
+but it does something completely different; namely it doesn't
+dispatch.  That seems inconsistent and confusing (or maybe
+I am confused about your proposal).
+
+I don't think we have to introduce the whole "'class-only" model
+to describe the semantics of these as always dispatching.  We could
+require that they only be given dynamically tagged or
+tag-indeterminate operands, though that seems like a somewhat
+separate decision.
+
+In any case, it would help to have examples of all 4 alternatives,
+in particular show *calls* on these renamings, and what are
+the effects.  I think that may make it more clear which
+alternatives are acceptable.  My gut feel is that the
+one you have chosen will just look too weird when you
+start looking at calls, both inside and outside the
+generic.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Tuesday, June 17, 2003  4:03 PM
+
+> With the current rules for renaming dispatching operations,
+> they lose their dispatching-ness if they are no longer
+> primitive.  But if you call a subprogram and its renaming
+> with exactly the same parameters, it will either be illegal,
+> or do the same thing.
+
+Huh? The first sentence says that the renamings may not do the same thing,
+then the second sentence says that they do. One of these must be wrong!
+
+If we have:
+
+   package P is
+      type T is tagged...
+      procedure Prim (O : in T); -- (Prim1)
+   end P;
+
+   with P;
+   package Q is
+      type T2 is new P.T with ...
+      procedure Prim (O : in T2); -- (Prim2)
+   end Q;
+
+   with P, Q;
+   procedure Main is
+       Obj : Q.T2;
+       procedure Ren_Prim (O : in P.T) renames P.Prim;
+   begin
+       P.Prim (P.T'Class(Obj)); -- Dispatches, calls Prim2.
+       Ren_Prim (P.T'Class(Obj)); -- Does not dispatch, calls Prim1.
+   end Main;
+
+It's pretty clear that this renaming is legal, and we're calling the same
+subprogram with the same parameters, and we're getting a completely
+different result. So, how is Gary's suggestion going to cause things to get
+more confusing? Nobody has a clue about this as it is. :-)
+
+(BTW, this whole issue seems to be unnecessary. The existing blanket
+legality rules (12.3(11)) clearly covers this, with assume-the-best in the
+spec and assume-the-worst in the body. There doesn't seem to be any
+requirement to change the RM for this [there is no "hole"], unless we
+believe that there is substantial use of this 'feature' caused by buggy
+compilers. One would expect that some compilers already make this illegal.
+Thus, I vote for alternative 1 in the absence of some evidence that a change
+is actually needed.)
+
+****************************************************************
+

Questions? Ask the ACAA Technical Agent