3.10 Access Types
{access type}
{access value}
{designate}
A value of an access type (an
access value)
provides indirect access to the object or subprogram it
designates.
Depending on its type, an access value can designate either subprograms,
objects created by allocators (see
4.8), or
more generally
aliased objects of an appropriate type.
{pointer:
See access value} {pointer
type: See access type}
Discussion: A
name
denotes an entity; an access value
designates an entity.
The “dereference” of an access value X, written “X.
all”,
is a
name
that denotes the entity designated by X.
Language Design Principles
Access values should always be well defined
(barring uses of certain unchecked features of Section 13). In particular,
uninitialized access variables should be prevented by compile-time rules.
Syntax
general_access_modifier ::= all |
constant
Static Semantics
{
8652/0012}
{
AI95-00062-01}
{access-to-object type} {access-to-subprogram
type} {pool-specific
access type} {general
access type} There are two kinds of access
types,
access-to-object types, whose values designate objects,
and
access-to-subprogram types, whose values designate subprograms.
{storage pool} Associated
with an access-to-object type is a
storage pool; several access
types may share the same storage pool. All descendants of an access type
share the same storage pool.
{pool element}
A storage pool is an area of storage used to hold
dynamically allocated objects (called
pool elements) created by
allocators[; storage pools are described further in
13.11,
“
Storage Management”].
{pool-specific access
type} {general
access type} Access-to-object types are
further subdivided into
pool-specific access types, whose values
can designate only the elements of their associated storage pool, and
general access types, whose values can designate the elements
of any storage pool, as well as aliased objects created by declarations
rather than allocators, and aliased subcomponents of other objects.
Implementation Note: The value of an
access type will typically be a machine address. However, a value of
a pool-specific access type can be represented as an offset (or index)
relative to its storage pool, since it can point only to the elements
of that pool.
{
AI95-00225-01}
{
AI95-00363-01}
{aliased} A
view of an object is defined to be
aliased if it is defined by
an
object_declaration
or
component_definition
with the reserved word
aliased, or by a renaming of an aliased
view. In addition, the dereference of an access-to-object value denotes
an aliased view, as does a view conversion (see
4.6)
of an aliased view. The current instance of a limited tagged type, a
protected type, a task type, or a type that has the reserved word
limited
in its full definition is also defined to be aliased. Finally, a formal
parameter or generic formal object of a tagged type is defined to be
aliased. [Aliased views are the ones that can be designated by an access
value.]
Glossary entry: {Aliased} An aliased
view of an object is one that can be designated by an access value. Objects
allocated by allocators are aliased. Objects can also be explicitly declared
as aliased with the reserved word aliased. The Access attribute
can be used to create an access value designating an aliased object.
Ramification: The current instance of
a nonlimited type is not aliased.
The object created by an allocator is aliased,
but not its subcomponents, except of course for those that themselves
have
aliased in their
component_definition.
The renaming of an aliased object is aliased.
Slices are never aliased. See
4.1.2
for more discussion.
Reason: {
AI95-00225-01}
The current instance of a limited type is defined to be aliased so that
an access discriminant of a component can be initialized with T'Access
inside the definition of T. Note that we don't want this to apply to
a type that could become nonlimited later within its immediate scope,
so we require the full definition to be limited.
A formal parameter of a tagged type is defined
to be aliased so that a (tagged) parameter X may be passed to an access
parameter P by using P => X'Access. Access parameters are most important
for tagged types because of dispatching-on-access-parameters (see
3.9.2).
By restricting this to formal parameters, we minimize problems associated
with allowing components that are not declared aliased to be pointed-to
from within the same record.
A view conversion
of an aliased view is aliased so that the type of an access parameter
can be changed without first converting to a named access type. For example:
type T1 is tagged ...;
procedure P(X : access T1);
type T2 is new T1 with ...;
procedure P(X : access T2) is
begin
P(T1(X.all)'Access); -- hand off to T1's P
. . . -- now do extra T2-specific processing
end P;
We considered making more kinds of objects aliased
by default. In particular, any object of a by-reference type will pretty
much have to be allocated at an addressable location, so it can be passed
by reference without using bit-field pointers. Therefore, one might wish
to allow the Access and Unchecked_Access attributes for such objects.
However, private parts are transparent to the definition of “by-reference
type”, so if we made all objects of a by-reference type aliased,
we would be violating the privacy of private parts. Instead, we would
have to define a concept of “visibly by-reference” and base
the rule on that. This seemed to complicate the rules more than it was
worth, especially since there is no way to declare an untagged limited
private type to be by-reference, since the full type might by nonlimited.
Discussion: Note that we do not use the
term “aliased” to refer to formal parameters that are referenced
through multiple access paths (see
6.2).
An
access_to_object_definition
defines an access-to-object type and its first subtype;
{designated
subtype (of a named access type)} {designated
type (of a named access type)} the
subtype_indication
defines the
designated subtype of the access type. If a
general_access_modifier
appears, then the access type is a general access type.
{access-to-constant
type} If the modifier is the reserved
word
constant, then the type is an
access-to-constant type[;
a designated object cannot be updated through a value of such a type].
{access-to-variable type}
If the modifier is the reserved word
all,
then the type is an
access-to-variable type[; a designated object
can be both read and updated through a value of such a type]. If no
general_access_modifier
appears in the
access_to_object_definition,
the access type is a pool-specific access-to-variable type.
To be honest: The type of the designated
subtype is called the designated type.
Reason: The modifier all was picked
to suggest that values of a general access type could point into “all”
storage pools, as well as to objects declared aliased, and that “all”
access (both read and update) to the designated object was provided.
We couldn't think of any use for pool-specific access-to-constant types,
so any access type defined with the modifier constant is considered
a general access type, and can point into any storage pool or at other
(appropriate) aliased objects.
Implementation Note: The predefined generic
Unchecked_Deallocation can be instantiated for any named access-to-variable
type. There is no (language-defined) support for deallocating objects
designated by a value of an access-to-constant type. Because of this,
an allocator for an access-to-constant type can allocate out of a storage
pool with no support for deallocation. Frequently, the allocation can
be done at link-time, if the size and initial value are known then.
Discussion: For the purpose of generic
formal type matching, the relevant subclasses of access types are access-to-subprogram
types, access-to-constant types, and (named) access-to-variable types,
with its subclass (named) general access-to-variable types. Pool-specific
access-to-variable types are not a separately matchable subclass of types,
since they don't have any “extra” operations relative to
all (named) access-to-variable types.
{access-to-subprogram
type} An
access_to_subprogram_definition
defines an access-to-subprogram type and its first subtype;
{designated
profile (of an access-to-subprogram type)} the
parameter_profile
or
parameter_and_result_profile
defines the
designated profile of the access type.
{calling
convention (associated with a designated profile)} There
is a
calling convention associated with the designated profile[;
only subprograms with this calling convention can be designated by values
of the access type.] By default, the calling convention is “
protected”
if the reserved word
protected appears, and “Ada”
otherwise. [See
Annex B for how to override this
default.]
Ramification: The calling convention
protected is in italics to emphasize that it cannot be specified
explicitly by the user. This is a consequence of it being a reserved
word.
Implementation Note: {
AI95-00254-01}
For a named access-to-subprogram type, the representation of an access
value might include implementation-defined information needed to support
up-level references — for example, a static link. The accessibility
rules (see
3.10.2) ensure that in a "global-display-based"
implementation model (as opposed to a static-link-based model), a named
access-to-(unprotected)-subprogram value need consist only of the address
of the subprogram. The global display is guaranteed to be properly set
up any time the designated subprogram is called. Even in a static-link-based
model, the only time a static link is definitely required is for an access-to-subprogram
type declared in a scope nested at least two levels deep within subprogram
or task bodies, since values of such a type might designate subprograms
nested a smaller number of levels. For the normal case of a named access-to-subprogram
type declared at the outermost (library) level, a code address by itself
should be sufficient to represent the access value in many implementations.
For access-to-protected-subprogram, the access
values will necessarily include both an address (or other identification)
of the code of the subprogram, as well as the address of the associated
protected object. This could be thought of as a static link, but it will
be needed even for global-display-based implementation models. It corresponds
to the value of the “implicit parameter” that is passed into
every call of a protected operation, to identify the current instance
of the protected type on which they are to operate.
Any Elaboration_Check is performed when a call
is made through an access value, rather than when the access value is
first "created" via a 'Access. For implementation models that
normally put that check at the call-site, an access value will have to
point to a separate entry point that does the check. Alternatively, the
access value could point to a "subprogram descriptor" that
consisted of two words (or perhaps more), the first being the address
of the code, the second being the elaboration bit. Or perhaps more efficiently,
just the address of the code, but using the trick that the descriptor
is initialized to point to a Raise-Program-Error routine initially, and
then set to point to the "real" code when the body is elaborated.
For implementations that share code between
generic instantiations, the extra level of indirection suggested above
to support Elaboration_Checks could also be used to provide a pointer
to the per-instance data area normally required when calling shared code.
The trick would be to put a pointer to the per-instance data area into
the subprogram descriptor, and then make sure that the address of the
subprogram descriptor is loaded into a "known" register whenever
an indirect call is performed. Once inside the shared code, the address
of the per-instance data area can be retrieved out of the subprogram
descriptor, by indexing off the "known" register.
{
AI95-00254-01}
Note that access parameters of an anonymous access-to-subprogram type
are permitted. Such parameters represent full “downward”
closures, meaning that in an implementation that uses a per-task (global)
display, the display will have to be passed as a hidden parameter, and
reconstructed at the point of call.
{
AI95-00230-01}
{
AI95-00231-01}
{null value (of an access type)}
For each access type, there is a null access value
designating no entity at all, which can be obtained by (implicitly) converting
the literal
null to the access type. [The null value of an access
type is the default initial value of the type.] Non-null values of an
access-to-object type are obtained by evaluating an
allocator[,
which returns an access value designating a newly created object (see
3.10.2)], or in the case of a general access-to-object
type, evaluating an
attribute_reference
for the Access or Unchecked_Access attribute of an aliased view of an
object. Non-null values of an access-to-subprogram type are obtained
by evaluating an
attribute_reference
for the Access attribute of a non-intrinsic subprogram..
Reason: {
AI95-00231-01}
An
access_definition
used in a controlling parameter excludes null because it is necessary
to read the tag to dispatch, and null has no tag. We would have preferred
to require
not null to be specified for such parameters, but that
would have been too incompatible with Ada 95 code to require.
{
AI95-00416-01}
Note that we considered imposing a similar implicit null exclusion for
controlling access results, but chose not to do that, because there is
no Ada 95 compatibility issue, and there is no automatic null check inherent
in the use of a controlling access result. If a null check is necessary,
it is because there is a dereference of the result, or because the value
is passed to a parameter whose subtype excludes null. If there is no
dereference of the result, a null return value is perfectly acceptable,
and can be a useful indication of a particular status of the call.
{
8652/0013}
{
AI95-00012-01}
{constrained (subtype) [partial]}
{unconstrained (subtype)
[partial]} [All subtypes of an access-to-subprogram
type are constrained.] The first subtype of a type defined by an
access_definition
or an
access_to_object_definition
is unconstrained if the designated subtype is an unconstrained array
or discriminated subtype; otherwise it is constrained.
Reason: {
AI95-00363-01}
Only
composite_constraints
are permitted for an access type, and only on access-to-composite types.
A constraint on an access-to-scalar or access-to-access type might be
violated due to assignments via other access paths that were not so constrained.
By contrast, if the designated subtype is an array or discriminated type
without defaults, the constraint could not be violated by unconstrained
assignments, since array objects are always constrained, and discriminated
objects are also constrained when the type does not have defaults for
its discriminants. Constraints are not allowed on general access-to-unconstrained
discriminated types if the type has defaults for its discriminants; constraints
on pool-specific access types are usually allowed because allocated objects
are usually constrained by their initial value.
Legality Rules
Reason: {
AI95-00231-01}
This is similar to doubly constraining a composite subtype, which we
also don't allow.
Dynamic Semantics
{
AI95-00231-01}
{compatibility (composite_constraint
with an access subtype) [partial]} A
composite_constraint
is
compatible with an unconstrained access subtype if it is compatible
with the designated subtype. A
null_exclusion
is compatible with any access subtype that does not exclude null.
{satisfies
(for an access value) [partial]} An access
value
satisfies a
composite_constraint
of an access subtype if it equals the null value of its type or if it
designates an object whose value satisfies the constraint. An access
value satisfies an exclusion of the null value if it does not equal the
null value of its type.
{elaboration (access_type_definition)
[partial]} The elaboration of an
access_type_definition
creates the access type and its first subtype. For an access-to-object
type, this elaboration includes the elaboration of the
subtype_indication,
which creates the designated subtype.
80 Access values are called “pointers”
or “references” in some other languages.
81 Each access-to-object type has an associated
storage pool; several access types can share the same pool. An object
can be created in the storage pool of an access type by an
allocator
(see
4.8) for the access type. A storage pool
(roughly) corresponds to what some other languages call a “heap.”
See
13.11 for a discussion of pools.
Examples
Examples of access-to-object
types:
{
AI95-00433-01}
type Peripheral_Ref
is not null access Peripheral; --
see 3.8.1
type Binop_Ptr
is access all Binary_Operation'Class;
--
general access-to-class-wide, see 3.9.1
Example of an access
subtype:
subtype Drum_Ref
is Peripheral_Ref(Drum); --
see 3.8.1
Example of an access-to-subprogram
type:
type Message_Procedure is access procedure (M : in String := "Error!");
procedure Default_Message_Procedure(M : in String);
Give_Message : Message_Procedure := Default_Message_Procedure'Access;
...
procedure Other_Procedure(M : in String);
...
Give_Message := Other_Procedure'Access;
...
Give_Message("File not found."); -- call with parameter (.all is optional)
Give_Message.all; -- call with no parameters
Extensions to Ada 83
Wording Changes from Ada 83
We use the term "storage pool" to
talk about the data area from which allocation takes place. The term
"collection" is no longer used. ("Collection" and
"storage pool" are not the same thing because multiple unrelated
access types can share the same storage pool; see
13.11
for more discussion.)
Inconsistencies With Ada 95
{
AI95-00231-01}
{
inconsistencies with Ada 95}
Access discriminants
and non-controlling access parameters no longer exclude null. A program
which passed
null to such an access discriminant or access parameter
and expected it to raise Constraint_Error may fail when compiled with
Ada 2005. One hopes that there no such programs outside of the ACATS.
(Of course, a program which actually wants to pass
null will work,
which is far more likely.)
{
AI95-00363-01}
Most unconstrained aliased objects with defaulted discriminants are no
longer constrained by their initial values. This means that a program
that raised Constraint_Error from an attempt to change the discriminants
will no longer do so. The change only affects programs that depended
on the raising of Constraint_Error in this case, so the inconsistency
is unlikely to occur outside of the ACATS. This change may however cause
compilers to implement these objects differently, possibly taking additional
memory or time. This is unlikely to be worse than the differences caused
by any major compiler upgrade.
Incompatibilities With Ada 95
{
AI95-00225-01}
{
incompatibilities with Ada 95}
Amendment
Correction: The rule defining when a current instance of a limited
type is considered to be aliased has been tightened to apply only to
types that cannot become nonlimited. A program that attempts to take
'Access of the current instance of a limited type that can become nonlimited
will be illegal in Ada 2005. While original Ada 95 allowed the current
instance of any limited type to be treated as aliased, this was inconsistently
implemented in compilers, and was likely to not work as expected for
types that are ultimately nonlimited.
Extensions to Ada 95
{
AI95-00231-01}
{
extensions to Ada 95}
The
null_exclusion
is new. It can be used in both anonymous and named access type definitions.
It is most useful to declare that parameters cannot be
null, thus
eliminating the need for checks on use.
{
AI95-00231-01}
{
AI95-00254-01}
{
AI95-00404-01}
The kinds of anonymous access types allowed were increased by adding
anonymous access-to-constant and anonymous access-to-subprogram types.
Anonymous access-to-subprogram types used as parameters allow passing
of subprograms at any level.
Wording Changes from Ada 95
{
8652/0012}
{
AI95-00062-01}
Corrigendum: Added accidentally-omitted wording that says that
a derived access type shares its storage pool with its parent type. This
was clearly intended, both because of a note in
3.4,
and because anything else would have been incompatible with Ada 83.
{
8652/0013}
{
AI95-00012-01}
Corrigendum: Fixed typographical errors in the description of
when access types are constrained.
{
AI95-00230-01}
The wording was fixed to allow
allocators
and the literal
null for anonymous access types. The former was
clearly intended by Ada 95; see the Implementation Advice in
13.11.
{
AI95-00363-01}
The rules about aliased objects being constrained by their initial values
now apply only to allocated objects, and thus have been moved to
4.8,
“
Allocators”.