CVS difference for ais/ai-00344.txt

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

--- ais/ai-00344.txt	2004/03/02 04:45:00	1.2
+++ ais/ai-00344.txt	2004/04/06 21:51:27	1.3
@@ -518,3 +518,644 @@
 more out of this than they'll actually get.
 
 ****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, April 1, 2004  11:19 AM
+
+Randy, Steve Baird, and I have been having a bit
+of a side-bar discussion on paragraph 3.9.1(4) and
+how it relates to AI-344 on allowing extending
+in nested scopes.  The most recent version of AI-344
+did not touch 3.9.1(4) which disallows extending
+in a generic body a type defined outside it,
+since we would be allowing extension in all
+kinds of other contexts.
+
+After a few back-and-forths with Randy, we
+came up with the following wording for 3.9.1(4)
+which seems to be satisfactory, and
+avoids contract model issues associated with extending
+types with unknown number of functions that require
+overriding (due to a controlling result).  It also
+allows slot numbers to be assigned for any new
+operations, since the parent is not a formal type.
+
+    Within the body of a generic unit, or the body of
+    any of its descendant library units, a tagged type
+    shall not be declared as a descendant
+    of a formal type of the generic unit.
+
+This is carefully worded to handle child units, as well
+as interfaces (hopefully).
+
+The net effect is that you *can* extend a type
+declared outside the generic body, so long as it
+is not a descendant of a formal.  The anticipated
+implementation model is still that the new type
+would have a component that pointed to a block
+of information which would contain static link/
+display/instance descriptor/whatever.  Wrappers
+for the dispatching operations would load this
+information into appropriate registers, etc.,
+and then call/jump to the user-defined primitive
+subprograms.  These are essentially the same
+thing as "trampolines" which are used by GNU C
+and friends to support nested functions.
+
+I have attached below the mail sent by Steve Baird,
+Randy, and myself, for the record.
+
+If there are no major objections, I will include
+this change in the next version of AI-344.
+
+----------
+
+ From Steve, 3/29/04, 6:54 PM:
+
+I strongly want to be able to instantiate the various container generics
+anywhere (e.g. inside of a subprogram which is inside of a generic body),
+even if they are implemented using controlled types.
+
+This runs afoul of 3.9.1(4) .
+
+If you've already addressed this problem, then this message can be
+ignored.
+
+Given AI-344's runtime checks, the only remaining need for 3.9.1(4) stems,
+as I understand it,  from the case described in AARM 3.9.1(4.a-j).
+
+This case can only arise if the parent  type is abstract.
+
+Could 3.9.1(4) be relaxed so that it only applies to abstract types?
+
+I'm not claiming this is elegant, but it would allow an important case.
+
+----------
+
+ From Tuck to Randy and then his reply (3/31/04):
+
+ >> Hi Randy,
+ >>     There seems to be some interest in pushing forward AI-344, nested type
+ >> extensions.  Paragraph 3.9.1(4) disallowing extending in generic
+ >> bodies has been an issue.  It seems somewhat arbitrary, if we allow
+ >> extending pretty much anywhere else.  I was thinking that the following
+ >> might solve the problem:
+ >>
+ >> Replace 3.9.1(4) with:
+ >>
+ >>     A type extension shall not be declared in a generic body if the parent
+ >>     type is a formal type of the generic.
+ >>
+ >> Would this be implementable in your compiler?  Notice that I allow the
+ >> parent to be descended from a formal type, but not a direct use of the
+ >> parent type.  That avoids the danger that the parent type is class-wide.
+ >>
+ >> Presumably with AI-251, this would become:
+ >>
+ >>     A type extension shall not be declared in a generic body if the parent
+ >>     type or one of the ancestor interface types is a formal type of the
+ >>     generic.
+ >>
+ >> Can you think about this a bit and let me know?
+
+
+I thought about this some last week while I was working on the minutes.
+
+One comment is that I never saw any actual reaction to keeping 3.9.1(4). The
+only person who seemed to care was you. So I'd suggest not giving this rule
+an importance out of proportion to reality. (After all, most types can be
+hoisted to the spec.)
+
+First of all, it will be very hard to implement any part of AI-344 in our
+compiler. We implicitly add generic descriptor parameters for all
+subprograms (declarations and calls) if they are declared in a generic.
+Doing this causes the other parameters to be renumbered automatically. The
+implementation you've suggested for AI-344 requires this not to be true for
+wrappers for tagged types. That will require major restructuring in the
+compiler - either the wrappers would have to be created by a totally
+separate set of code (meaning the duplication of the 1500 lines of call
+generation code - sounds very bad) or the automatic, under the covers
+operation of generics would have to be gotten rid of. Neither is likely to
+be practical; I'd expect that a likely implementation of AI-344 would
+completely disallow it in generics.
+
+In any case, I don't want to stand in the way of progress here (unless it is
+clearly going too far, like the generic instantiation junk). So most of the
+discussion is more hypothetical than what actually can be implemented. It's
+about what could be implemented with heroic efforts to provide a usable
+solution, as opposed to what's clearly impossible or outrageous.
+
+Besides implementation problems, that rule is needed to avoid contract model
+issues. I hadn't thought about class-wide; the AARM mainly discusses it in
+terms of must-be-overridden functions. The example is something like:
+
+      generic
+          type Foo is tagged private;
+      package Gen is
+          ...
+      end Gen;
+
+      package body Gen is
+          type New_Foo is new Foo with private;
+             -- ??
+      end Gen;
+
+      type Bar is tagged ...;
+      function A_Bar return Bar;
+
+      package New_Gen is new Gen(Bar); --??
+
+Since A_Bar is a primitive function returning an object of Bar, it must be
+overridden. But how would New_Foo know to do that? It's clear that the type
+cannot depend on a formal.
+
+There is also a practical contract violation here. We have a meta-rule that
+tags always can be created statically. We certainly adhere to that rule by
+generating the tags for types declared in a generic at instantiation time.
+There is no way to do that for a type that depends on a formal that declared
+in the body. (And note that here, this is true even if there is an
+intermediate type declared in the specification, because that cannot tell us
+all of the overriding needed.)
+
+      generic
+          type Foo is tagged private;
+      package Gen is
+          type Foolike is new Foo with null record;
+      end Gen;
+
+      package body Gen is
+          type New_Foo is new Foolike with private;
+          procedure Some_Operation (A : New_Foo); --??
+      end Gen;
+
+      type Bar is tagged ...;
+      function Some_Operation (A : Bar);
+
+      package New_Gen is new Gen(Bar);
+
+Some_Operation for New_Foo is overriding and must be replaced in the tag for
+New_Foo. But how is an implementation supposed to figure that out? It cannot
+know whether Foo has a primitive operation Some_Operation.
+
+This could be "solved" by declaring Some_Operation to be not overriding. But
+that would violate the principle that a generic works the same as a
+non-generic. (We certainly aren't going to declare this as non-overriding
+outside of a generic, that would be a gratuitous incompatibility.) Moreover,
+there isn't any way I can think of to create this tag at runtime (short of
+including the entire Ada symboltable and code in the program, which isn't
+remotely practical). Because you'd have to pass the name and parameter and
+result profile with every slot in order to figure out any if there is any
+overriding. (Usually we assign slot numbers to these, but in this case
+that's not possible.)
+
+This gets even more interesting when there is a package spec in the body:
+
+    package body Gen is
+       package Nested is
+          type New_Foo is new Foolike with private;
+          procedure Some_Operation (A : New_Foo); --??
+       end Nested;
+    end Gen;
+
+Here, Some_Operation is clearly a primitive of New_Foo. How do we assign a
+slot number? It might override something that comes from the formal, or it
+might not.
+
+So I'm certain that any relaxation of the rule would have to disallow
+anything that depended on a formal in any way. (Direct dependence is not
+enough.)
+
+I haven't been able to decide if derivations from unrelated types (such as
+"Controlled") are a problem or not. Clearly, you could use the same
+techniques that you use for non-generic types. The problem (if there is one)
+would occur if you need any local data space for the wrappers. This I don't
+really know.
+
+At the meeting, we discussed the fact that the wrappers probably would need
+a runtime data structure (linked through the dynamic stack, perhaps, or
+perhaps on the finalization chain) to find the appropriate display or static
+link. Presuming that implementers sign up for that overhead (which is an
+open question to me, it seems significant, but at least not distributed),
+then that mechanism would provide a mechanism for finding any data needed.
+So on that basis, it seems like it *might* be possible to implement. (Of
+course, generating the wrappers would be a big problem because of the
+generic parameter issue mentioned previously. But the question is that is
+would be possible.)
+
+So I'd probably not oppose strenuously a rule like:
+
+      A type extension shall not be declared in a generic body if the parent
+      type is descended from a formal type of a generic.
+
+(Note that we have to worry about nested and child cases here, too. It
+doesn't matter whose formal it is, we don't want to be dependent on it. But
+we don't want this to apply to types declared in instances, so I'm not sure
+if the above wording is right. Perhaps we need to say something about "the
+generic or its ancestors".)
+
+P.S. I presume that we'll attach this discussion to the AI? I don't want to
+have to write this note again!!!
+
+-------------------
+ From Tuck to Randy and then his reply again (3/31/04):
+
+...
+
+ >> Since we disallow use throughout the generic body, I think this handles
+ >> nested generics and subunits (since they are "plugged" into their stub
+ >> prior to legality checks).  Children seem like a problem as you say,
+ >> since their bodies are not considered nested within the ancestor.
+ >> The "ancestor" of a generic is not always well defined, so
+ >> perhaps the following:
+ >>
+ >>    Within the body of a generic unit, or the body of any of its descendant
+ >>    library units, a tagged type shall not be declared as a descendant
+ >>    of a formal type of the generic unit.
+ >>
+ >> This hopefully covers interface types as well.
+
+
+I think this works.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, April 2, 2004  9:24 PM
+
+Unfortunately, I've thought of another case where there is trouble creating
+a tag in the body without knowing the actual types involved. Consider the
+following:
+
+    type Base is tagged ...
+    procedure Proc (Obj : in out Base; Arg : in Integer); -- (1)
+
+    subtype Short_String is String (1..10);
+
+    generic
+        type Frob is private;
+    package Gen2 is
+        ...
+    end Gen2;
+
+    package body Gen2 is
+        type Der is new Base with ...;
+        procedure Proc (Obj : in out Der; Arg : in Frob); --(2)
+        ...
+    end Gen2;
+
+    package Short is new Gen2 (Short_String);
+    package Int is new Gen2 (Integer);
+
+In Short (1) and (2) have different profiles, and thus overload each other.
+Therefore (2) gets a new slot (which can be calculated at compile-time,
+given the above rule).
+
+In Int, however, (2) overrides (1). That means (2) is supposed to get the
+slot of (1).
+
+In simple cases like this one, it probably would be possible to figure out
+the slots by some sort of run-time tag modification. If there were multiple
+such parameters, though, it could get pretty messy.
+
+There also is a practical problem in our implementation. The parameter
+passing implementation of Arg in (1) would be copy-in; while for Arg in (2)
+it would be by-reference with the copy made for by-copy types at the call
+site. Thus, if the wrapper for (2) was just stuck into the slot for (1), it
+would have the wrong parameter passing code. Not good.
+
+I'd suggest some sort of rule to fix this problem, but I can't think of
+anything off-hand that doesn't throw the baby out with the bathwater. The
+easy rule (the tagged type cannot have any primitives which depend on
+generic formals) would prevent pretty much everything useful. And for types
+like Controlled, it doesn't matter anyway, since the base type doesn't have
+any operations that could cause problems.
+
+But a rule that only eliminated problem cases would have to talk about the
+intersection of possible profiles of all of the primitives, which seems
+awful to describe and implement. Something like "Within the body...., a type
+extension is illegal if a primitive operation of the type has one or more
+operands which depend on a formal type of a generic, and the primitive could
+override another primitive of the type in any possible instantiation of the
+generic." (along with the previous restriction, of course).
+
+I think this would still allow containers and Controlled in the bodies of
+generics (presuming the containers were derived directly from Controlled),
+but would avoid the problem cases.
+
+Alternatively, we could simply say that such "overrides" don't override in a
+generic body; but of course, that would mean that making a unit generic
+could change its semantics. I don't like that much (and I'd think that
+template implementations would like it less).
+
+****************************************************************
+
+From: Robert I. Eachus
+Sent: Friday, April 2, 2004  10:08 PM
+
+I guess I see the problem, but I don't see it.  The problem occurs only
+if there is a call inside the generic:
+
+Proc(Some_Der, N); -- where N is of type Integer.
+
+In the first instance Short, this call goes to the (derived) Proc
+declared inside Gen2.  Your assumption in the Int case is that this call
+should go to the  explicitly declared Proc.  But I don't see how it
+can.  The only visible Proc inside the generic with an Integer parameter
+is the implicitly declared one.
+
+If Der and Proc were declared in the generic package spec, then there
+could be external calls where the overriding must be recognized.  But
+that happens only if the overriding is in the spec, which is not the
+case in question here.
+
+Now if Der was declared in the spec, and there was an explicit Proc in
+the body, that could cause a problem. However, I think a rule that said
+that such an overriding was not visible outside the (generic) unit would
+follow the principle of least surprise.  I think this is the weird case
+Randy is worried about.  However, I think that it may be one where a
+more general fix is needed.  Notice that a "normal" overriding in the
+body of a generic is fine.  It is a visible overriding inside the
+generic as well as externally.  The compiler knows that it is an
+overriding and can deal with it accordingly.  In this "accidental"
+overriding case, only if the author of both the generic body and of the
+instantiation is the same  (Ada language lawyer) programmer, would an
+overriding visible (only) outside the generic be anything other than
+surprising.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, April 2, 2004  11:27 PM
+
+No, the problem also occurs when there is a dispatching call on Base'Class.
+Which Proc gets calls for an object of Der? That call doesn't have to be
+inside of the generic; any class-wide call will exhibit the difference in
+behavior.
+
+Note that the use of overridding indicators would show the problem, as
+neither "overriding" nor "not overriding" would be legal on Proc
+(assume-the-worst in a generic body). But they wouldn't help...
+
+In any case, I'm mostly concerned about the implementation effects on an
+implementation that tries to share bodies. If there is "conditional"
+overriding in the body, sharing is going to be very hard or impossible. And
+since none of this is visible in the spec (we're only discussing 3.9.1(4),
+which only applies to types completely declared in the body), there can be
+no body-agnostic sharing in such a case.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Saturday, April 3, 2004  7:41 AM
+
+> Unfortunately, I've thought of another case where there is trouble creating
+> a tag in the body without knowing the actual types involved. ...
+
+I think we would want to treat this analogously to the
+situation where a type has a private primitive.  It's slot
+is not reused on a type extension even if a primitive with an equivalent
+profile is declared outside the scope of the private
+primitive.  Instead, the new primitive gets a new slot.
+
+In this case, I would say that in a generic body, if a parameter's
+types might be different in some instances, then the primitive
+gets a new slot in all instances.  I can't imagine any
+value to having overriding vs. overloading depend on the
+actuals, since no code outside the instance is going to be
+able to name this type.  Yes it could see such an object
+at run-time via 'Class, but I don't think that argues
+for making the overriding vs. overloading instance-specific.
+
+In the example you give below, the declaration of
+"Proc" (2) in the generic body would not override the
+inherited primitive "Proc" (1), and in fact wouldn't even
+be a primitive.  If Der were declared in a nested
+package spec, then the new Proc (2) would be a new primitive
+of Der and get a new slot, even if Frob turned out to be
+Integer.
+
+> ... Consider the
+> following:
+>
+>     type Base is tagged ...
+>     procedure Proc (Obj : in out Base; Arg : in Integer); -- (1)
+>
+>     subtype Short_String is String (1..10);
+>
+>     generic
+>         type Frob is private;
+>     package Gen2 is
+>         ...
+>     end Gen2;
+>
+>     package body Gen2 is
+>         type Der is new Base with ...;
+>         procedure Proc (Obj : in out Der; Arg : in Frob); --(2)
+>         ...
+>     end Gen2;
+>
+>     package Short is new Gen2 (Short_String);
+>     package Int is new Gen2 (Integer);
+>
+> In Short (1) and (2) have different profiles, and thus overload each other.
+> Therefore (2) gets a new slot (which can be calculated at compile-time,
+> given the above rule).
+>
+> In Int, however, (2) overrides (1). That means (2) is supposed to get the
+> slot of (1).
+
+Naaah.  Overriding should be something that is decided before
+instantiation for generic bodies.
+
+> In simple cases like this one, it probably would be possible to figure out
+> the slots by some sort of run-time tag modification. If there were multiple
+> such parameters, though, it could get pretty messy.
+
+Blech.  I can't imagine any value to allowing overriding vs.
+overloading to depend on the actual when in a generic body.
+
+> ...
+> Alternatively, we could simply say that such "overrides" don't override in a
+> generic body; but of course, that would mean that making a unit generic
+> could change its semantics.
+
+You aren't just making the unit generic (i.e. wrapping the
+word "generic" around it).  You are also taking one of the
+types and making it a formal type.  To me that implies you
+want the behavior to be "generic" across changes in such
+a type.  You don't want it to suddenly have a differnt
+behavior if the type happens to match some parameter type
+of some primitive of some tagged type that happens to be
+used inside the body.  In fact, if you did that, you would
+create an interesting capability to do a run-time test for
+(untagged) type identity, which is *not* something we have
+ever provided in a generic, and I don't think we want to
+start now!
+
+> ... I don't like that much (and I'd think that
+> template implementations would like it less).
+
+Having a template implementation doesn't change
+my view.  Overriding vs. overloading within the body
+should be decided when you compile the generic, not
+at the time of the instance.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Saturday, April 3, 2004  4:46 PM
+
+Tucker said, replying to me:
+
+> > ...
+> > Alternatively, we could simply say that such "overrides" don't override in a
+> > generic body; but of course, that would mean that making a unit generic
+> > could change its semantics.
+>
+> You aren't just making the unit generic (i.e. wrapping the
+> word "generic" around it).  You are also taking one of the
+> types and making it a formal type.  To me that implies you
+> want the behavior to be "generic" across changes in such
+> a type.  You don't want it to suddenly have a differnt
+> behavior if the type happens to match some parameter type
+> of some primitive of some tagged type that happens to be
+> used inside the body.  In fact, if you did that, you would
+> create an interesting capability to do a run-time test for
+> (untagged) type identity, which is *not* something we have
+> ever provided in a generic, and I don't think we want to
+> start now!
+
+OK, that seems like a good argument.
+
+> > ... I don't like that much (and I'd think that
+> > template implementations would like it less).
+>
+> Having a template implementation doesn't change
+> my view.  Overriding vs. overloading within the body
+> should be decided when you compile the generic, not
+> at the time of the instance.
+
+Of course, this means further futzing with the overriding rules. That
+doesn't seem too pleasant, either. But the goal (being able to use
+controlled types and containers in generic bodies) seems worthwhile.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Sunday, April 4, 2004  1:26 PM
+
+I'm not sure that's true (though you may have something specific
+in mind).  The rules I can find generally say that everything
+relating to overloading and overriding is decided when you
+compile the generic body.  Visible part of instances are
+different, but the body seems pretty uniformly to rely on
+overloading/overriding decisions made when the generic body
+is compiled.  E.g. 8.3(13), 8.3(26/1), 12.3(14), 12.3(18).
+
+****************************************************************
+
+From: Pascal Leroy
+Sent: Monday, April 5, 2004  6:36 AM
+
+But this is not new futzing.  As Tuck pointed out, declarations in the
+body should work like declarations in the private part, and you probably
+do quite a bit of futzing already to implement 12.3(18).  So you have a
+firm foundation on which to build even more futzing.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Tuesday, April 6, 2004  4:22 PM
+
+No, I was thinking about in the Standard, not in our compiler. But you and
+Tucker have pointed out 12.3(18), so the Standard is already correct on this
+point. No futzing needed. Some implementers might need to futz if they don't
+follow 12.3(18) properly, but that's not a problem for this group to worry
+about.
+
+****************************************************************
+
+From: Robert I. Eachus
+Sent: Monday, April 5, 2004  1:46 AM
+
+>No, the problem also occurs when there is a dispatching call on Base'Class.
+>Which Proc gets calls for an object of Der? That call doesn't have to be
+>inside of the generic; any class-wide call will exhibit the difference in
+>behavior.
+
+My head is starting to hurt.  Right now, Der is declared nested in a
+generic package body.  You are proposing to assign it to a "global"
+object of type Base'Class, so that it can be operated on outside the
+generic.  But there is no way to do that directly, you have to have an
+access type declared access (all) Base'Class.  Now you can use an
+allocator for this access type inside the generic, and assign its value
+to an object of the access type.  See RM 3.10.2(14&15).
+
+So it seems to me that it would be much easier to do any required magic
+as a part of that allocator.  An allocator for a class-wide type seems
+pretty magic to me anyway.  In fact, in exactly the case we are
+discussing, the magic rules in  3.10.2(20) and 3.9.1(3) seem to combine
+to allow, for example, type extension in a generic body, the generic
+body can be instantiated inside some procedure, and assignment of a
+value created by allocator to an access object in the procedure body
+would be legal.  In that case, there could be for example a generic
+formal subprogram, that gets inherited in the generic, and must be
+dispatched to.
+
+So I guess to me it seems silly to distribute overhead to any case other
+than allocators in package bodies for class-wide access types which
+allocate an object of a tagged type declared in the generic package
+body.   Furthermore since the painful cases require that the allocator
+be in the sequence of statements for such a generic package, why not
+just outlaw it and be done.  (If you have an object of the class-wide
+access type in the package body, with an initial value, that object can
+only be seen within the package body.  It is assignments in the sequence
+of statements at the end of the generic package body that can cause
+problems, and it seems to me to be simple enough to treat that scope as
+statically deeper than the rest of the generic package.  I have created
+packages with a sequence of statements at the end, I have even done it
+with generic packages.  But assignments outside the generic--of an
+object of a type created in the generic BODY?  Come on...
+
+>Note that the use of overridding indicators would show the problem, as
+>neither "overriding" nor "not overriding" would be legal on Proc
+>(assume-the-worst in a generic body). But they wouldn't help...
+>
+>In any case, I'm mostly concerned about the implementation effects on an
+>implementation that tries to share bodies. If there is "conditional"
+>overriding in the body, sharing is going to be very hard or impossible. And
+>since none of this is visible in the spec (we're only discussing 3.9.1(4),
+>which only applies to types completely declared in the body), there can be
+>no body-agnostic sharing in such a case.
+>
+I don't know what the "best" way to deal with this is.  But I think
+Tucker and I are leaning the same way--allow the useful functionality
+even if we have to keep some restrictions.  Allowing derived types in
+generic package bodies while not allowing assignment of access values to
+objects of the derived type outside the generic seems like a very
+livable restriction.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Monday, April 5, 2004  1:45 PM
+
+> My head is starting to hurt.  Right now, Der is declared nested in a
+> generic package body.  You are proposing to assign it to a "global"
+> object of type Base'Class, so that it can be operated on outside the
+> generic.
+
+Not at all. First of all, 'Der' isn't neccessarily nested; it's in the body,
+but could be at library level depending on the instantiation. But in any
+case, 'leaking' the object out isn't necessary.
+
+All you need to do is call a class-wide routine on the base class with a
+parameter of type Der. If that routine then does a dispatching call on Proc,
+you can tell whether it's been overridden or not.
+
+In any case, Tucker's argument seems strong enough to avoid the issue in the
+first place, by simply saying that such things are not overriding. *That* we
+can implement without trouble in a shared implementation.
+
+****************************************************************
+

Questions? Ask the ACAA Technical Agent