Skip to main content

4.8 Allocators

danger

This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue

1

[The evaluation of an allocator creates an object and yields an access value that designates the object. ]

Syntax

2/3

allocator ::=
new [subpool_specification] subtype_indication
| new [subpool_specification] qualified_expression

2.1/3

subpool_specification ::= (subpool_handle_name)

2.2/3

For an allocator with a subtype_indication, the subtype_indication shall not specify a null_exclusion.

2.a/3
reason

Such an uninitialized allocator would necessarily raise Constraint_Error, as the default value is null. Also note that the syntax does not allow a null_exclusion in an initialized allocator, so it makes sense to make the uninitialized case illegal as well.

Name Resolution Rules

3/3

{8652/0010} The expected type for an allocator shall be a single access-to-object type with designated type D such that either D covers the type determined by the subtype_mark of the subtype_indication or qualified_expression, or the expected type is anonymous and the determined type is D'Class. A subpool_handle_name is expected to be of any type descended from Subpool_Handle, which is the type used to identify a subpool, declared in package System.Storage_Pools.Subpools (see 13.11.4).

3.a
discussion

See 8.6, “The Context of Overload Resolution” for the meaning of “shall be a single ... type whose ...”.

3.a.1/1
ramification

{8652/0010} An allocator is allowed as a controlling parameter of a dispatching call (see 3.9.2).

Legality Rules

4

An initialized allocator is an allocator with a qualified_expression. An uninitialized allocator is one with a subtype_indication. In the subtype_indication of an uninitialized allocator, a constraint is permitted only if the subtype_mark denotes an [unconstrained] composite subtype; if there is no constraint, then the subtype_mark shall denote a definite subtype.

4.a
ramification

For example, ... new S'Class ... (with no initialization expression) is illegal, but ... new S'Class'(X) ... is legal, and takes its tag and constraints from the initial value X. (Note that the former case cannot have a constraint.)

5/2

If the type of the allocator is an access-to-constant type, the allocator shall be an initialized allocator.

5.a/2
This paragraph was deleted.
5.1/3

If a subpool_specification is given, the type of the storage pool of the access type shall be a descendant of Root_Storage_Pool_With_Subpools.

5.2/3

If the designated type of the type of the allocator is class-wide, the accessibility level of the type determined by the subtype_indication or qualified_expression shall not be statically deeper than that of the type of the allocator.

5.b/2
reason

This prevents the allocated object from outliving its type.

5.3/3

If the subtype determined by the subtype_indication or qualified_expression of the allocator has one or more access discriminants, then the accessibility level of the anonymous access type of each access discriminant shall not be statically deeper than that of the type of the allocator (see 3.10.2).

5.c/2
reason

This prevents the allocated object from outliving its discriminants.

5.4/3

An allocator shall not be of an access type for which the Storage_Size has been specified by a static expression with value zero or is defined by the language to be zero.

5.d/2
reason

An allocator for an access type that has Storage_Size specified to be zero is required to raise Storage_Error anyway. It's better to detect the error at compile-time, as the allocator might be executed infrequently. This also simplifies the rules for Pure units, where we do not want to allow any allocators for library-level access types, as they would represent state.

5.e/3

We don't need a special rule to cover generic formals (unlike many other similar Legality Rules). There are only two cases of interest. For formal access types, the Storage_Size property is not known in the generic, and surely isn't static, so this Legality Rule can never apply. For a formal derived type, this Legality Rule can only be triggered by a parent type having one of the appropriate properties. But Storage_Size can never be specified for a derived access type, so it always has the same value for all child types; additionally, a type derived from a remote access type (which has Storage_Size defined to be zero) is also a remote access type. That means that any actual that would match the formal derived type necessarily has the same Storage_Size properties, so it is harmless (and preferable) to check them in the body - they are always known in that case. For other formal types,allocators are not allowed, so we don't need to consider them. So we don't need an assume-the-best rule here.

5.5/3

If the designated type of the type of the allocator is limited, then the allocator shall not be used to define the value of an access discriminant, unless the discriminated type is immutably limited (see 7.5).

5.f/3
reason

Because coextensions work very much like parts, we don't want users creating limited coextensions for nonlimited types. This would be similar to extending a nonlimited type with a limited component. We check this on the allocator. Note that there is an asymmetry in what types are considered limited; this is required to preserve privacy. We have to assume that the designated type might be limited as soon as we see a limited partial view, but we want to ensure that the containing object is of a type that is always limited.

5.6/3

In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit.

5.g/3
discussion

This applies to all of the Legality Rules of this subclause.

Static Semantics

6/3

If the designated type of the type of the allocator is elementary, then the subtype of the created object is the designated subtype. If the designated type is composite, then the subtype of the created object is the designated subtype when the designated subtype is constrained or there is an ancestor of the designated type that has a constrained partial view; otherwise, the created object is constrained by its initial value [(even if the designated subtype is unconstrained with defaults)].

6.a
discussion

See AI83-00331.

6.b/2
reason

All objects created by an allocator are aliased, and most aliased composite objects need to be constrained so that access subtypes work reasonably. Problematic access subtypes are prohibited for types with a constrained partial view.

