[Home] [Prev] [Next] [Index]

3.10 Access Types

3.10 Access Types

1
A value of an access type (an access value) provides indirect access to the object or subprogram it designates.  Depending on its type, an access value can designate either subprograms, objects created by allocators (see 4.8), or more generally aliased objects of an appropriate type.

1.a
Discussion:  A name denotes an entity; an access value designates an entity.  The "dereference" of an access value X, written "X.all", is a name that denotes the entity designated by X.

Language Design Principles

1.b
Access values should always be well defined (barring uses of certain unchecked features of Section 13). In particular, uninitialized access variables should be prevented by compile-time rules.

Syntax

2
access_type_definition ::=
    access_to_object_definition
  | access_to_subprogram_definition

3
access_to_object_definition ::=
    access [general_access_modifier] subtype_indication

4
general_access_modifier ::= all | constant

5
access_to_subprogram_definition ::=
    access [protected] procedure parameter_profile
  | access [protected] function  parameter_and_result_profile

6
access_definition ::= access subtype_mark

Static Semantics

7
There are two kinds of access types, access-to-object types, whose values designate objects, and access-to-subprogram types, whose values designate subprograms. Associated with an access-to-object type is a storage pool; several access types may share the same storage pool. A storage pool is an area of storage used to hold dynamically allocated objects (called pool elements) created by allocators[; storage pools are described further in 13.11, "Storage Management"].

8
Access-to-object types are further subdivided into pool-specific access types, whose values can designate only the elements of their associated storage pool, and general access types, whose values can designate the elements of any storage pool, as well as aliased objects created by declarations rather than allocators, and aliased subcomponents of other objects.

8.a
Implementation Note: The value of an access type will typically be a machine address. However, a value of a pool-specific access type can be represented as an offset (or index) relative to its storage pool, since it can point only to the elements of that pool.

9
A view of an object is defined to be aliased if it is defined by an object_declaration or component_definition with the reserved word aliased, or by a renaming of an aliased view. In addition, the dereference of an access-to-object value denotes an aliased view, as does a view conversion (see 4.6) of an aliased view.  Finally, the current instance of a limited type, and a formal parameter or generic formal object of a tagged type are defined to be aliased. [Aliased views are the ones that can be designated by an access value.] If the view defined by an object_declaration is aliased, and the type of the object has discriminants, then the object is constrained; if its nominal subtype is unconstrained, then the object is constrained by its initial value. [Similarly, if the object created by an allocator has discriminants, the object is constrained, either by the designated subtype, or by its initial value.]

9.a
Glossary entry: An aliased view of an object is one that can be designated by an access value. Objects allocated by allocators are aliased. Objects can also be explicitly declared as aliased with the reserved word aliased. The Access attribute can be used to create an access value designating an aliased object.

9.b
Ramification: The current instance of a nonlimited type is not aliased.

9.c
The object created by an allocator is aliased, but not its subcomponents, except of course for those that themselves have aliased in their component_definition.

9.d
The renaming of an aliased object is aliased.

9.e
Slices are never aliased. See 4.1.2 for more discussion.

9.f
Reason: The current instance of a limited type is defined to be aliased so that an access discriminant of a component can be initialized with T'Access inside the definition of T.

9.g
A formal parameter of a tagged type is defined to be aliased so that a (tagged) parameter X may be passed to an access parameter P by using P => X'Access.  Access parameters are most important for tagged types because of dispatching-on-access-parameters (see 3.9.2). By restricting this to formal parameters, we minimize problems associated with allowing components that are not declared aliased to be pointed-to from within the same record.

9.h
A view conversion of an aliased view is aliased so that the type of an access parameter can be changed without first converting to a named access type.  For example:

9.i
type T1 is tagged ...;
procedure P(X : access T1);

