Contents Index Search Previous Next
3.10.2 Operations of Access Types
1
[The attribute Access is used to create access
values designating aliased objects and non-intrinsic subprograms. The
``accessibility'' rules prevent dangling references (in the absence of
uses of certain unchecked features -- see Section 13).]
Language Design Principles
1.a
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.
1.b
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
2
{expected type (access attribute_reference)
[partial]} For an
attribute_reference
with
attribute_designator Access
(or Unchecked_Access -- see
13.10), the expected
type shall be a single access type[; the
prefix
of such an
attribute_reference is
never interpreted as an
implicit_dereference].
{expected profile (Access attribute_reference prefix)
[partial]} If the expected type is an access-to-subprogram
type, then the expected profile of the
prefix
is the designated profile of the access type.
2.a
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.
Static Semantics
3
{accessibility level}
{level (accessibility)}
{deeper (accessibility level)}
{depth (accessibility level)}
{dangling references (prevention
via accessibility rules)} {lifetime}
[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
task_body,
a
block_statement, a
subprogram_body,
an
entry_body, or an
accept_statement.
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.]
4
{statically deeper}
{deeper (statically)}
[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.]
5
Each master, and
each entity and view created by it, has an accessibility level:
6
- 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).
7
- An entity or view created by a declaration
has the same accessibility level as the innermost enclosing master, except
in the cases of renaming and derived access types described below. A
parameter of a master has the same accessibility level as the master.
8
- 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.
9
- The accessibility level of a view
conversion is the same as that of the operand.
10
- For a function whose result type is
a return-by-reference type, the accessibility level of the result object
is the same as that of the master that elaborated the function body.
For any other function, the accessibility level of the result object
is that of the execution of the called function.
11
- The accessibility level of a derived
access type is the same as that of its ultimate ancestor.
12
- The accessibility level of the anonymous
access type of an access discriminant is the same as that of the containing
object or associated constrained subtype.
13
- The accessibility level of the anonymous
access type of an access parameter is the same as that of the view designated
by the actual. If the actual is an allocator,
this is the accessibility level of the execution of the called subprogram.
14
- The accessibility level of an object
created by an allocator is the same
as that of the access type.
15
- The accessibility level of a view
of an object or subprogram denoted by a dereference of an access value
is the same as that of the access type.
16
- 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.
17
{statically
deeper} {deeper (statically)}
One accessibility level is defined to be
statically
deeper than another in the following cases:
18
- 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.
18.a
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.
18.b
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.
19
- The statically deeper relationship
does not apply to the accessibility level of the anonymous type of an
access parameter; that is, such an accessibility level is not considered
to be statically deeper, nor statically shallower, than any other.
20
- 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.
21
- 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.
21.a
Ramification: In other
words, the rules are checked at compile time of the type_declaration,
in an assume-the-worst manner.
22
{library level}
{level (library)} 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.
22.a
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.
22.b
To
be honest: The definition of the accessibility level of the anonymous
type of an access parameter 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:
22.c
- if the actual
is an expression of a named access type -- the accessibility level of
that type;
22.d
- if the actual
is an allocator -- the accessibility
level of the execution of the called subprogram;
22.e/1
- if the actual
is a reference to the Access attribute -- the accessibility level of
the view denoted by the prefix
prefix;
22.f
- if the actual
is a reference to the Unchecked_Access attribute -- library accessibility
level;
22.g
- if the actual
is an access parameter -- the accessibility level of its type.
22.h
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.
22.i
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.)
22.j
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.
22.k
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.
22.l
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.
22.m
If Y is statically deeper than
X, this implies that Y will be (dynamically) deeper than X in all possible
executions.
22.n
Most
accessibility checking is done at compile time; the rules are stated
in terms of ``statically deeper than''. The exceptions are:
22.o
- Checks involving
access parameters. 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.
22.p
- Checks involving
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.
22.q
- Checks during
function return.
22.r
Note that run-time checks are
not required for access discriminants, because their accessibility is
determined statically by the accessibility level of the enclosing object.
22.s
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.
22.t
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.
22.u
Implementation Note: 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 access parameters or shared generic
bodies are involved.
22.v
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.
22.w
Accessibility is not enforced
at compile time for access parameters. 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.
22.x
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.
22.y
For
each access parameter, the static depth passed depends on the actual,
as follows:
22.z
- If the actual
is an expression of a named access type, pass the static nesting level
of that type.
22.aa
- If the actual
is an allocator, pass the static
nesting level of the caller, plus one.
22.bb/1
- If the actual
is a reference to the Access attribute, pass the level of the view denoted
by the prefix
prefix.
22.cc
- If the actual
is a reference to the Unchecked_Access attribute, pass 0 (the library
accessibility level).
22.dd
- If the actual
is an access parameter, 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.
22.ee
For the Accessibility_Check
associated with a type_conversion
of an access parameter 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.
22.ff
This method, using statically
known values most of the time, is efficient, and, more importantly, avoids
distributed overhead.
22.gg
Discussion:
Examples of accessibility:
22.hh
package body Lib_Unit is
type T is tagged ...;
type A0 is access all T;
Global: A0 := ...;
procedure P(X: 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;
22.ii
The above illegal statements
are illegal because the accessibility level 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.
22.jj
Here's
an example involving access parameters:
22.kk
procedure Main is
type Level_1_Type is access all Integer;
22.ll
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;
22.mm
procedure Q(X: access Integer) is
procedure Nested(X: access Integer) is
begin
P(X);
end Nested;
begin
Nested(X);
end Q;
22.nn
procedure R is
Level_2: aliased Integer;
begin
Q(Level_2'Access); -- (3)
end R;
22.oo
Level_1: aliased Integer;
begin
Q(Level_1'Access); -- (4)
R;
end Main;
22.pp
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).
22.qq
Within
a type_declaration, the rules are
checked in an assume-the-worst manner. For example:
22.rr
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
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;
22.ss
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.
22.tt
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.
23
The following attribute
is defined for a prefix X that denotes
an aliased view of an object:
24/1
- X'Access
-
{8652/0010}
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.
{Unchecked_Access attribute: See also Access attribute}
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 :
25
- 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.]
25.a
Discussion: The current
instance of a limited type is considered a variable.
26
- The view shall not be a subcomponent
that depends on discriminants of a variable whose nominal subtype is
unconstrained, unless this subtype is indefinite, or the variable is
aliased.
26.a
Discussion: This restriction
is intended to be similar to the restriction on renaming discriminant-dependent
subcomponents.
26.b
Reason: This prevents
references to subcomponents that might disappear or move or change constraints
after creating the reference.
26.c
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:
26.d
- Place non-discriminant-dependent
components with any aliased parts at offsets preceding any discriminant-dependent
components in a discriminated record type with defaulted discriminants.
26.e
- 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.
26.f
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.
26.g
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).
27/1
- {8652/0010}
If A is a named access type and D is a tagged type
the
designated type of A is tagged, then the type of the view
shall be covered by D the designated type; 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 A's designated type is not tagged, then
the type of the view shall be D the same, and either
A's designated subtype shall either statically match
the nominal subtype of the view or be , or the designated subtype
shall be discriminated and unconstrained; {statically
matching (required) [partial]}
27.a
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.
27.a.1/1
Ramification: {8652/0010}
An access attribute can be used as the controlling operand in a dispatching
call; see 3.9.2.
28
- The accessibility level of
the view shall not be statically deeper than that of the access type
A. 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. {accessibility
rule (Access attribute) [partial]} {generic
contract issue [partial]}
28.a
Ramification: In an instance
body, a run-time check applies.
28.b
If A is an anonymous
access type, then the view can never have a deeper accessibility level
than A, 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.
29
- {Accessibility_Check
[partial]} {check, language-defined
(Accessibility_Check)} {Program_Error
(raised by failure of run-time check)} 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.
29.a
Ramification: The check
is needed for access parameters and in instance bodies.
29.b
Implementation Note: This
check requires that some indication of lifetime is passed as an implicit
parameter along with access parameters. No such requirement applies to
access discriminants, since the checks associated with them are all compile-time
checks.
30
- {implicit
subtype conversion (Access attribute) [partial]} 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.
31
The following attribute is defined for a prefix
P that denotes a subprogram:
32
- P'Access
-
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. {accessibility rule (Access attribute) [partial]}
The accessibility level of P shall not be statically
deeper than that of S. {generic contract issue
[partial]} 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. {subtype
conformance (required)} If the subprogram
denoted by P is declared within a generic body, S shall be declared
within the generic body.
32.a
Discussion: 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. 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.
32.b
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.
33
81 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).
34
82 {predefined
operations (of an access type) [partial]} 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). {subtype
conformance [partial]} Named access types
have predefined equality operators; anonymous access types do not (see
4.5.2).
34.a
Reason: 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, and ":=" is defined though useless since all instances
are constant. The literal null is also defined for the purposes
of overload resolution, but is disallowed by a Legality Rule of this
subclause.
35
83 The object or subprogram
designated by an access value can be named with a dereference, either
an explicit_dereference or an implicit_dereference.
See 4.1.
36
84 A call through the dereference
of an access-to-subprogram value is never a dispatching call.
36.a
37
85 {downward
closure} {closure (downward)}
The accessibility rules imply that it is not possible
to use the Access attribute to implement ``downward closures'' -- that
is, to pass a more-nested subprogram as a parameter to a less-nested
subprogram, as might be desired for example for an iterator abstraction.
Instead, downward closures can be implemented using generic formal subprograms
(see 12.6). Note that Unchecked_Access is
not allowed for subprograms.
38
86 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.
39
87 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.
39.a
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
40
Example of use
of the Access attribute:
41
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
41.a
{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.
Contents Index Search Previous Next Legal