6.c/2
discussion

If there is a constrained partial view of the type, this allows the objects to be unconstrained. This eliminates privacy breaking (we don't want the objects to act differently simply because they're allocated). Such a created object is effectively constrained by its initial value if the access type is an access-to-constant type, or the designated type is limited (in all views), but we don't need to state that here. It is implicit in other rules. Note, however, that a value of an access-to-constant type can designate a variable object via 'Access or conversion, and the variable object might be assigned by some other access path, and that assignment might alter the discriminants.

Dynamic Semantics

7/2

For the evaluation of an initialized allocator, the evaluation of the qualified_expression is performed first. An object of the designated type is created and the value of the qualified_expression is converted to the designated subtype and assigned to the object.

7.a
ramification

The conversion might raise Constraint_Error.

8

For the evaluation of an uninitialized allocator, the elaboration of the subtype_indication is performed first. Then:

9/2
  • If the designated type is elementary, an object of the designated subtype is created and any implicit initial value is assigned;
  • 10/2
  • {8652/0002} If the designated type is composite, an object of the designated type is created with tag, if any, determined by the subtype_mark of the subtype_indication. This object is then initialized by default (see 3.3.1) using the subtype_indication to determine its nominal subtype. A check is made that the value of the object belongs to the designated subtype. Constraint_Error is raised if this check fails. This check and the initialization of the object are performed in an arbitrary order.
10.a
discussion

AI83-00150.

10.1/3

For any allocator, if the designated type of the type of the allocator is class-wide, then a check is made that the master of the type determined by the subtype_indication, or by the tag of the value of the qualified_expression, includes the elaboration of the type of the allocator. If any part of the subtype determined by the subtype_indication or qualified_expression of the allocator (or by the tag of the value if the type of the qualified_expression is class-wide) has one or more access discriminants, then a check is made that the accessibility level of the anonymous access type of each access discriminant is not deeper than that of the type of the allocator. Program_Error is raised if either such check fails.

10.b/3
reason

The master check on class-wide types prevents the allocated object from outliving its type. We need the run-time check in instance bodies, or when the type of the qualified_expression is class-wide (other cases are statically detected).

10.b.1/3

We can't use the normal accessibility level “deeper than” check here because we may have “incomparable” levels if the appropriate master and the type declaration belong to two different tasks. This can happen when checking the master of the tag for an allocator initialized by a parameter passed in to an accept statement, if the type of the allocator is an access type declared in the enclosing task body. For example:

10.b.2/3

task body TT is type Acc_TC is access T'Class; P : Acc_TC; begin accept E(X : T'Class) do P := new T'Class'(X); -- Master check on tag of X. -- Can't use "accessibility levels" since they might be incomparable. -- Must revert to checking that the master of the type identified by -- X'tag includes the elaboration of Acc_TC, so it is sure to outlive it. end E;

10.c/2

The accessibility check on access discriminants prevents the allocated object from outliving its discriminants.

10.2/2

If the object to be created by an allocator has a controlled or protected part, and the finalization of the collection of the type of the allocator (see 7.6.1) has started, Program_Error is raised.

10.d/2
reason

If the object has a controlled or protected part, its finalization is likely to be nontrivial. If the allocation was allowed, we could not know whether the finalization would actually be performed. That would be dangerous to otherwise safe abstractions, so we mandate a check here. On the other hand, if the finalization of the object will be trivial, we do not require (but allow) the check, as no real harm could come from late allocation.

10.e/2
discussion

This check can only fail if an allocator is evaluated in code reached from a Finalize routine for a type declared in the same master. That's highly unlikely; Finalize routines are much more likely to be deallocating objects than allocating them.

10.3/2

If the object to be created by an allocator contains any tasks, and the master of the type of the allocator is completed, and all of the dependent tasks of the master are terminated (see 9.3), then Program_Error is raised.

10.f/2
reason

A task created after waiting for tasks has finished could depend on freed data structures, and certainly would never be awaited.

10.4/3

If the allocator includes a subpool_handle_name, Constraint_Error is raised if the subpool handle is null. Program_Error is raised if the subpool does not belong (see 13.11.4) to the storage pool of the access type of the allocator.

10.g/3
implementation note

This can be implemented by comparing the result of Pool_of_Subpool to a reference to the storage pool object. Pool_of_Subpool's parameter is not null, so the check for null falls out naturally.

10.h/3
reason

This detects cases where the subpool belongs to another pool, or to no pool at all. This includes detecting dangling subpool handles so long as the subpool object (the object designated by the handle) still exists. (If the subpool object has been deallocated, execution is erroneous; it is likely that this check will still detect the problem, but there cannot be a guarantee.)

11

[If the created object contains any tasks, they are activated (see 9.2).] Finally, an access value that designates the created object is returned.

Bounded (Run-Time) Errors

11.1/2

It is a bounded error if the finalization of the collection of the type (see 7.6.1) of the allocator has started. If the error is detected, Program_Error is raised. Otherwise, the allocation proceeds normally.

11.a/2
discussion

This check is required in some cases; see above.

12

NOTE 1 Allocators cannot create objects of an abstract type. See 3.9.3.

