3.10 Access Types
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.
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
{
AI05-0299-1}
{
AI12-0449-1}
Access values should always be well defined (barring uses of certain
unchecked features of Clause
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}
There
are two kinds of access types,
access-to-object types, whose values
designate objects, and
access-to-subprogram types, whose values
designate subprograms.
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.
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”].
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.
Term entry: aliased view —
view of an object that can be designated by an access value
Note: 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;
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.
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].
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.
An
access_to_subprogram_definition
defines an access-to-subprogram type and its first subtype;
the
parameter_profile
or
parameter_and_result_profile
defines the
designated profile of the access type.
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}
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.] Nonnull 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. Nonnull values of an access-to-subprogram type are obtained by
evaluating an
attribute_reference
for the Access attribute of a nonintrinsic 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.
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}
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.
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.
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.
NOTE 1 Access values are called “pointers”
or “references” in some other languages.
NOTE 2 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}
{
AI12-0056-1}
type Frame
is access Matrix; --
see 3.6
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
{
AI05-0190-1}
We use the term "storage pool" to talk about the data area
from which allocation takes place. The term "collection" is
only used for finalization. ("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}
Access discriminants and noncontrolling 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}
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.
{
AI12-0289-1}
Correction: Static matching (see
4.9.1)
requires that both anonymous access types exclude null; and full conformance
requires statically matching subtypes. Because of the definition of “excludes
null” given in this subclause, an access parameter that designates
an untagged private type P (which does not exclude null) does not match
its completion if P is completed with a tagged type (in that case, the
parameter is controlling and thus excludes null, regardless of whether
there is an explicit null exclusion on the body). Similar considerations
apply in contexts where mode conformance and subtype conformance are
required (for instance, subprogram renaming).
Extensions to Ada 95
{
AI95-00231-01}
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”.
Wording Changes from Ada 2005
{
AI05-0053-1}
{
AI05-0277-1}
Correction: The rule about a current instance being aliased now
is worded in terms of immutably limited types. Wording was also added
to make extended return object declarations that have the keyword
aliased
be considered aliased. This latter was a significant oversight in Ada
2005 — technically, the keyword
aliased had no effect. But
of course implementations followed the intent, not the letter of the
Reference Manual.
{
AI05-0142-4}
Explicitly aliased parameters (see
6.1) are
defined to be aliased.
Extensions to Ada 2012
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe