!standard 4.8(5.3/2) 08-02-04 AI05-0052-1/04 !standard 7.5(6.1/2) !class binding interpretation 07-05-15 !status work item 07-05-15 !status received 07-05-14 !priority Medium !difficulty Hard !qualifier Omission !subject Coextensions and distributed overhead !summary An allocator that is used to create a coextension is illegal if the designated type might be limited but the enclosing object is not inherently limited. The term "inherently limited type" is (re-)introduced to simplify the presentation of this and other legality rules. !question In the case of a call to a function with a class-wide nonlimited result type, or of a dispatching call to a function with a nonlimited controlling result type, the current language definition seems to impose a substantial distributed overhead requirement in order to cope with the (typically remote) possibility that the function result might have a coextension with a task part. This expense is incurred even if the program never allocates any such coextension. Was this intended? !recommendation Statically reject an allocator used to create a coextensions if the designated type might be limited but the enclosing object might be of a nonlimited type. !wording Add after 4.8(5.3/2): If the designated type of the type of the allocator is limited, then the allocator shall not be used to define the discriminant of an object, unless the object is of an inherently limited type (see 7.5). AARM Note: Because coextensions work very much like parts, we don't want users creating limited coextensions for nonlimited types. This would be similar to extending a nonlimited type with a limited component. We check this on the allocator. Note that there is an asymmetry in what types are considered limited; this is required to preserve privacy. We have to assume that the designated type might be limited as soon as we see a limited partial view, but we want to ensure that the containing object is of a truly limited type. Add after 7.5 (6.1/2): A view of a type is inherently limited if it is one of the following: * An explicitly limited record; * A tagged limited private type; * A task type, a protected type, or a synchronized interface; * A type derived from an inherently limited type; * A type with a part that is of a inherently limited type. AARM note: an inherently limited type is a type that cannot become nonlimited subsequently in a private part or in a child unit. If a view of the type makes it inherently limited, then no assignment or copying operations are ever available for objects of the type, and it is safe for such objects to have access discriminants that designate other limited objects. !discussion Consider the following example: package Pkg is type T1 is tagged null record; function F return T1'Class; end Pkg; package body Pkg is task type Tsk; task body Tsk is separate; type T2 (Task_Ref : access Tsk) is new T1 with null record; function F return T1'Class is begin return T2'(Task_Ref => new Tsk); end F; end Pkg; with Pkg; procedure Pkg_Client is X : Pkg.T1'Class := Pkg.F; begin null; end; A function return object and its coextension have the same master (see 3.10.2(10.1/2)). Ada 2005 introduced access discriminants for nonlimited types. This means that a call to a function which returns a *nonlimited* class-wide type or a dispatching call to a function with a *nonlimited* controlling result type must be able to cope with the possibility that the function result will include dependent tasks. The runtime overhead associated with this is likely to be significant, even if the compiler optimizes for the common case where no such tasks are returned. This is a heavy price to pay for such a rarely-used feature. Users may be displeased when recompiling an Ada95 program as an Ada2005 program generates worse code. In the case of the preceding example, some kind of restriction is needed if a caller of Pkg.F is to be able to assume that it does not need to cope with tasks. The other choice, of course, is to leave the language unchanged and simply pay the performance cost for this feature. Informally, an object's coextension can be thought of as being a "part" of of the object. This is the model that the language designers had in mind. An extension of a nonlimited type cannot add a limited component. Leaning on our informal definition of "part" as it relates to coextensions leads to the conclusion that an extension of a nonlimited type should not have an access discriminant which designates a limited type, or at least that such an access discriminant should not designate a coextension. Checking rules like these is problematical, because it is not always known whether or not a type is limited. Consider, for example, a generic body which allocates a coextension for an object of a formal limited type; the corresponding actual type might or might not be limited. Another possible approach would be to disallow access-to-limited non-inherited access discriminants for a nonlimited extension unless the parent type already has such a discriminant. The general idea is to require the decision about the legality of task "parts" for a nonlimited type to be made when the nonderived root of the derivation hierarchy is declared. The easiest solution would be a runtime check when a coextension is allocated which fails if the coextension's type has a part that is of a task, protected, or explicitly limited record type and the owning object's type does not. In the case of the preceding example, this check would raise an exception at the point of the allocator within Pkg.F. For implementations which do not generate shared code for generics, the outcome of this check would always be known at code-generation time. However, we generally prefer compile-time checks to runtime checks, especially in cases where there is no acceptable implementation. Thus, we prefer a static check. In order to avoid rejecting useful types in the absence of coextensions, we chose to make a conservative check on the creation of coextensions, rather than limiting the kinds of allowable discriminants. It is perfectly reasonable to have a discriminant that accesses an existing task object, and there is no reason to disallow such constructs. In order for the check to work, we have to make any coextension allocator for a type that *might* be limited illegal unless it is a coextension of a type that is *known* to be limited. That is, there cannot be a case where the real coextension type turns out to be limited and the real type of the type with the access discriminant is not limited. Note that this rule in incompatible with Ada 95 in unusual cases; those where a coextension is created with a limited type (including limited private types) and the type with the discriminant is not known to be limited (usually a limited private type.) Coextension use is rare enough that this is not that important. !ACATS test An ACATS B-Test is needed to check this rule. !appendix From private mail of Randy Brukardt replying to a query from Steve Baird (the original author of this AI): > So what do you think? Is there a real problem here? > If so, then what solution would you prefer? Ban coextensions. They're not worth it in any sense of the word. As to a practical solution, I simply don't care. I'll only implement coextensions in Janus/Ada if (a) virtually everything else is finished; and (b) there is an actual demand for them [or for a formal validation that required them]. I truly doubt that either (a) or (b) will ever occur, and the odds of both are even less. Anyway, the point is that the issue doesn't affect us, so I don't think it makes sense to talk about a solution. Moreover, the implementation burden is not that high for Janus/Ada (it simply means returning "Could_Be_Task" for all class-wide types) and using the same scheme as used for limited returns. So I can't get too excited about it. True, there is a distributed overhead, but since the real issue of such overhead comes from controlled types (which we have to assume are present for class-wide types) and from the possibility of coextentions of any sort (in the absence of a ban), it just isn't that much more. We might have to add some more stuff to the "stub" tasking system (used for programs that don't have any tasks, but do have generics or other things that *might* use a task, and thus have some [meaningless] task operations). As such, I don't see much point in adding a restriction here. But I realize that other implementations may have much more serious issues. Net-net: I can't endorse anything for this issue. **************************************************************** From: Edmond Schonberg Sent: Monday, February 4, 2008 1:20 PM > [Is it time to define "inherently limited" in the RM? I'd like to replace the > first two bullets with, "If the type of the newly-created object is inherently > limited, or has controlled parts, the anonymous object is built in place." > That's not quite the same thing -- it requires b-i-p in more cases than the > current RM.] Yes, this is AI-52, for which I sent an update on Saturday night. Proposed definition: Add after 7.5 (6.1/2): A type is inherently limited if it is one of the following: a) An explicitly limited record b) A tagged limited private type c) A task type, a protected type, or a synchronized interface. d) A type derived from an inherently limited type. **************************************************************** From: Gary Dismukes Sent: Monday, February 4, 2008 1:20 PM It seems that the list should also include types that have a part that is of an inherently limited type. **************************************************************** From: Robert A. Duff Sent: Monday, February 4, 2008 2:48 PM > > A type is inherently limited if it is one of the following: ... Is it intended to be a property of a type, or a property of a view of a type? **************************************************************** From: Edmond Schonberg Sent: Monday, February 4, 2008 2:59 PM Most definitely a property of a type, but one that can be determined by a partial view alone. **************************************************************** From: Edmond Schonberg Sent: Monday, February 4, 2008 3:00 PM > It seems that the list should also include types that have a part > that is of an inherently limited type. You're right, I thought that fell out automatically, but it doesn't, so it has to be added to the list. **************************************************************** From: Tucker Taft Sent: Monday, February 4, 2008 3:20 PM I don't quite understand the requirement to be determinable from the partial view. What about an untagged limited private type? You really don't know anything about the full type. **************************************************************** From: Randy Brukardt Sent: Monday, February 4, 2008 8:51 PM > Is it intended to be a property of a type, or a property of a > view of a type? To be used in Legality Rules, it generally will have to be a property of a view of a type. (Else we would break privacy.) As such, the definition may not be appropriate for dynamic rules. **************************************************************** From: Randy Brukardt Sent: Monday, February 4, 2008 9:01 PM > I don't quite understand the requirement to be > determinable from the partial view. What about > an untagged limited private type? You really > don't know anything about the full type. Correct, and clearly such a view of a type is *not* inherently limited. Anything else would break privacy for Legality Rules (which is what Ed is using the term for). Thus it is clear this is a view-specific term. More generally, the Standard is very confused about view vs. non-view (and indeed we have an AI to fix this, at least in the sense of acknowledging that the Standard is confused). I think that leads to *us* being confused about what is a property of a view or a property of a type. ****************************************************************