13

NOTE 2 If any part of the created object is controlled, the initialization includes calls on corresponding Initialize or Adjust procedures. See 7.6.

14/5

NOTE 3 As explained in 13.11, “Storage Management”, the storage for an object allocated by an allocator comes from a storage pool (possibly user defined). The exception Storage_Error is raised by an allocator if there is not enough storage. Instances of Unchecked_Deallocation can be used to explicitly reclaim storage.

15/5

NOTE 4 Implementations can, if desired, provide garbage collection.

15.a
ramification

Note that in an allocator, the exception Constraint_Error can be raised by the evaluation of the qualified_expression, by the elaboration of the subtype_indication, or by the initialization.

15.b
discussion

By default, the implementation provides the storage pool. The user may exercise more control over storage management by associating a user-defined pool with an access type.

Examples

16

Examples of allocators:

17

new Cell'(0, null, null) -- initialized explicitly, see 3.10.1 new Cell'(Value => 0, Succ => null, Pred => null) -- initialized explicitly new Cell -- not initialized 18 new Matrix(1 .. 10, 1 .. 20) -- the bounds only are given new Matrix'(1 .. 10 => (1 .. 20 => 0.0)) -- initialized explicitly 19 new Buffer(100) -- the discriminant only is given new Buffer'(Size => 80, Pos => 0, Value => (1 .. 80 => 'A')) -- initialized explicitly 20 Expr_Ptr'(new Literal) -- allocator for access-to-class-wide type, see 3.9.1 Expr_Ptr'(new Literal'(Expression with 3.5)) -- initialized explicitly

Incompatibilities With Ada 83

20.a/1

The subtype_indication of an uninitialized allocator may not have an explicit constraint if the designated type is an access type. In Ada 83, this was permitted even though the constraint had no effect on the subtype of the created object.

Extensions to Ada 83

20.b

Allocators creating objects of type T are now overloaded on access types designating T'Class and all class-wide types that cover T.

20.c

Implicit array subtype conversion (sliding) is now performed as part of an initialized allocator.

Wording Changes from Ada 83

20.d

We have used a new organization, inspired by the ACID document, that makes it clearer what is the subtype of the created object, and what subtype conversions take place.

20.e

Discussion of storage management issues, such as garbage collection and the raising of Storage_Error, has been moved to 13.11, “Storage Management”.

Inconsistencies With Ada 95

20.f/2

If the designated type has a constrained partial view, the allocated object can be unconstrained. This might cause the object to take up a different amount of memory, and might cause the operations to work where they previously would have raised Constraint_Error. It's unlikely that the latter would actually matter in a real program (Constraint_Error usually indicates a bug that would be fixed, not left in a program.) The former might cause Storage_Error to be raised at a different time than in an Ada 95 program.

Incompatibilities With Ada 95

20.g/2

An allocator for an access type that has Storage_Size specified to be zero is now illegal. Ada 95 allowed the allocator, but it had to raise Storage_Error if executed. The primary impact of this change should be to detect bugs.

Extensions to Ada 95

20.h/2

{8652/0010} Corrigendum: An allocator can be a controlling parameter of a dispatching call. This was an oversight in Ada 95.

20.i/2

Initialized allocators are allowed when the designated type is limited.

Wording Changes from Ada 95

20.j/2

{8652/0002} Corrigendum: Clarified the elaboration of per-object constraints for an uninitialized allocator.

20.k/2

Program_Error is now raised if the allocator occurs after the finalization of the collection or the waiting for tasks. This is not listed as an incompatibility as the Ada 95 behavior was unspecified, and Ada 95 implementations tend to generate programs that crash in this case.

20.l/2

Added accessibility checks to class-wide allocators. These checks could not fail in Ada 95 (as all of the designated types had to be declared at the same level, so the access type would necessarily have been at the same level or more nested than the type of allocated object).

20.m/2

Revised the description of evaluation of uninitialized allocators to use “initialized by default” so that the ordering requirements are the same for all kinds of objects that are default-initialized.

20.n/2

Added accessibility checks to access discriminants of allocators. These checks could not fail in Ada 95 as the discriminants always have the accessibility of the object.

Incompatibilities With Ada 2005

20.o/3
correction

Added a rule to prevent limited coextensions of nonlimited types. Allowing this would have far-reaching implementation costs. Because of those costs, it seems unlikely that any implementation ever supported it properly and thus it is unlikely that any existing code depends on this capability.

20.p/3
correction

Added a rule to make null_exclusions illegal for uninitialized allocators, as such an allocator would always raise Constraint_Error. Programs that depend on the unconditional raising of a predefined exception should be very rare.

Extensions to Ada 2005

20.q/3

Subpool handles (see 13.11.4) can be specified in an allocator.

Wording Changes from Ada 2005

20.r/3
correction

Corrected the master check for tags since the masters may be for different tasks and thus incomparable.

20.s/3
correction

Corrected the rules for when a designated object is constrained by its initial value so that types derived from a partial view are handled properly.

20.t/3
correction

Corrected the accessibility check for access discriminants so that it does not depend on the designated type (which might not have discriminants when the allocated type does).