9.j
type T2 is new T1 with ...;
procedure P(X : access T2) is
begin
    P(T1(X.all)'Access);  -- hand off to T1's P
    . . .      -- now do extra T2-specific processing
end P;

9.k
The rule about objects with discriminants is necessary because values of a constrained access subtype can designate an object whose nominal subtype is unconstrained; without this rule, a check on every use of such values would be required to ensure that the discriminants of the object had not changed. With this rule (among others), we ensure that if there might exist aliased views of a discriminated object, then the object is necessarily constrained. Note that this rule is necessary only for untagged types, since a discriminant of a tagged type can't have a default, so all tagged discriminated objects are always constrained anyway.

9.l
We considered making more kinds of objects aliased by default. In particular, any object of a by-reference type will pretty much have to be allocated at an addressable location, so it can be passed by reference without using bit-field pointers.  Therefore, one might wish to allow the Access and and Unchecked_Access attributes for such objects. However, private parts are transparent to the definition of "by-reference type", so if we made all objects of a by-reference type aliased, we would be violating the privacy of private parts. Instead, we would have to define a concept of "visibly by-reference" and base the rule on that. This seemed to complicate the rules more than it was worth, especially since there is no way to declare an untagged limited private type to be by-reference, since the full type might by nonlimited.

9.m
Discussion:  Note that we do not use the term "aliased" to refer to formal parameters that are referenced through multiple access paths (see 6.2).

10
An access_to_object_definition defines an access-to-object type and its first subtype; the subtype_indication defines the designated subtype of the access type. If a general_access_modifier appears, then the access type is a general access type. If the modifier is the reserved word constant, then the type is an access-to-constant type[; a designated object cannot be updated through a value of such a type]. If the modifier is the reserved word all, then the type is an access-to-variable type[; a designated object can be both read and updated through a value of such a type]. If no general_access_modifier appears in the access_to_object_definition, the access type is a pool-specific access-to-variable type.

10.a
To be honest: The type of the designated subtype is called the designated type.

10.b
Reason: The modifier all was picked to suggest that values of a general access type could point into "all" storage pools, as well as to objects declared aliased, and that "all" access (both read and update) to the designated object was provided. We couldn't think of any use for pool-specific access-to-constant types, so any access type defined with the modifier constant is considered a general access type, and can point into any storage pool or at other (appropriate) aliased objects.

10.c
Implementation Note: The predefined generic Unchecked_Deallocation can be instantiated for any named access-to-variable type.  There is no (language-defined) support for deallocating objects designated by a value of an access-to-constant type.  Because of this, an allocator for an access-to-constant type can allocate out of a storage pool with no support for deallocation. Frequently, the allocation can be done at link-time, if the size and initial value are known then.

10.d
Discussion:  For the purpose of generic formal type matching, the relevant subclasses of access types are access-to-subprogram types, access-to-constant types, and (named) access-to-variable types, with its subclass (named) general access-to-variable types. Pool-specific access-to-variable types are not a separately matchable subclass of types, since they don't have any "extra" operations relative to all (named) access-to-variable types.

11
An access_to_subprogram_definition defines an access-to-subprogram type and its first subtype; the parameter_profile or parameter_and_result_profile defines the designated profile of the access type. There is a calling convention associated with the designated profile[; only subprograms with this calling convention can be designated by values of the access type.] By default, the calling convention is "protected" if the reserved word protected appears, and "Ada" otherwise. [See for how to override this default.]

11.a
Ramification: The calling convention protected is in italics to emphasize that it cannot be specified explicitly by the user.  This is a consequence of it being a reserved word.

11.b
Implementation Note: For an access-to-subprogram type, the representation of an access value might include implementation-defined information needed to support up-level references - for example, a static link. The accessibility rules (see 3.10.2) ensure that in a "global-display-based" implementation model (as opposed to a static-link-based model), an access-to-(unprotected)-subprogram value need consist only of the address of the subprogram.  The global display is guaranteed to be properly set up any time the designated subprogram is called. Even in a static-link-based model, the only time a static link is definitely required is for an access-to-subprogram type declared in a scope nested at least two levels deep within subprogram or task bodies, since values of such a type might designate subprograms nested a smaller number of levels.  For the normal case of an access-to-subprogram type declared at the outermost (library) level, a code address by itself should be sufficient to represent the access value in many implementations.

11.c
For access-to-protected-subprogram, the access values will necessarily include both an address (or other identification) of the code of the subprogram, as well as the address of the associated protected object. This could be thought of as a static link, but it will be needed even for global-display-based implementation models.  It corresponds to the value of the "implicit parameter" that is passed into every call of a protected operation, to identify the current instance of the protected type on which they are to operate.

11.d
Any Elaboration_Check is performed when a call is made through an access value, rather than when the access value is first "created" via a 'Access. For implementation models that normally put that check at the call-site, an access value will have to point to a separate entry point that does the check.  Alternatively, the access value could point to a "subprogram descriptor" that consisted of two words (or perhaps more), the first being the address of the code, the second being the elaboration bit. Or perhaps more efficiently, just the address of the code, but using the trick that the descriptor is initialized to point to a Raise-Program-Error routine initially, and then set to point to the "real" code when the body is elaborated.

11.e
For implementations that share code between generic instantiations, the extra level of indirection suggested above to support Elaboration_Checks could also be used to provide a pointer to the per-instance data area normally required when calling shared code. The trick would be to put a pointer to the per-instance data area into the subprogram descriptor, and then make sure that the address of the subprogram descriptor is loaded into a "known" register whenever an indirect call is performed. Once inside the shared code, the address of the per-instance data area can be retrieved out of the subprogram descriptor, by indexing off the "known" register.

11.f
Essentially the same implementation issues arise for calls on dispatching operations of tagged types, except that the static link is always known "statically."

11.g
Note that access parameters of an anonymous access-to-subprogram type are not permitted. If there were such parameters, full "downward" closures would be required, meaning that in an implementation that uses a per-task (global) display, the display would have to be passed as a hidden parameter, and reconstructed at the point of call. This was felt to be an undue implementation burden, given that an equivalent (actually, more general) capability is available via formal subprogram parameters to a generic.

12
An access_definition defines an anonymous general access-to-variable type; the subtype_mark denotes its designated subtype. [An access_definition is used in the specification of an access discriminant (see 3.7) or an access parameter (see 6.1).]

13
For each (named) access type, there is a literal null which has a null access value designating no entity at all. [The null value of a named access type is the default initial value of the type.] Other values of an access type are obtained by evaluating an attribute_reference for the Access or Unchecked_Access attribute of an aliased view of an object or non-intrinsic subprogram, or, in the case of a named access-to-object type, an allocator[, which returns an access value designating a newly created object (see 3.10.2)].

13.a
Ramification: A value of an anonymous access type (that is, the value of an access parameter or access discriminant) cannot be null.

13.b
Reason: Access parameters allow dispatching on the tag of the object designated by the actual parameter (which gets converted to the anonymous access type as part of the call). In order for dispatching to work properly, there had better be such an object. Hence, the type conversion will raise Constraint_Error if the value of the actual parameter is null.

14
[All subtypes of an access-to-subprogram type are constrained.]  The first subtype of a type defined by an access_type_definition or an access_to_object_definition is unconstrained if the designated subtype is an unconstrained array or discriminated type; otherwise it is constrained.

14.a
Proof: The Legality Rules on range_constraints (see 3.5) do not permit the subtype_mark of the subtype_indication to denote an access-to-scalar type, only a scalar type. The Legality Rules on index_constraints (see 3.6.1) and discriminant_constraints (see 3.7.1) both permit access-to-composite types in a subtype_indication with such _constraints.  Note that an access-to-access-to-composite is never permitted in a subtype_indication with a constraint.

14.b
Reason: Only composite_constraints are permitted for an access type, and only on access-to-composite types. A constraint on an access-to-scalar or access-to-access type might be violated due to assignments via other access paths that were not so constrained.  By contrast, if the designated subtype is an array or discriminated type, the constraint could not be violated by unconstrained assignments, since array objects are always constrained, and aliased discriminated objects are also constrained (by fiat, see Static Semantics).

Dynamic Semantics

15
A composite_constraint is compatible with an unconstrained access subtype if it is compatible with the designated subtype. An access value satisfies a composite_constraint of an access subtype if it equals the null value of its type or if it designates an object whose value satisfies the constraint.

16
The elaboration of an access_type_definition creates the access type and its first subtype. For an access-to-object type, this elaboration includes the elaboration of the subtype_indication, which creates the designated subtype.

17
The elaboration of an access_definition creates an anonymous general access-to-variable type [(this happens as part of the initialization of an access parameter or access discriminant)].

NOTES

18 77
Access values are called "pointers" or "references" in some other languages.

19 78
Each access-to-object type has an associated storage pool; several access types can share the same pool. An object can be created in the storage pool of an access type by an allocator (see 4.8) for the access type. A storage pool (roughly) corresponds to what some other languages call a "heap." See 13.11 for a discussion of pools.

20 79
Only index_constraints and discriminant_constraints can be applied to access types (see 3.6.1 and 3.7.1).

Examples

21
Examples of access-to-object types:

22
type Peripheral_Ref is access Peripheral;  --  see 3.8.1
type Binop_Ptr is access all Binary_Operation'Class;
                                           -- general access-to-class-wide, see 3.9.1

23
Example of an access subtype:

24
subtype Drum_Ref is Peripheral_Ref(Drum);  --  see 3.8.1

25
Example of an access-to-subprogram type:

26
type Message_Procedure is access procedure (M : in String := "Error!");
procedure Default_Message_Procedure(M : in String);
Give_Message : Message_Procedure := Default_Message_Procedure'Access;
...
procedure Other_Procedure(M : in String);
...
Give_Message := Other_Procedure'Access;
...
Give_Message("File not found.");  --call with parameter (.all is optional)
Give_Message.all;                 --call with no parameters

Extensions to Ada 83

26.a
The syntax for access_type_definition is changed to support general access types (including access-to-constants) and access-to-subprograms. The syntax rules for general_access_modifier and access_definition are new.

Wording Changes From Ada 83

26.b
We use the term "storage pool" to talk about the data area from which allocation takes place. The term "collection" is no longer used. ("Collection" and "storage pool" are not the same thing because multiple unrelated access types can share the same storage pool; see 13.11 for more discussion.)

3.10.1 Incomplete Type Declarations

1
There are no particular limitations on the designated type of an access type.  In particular, the type of a component of the designated type can be another access type, or even the same access type.  This permits mutually dependent and recursive access types. An incomplete_type_declaration can be used to introduce a type to be used as a designated type, while deferring its full definition to a subsequent full_type_declaration.

Syntax

2
incomplete_type_declaration ::= type defining_identifier [discriminant_part];

Legality Rules

3
An incomplete_type_declaration requires a completion, which shall be a full_type_declaration. [If the incomplete_type_declaration occurs immediately within either the visible part of a package_specification or a declarative_part, then the full_type_declaration shall occur later and immediately within this visible part or declarative_part. If the incomplete_type_declaration occurs immediately within the private part of a given package_specification, then the full_type_declaration shall occur later and immediately within either the private part itself, or the declarative_part of the corresponding package_body.]

3.a
Proof: This is implied by the next AARM-only rule, plus the rules in 3.11.1, "Completions of Declarations" which require a completion to appear later and immediately within the same declarative region.

3.b
To be honest: If the incomplete_type_declaration occurs immediately within the visible part of a package_specification, then the full_type_declaration shall occur immediately within this visible part.

3.c
To be honest: If the implementation supports it, an incomplete_type_declaration can be completed by a pragma Import.

4
If an incomplete_type_declaration has a known_discriminant_part, then a full_type_declaration that completes it shall have a fully conforming (explicit) known_discriminant_part (see 6.3.1). [If an incomplete_type_declaration has no discriminant_part (or an unknown_discriminant_part), then a corresponding full_type_declaration is nevertheless allowed to have discriminants, either explicitly, or inherited via derivation.]

5
The only allowed uses of a name that denotes an incomplete_type_declaration are as follows:

5.a
Discussion:  No need to say "prior to the end of the full_type_declaration" since the name would not denote the incomplete_type_declaration after the end of the full_type_declaration. Also, with child library units, it would not be well defined whether they come before or after the full_type_declaration for deferred incomplete types.

6 ·
as the subtype_mark in the subtype_indication of an access_to_object_definition; [the only form of constraint allowed in this subtype_indication is a discriminant_constraint;]

6.a
Implementation Note: We now allow discriminant_constraints even if the full type is deferred to the package body.  However, there is no particular implementation burden because we have dropped the concept of the dependent compatibility check. In other words, we have effectively repealed AI-00007.

7 ·
as the subtype_mark defining the subtype of a parameter or result of an access_to_subprogram_definition;

7.a
Reason: This allows, for example, a record to have a component designating a subprogram that takes that same record type as a parameter.

8 ·
as the subtype_mark in an access_definition;

9 ·
as the prefix of an attribute_reference whose attribute_designator is Class; such an attribute_reference is similarly restricted to the uses allowed here; when used in this way, the corresponding full_type_declaration shall declare a tagged type, and the attribute_reference shall occur in the same library unit as the incomplete_type_declaration.

9.a
Reason: This is to prevent children from imposing requirements on their ancestor library units for deferred incomplete types.

10
A dereference (whether implicit or explicit - see 4.1) shall not be of an incomplete type.

Static Semantics

11
An incomplete_type_declaration declares an incomplete type and its first subtype; the first subtype is unconstrained if a known_discriminant_part appears.

11.a
Reason: If an unknown_discriminant_part or no discriminant_part appears, then the constrainedness of the first subtype doesn't matter for any other rules or semantics, so we don't bother defining it. The case with a known_discriminant_part is the only case in which a constraint could later be given in a subtype_indication naming the incomplete type.

Dynamic Semantics

12
The elaboration of an incomplete_type_declaration has no effect.

12.a
Reason: An incomplete type has no real existence, so it doesn't need to be "created" in the usual sense we do for other types. It is roughly equivalent to a "forward;" declaration in Pascal. Private types are different, because they have a different set of characteristics from their full type.

NOTES

13 80
Within a declarative_part, an incomplete_type_declaration and a corresponding full_type_declaration cannot be separated by an intervening body. This is because a type has to be completely defined before it is frozen, and a body freezes all types declared prior to it in the same declarative_part (see 13.14).

Examples

14
Example of a recursive type:

15
type Cell;  --  incomplete type declaration
type Link is access Cell;

16
type Cell is
   record
      Value  : Integer;
      Succ   : Link;
      Pred   : Link;
   end record;

17
Head   : Link  := new Cell'(0, null, null);
Next   : Link  := Head.Succ;

18
Examples of mutually dependent access types:

19
type Person(<>);    -- incomplete type declaration
type Car;           -- incomplete type declaration

20
type Person_Name is access Person;
type Car_Name    is access all Car;

21
type Car is
   record
      Number  : Integer;
      Owner   : Person_Name;
   end record;

22
type Person(Sex : Gender) is
   record
      Name     : String(1 .. 20);
      Birth    : Date;
      Age      : Integer range 0 .. 130;
      Vehicle  : Car_Name;
      case Sex is
         when M => Wife           : Person_Name(Sex => F);
         when F => Husband        : Person_Name(Sex => M);
      end case;
   end record;

23
My_Car, Your_Car, Next_Car : Car_Name := new Car;  -- see 4.8
George : Person_Name := new Person(M);
   ...
George.Vehicle := Your_Car;

Extensions to Ada 83

23.a
The full_type_declaration that completes an incomplete_type_declaration may have a known_discriminant_part even if the incomplete_type_declaration does not.

23.b
A discriminant_constraint may be applied to an incomplete type, even if it its completion is deferred to the package body, because there is no "dependent compatibility check" required any more. Of course, the constraint can be specified only if a known_discriminant_part was given in the incomplete_type_declaration.  As mentioned in the previous paragraph, that is no longer required even when the full type has discriminants.

Wording Changes From Ada 83

23.c
Dereferences producing incomplete types were not explicitly disallowed in RM83, though AI-00039 indicated that it was not strictly necessary since troublesome cases would result in Constraint_Error at run time, since the access value would necessarily be null. However, this introduces an undesirable implementation burden, as illustrated by Example 4 of AI-00039:

23.d
package Pack is
    type Pri is private;
private
    type Sep;
    type Pri is access Sep;
    X : Pri;
end Pack;

23.e
package body Pack is --Could be separately compiled!
    type Sep is ...;
    X := new Sep;
end Pack;

23.f
pragma Elaborate(Pack);
private package Pack.Child is
    I : Integer := X.all'Size; --Legal, by AI-00039.
end Pack.Child;

23.g
Generating code for the above example could be a serious implementation burden, since it would require all aliased objects to store size dope, and for that dope to be in the same format for all kinds of types (or some other equivalently inefficient implementation). On the contrary, most implementations allocate dope differently (or not at all) for different designated subtypes.

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
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]. 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
[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
[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
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
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 ·   
if the actual is a reference to the Access attribute -  the accessibility level of the view denoted by the 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 RM9X, 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 RM9X 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.ab ·   
If the actual is a reference to the Access attribute, pass the level of the view denoted by the prefix.
22.ac ·   
If the actual is a reference to the Unchecked_Access attribute, pass 0 (the library accessibility level).
22.ad ·   
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.ae
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.af
This method, using statically known values most of the time, is efficient, and, more importantly, avoids distributed overhead.

22.ag
Discussion:  Examples of accessibility:

22.ah
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.ai
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.aj
Here's an example involving access parameters:

22.ak
procedure Main is
    type Level_1_Type is access all Integer;

22.al
    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.am
    procedure Q(X: access Integer) is
        procedure Nested(X: access Integer) is
        begin
            P(X);
        end Nested;
    begin
        Nested(X);
    end Q;

22.an
    procedure R is
        Level_2: aliased Integer;
    begin
        Q(Level_2'Access); --(3)
    end R;

22.ao
    Level_1: aliased Integer;
begin
    Q(Level_1'Access); --(4)
    R;
end Main;

22.ap
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.aq
Within a type_declaration, the rules are checked in an assume-the-worst manner. For example:

22.ar
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.as
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.at
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
X'Access 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:

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
Change: Fixed this rule to match the rule for renaming (see 8.5.1, "Object Renaming Declarations").

26.b
Discussion:  This restriction is intended to be similar to the restriction on renaming discriminant-dependent subcomponents.

26.c
Reason: This prevents references to subcomponents that might disappear or move or change constraints after creating the reference.

26.d
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.e ·   
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.f ·   
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.g
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.h
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 ·
If the designated type of A is tagged, then the type of the view shall be covered by the designated type; if A's designated type is not tagged, then the type of the view shall be the same, and either A's designated subtype shall statically match the nominal subtype of the view, or the designated subtype shall be discriminated and unconstrained;

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.

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.

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

NOTES

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
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 (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
Proof: See 3.9.2.

37 85
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
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 AI-00154.



[Home] [Prev] [Next] [Index]

documentation@rational.com
Copyright © 1993-1998, Rational Software Corporation.   All rights reserved.