Version 1.1 of ai05s/ai05-0052-1.txt
!standard 3.10.2(10.1/2) 07-05-15 AI05-0052-1/01
!class binding interpretation 07-05-15
!status work item 07-05-15
!status received 07-05-14
!subject Coextensions and distributed overhead
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
Was this intended?
Not clear. Ban non-limited coextensions? See discussion for ideas.
Consider the following example:
package Pkg is
type T1 is tagged null record;
function F return T1'Class;
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
return T2'(Task_Ref => new Tsk);
procedure Pkg_Client is
X : Pkg.T1'Class := Pkg.F;
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. This could be accomplished through some combination of
static and dynamic checking. 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 might
lead to the conclusion that an extension of a nonlimited type should not
have an access discriminant which designates a limited type (which can
be checked statically), or at least that such an access discriminant should
not designate a coextension (which cannot). 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.
A more liberal 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. Unfortunately, this approach
probably only makes sense if it can be checked statically and it appears
that it cannot.
The simplest solution might 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
Still, a static check which is somewhat conservative might be preferable to a
more precise dynamic check.
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.
Questions? Ask the ACAA Technical Agent