Version 1.6 of ai05s/ai05-0052-1.txt
!standard 4.8(5.3/2) 08-02-24 AI05-0052-1/05
!standard 7.5(8/2)
!class binding interpretation 07-05-15
!status ARG Approved 7-0-1 08-02-09
!status work item 07-05-15
!status received 07-05-14
!priority Medium
!difficulty Hard
!qualifier Omission
!subject Coextensions and distributed overhead
!summary
Statically reject an allocator used to create a coextension if
the designated type is limited but the enclosing object might be
of a nonlimited type.
The term "immutably limited type" is introduced to simplify the
presentation of this and other legality rules.
All of the legality rules of 4.8 apply in the private part of
an instance.
!question
First:
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?
Second, in an apparently unrelated topic:
In an instance, 12.3(11) says that the Legality Rules are normally
checked in the visible part of an instance, but not the private part.
However, there are lots of Legality Rules that the RM says are also
checked in the private part.
Should 4.8(5.1) be one of those? It looks like a possible oversight
that it isn't.
package Pak1 is
type Root is tagged null record;
type Root_Acc is access all Root'Class;
generic
type T is new Root with private;
Init : T;
package Gen_Pack is
private
X : Root_Acc := new T' (Init);
end Gen_Pack;
end Pak1;
with Pak1;
procedure Proc1 is
type Child is new Pak1.Root with record ... end record;
Child_Init : constant Child := ...;
package Inst is new Pak1.Gen_Pack (Child, Child_Init);
begin
...
end Proc1;
With the current wording of the Standard, it appears that the instance
would be illegal if X were in the visible part of Gen_Pack; but with X
in the private part, the program is legal but will raise an exception
at runtime. There doesn't seem to be a good reason for this difference.
!recommendation
(See summary.)
!wording
Modify 4.8(5.3/2) as follows:
An allocator shall not be of an access type for which the Storage_Size
has been specified by a static expression with value zero or is defined
by the language to be zero. In addition to the places where Legality Rules
normally apply (see 12.3), this rule applies also in the private part of
an instance of a generic unit. This rule does not apply in the body of
a generic unit or within a body declared within the declarative region
of a generic unit, if the type of the allocator is a descendant of a
formal access type declared within the formal part of the generic unit.
{If the designated type of the type of the allocator is limited, then
the allocator shall not be used to define the value of an access
discriminant, unless the discriminated type is immutably limited (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 type that is always limited.}
{In addition to the places where Legality Rules
normally apply (see 12.3), these rules apply also in the private part
of an instance of a generic unit.}
Add after 7.5 (8/2):
A type is immutably limited if it is one of the following:
* A descendant of an explicitly limited record type;
* A descendant of a non-formal tagged limited private type;
* A descendant of a task type, a protected type, or a synchronized interface;
* A descendant of a formal limited private type, except within the body of a generic unit
or a body declared within the declarative region of a generic unit, if the type is
declared within the formal part of the generic unit;
* A type with a part that is of an immutably limited type.
AARM Discussion: An immutably 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 immutably limited, then no copying (assignment) operations
are ever available for objects of the type. This allows other properties;
for instance, it is safe for such objects to have access discriminants that
have defaults or designate other limited objects.
AARM Ramification: A limited interface is not immutably limited; a type derived
from it can be non-limited.
!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.
For the second question, we note that would need to check the new rule in the
private part of an instance, and 4.8(5.3/2) already has such a rule.
The example in the question makes it clear that 4.8(5.1/2) also should be checked
in the private part. There doesn't seem to be any benefit in postponing detection
of an error from compile-time to run-time.
4.8(5.2/2) should also apply in the private part of an instance, for similar
reasons to 4.8(5.1/2), and also just to avoid confusion of having different
policies apply to different legality rules for a single entity (in this case,
an allocator).
So, we factor out the "applies in the private part of an instance" wording into
a separate pargraph that applies to all of the legality rules of 4.8.
!corrigendum 4.8(5.3/2)
Replace the paragraph:
An allocator shall not be of an access type for which the Storage_Size
has been specified by a static expression with value zero or is defined by
the language to be zero. In addition to the places where Legality Rules
normally apply (see 12.3), this rule applies also in the private part
of an instance of a generic unit. This rule does not apply in the body
of a generic unit or within a body declared within the declarative region
of a generic unit, if the type of the allocator is a descendant of a
formal access type declared within the formal part of the generic unit.
by:
An allocator shall not be of an access type for which the Storage_Size
has been specified by a static expression with value zero or is defined by
the language to be zero. This rule does not apply in the body
of a generic unit or within a body declared within the declarative region
of a generic unit, if the type of the allocator is a descendant of a
formal access type declared within the formal part of the generic unit.
If the designated type of the type of the allocator is limited, then
the allocator shall not be used to define the value of an access
discriminant, unless the discriminated type is immutably limited (see 7.5).
In addition to the places where Legality Rules
normally apply (see 12.3), these rules apply also in the private part
of an instance of a generic unit.
!corrigendum 7.5(8/2)
Insert after the paragraph:
There are no predefined equality operators for a limited type.
the new paragraphs:
A type is immutably limited if it is one of the following:
- A descendant of an explicitly limited record type;
- A descendant of a non-formal tagged limited private type;
- A descendant of a task type, a protected type, or a synchronized interface;
- A descendant of a formal limited private type, except within the body of a
generic unit or a body declared within the declarative region of a generic unit,
if the type is declared within the formal part of the generic unit;
- A type with a part that is of an immutably limited type.
!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.
****************************************************************
!topic Should 4.8(5.1) be checked in the private part of an instance?
!reference RM05 4.8(5.1), 12.3(11)
!from Adam Beneschan 08-01-10
!discussion
In an instance, 12.3(11) says that the Legality Rules are normally
checked in the visible part of an instance, but not the private part.
However, there are lots of Legality Rules that the RM says are also
checked in the private part.
Should 4.8(5.1) be one of those? It looks like a possible oversight
that it isn't.
package Pak1 is
type Root is tagged null record;
type Root_Acc is access all Root'Class;
generic
type T is new Root with private;
Init : T;
package Gen_Pack is
private
X : Root_Acc := new T' (Init);
end Gen_Pack;
end Pak1;
with Pak1;
procedure Proc1 is
type Child is new Pak1.Root with record ... end record;
Child_Init : constant Child := ...;
package Inst is new Pak1.Gen_Pack (Child, Child_Init);
begin
...
end Proc1;
It appears that the instance would be illegal if X were in the visible
part of Gen_Pack; but with X in the private part, the program is legal
but will raise an exception at runtime. There doesn't seem to be a
good reason for this. (GNAT rejects this program, by the way.)
****************************************************************
From: Randy Brukardt
Sent: Thursday, January 10, 2008 9:06 PM
> In an instance, 12.3(11) says that the Legality Rules are normally
> checked in the visible part of an instance, but not the private part.
> However, there are lots of Legality Rules that the RM says are also
> checked in the private part.
I don't think there is much doubt that we got the default wrong on this; it
should always be checked in the private part unless there is a rule that
says it is not. But it is waaayyy too late to make that kind of change.
> Should 4.8(5.1) be one of those? It looks like a possible oversight
> that it isn't.
Most certainly. There are probably only a handful of rules in the entire
standard that it shouldn't apply to. Of course, there are many where it does
not matter either way.
> package Pak1 is
>
> type Root is tagged null record;
> type Root_Acc is access all Root'Class;
>
> generic
> type T is new Root with private;
> Init : T;
> package Gen_Pack is
> private
> X : Root_Acc := new T' (Init);
> end Gen_Pack;
>
> end Pak1;
>
>
> with Pak1;
> procedure Proc1 is
> type Child is new Pak1.Root with record ... end record;
> Child_Init : constant Child := ...;
> package Inst is new Pak1.Gen_Pack (Child, Child_Init);
> begin
> ...
> end Proc1;
>
> It appears that the instance would be illegal if X were in the visible
> part of Gen_Pack; but with X in the private part, the program is legal
> but will raise an exception at runtime. There doesn't seem to be a
> good reason for this. (GNAT rejects this program, by the way.)
I suspect GNAT is just following good sense here.
****************************************************************
From: Pascal Leroy
Sent: Friday, January 11, 2008 1:35 AM
I can't verify this, but I suspect that the IBM compiler also rejects this code.
****************************************************************
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.
****************************************************************
From: Edmond Schonberg
Sent: Monday, February 4, 2008 10:24 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.
Precisely, that is not inherently limited, and thus cannot have an
access discriminant whose designated type is limited.
****************************************************************
Questions? Ask the ACAA Technical Agent