Contents   Index   Search   Previous   Next

3.10.2 Operations of Access Types

   [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

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

   {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.
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

   {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.]
   {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.]
   Each master, and each entity and view created by it, has an accessibility level:
    {statically deeper} {deeper (statically)} One accessibility level is defined to be statically deeper than another in the following cases:
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.
Ramification: In other words, the rules are checked at compile time of the type_declaration, in an assume-the-worst manner.
    {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.
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 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:
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:
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.
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: 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.
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. 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, the static depth passed depends on the actual, as follows:
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.
This method, using statically known values most of the time, is efficient, and, more importantly, avoids distributed overhead.
Discussion: Examples of accessibility:
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.
        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;
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.
Here's an example involving access parameters:
procedure Main is
    type Level_1_Type is access all Integer;
    procedure P(X: access Integer) is
        type Nested_Type is access all Integer;
        ... Nested_Type(X) ... -- (1)
        ... Level_1_Type(X) ... -- (2)
    end P;
    procedure Q(X: access Integer) is
        procedure Nested(X: access Integer) is
        end Nested;
    end Q;
    procedure R is
        Level_2: aliased Integer;
        Q(Level_2'Access); -- (3)
    end R;
    Level_1: aliased Integer;
    Q(Level_1'Access); -- (4)
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:
package P is
    type Int_Ptr is access all Integer;
    type Rec(D: access Integer) is limited 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
            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:
{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 :
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:
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).
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.
Ramification: {8652/0010} An access attribute can be used as the controlling operand in a dispatching call; see 3.9.2.
Ramification: In an instance body, a run-time check applies.
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.
{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.
Ramification: The check is needed for access parameters and in instance bodies.
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.
{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.
    The following attribute is defined for a prefix P that denotes a subprogram:
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.
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.
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.
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).
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).
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.
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.
84  A call through the dereference of an access-to-subprogram value is never a dispatching call.
Proof: See 3.9.2.
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.
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.
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.
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.


    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

{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