3.10 Access Types
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.
an entity; an access value designates
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.
general_access_modifier ::= all
are two kinds of access types, access-to-object
types, whose values
designate objects, and access-to-subprogram
types, whose values
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
) created by allocators[; storage pools are described
further in 13.11
types are further subdivided into pool-specific
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.
A view of an object is defined to be aliased
if it is defined by an object_declaration,
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 Finally,
current instance of an immutably limited
type (see 7.5) is 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,
as is the return object of an extended_return_statement
(see 6.5) that is of an immutably limited type.
a formal parameter or
generic formal object of a tagged type is are
defined to be aliased. [Aliased views are the ones that can be designated
by an access value.] If
the view defined by an object_declaration
is aliased, and the type of the object has discriminants, then the object
is constrained; if its nominal subtype is unconstrained, then the object
is constrained by its initial value. [Similarly, if the object created
by an allocator
has discriminants, the object is constrained, either by the designated
subtype, or by its initial value.]
Glossary entry: 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
in their component_definition
The renaming of an aliased object is aliased.
Slices are never aliased. See 4.1.2
for more discussion.
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
P(T1(X.all)'Access); -- hand off to T1's P
. . . -- now do extra T2-specific processing
The rule about objects with discriminants is necessary
because values of a constrained access subtype can designate an object
whose nominal subtype is unconstrained; without this rule, a check on
every use of such values would be required to ensure that the discriminants
of the object had not changed. With this rule (among others), we ensure
that if there might exist aliased views of a discriminated object, then
the object is necessarily constrained. Note that this rule is necessary
only for untagged types, since a discriminant of a tagged type can't
have a default, so all tagged discriminated objects are always constrained
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 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.
Note that we do not use the
term “aliased” to refer to formal parameters that are referenced
through multiple access paths (see 6.2
defines an access-to-object type and its first subtype;
defines the designated subtype
of the access type. If a general_access_modifier
appears, then the access type is a general access type.
the modifier is the reserved word constant
, then the type is an
[; 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
[; 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.
defines an access-to-subprogram type and its first subtype;
defines the designated profile
of the access type.
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
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
For a named an
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
) ensure that in a "global-display-based"
implementation model (as opposed to a static-link-based model), a
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
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.
Essentially the same implementation issues arise
for calls on dispatching operations of tagged types, except that the
static link is always known "statically."
Note that access parameters of an anonymous access-to-subprogram type
permitted. Such If
there were such
full “downward” closureswould be required
meaning that in an implementation that uses a per-task (global) display,
the display will would
have to be passed as a hidden parameter, and reconstructed at the point
of call. This was felt to be an undue implementation
burden, given that an equivalent (actually, more general) capability
is available via formal subprogram parameters to a generic.
For each (named)
type, there is a literal null which has
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 a
access type is the default initial value of the type.] Non-null Other
values of an access-to-object
type are obtained
by evaluating an attribute_reference
for the Access or Unchecked_Access attribute of an aliased view of an
object or non-intrinsic subprogram, or, in the case of a named access-to-object
which returns an access value designating a newly created object (see
)], 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.
A value of an anonymous access type (that is, the
value of an access parameter or access discriminant) cannot be null.
Access parameters allow dispatching on the tag
of the object designated by the actual parameter (which gets converted
to the anonymous access type as part of the call). In order for dispatching
to work properly, there had better be such an object. Hence, the type
conversion will raise Constraint_Error if the value of the actual parameter
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.
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.
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 aliased
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.(by fiat,
see Static Semantics).
This is similar to doubly constraining a composite
subtype, which we also don't allow.
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
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.
The elaboration of an access_definition
creates an anonymous general
type [(this happens as part of the initialization
of an access parameter or access discriminant)]
84 Access values are called “pointers”
or “references” in some other languages.
85 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
) for the access type. A storage pool
(roughly) corresponds to what some other languages call a “heap.”
for a discussion of pools.
Examples of access-to-object
Peripheral_Ref is not null access
Peripheral; -- see 3.8.1
Binop_Ptr is access all
-- general access-to-class-wide, see 3.9.1
Example of an
Peripheral_Ref(Drum); -- see 3.8.1
Example of an
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
only used for finalization no
. ("Collection" and "storage pool"
are not the same thing because multiple unrelated access types can share
the same storage pool; see 13.11
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
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
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
Extensions to Ada 95
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.
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
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.
Corrigendum: Fixed typographical errors
in the description of when access types are constrained.
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.
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
Correction: The rule about a current instance
being aliased now is worded in terms of immutably limited types. Return
objects of such types are also considered aliased.
Explicitly aliased parameters (see 6.1)
are defined to be aliased.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe