3.10.2 Operations of Access Types
{
AI05-0299-1}
[The attribute Access is used to create access values designating aliased
objects and nonintrinsic subprograms. The “accessibility”
rules prevent dangling references (in the absence of uses of certain
unchecked features — see Clause
13).]
Language Design Principles
It should be possible for an access value to
designate an object declared by an object declaration, or a subcomponent
thereof. In implementation terms, this means pointing at stack-allocated
and statically allocated data structures. However, dangling references
should be prevented, primarily via compile-time rules, so long as features
like Unchecked_Access and Unchecked_Deallocation are not used.
In order to create such access values, we require
that the access type be a general access type, that the designated object
be aliased, and that the accessibility rules be obeyed.
Name Resolution Rules
{
AI95-00235-01}
A is an access-to-object type with designated type
D and
the type of the
prefix
is
D'Class or is covered by
D, or
{
AI95-00235-01}
A is an access-to-subprogram type whose designated profile is
type conformant with that of the prefix.
Discussion: Saying that the expected
type shall be a "single access type" is our "new"
way of saying that the type has to be determinable from context using
only the fact that it is an access type. See
4.2
and
8.6. Specifying the expected profile only
implies type conformance. The more stringent subtype conformance is required
by a Legality Rule. This is the only Resolution Rule that applies to
the
name in
a
prefix of
an
attribute_reference.
In all other cases, the
name
has to be resolved without using context. See
4.1.4.
{
AI95-00235-01}
Saying “single access type” is a bit of a fudge. Both the
context and the
prefix
may provide both multiple types; “single” only means that
a single, specific interpretation must remain after resolution. We say
“single” here to trigger the Legality Rules of
8.6.
The resolution of an access attribute is similar to that of an
assignment_statement.
For example:
type Int_Ptr is access all Integer;
type Char_Ptr is access all Character;
type Float_Ptr is access all Float;
function Zap (Val : Int_Ptr) return Float; -- (1)
function Zap (Val : Float_Ptr) return Float; -- (2)
function Zop return Int_Ptr; -- (3)
function Zop return Char_Ptr; -- (4)
Result : Float := Zap (Zop.all'Access); -- Resolves to Zap (1) and Zop (3).
Static Semantics
{
AI95-00162-01}
[The
accessibility rules, which prevent dangling references, are written in
terms of
accessibility levels, which reflect the run-time nesting
of
masters. As explained in
7.6.1,
a master is the execution of a certain construct, such as a
subprogram_body.
An accessibility level is
deeper than another if it is more deeply
nested at run time. For example, an object declared local to a called
subprogram has a deeper accessibility level than an object declared local
to the calling subprogram. The accessibility rules for access types require
that the accessibility level of an object designated by an access value
be no deeper than that of the access type. This ensures that the object
will live at least as long as the access type, which in turn ensures
that the access value cannot later designate an object that no longer
exists. The Unchecked_Access attribute may be used to circumvent the
accessibility rules.]
Discussion: {
AI05-0005-1}
The Unchecked_Access attribute acts as if the object was declared at
library-level; this applies even when it is used as the value of anonymous
access type. See
13.10.
Subclause
3.10.2,
home of the accessibility rules, is informally known as the “Heart
of Darkness” amongst the maintainers of Ada. Woe unto all who enter
here (well, at least unto anyone that needs to understand any of these
rules).
[A given
accessibility level is said to be
statically deeper than another
if the given level is known at compile time (as defined below) to be
deeper than the other for all possible executions. In most cases, accessibility
is enforced at compile time by Legality Rules. Run-time accessibility
checks are also used, since the Legality Rules do not cover certain cases
involving access parameters and generic packages.]
Each master, and each
entity and view created by it, has an accessibility level:
The accessibility level of a given master is deeper
than that of each dynamically enclosing master, and deeper than that
of each master upon which the task executing the given master directly
depends (see
9.3).
{
AI95-00162-01}
{
AI95-00416-01}
{
AI05-0235-1}
{
AI12-0067-1}
{
AI12-0089-1}
An entity or view defined by a declaration and created as part of its
elaboration has the same accessibility level as the innermost master
of the declaration except in the cases of renaming and derived access
types described below. Other than for an explicitly aliased parameter
of a function or generic function, a formal parameter of a callable
entity has the same accessibility level as the master representing the
invocation of the entity.
Reason: {
AI95-00416-01}
This rule defines the “normal” accessibility of entities.
In the absence of special rules below, we intend for this rule to apply.
Discussion: {
AI95-00416-01}
{
AI12-0005-1}
This rule defines the accessibility of all named access types, as well
as the accessibility level of
all anonymous
access types
in a component_definition other
than those for access parameters and access discriminants. Special
rules exist for the accessibility level of
other such
anonymous types. Components
, stand-alone objects,
and function results whose (anonymous) type is defined by an
access_definition
have accessibility levels corresponding to named access types defined
at the same point.
Ramification: {
AI95-00230-01}
Because accessibility level is determined by where the
access_definition
is elaborated, for a type extension, the anonymous access types of components
(other than access discriminants) inherited from the parent have the
same accessibility as they did in the parent; those in the extension
part have the accessibility determined by the scope where the type extension
is declared. Similarly, the types of the nondiscriminant access components
of a derived untagged type have the same accessibility as they did in
the parent.
To be honest: {
AI05-0235-1}
We use "invocation of" in the parameter case as a master is
formally an execution of something. But we mean this to be interpreted
statically (for instance, as the body of the subprogram) for the purposes
of computing "statically deeper than" (see below).
Ramification: {
AI05-0235-1}
Note that accessibility can differ depending on the view of an object
(for both static and dynamic accessibility). For instance, the accessibility
level of a formal parameter may be different than the accessibility level
of the corresponding actual parameter. This occurs in other cases as
well.
Reason: {
AI05-0235-1}
We define the (dynamic) accessibility of formal parameters in order that
it does not depend on the parameter passing model (by-reference or by-copy)
as that is implementation defined. Otherwise, there would be a portability
issue.
The accessibility level of a view of an object
or subprogram defined by a
renaming_declaration
is the same as that of the renamed view.
{
AI95-00318-02}
{
AI95-00416-01}
{
AI05-0234-1}
{
AI12-0027-1}
The accessibility level of an
aggregate
that is used (in its entirety) to directly initialize part of an object
is that of the object being initialized. In other contexts, the accessibility
level of an
aggregate
is that of the innermost master that evaluates the
aggregate.
Corresponding rules apply to a value conversion (see 4.6).
{
AI05-0234-1}
The accessibility level of the result of a function call is that of the
master of the function call, which is determined by the point
of call as follows:
If the result is used (in its entirety)
to directly initialize part of an object, the master is that of the object
being initialized. In the case where the initialized object is a coextension
(see below) that becomes a coextension of another object, the master
is that of the eventual object to which the coextension will be transferred.
To be honest: {
AI95-00416-01}
The first sentence is talking about a static use of the entire return
object — a slice that happens to be the entire return object doesn't
count. On the other hand, this is intended to allow parentheses and
qualified_expressions.
If the result is of an anonymous
access type and is the operand of an explicit conversion, the master
is that of the target type of the conversion;
If the result is of an anonymous
access type and defines an access discriminant, the master is the same
as that for an object created by an anonymous
allocator
that defines an access discriminant (even if the access result is of
an access-to-subprogram type).
If the call itself defines the result
of a function to which one of the above rules applies, these rules are
applied recursively;
In other cases, the master of the
call is that of the innermost master that evaluates the function call.
Ramification: {
AI95-00318-02}
{
AI95-00416-01}
The “innermost master which evaluated the function call”
does not include the function call itself (which might be a master).
{
AI95-00318-02}
{
AI95-00416-01}
We really mean the innermost master here, which could be a very short
lifetime. Consider a function call used as a parameter of a procedure
call. In this case the innermost master which evaluated the function
call is the procedure call.
Ramification: {
AI05-0234-1}
These rules do not mention whether the result object is built-in-place
(see
7.6). In particular, in the case where
building in place is optional, the choice whether or not to build-in-place
has no effect on masters, lifetimes, or accessibility.
Implementation
Note: {
AI05-0234-1}
There are several cases where the implementation may have to pass in
the accessibility level of the result object on a call, to support later
rules where the accessibility level comes from the master of the call:
when the function result may have a part with
access discriminants;
when the function result type is an anonymous
access type;
when the function result is built-in-place;
when the function has an explicitly aliased
parameter.
In particular, this implies passing a level
parameter when the result type is class-wide, since descendants may add
access discriminants. For most implementations this will mean that functions
with controlling results will also need a level parameter.
{
AI05-0284-1}
In the case of a call to a function whose result type is an anonymous
access type, the accessibility level of the type of the result of the
function call is also determined by the point of call as described above.
{
AI95-00416-01}
Within a return statement, the accessibility level of the return object
is that of the execution of the return statement. If the return statement
completes normally by returning from the function, then prior to leaving
the function, the accessibility level of the return object changes to
be a level determined by the point of call, as does the level of any
coextensions (see below) of the return object.
Reason: We define the accessibility level
of the return object during the return statement to be that of the return
statement itself so that the object may be designated by objects local
to the return statement, but not by objects outside the return statement.
In addition, the intent is that the return object gets finalized if the
return statement ends without actually returning (for example, due to
propagating an exception, or a goto). For a normal return, of course,
no finalization is done before returning.
The accessibility level of a derived access type
is the same as that of its ultimate ancestor.
If the value of the access discriminant
is determined by a
discriminant_association
in a
subtype_indication,
the accessibility level of the object or subprogram designated by the
associated value (or library level if the value is null);
Discussion:
This deals with the following cases, when they occur in the context
of an
allocator
or return statement:
A discriminant of an object with a constrained
nominal subtype, including constrained components, the result of calling
a function with a constrained result subtype, the dereference of an access-to-constrained
subtype, etc.
Ramification: {
AI05-0281-1}
The
subtype_indication
mentioned in this bullet is not necessarily the one given in the
allocator
or return statement that is determining the accessibility level; the
constrained subtype might have been defined in an earlier declaration
(as a named subtype).
{
AI05-0005-1}
If the value for this rule and the next one is derived from an Unchecked_Access
attribute, the accessibility is library-level no matter what the accessibility
level of the object is (see
13.10).
{
AI05-0234-1}
If the value of the access discriminant is determined by a
default_expression
in the declaration of the discriminant, the level of the object or subprogram
designated by the associated value (or library level if null);
Discussion: This covers the case of an
unconstrained subcomponent of a limited type with defaulted access discriminants.
{
AI05-0004-1}
If the value of the access discriminant is determined by a
record_component_association
in an
aggregate,
the accessibility level of the object or subprogram designated by the
associated value (or library level if the value is null);
Discussion: In this bullet, the
aggregate
has to occur in the context of an
allocator
or return statement, while the
subtype_indication
of the previous bullet can occur anywhere (it doesn't have to be directly
given in the
allocator
or return statement).
In other cases, where the value
of the access discriminant is determined by an object with an unconstrained
nominal subtype, the accessibility level of the object.
Discussion: {
AI95-00416-01}
In other words, if you know the value of the discriminant for an
allocator
or return statement from a discriminant constraint or an
aggregate
component association, then that determines the accessibility level;
if you don't know it, then it is based on the object itself.
{
AI95-00416-01}
The accessibility level of the anonymous access type of an access discriminant
in any other context is that of the enclosing object.
{
AI95-00162-01}
{
AI95-00254-01}
{
AI05-0270-1}
The accessibility level of the anonymous access type of an access parameter
specifying an access-to-object type is the same as that of the view designated
by the actual (or library-level if the actual is null).
Ramification: {
AI05-0005-1}
If the value of the actual is derived from an Unchecked_Access attribute,
the accessibility is always library-level (see
13.10).
{
AI95-00254-01}
The accessibility level of the anonymous access type of an access parameter
specifying an access-to-subprogram type is deeper than that of any master;
all such anonymous access types have this same level.
Reason: These
represent “downward closures” and thus require passing of
static links or global display information (along with generic sharing
information if the implementation does sharing) along with the address
of the subprogram. We must prevent conversions of these to types with
“normal” accessibility, as those typically don't include
the extra information needed to make a call.
{
AI05-0148-1}
{
AI05-0240-1}
{
AI12-0070-1}
The accessibility level of the type of a stand-alone object of an anonymous
access-to-object type is the same as the accessibility level of the type
of the access value most recently assigned to the object[; accessibility
checks ensure that this is never deeper than that of the declaration
of the stand-alone object].
{
AI05-0142-4}
{
AI05-0240-1}
{
AI12-0070-1}
The accessibility level of an explicitly aliased (see
6.1)
formal parameter in a function body is determined by the point of call;
it is the same level that the return object ultimately will have.
{
AI95-00416-01}
{
AI05-0051-1}
{
AI05-0253-1}
The accessibility level of an object created by an
allocator
is the same as that of the access type, except for an
allocator
of an anonymous access type (an
anonymous allocator) in
certain
contexts, as follows: For an anonymous allocator that defines the result
of a function with an access result, the accessibility level is determined
as though the
allocator
were in place of the call of the function; in the special case of a call
that is the operand of a type conversion, the level is that of the target
access type of the conversion. For an anonymous allocator defining the
value of an access parameter, the accessibility level is that of the
innermost master of the call. For an anonymous allocator whose type is
that of a stand-alone object of an anonymous access-to-object type, the
accessibility level is that of the declaration of the stand-alone object.
For one defining an access discriminant, the accessibility level is determined
as follows:
{
AI95-00416-01}
{
AI05-0024-1}
{
AI05-0066-1}
In the first case, the allocated object is said to
be a
coextension of the object whose discriminant designates it,
as well as of any object of which the discriminated object is itself
a coextension or subcomponent. If the allocated object is a coextension
of an anonymous object representing the result of an aggregate or function
call that is used (in its entirety) to directly initialize a part of
an object, after the result is assigned, the coextension becomes a coextension
of the object being initialized and is no longer considered a coextension
of the anonymous object. All coextensions of an object [(which have not
thus been transfered by such an initialization)] are finalized when the
object is finalized (see
7.6.1).
Ramification: The rules of access discriminants
are such that when the space for an object with a coextension is reclaimed,
the space for the coextensions can be reclaimed. Hence, there is implementation
advice (see 13.11) that an object and its coextensions all be allocated
from the same storage pool (or stack frame, in the case of a declared
object).
{
AI05-0051-1}
Within a return statement, the accessibility level of the anonymous access
type of an access result is that of the master of the call.
{
AI05-0014-1}
The accessibility level of a view of an object or subprogram designated
by an access value is the same as that of the access type.
Discussion: {
AI05-0005-1}
{
AI05-0014-1}
This rule applies even when no dereference exists, for example when an
access value is passed as an access parameter. This rule ensures that
implementations are not required to include dynamic accessibility values
with all access values.
The accessibility level of a component, protected
subprogram, or entry of (a view of) a composite object is the same as
that of (the view of) the composite object.
One
accessibility level is defined to be
statically deeper than another
in the following cases:
For a master that is statically nested within another
master, the accessibility level of the inner master is statically deeper
than that of the outer master.
To be honest: Strictly speaking, this
should talk about the constructs (such as subprogram_bodies)
being statically nested within one another; the masters are really the
executions of those constructs.
To be honest: If a given accessibility
level is statically deeper than another, then each level defined to be
the same as the given level is statically deeper than each level defined
to be the same as the other level.
{
AI95-00254-01}
The accessibility level of the anonymous access type of an access parameter
specifying an access-to-subprogram type is statically deeper than that
of any master; all such anonymous access types have this same level.
Ramification: This rule means that it
is illegal to convert an access parameter specifying an access to subprogram
to a named access to subprogram type, but it is allowed to pass such
an access parameter to another access parameter (the implicit conversion's
accessibility will succeed).
{
AI95-00254-01}
{
AI05-0082-1}
The statically deeper relationship does not apply to the accessibility
level of the anonymous type of an access parameter specifying an access-to-object
type nor does it apply to a descendant of a generic formal type; that
is, such an accessibility level is not considered to be statically deeper,
nor statically shallower, than any other.
{
AI05-0148-1}
The statically deeper relationship does not apply to the accessibility
level of the type of a stand-alone object of an anonymous access-to-object
type; that is, such an accessibility level is not considered to be statically
deeper, nor statically shallower, than any other.
Ramification: In these cases, we use
dynamic accessibility checks.
{
AI05-0142-4}
{
AI05-0235-1}
{
AI12-0089-1}
{
AI12-0157-1}
Inside a return statement that applies to a function
or
generic function F,
or the return
expression of an expression function F, when determining
whether the accessibility level of an explicitly aliased parameter of
F is statically deeper than the level of the return object of
F, the level of the return object is considered to be the same
as that of the level of the explicitly aliased parameter; for statically
comparing with the level of other entities, an explicitly aliased parameter
of
F is considered to have the accessibility level of the body
of
F.
{
AI05-0051-1}
{
AI05-0234-1}
{
AI05-0235-1}
{
AI12-0089-1}
{
AI12-0157-1}
For determining whether a level is statically deeper than the level of
the anonymous access type of an access result of a function
or generic function F, when within a return statement that
applies to
F or the return expression of
expression function F the function,
the level of the master of the call is presumed to be the same as that
of the level of the master that elaborated the
function body
of F.
To be honest: {
AI05-0235-1}
This rule has no effect if the previous bullet also applies (that is,
the “a level” is of an explicitly aliased parameter).
[For determining whether one level is statically
deeper than another when within a generic package body, the generic package
is presumed to be instantiated at the same level as where it was declared;
run-time checks are needed in the case of more deeply nested instantiations.]
Proof: {
AI05-0082-1}
A generic package does not introduce a new master, so it has the static
level of its declaration; the rest follows from the other “statically
deeper” rules.
For determining whether one level is statically
deeper than another when within the declarative region of a
type_declaration,
the current instance of the type is presumed to be an object created
at a deeper level than that of the type.
Ramification: In other words, the rules
are checked at compile time of the
type_declaration,
in an assume-the-worst manner.
The accessibility
level of all library units is called the
library level; a library-level
declaration or entity is one whose accessibility level is the library
level.
Ramification: Library_unit_declarations
are library level. Nested declarations are library level if they are
nested only within packages (possibly more than one), and not within
subprograms, tasks, etc.
To be honest:
The definition of the accessibility level of the anonymous type of
an access parameter specifying an access-to-object type cheats a bit,
since it refers to the view designated by the actual, but access values
designate objects, not views of objects. What we really mean is the view
that “would be” denoted by an expression “X.all”,
where X is the actual, even though such an expression is a figment of
our imagination. The definition is intended to be equivalent to the following
more verbose version: The accessibility level of the anonymous type of
an access parameter is as follows:
if the actual is an expression of a named
access type — the accessibility level of that type;
if the actual is an
allocator
— the accessibility level of the execution of the called subprogram;
if the actual is a reference to the Access
attribute — the accessibility level of the view denoted by the
prefix;
if the actual is a reference to the Unchecked_Access
attribute — library accessibility level;
if the actual is an access parameter —
the accessibility level of its type.
Note that the
allocator
case is explicitly mentioned in the RM95, because otherwise the definition
would be circular: the level of the anonymous type is that of the view
designated by the actual, which is that of the access type.
Discussion: A deeper accessibility level
implies a shorter maximum lifetime. Hence, when a rule requires X to
have a level that is “not deeper than” Y's level, this requires
that X has a lifetime at least as long as Y. (We say “maximum lifetime”
here, because the accessibility level really represents an upper bound
on the lifetime; an object created by an
allocator
can have its lifetime prematurely ended by an instance of Unchecked_Deallocation.)
Package elaborations are not masters, and are
therefore invisible to the accessibility rules: an object declared immediately
within a package has the same accessibility level as an object declared
immediately within the declarative region containing the package. This
is true even in the body of a package; it jibes with the fact that objects
declared in a
package_body
live as long as objects declared outside the package, even though the
body objects are not visible outside the package.
Note that the level of the
view denoted
by X.
all can be different from the level of the
object
denoted by X.
all. The former is determined by the type of X; the
latter is determined either by the type of the
allocator,
or by the master in which the object was declared. The former is used
in several Legality Rules and run-time checks; the latter is used to
define when X.
all gets finalized. The level of a view reflects
what we can conservatively “know” about the object of that
view; for example, due to
type_conversions,
an access value might designate an object that was allocated by an
allocator
for a different access type.
Similarly, the level of the view denoted by
X.all.Comp can be different from the level of the object denoted
by X.all.Comp.
If Y is statically deeper than X, this implies
that Y will be (dynamically) deeper than X in all possible executions.
Most accessibility
checking is done at compile time; the rules are stated in terms of “statically
deeper than”. The exceptions are:
Checks involving access parameters of an access-to-object
type. The fact that “statically deeper than” is not defined
for the anonymous access type of an access parameter implies that any
rule saying “shall not be statically deeper than” does not
apply to such a type, nor to anything defined to have “the same”
level as such a type.
{
AI05-0082-1}
Checks involving generic formal types and their descendants. This is
because the actual type can be more or less deeply nested than the generic
unit. Note that this only applies to the generic unit itself, and not
to the instance. Any static checks needed in the instance will be performed.
Any other checks (such as those in the generic body) will require a run-time
check of some sort (although implementations that macro-expand generics
can determine the result of the check when the generic is expanded).
{
AI05-0082-1}
Checks involving other entities and views within generic packages. This
is because an instantiation can be at a level that is more deeply nested
than the generic package itself. In implementations that use a macro-expansion
model of generics, these violations can be detected at macro-expansion
time. For implementations that share generics, run-time code is needed
to detect the error.
{
AI05-0005-1}
Note that run-time checks are not required for access discriminants (except
during function returns and
allocators),
because their accessibility is determined statically by the accessibility
level of the enclosing object.
The accessibility level of the result object
of a function reflects the time when that object will be finalized; we
don't allow pointers to the object to survive beyond that time.
We sometimes use the terms “accessible”
and “inaccessible” to mean that something has an accessibility
level that is not deeper, or deeper, respectively, than something else.
Implementation Note: {
AI95-00318-02}
{
AI95-00344-01}
{
AI95-00416-01}
If an accessibility Legality Rule is satisfied, then the corresponding
run-time check (if any) cannot fail (and a reasonable implementation
will not generate any checking code) unless one of the cases requiring
run-time checks mentioned previously is involved.
Accessibility levels are defined in terms of
the relations “the same as” and “deeper than”.
To make the discussion more concrete, we can assign actual numbers to
each level. Here, we assume that library-level accessibility is level
0, and each level defined as “deeper than” is one level deeper.
Thus, a subprogram directly called from the environment task (such as
the main subprogram) would be at level 1, and so on.
Accessibility is not enforced at compile time
for access parameters of an access-to-object type. The “obvious”
implementation of the run-time checks would be inefficient, and would
involve distributed overhead; therefore, an efficient method is given
below. The “obvious” implementation would be to pass the
level of the caller at each subprogram call, task creation, etc. This
level would be incremented by 1 for each dynamically nested master. An
Accessibility_Check would be implemented as a simple comparison —
checking that X is not deeper than Y would involve checking that X <=
Y.
A more efficient method is based on passing
static nesting levels (within constructs that correspond at run
time to masters — packages don't count). Whenever an access parameter
is passed, an implicit extra parameter is passed with it. The extra parameter
represents (in an indirect way) the accessibility level of the anonymous
access type, and, therefore, the level of the view denoted by a dereference
of the access parameter. This is analogous to the implicit “Constrained”
bit associated with certain formal parameters of an unconstrained but
definite composite subtype. In this method, we avoid distributed overhead:
it is not necessary to pass any extra information to subprograms that
have no access parameters. For anything other than an access parameter
and its anonymous type, the static nesting level is known at compile
time, and is defined analogously to the RM95 definition of accessibility
level (e.g. derived access types get their nesting level from their parent).
Checking “not deeper than” is a "<=" test on
the levels.
For each access
parameter of an access-to-object type, the static depth passed depends
on the actual, as follows:
If the actual is an expression of a named
access type, pass the static nesting level of that type.
If the actual is an
allocator,
pass the static nesting level of the caller, plus one.
If the actual is a reference to the Access
attribute, pass the level of the view denoted by the
prefix.
If the actual is a reference to the Unchecked_Access
attribute, pass 0 (the library accessibility level).
If the actual is an access parameter of an
access-to-object type, usually just pass along the level passed in. However,
if the static nesting level of the formal (access) parameter is greater
than the static nesting level of the actual (access) parameter, the level
to be passed is the minimum of the static nesting level of the access
parameter and the actual level passed in.
For the Accessibility_Check associated with
a
type_conversion
of an access parameter of an access-to-object type of a given subprogram
to a named access type, if the target type is statically nested within
the subprogram, do nothing; the check can't fail in this case. Otherwise,
check that the value passed in is <= the static nesting depth of the
target type. The other Accessibility_Checks are handled in a similar
manner.
This method, using statically known values most
of the time, is efficient, and, more importantly, avoids distributed
overhead.
{
AI05-0148-1}
The implementation of accessibility checks for stand-alone objects of
anonymous access-to-object types can be similar to that for anonymous
access-to-object parameters. A static level suffices; it can be calculated
using rules similar to those previously described for access parameters.
{
AI05-0148-1}
One important difference between the stand-alone access variables and
access parameters is that one can assign a local access parameter to
a more global stand-alone access variable. Similarly, one can assign
a more global access parameter to a more local stand-alone access variable.
{
AI05-0148-1}
For these cases, it is important to note that the “correct”
static accessibility level for an access parameter assigned to a stand-alone
access object is the minimum of the passed in level and the static accessibility
level of the stand-alone object itself. This is true since the static
accessibility level passed in might be deeper than that of the stand-alone
object, but the dynamic accessibility of the passed in object clearly
must be shallower than the stand-alone object (whatever is passed in
must live at least as long as the subprogram call). We do not need to
keep a more local static level as accesses to objects statically deeper
than the stand-alone object cannot be stored into the stand-alone object.
Discussion:
Examples of accessibility:
{
AI05-0005-1}
package body Lib_Unit
is
type T
is tagged ...;
type A0
is access all T;
Global: A0 := ...;
procedure P(X:
in out T)
is
Y:
aliased T;
type A1
is access all T;
Ptr0: A0 := Global; --
OK.
Ptr1: A1 := X'Access; --
OK.
begin
Ptr1 := Y'Access; --
OK;
Ptr0 := A0(Ptr1); --
Illegal type conversion!
Ptr0 := X'Access; --
Illegal reference to Access attribute!
Ptr0 := Y'Access; --
Illegal reference to Access attribute!
Global := Ptr0; --
OK.
end P;
end Lib_Unit;
{
AI05-0005-1}
The above illegal statements are illegal because the accessibility levels
of X and Y are statically deeper than the accessibility level of A0.
In every possible execution of any program including this library unit,
if P is called, the accessibility level of X will be (dynamically) deeper
than that of A0. Note that the accessibility levels of X and Y are the
same.
Here's an example
involving access parameters of an access-to-object type:
procedure Main is
type Level_1_Type is access all Integer;
procedure P(X: access Integer) is
type Nested_Type is access all Integer;
begin
... Nested_Type(X) ... -- (1)
... Level_1_Type(X) ... -- (2)
end P;
procedure Q(X: access Integer) is
procedure Nested(X: access Integer) is
begin
P(X);
end Nested;
begin
Nested(X);
end Q;
procedure R is
Level_2: aliased Integer;
begin
Q(Level_2'Access); -- (3)
end R;
Level_1: aliased Integer;
begin
Q(Level_1'Access); -- (4)
R;
end Main;
The run-time Accessibility_Check at (1) can
never fail, and no code should be generated to check it. The check at
(2) will fail when called from (3), but not when called from (4).
Within a
type_declaration,
the rules are checked in an assume-the-worst manner. For example:
{
AI05-0298-1}
package P
is
type Int_Ptr
is access all Integer;
type Rec(D:
access Integer)
is limited private;
private
type Rec_Ptr
is access all Rec;
function F(X: Rec_Ptr)
return Boolean;
function G(X:
access Rec)
return Boolean;
type Rec(D:
access Integer)
is
limited record
C1: Int_Ptr := Int_Ptr(D); --
Illegal!
C2: Rec_Ptr := Rec'Access; --
Illegal!
C3: Boolean := F(Rec'Access); --
Illegal!
C4: Boolean := G(Rec'Access);
end record;
end P;
C1, C2, and C3 are all illegal, because one
might declare an object of type Rec at a more deeply nested place than
the declaration of the type. C4 is legal, but the accessibility level
of the object will be passed to function G, and constraint checks within
G will prevent it from doing any evil deeds.
Note that we cannot defer the checks on C1,
C2, and C3 until compile-time of the object creation, because that would
cause violation of the privacy of private parts. Furthermore, the problems
might occur within a task or protected body, which the compiler can't
see while compiling an object creation.
The following attribute
is defined for a
prefix
X that denotes an aliased view of an object:
X'Access
{
8652/0010}
{
AI95-00127-01}
X'Access yields an access value that designates the object denoted by
X. The type of X'Access is an access-to-object type, as determined by
the expected type. The expected type shall be a general access type.
X shall denote an aliased view of an object[, including
possibly the current instance (see
8.6) of
a limited type within its definition, or a formal parameter or generic
formal object of a tagged type]. The view denoted by the
prefix
X shall satisfy the following additional requirements, presuming the
expected type for X'Access is the general access type
A with designated
type
D:
If A is an access-to-variable type,
then the view shall be a variable; [on the other hand, if A is
an access-to-constant type, the view may be either a constant or a variable.]
Discussion: The current instance of a
limited type is considered a variable.
Discussion: This restriction is intended
to be similar to the restriction on renaming discriminant-dependent subcomponents.
Reason: This prevents references to subcomponents
that might disappear or move or change constraints after creating the
reference.
Implementation
Note: There was some thought to making this restriction more stringent,
roughly: "X shall not denote a subcomponent of a variable with discriminant-dependent
subcomponents, if the nominal subtype of the variable is an unconstrained
definite subtype." This was because in some implementations, it
is not just the discriminant-dependent subcomponents that might move
as the result of an assignment that changed the discriminants of the
enclosing object. However, it was decided not to make this change because
a reasonable implementation strategy was identified to avoid such problems,
as follows:
Place non-discriminant-dependent components
with any aliased parts at offsets preceding any discriminant-dependent
components in a discriminated record type with defaulted discriminants.
Preallocate the maximum space for unconstrained
discriminated variables with aliased subcomponents, rather than allocating
the initial size and moving them to a larger (heap-resident) place if
they grow as the result of an assignment.
Note that for objects of a by-reference type,
it is not an error for a programmer to take advantage of the fact that
such objects are passed by reference. Therefore, the above approach is
also necessary for discriminated record types with components of a by-reference
type.
To make the above strategy work, it is important
that a component of a derived type is defined to be discriminant-dependent
if it is inherited and the parent subtype constraint is defined in terms
of a discriminant of the derived type (see
3.7).
{
8652/0010}
{
AI95-00127-01}
{
AI95-00363-01}
If
A is a named access type and
D is a tagged type, then
the type of the view shall be covered by
D; if
A is anonymous
and
D is tagged, then the type of the view shall be either
D'Class
or a type covered by
D; if
D is untagged, then the type
of the view shall be
D, and either:
{
AI95-00363-01}
the designated subtype of
A shall statically match the nominal
subtype of the view; or
{
AI95-00363-01}
{
AI05-0041-1}
{
AI12-0095-1}
D shall be discriminated in its full view and unconstrained in
any partial view, and the designated subtype of
A shall be unconstrained.
For the purposes of determining within a generic body whether D
is unconstrained in any partial view, a discriminated subtype is considered
to have a constrained partial view if it is a descendant of an untagged
generic formal private or derived type.
Implementation Note: This ensures that
the dope for an aliased array object can always be stored contiguous
with it, but need not be if its nominal subtype is constrained.
{
AI95-00363-01}
This does not require that types have a partial view in order to allow
an access attribute of an unconstrained discriminated object, only that
any partial view that does exist is unconstrained.
Discussion: {
AI12-0095-1}
We assume the worst in a generic body whether or
not a formal subtype has a constrained partial view; specifically, in
a generic body a discriminated subtype is considered to have a constrained
partial view if it is a descendant of an untagged generic formal private
or derived type (see 12.5.1 for the formal
definition of this rule).
{
AI05-0041-1}
The accessibility level of the view shall not be statically deeper than
that of the access type
A.
Ramification: In an instance body, a
run-time check applies.
{
AI95-00230-01}
If
A is an anonymous access-to-object type of an access parameter,
then the view can never have a deeper accessibility level than
A.
The same is true for an anonymous access-to-object type of an access
discriminant, except when X'Access is used to initialize an access discriminant
of an object created by an
allocator.
The latter case is illegal if the accessibility level of X is statically
deeper than that of the access type of the
allocator;
a run-time check is needed in the case where the initial value comes
from an access parameter. Other anonymous access-to-object types have
"normal" accessibility checks.
{
AI05-0041-1}
In addition to the places where Legality Rules normally apply (see
12.3),
these requirements apply also in the private part of an instance of a
generic unit.
A
check is made that the accessibility level of X is not deeper than that
of the access type
A. If this check fails, Program_Error is raised.
Ramification: The check is needed for
access parameters of an access-to-object type and in instance bodies.
{
AI05-0024-1}
Because there are no access parameters permitted for task entries, the
accessibility levels are always comparable. We would have to switch to
the terminology used in
4.8 and
6.5
based on inclusion within masters if we relax this restriction. That
might introduce unacceptable distributed overhead.
Implementation Note: {
AI05-0148-1}
This check requires that some indication of lifetime is passed as an
implicit parameter along with access parameters of an access-to-object
type. A similar indication is required for stand-alone objects of anonymous
access-to-object types.No such requirement applies to other anonymous
access types, since the checks associated with them are all compile-time
checks.
If the nominal subtype
of X does not statically match the designated subtype of
A, a
view conversion of X to the designated subtype is evaluated (which might
raise Constraint_Error — see
4.6) and
the value of X'Access designates that view.
The following attribute is defined for a
prefix
P that denotes a subprogram:
P'Access
{
AI95-00229-01}
{
AI95-00254-01}
{
AI05-0239-1}
P'Access yields an access value that designates the subprogram denoted
by P. The type of P'Access is an access-to-subprogram type (
S),
as determined by the expected type.
The accessibility
level of P shall not be statically deeper than that of
S.
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. The profile of P shall be subtype conformant with the designated
profile of
S, and shall not be Intrinsic.
If
the subprogram denoted by P is declared within a generic unit, and the
expression P'Access occurs within the body of that generic unit or within
the body of a generic unit declared within the declarative region of
the generic unit, then the ultimate ancestor of
S shall be either
a nonformal type declared within the generic unit or an anonymous access
type of an access parameter.
Discussion: {
AI95-00229-01}
The part about generic bodies is worded in terms of the denoted subprogram,
not the denoted view; this implies that renaming is invisible to this
part of the rule. “Declared within the declarative region of the
generic” is referring to child and nested generic units. This rule
is partly to prevent contract model problems with respect to the accessibility
rules, and partly to ease shared-generic-body implementations, in which
a subprogram declared in an instance needs to have a different calling
convention from other subprograms with the same profile.
Overload resolution ensures only that the profile
is type conformant. This rule specifies that subtype conformance is required
(which also requires matching calling conventions). P cannot denote an
entry because access-to-subprogram types never have the entry
calling convention. P cannot denote an enumeration literal or an attribute
function because these have intrinsic calling conventions.
Legality Rules
a view conversion,
qualified_expression,
or parenthesized expression whose operand has distributed accessibility.
{
AI05-0188-1}
The statically deeper relationship does not apply to the accessibility
level of an
expression
having distributed accessibility; that is, such an accessibility level
is not considered to be statically deeper, nor statically shallower,
than any other.
{
AI05-0188-1}
Any static accessibility requirement that is imposed on an
expression
that has distributed accessibility (or on its type) is instead imposed
on the
dependent_expressions
of the underlying
conditional_expression.
This rule is applied recursively if a
dependent_expression
also has distributed accessibility.
Discussion: This means that any Legality
Rule requiring that the accessibility level of an
expression
(or that of the type of an
expression)
shall or shall not be statically deeper than some other level also applies,
in the case where the
expression
has distributed accessibility, to each
dependent_expression
of the underlying
conditional_expression.
91 The Unchecked_Access attribute yields
the same result as the Access attribute for objects, but has fewer restrictions
(see
13.10). There are other predefined operations
that yield access values: an
allocator
can be used to create an object, and return an access value that designates
it (see
4.8); evaluating the literal
null
yields a null access value that designates no entity at all (see
4.2).
92 {
AI95-00230-01}
The predefined operations of an access type also
include the assignment operation, qualification, and membership tests.
Explicit conversion is allowed between general access types with matching
designated subtypes; explicit conversion is allowed between access-to-subprogram
types with subtype conformant profiles (see
4.6).
Named access types have predefined equality operators;
anonymous access types do not, but they can use the predefined equality
operators for
universal_access (see
4.5.2).
Reason: {
AI95-00230-01}
Anonymous access types can use the universal access equality operators
declared in Standard, while named access types cannot for compatibility
reasons. By not having equality operators for anonymous access types,
we eliminate the need to specify exactly where the predefined operators
for anonymous access types would be defined, as well as the need for
an implementer to insert an implicit declaration for "=", etc.
at the appropriate place in their symbol table. Note that ":=",
'Access, and ".
all" are defined.
94 A call through the dereference of an
access-to-subprogram value is never a dispatching call.
95 {
AI95-00254-01}
The Access attribute for subprograms
and parameters of an anonymous access-to-subprogram type may together
be used to implement “downward closures” — that is,
to pass a more-nested subprogram as a parameter to a less-nested subprogram,
as might be appropriate for an iterator abstraction or numerical integration.
Downward closures can also be implemented using generic formal subprograms
(see
12.6). Note that Unchecked_Access is
not allowed for subprograms.
96 Note that using an access-to-class-wide
tagged type with a dispatching operation is a potentially more structured
alternative to using an access-to-subprogram type.
97 An implementation may consider two access-to-subprogram
values to be unequal, even though they designate the same subprogram.
This might be because one points directly to the subprogram, while the
other points to a special prologue that performs an Elaboration_Check
and then jumps to the subprogram. See
4.5.2.
Ramification: If equality of access-to-subprogram
values is important to the logic of a program, a reference to the Access
attribute of a subprogram should be evaluated only once and stored in
a global constant for subsequent use and equality comparison.
Examples
Example of use of
the Access attribute:
Martha : Person_Name :=
new Person(F); --
see 3.10.1
Cars :
array (1..2)
of aliased Car;
...
Martha.Vehicle := Cars(1)'Access;
George.Vehicle := Cars(2)'Access;
Extensions to Ada 83
We no longer make things
like 'Last and ".component" (basic) operations of an access
type that need to be "declared" somewhere. Instead, implicit
dereference in a
prefix
takes care of them all. This means that there should never be a case
when X.
all'Last is legal while X'Last is not. See AI83-00154.
Incompatibilities With Ada 95
{
AI95-00363-01}
Aliased variables are not necessarily constrained
in Ada 2005 (see
3.6). Therefore, a subcomponent
of an aliased variable may disappear or change shape, and taking 'Access
of such a subcomponent thus is illegal, while the same operation would
have been legal in Ada 95. Note that most allocated objects are still
constrained by their initial value (see
4.8),
and thus legality of 'Access didn't change for them. For example:
type T1 (D1 : Boolean := False) is
record
case D1 is
when False =>
C1 : aliased Integer;
when True =>
null;
end case;
end record;
type Acc_Int is access all Integer;
A_T : aliased T1;
Ptr : Acc_Int := A_T.C1'Access; -- Illegal in Ada 2005, legal in Ada 95
A_T := (D1 => True); -- Raised Constraint_Error in Ada 95, but does not
-- in Ada 2005, so Ptr would become invalid when this
-- is assigned (thus Ptr is illegal).
{
AI95-00363-01}
If a discriminated full type has a partial view (private type) that is
constrained, we do not allow 'Access on objects to create a value of
an object of an access-to-unconstrained type. Ada 95 allowed this attribute
and various access subtypes, requiring that the heap object be constrained
and thus making details of the implementation of the private type visible
to the client of the private type. See
4.8
for more on this topic.
{
AI95-00229-01}
{
AI95-00254-01}
Amendment Correction: Taking 'Access of a subprogram declared
in a generic unit in the body of that generic is no longer allowed. Such
references can easily be used to create dangling pointers, as Legality
Rules are not rechecked in instance bodies. At the same time, the rules
were loosened a bit where that is harmless, and also to allow any routine
to be passed to an access parameter of an access-to-subprogram type.
The now illegal uses of 'Access can almost always be moved to the private
part of the generic unit, where they are still legal (and rechecked upon
instantiation for possibly dangling pointers).
Extensions to Ada 95
{
8652/0010}
{
AI95-00127-01}
Corrigendum: Access attributes of objects
of class-wide types can be used as the controlling parameter in a dispatching
calls (see
3.9.2). This was an oversight
in Ada 95.
{
AI95-00235-01}
Amendment Correction: The type of the prefix can now be used in
resolving Access attributes. This allows more uses of the Access attribute
to resolve. For example:
type Int_Ptr is access all Integer;
type Float_Ptr is access all Float;
function Zap (Val : Int_Ptr) return Float;
function Zap (Val : Float_Ptr) return Float;
Value : aliased Integer := 10;
Result1 : Float := Zap (Value'access); -- Ambiguous in Ada 95; resolves in Ada 2005.
Result2 : Float := Zap (Int_Ptr'(Value'access)); -- Resolves in Ada 95 and Ada 2005.
This change is upward compatible; any expression
that does not resolve by the new rules would have failed a Legality Rule.
Wording Changes from Ada 95
{
AI95-00162-01}
Adjusted the wording to reflect the fact that expressions and function
calls are masters.
Incompatibilities With Ada 2005
{
AI05-0008-1}
Correction: Simplified the description of
when a discriminant-dependent component is allowed as the prefix of 'Access
to when the object is known to be constrained. This fixes a confusion
as to whether a subcomponent of an object that is not certain to be constrained
can be used as a prefix of 'Access. The fix introduces an incompatibility,
as the rule did not apply in Ada 95 if the prefix was a constant; but
it now applies no matter what kind of object is involved. The incompatibility
is not too bad, since most kinds of constants are known to be constrained.
{
AI05-0041-1}
Correction: Corrected the checks for the constrainedness of the
prefix of the Access attribute so that assume-the-worst is used in generic
bodies. This may make some programs illegal, but those programs were
at risk having objects disappear while valid access values still pointed
at them.
Extensions to Ada 2005
{
AI05-0082-1}
Correction: Eliminated the static accessibility
definition for generic formal types, as the actual can be more or less
nested than the generic itself. This allows programs that were illegal
for Ada 95 and for Ada 2005.
{
AI05-0148-1}
{
AI05-0253-1}
Eliminate the static accessibility definition for stand-alone objects
of anonymous access-to-object types. This allows such objects to be used
as temporaries without causing accessibility problems.
Wording Changes from Ada 2005
{
AI05-0014-1}
Correction: Corrected the rules so that the accessibility of the
object designated by an access object is that of the access type, even
when no dereference is given. The accessibility was not specified in
the past. This correction applies to both Ada 95 and Ada 2005.
{
AI05-0024-1}
Correction: Corrected accessibility rules for access discriminants
so that no cases are omitted.
{
AI05-0066-1}
Correction: Changed coextension rules so that coextensions that
belong to an anonymous object are transfered to the ultimate object.
{
AI05-0234-1}
Correction: Defined the term “master of the call”
to simplify other wording, especially that for the accessibility checks
associated with return statements and explicitly aliased parameters.
{
AI05-0270-1}
Correction: Defined the (omitted) accessibility level of null
values when those are passed as the actual of an access-to-object parameter.
Incompatibilities With Ada 2012
{
AI12-0027-1}
Corrigendum: Defined
the accessibility of a value conversion, so that it is treated like an
aggregate
built at the point of the conversion. This was previously unspecified,
so this change might be incompatible if an Ada implementation treated
the accessibility of such conversions as that of the operand type; in
that case, previous legal conversions probably will become illegal as
the lifetime of the conversion is very short. However, code that could
tell this difference is fairly rare (taking 'Access of a component of
a result of a type conversion), code depending on this accessibility
was not portable, and such code could have created an immediately dangling
pointer if the conversion actually made a copy (which is necessary in
some instances).
Wording Changes from Ada 2012
{
AI12-0067-1}
Corrigendum: Corrected so that it is clear
that explicitly aliased parameters in procedures have the same accessibility
as other parameters. Only explicitly aliased parameters in functions
are special.
{
AI12-0089-1}
Corrigendum: Corrected a number of rules
so that they clearly apply to generic functions as well as functions.
(Remember, a generic function is not a function.)
{
AI12-0095-1}
Corrigendum: Moved the assume the worst
rule about constrainedness of the prefix of attribute Access to 12.5.1,
as a number of places in the Standard need this rule.
{
AI12-0157-1}
Corrigendum: Ensured that the statically
deeper relationship applies within the return expression of an expression
function. (Dynamic properties apply by equivalence, but static properties
are handled separately.)
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe