Version 1.7 of ais/ai-00382.txt
!standard 8.6(17) 05-07-20 AI95-00382/05
!standard 9.1(19)
!standard 9.4(21)
!class amendment 04-08-27
!status Amendment 200Y 04-12-01
!status ARG Approved 8-0-2 04-11-19
!status work item 04-08-27
!status received 04-08-27
!priority Medium
!difficulty Medium
!subject Current instance rule and anonymous access types
!summary
(See proposal.)
!problem
AI95-00230 introduces the capability to use anonymous access types in a number
of new contexts, in particular in component_definitions. It appears however
that the current instance rule of 8.6(17) creates illegalities when an
anonymous access type is used as the type of a component. This is demonstrated
by the following example, excerpted from AI95-00230:
type Obj is
record
M : Integer;
Next : access Obj;
end record;
This example is likely to be very typical of user code, as linked structures
are ubiquitous. Therefore, one would very much like it to be legal, as it
avoids the declaration of a named access type. However, the current instance
rule states that Obj in the record type declaration is the name of an object
or value, so the construct "access Obj" is meaningless and therefore illegal.
!proposal
We are changing the current instance rule for type so that the type name
occurring in a access_definition does not denote the current instance of the
type, but rather the type itself.
Note that we are not changing the rules for named access types (the current
instance problem for named access types can only occur in task and protected
bodies).
A few examples of the effect of the new rule:
type Obj is
record
M : Integer;
Next : access Obj; --
Callback1 : access procedure (X : access Obj); --
Callback2 : access procedure (X : Obj); --
end record;
task type T;
task body T is
X : array (1..2) of access T; --
type A is access all T; --
procedure P (X : access T) is ... end P; --
procedure P (X : T) is ... end P; --
begin
...
end T;
!wording
Replace 8.6(17) by:
If a usage name appears within the declarative region of a type_declaration and
denotes that same type_declaration, then it denotes the current instance of the
type (rather than the type itself); the current instance of a type is the object
or value of the type that is associated with the execution that evaluates the
usage name. This rule does not apply if the usage name appears within the
subtype_mark of an access_definition for an access-to-object type, or within
the subtype of a parameter or result of an access-to-subprogram type.
AARM NOTE: The phrase "within the subtype_mark" is intended to cover a case like
"access T'Class" appearing within the declarative region of T: here T denotes
the type, not the current instance.
Replace 9.1(19) by:
2 Other than in an access_definition, the name of a task unit within the
declaration or body of the task unit denotes the current instance of the unit
(see 8.6), rather than the first subtype of the corresponding task type (and
thus the name cannot be used as a subtype_mark).
Replace 9.4(21) by:
13 Within the declaration or body of a protected unit other than in an
access_definition, the name of the protected unit denotes the current instance
of the unit (see 8.6), rather than the first subtype of the corresponding
protected type (and thus the name cannot be used as a subtype_mark).
!discussion
(See proposal.)
!example
Here is an example involving a linked structure with an anonymous access
type:
type Obj is
record
M : Integer;
Next : access Obj;
end record;
!corrigendum 8.6(17)
Replace the paragraph:
If a usage name appears within the declarative region of a type_declaration
and denotes that same type_declaration, then it denotes the current
instance of the type (rather than the type itself). The current instance of a
type is the object or value of the type that is associated with the execution
that evaluates the usage name.
by:
If a usage name appears within the declarative region of a type_declaration
and denotes that same type_declaration, then it denotes the current
instance of the type (rather than the type itself); the current instance of a
type is the object or value of the type that is associated with the execution
that evaluates the usage name. This rule does not apply if the usage name
appears within the subtype_mark of an access_definition for an
access-to-object type, or within the subtype of a parameter or result of an
access-to-subprogram type.
!corrigendum 9.1(19)
Replace the paragraph:
2 Within the declaration or body of a task unit, the name of the
task unit denotes the current instance of
the unit (see 8.6), rather than the first subtype of the corresponding task
type (and thus the name cannot be used as a subtype_mark).
by:
2 Other than in an access_definition, the name of a
task unit within the declaration or body of the task unit denotes the
current instance of the unit (see 8.6), rather than the first subtype of the
corresponding task type (and thus the name cannot be used as a
subtype_mark).
!corrigendum 9.4(21)
Replace the paragraph:
13 Within the declaration or body of a protected unit, the name
of the protected unit denotes the current instance of the unit (see 8.6),
rather than the first subtype of the corresponding protected type (and thus the
name cannot be used as a subtype_mark).
by:
13 Within the declaration or body of a protected unit other than
in an access_definition, the name of the protected unit denotes the
current instance of the unit (see 8.6), rather than the first subtype of the
corresponding protected type (and thus the name cannot be used as a
subtype_mark).
!ACATS test
ACATS tests need to be constructed for this rule.
!appendix
From: Tucker Taft
Sent: Monday, January 2, 2006 9:25 PM
Here is another comment that comes from reviewing
John's book. I think John may have noted the same
thing himself. It seems like the current instance
rule for naming the current instance of a type shouldn't
apply in contexts where a subtype name is required.
We made a special exception for "access T",
but "new T" seems like another case, as does "T'(...)".
One of John's examples bumps into the "new T" case
in a task type that is creating new instances of the
task type on the fly.
****************************************************************
From: Jean-Pierre Rosen
Sent: Tuesday, January 3, 2006 7:19 AM
Doesn't seem so necessary. In most cases, you can get around the current
instance rule by declaring a subtype. This would obviously not be
possible inside the type definition itself, but works everywhere else.
****************************************************************
From: Tucker Taft
Sent: Tuesday, January 3, 2006 7:48 AM
Here is an example where you might want the allocator
inside the type declaration:
type Tree(Is_Leaf : Boolean := True) is record
case Is_Leaf is
when True =>
Val : Integer := 0;
when False =>
Left : not null access Tree := new Tree; -- illegal
Right : not null access Tree := new Tree; -- illegal
end case;
end record;
****************************************************************
From: Randy Brukardt
Sent: Tuesday, January 3, 2006 3:18 PM
That looks pretty dubious to me; there would be no way to recover the
storage for these allocators, so they would be a potential storage leak.
For instance, if you wrote:
Obj : not null access Tree := new Tree(Is_Leaf => False);
there would be no way to recover the Left and Right objects. [Anonymous
access types can't be used to instantiation Unchecked_Deallocation - ED]
(They're not co-extensions, and they could be changed to other values
anyway, so they couldn't be freed with the outer object.)
"not null" components aren't likely to be used a lot because of issues like
this; usually you need a way to represent "no item", and null is perfect for
that. And when you don't need that, they need an existing item to point at
for initialization - meaning that they can't usefully be used in recursive
situations.
****************************************************************
From: Tucker Taft
Sent: Tuesday, January 3, 2006 3:52 PM
I suspect I could create more convincing examples.
I chose to use anonymous non-null access values, but
I could have used named access types, and I might
still want a component in one variant to be default
initialized with an allocator for some other variant.
Not being able to recover the storage is not always
relevant, since there is a class of programs that
builds up a data structure and uses it, but never
reclaims it before the program ends.
In any case, it seems a bit odd to allow the use of
"access T" inside of T but not "new T". I should
reiterate I see this as something to consider for
post Ada 2005.
****************************************************************
From: Pascal Leroy
Sent: Wednesday, January 4, 2006 3:15 AM
Now that we allow subtypes of incomplete types, it seems to me that the
following is legal:
type Tree;
subtype T is Tree;
type Tree(Is_Leaf : Boolean := True) is record
case Is_Leaf is
when True =>
Val : Integer := 0;
when False =>
Left : not null access Tree := new T; -- OK?
Right : not null access Tree := new T; -- OK?
end case;
end record;
If this works, it is actually quite similar to the trick you have to use
for task types.
****************************************************************
From: Pascal Leroy
Sent: Wednesday, January 4, 2006 3:55 AM
Note that if you changed the current instance rule for allocators and
qualified expressions, you would end up with rather cryptic expressions
like:
new T (T.Disc)
T'(T)
where the first T is a subtype name and the second T an object name (the
current instance). Also, the meaning of something like T'Size (subtype
size or current instance size?) would become rather unclear to the reader,
even if well-defined in the RM.
****************************************************************
Questions? Ask the ACAA Technical Agent