13.11.2 Unchecked Storage Deallocation
[
Unchecked
storage deallocation of an object designated by a value of an access
type is achieved by a call to an instance of the generic procedure Unchecked_Deallocation.]
Static Semantics
The following language-defined
generic library procedure exists:
{
AI05-0229-1}
{
AI12-0241-1}
{
AI12-0302-1}
{
AI12-0319-1}
generic
type Object(<>)
is limited private;
type Name
is access Object;
procedure Ada.Unchecked_Deallocation(X :
in out Name)
with Preelaborate, Nonblocking,
Global => in out Name'Storage_Pool,
Convention => Intrinsic;
pragma Preelaborate(Ada.Unchecked_Deallocation);
Reason: {
AI05-0229-1}
The aspect Convention implies that the attribute Access is not allowed
for instances of Unchecked_Deallocation.
Legality Rules
{
AI05-0157-1}
A call on an instance of Unchecked_Deallocation is illegal if the actual
access type of the instance is a 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.
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.
Discussion: This rule is the same as
the rule for
allocators.
We could have left the last sentence out, as a call to Unchecked_Deallocation
cannot occur in a specification as it is a procedure call, but we left
it for consistency and to avoid future maintenance hazards.
Dynamic Semantics
Given an instance of
Unchecked_Deallocation declared as follows:
procedure Free is
new Ada.Unchecked_Deallocation(
object_subtype_name, access_to_variable_subtype_name);
Procedure Free has
the following effect:
1.
After executing Free(X), the value of X is null.
2.
Free(X), when X is already equal to null, has no effect.
3.
{
AI95-00416-01}
{
AI05-0107-1}
{
AI12-0445-1}
Free(X), when X is not equal to
null first performs finalization
of the object designated by X (and any coextensions of the object —
see
3.10.2), as described in
7.6.1.
It then deallocates the storage occupied by the object designated by
X (and any coextensions). If the storage pool is a user-defined object,
then the storage is deallocated by calling Deallocate as described in
13.11. There is one exception: if the object
being freed contains tasks,
it is unspecified whether
the object
is might
not be deallocated.
Ramification: {
AI05-0107-1}
Free calls only the specified Deallocate procedure to do deallocation.
{
AI95-00416-01}
{
AI12-0148-1}
After
the finalization step of Free(X), the object
designated by X, and any subcomponents (and coextensions) thereof, no
longer exist; their storage can be reused for other purposes.
Bounded (Run-Time) Errors
It
is a bounded error to free a discriminated, unterminated task object.
The possible consequences are:
Reason: This is an error because the
task might refer to its discriminants, and the discriminants might be
deallocated by freeing the task object.
No exception is raised.
Program_Error or Tasking_Error is raised at the
point of the deallocation.
Program_Error or Tasking_Error is raised in the
task the next time it references any of the discriminants.
Implementation Note: This last case presumes
an implementation where the task references its discriminants indirectly,
and the pointer is nulled out when the task object is deallocated.
In the first two cases, the storage for the discriminants
(and for any enclosing object if it is designated by an access discriminant
of the task) is not reclaimed prior to task termination.
Ramification: The storage might never
be reclaimed.
{
AI12-0148-1}
An access value that designates a nonexistent object
is called a dangling reference.
Discussion: These
can result from use of Unchecked_Deallocation, Unchecked_Deallocate_Subpool,
and attribute Unchecked_Access. Bad results from Unchecked_Conversion
and from stream-oriented attributes are abnormal by 13.9.1,
which is stronger and thus takes precedence.
{
AI12-0148-1}
[If a dangling reference is dereferenced (implicitly
or explicitly), execution is erroneous (see below).] If there is no explicit
or implicit dereference, then it is a bounded error to
evaluate an expression whose result is a dangling reference. If the error
is detected, either Constraint_Error or Program_Error is raised. Otherwise,
execution proceeds normally, but with the possibility that the access
value designates some other existing object.
Reason: If a dangling
reference is compared with another access value, a result of either True
or False is allowed. We need to allow this so that simple implementations
of access values (for instance, as a bare address) can work if the memory
in question is reused. (The formal definition of access equality is that
it returns True if both access values designate the same object; that
can never be True if one of the values is a dangling reference, and the
other is not, but both values could refer to the same memory.) Membership
tests that do not involve an implicit dereference generally do not depend
on the access value at all.
We allow Constraint_Error
to be raised here so that dangling reference and null pointer checks
can be combined into a single check. If different exceptions are required,
then the checks have to be made separately - but there's little semantic
difference (neither designate a usable object).
Ramification: If
a dangling reference is assigned into an object, including being passed
to a formal parameter, that object also contains a dangling reference
afterwards.
Discussion: For
equality and membership operations on composite types, this applies to
any parts that are access types, as these operations are created based
on the operations of the components (which triggers the bounded error).
For other operations on composite types, the bounded error is not triggered.
For instance, an assignment of a composite object with a subcomponent
that is a dangling reference has to work normally; no exception can be
raised, but the target object will have a subcomponent that is a dangling
references, and a (direct) use of that subcomponent is again a bounded
error. This is similar to the way that assignments of invalid subcomponents
are handled (see 13.9.1).
Erroneous Execution
{
AI05-0033-1}
{
AI05-0262-1}
Evaluating a name that denotes
a nonexistent object, or a protected subprogram or subprogram renaming
whose associated object (if any) is nonexistent, is erroneous. The execution
of a call to an instance of Unchecked_Deallocation is erroneous if the
object was created other than by an
allocator
for an access type whose pool is Name'Storage_Pool.
Reason: {
AI05-0033-1}
{
AI05-0262-1}
The part about a protected subprogram is intended to cover the case of
an access-to-protected-subprogram where the associated object has been
deallocated. The part about a subprogram renaming is intended to cover
the case of a renaming of a prefixed view where the prefix object has
been deallocated, or the case of a renaming of an entry or protected
subprogram where the associated task or protected object has been deallocated.
Ramification: {
AI05-0157-1}
This text does not cover the case of a name that contains a null access
value, as
null does not denote an object (rather than denoting
a nonexistent object).
Implementation Advice
For a standard storage pool, Free should actually
reclaim the storage.
Implementation Advice: For a standard
storage pool, an instance of Unchecked_Deallocation should actually reclaim
the storage.
Ramification: {
AI95-00114-01}
This is not a testable property, since we do not know how much storage
is used by a given pool element, nor whether fragmentation can occur.
{
AI05-0157-1}
A call on an instance of Unchecked_Deallocation with a nonnull access
value should raise Program_Error if the actual access type of the instance
is a type for which the Storage_Size has been specified to be zero or
is defined by the language to be zero.
Implementation Advice: A call on an instance
of Unchecked_Deallocation with a nonnull access value should raise Program_Error
if the actual access type of the instance is a type for which the Storage_Size
has been specified to be zero or is defined by the language to be zero.
Discussion: If the call is not illegal
(as in a generic body), we recommend that it raise Program_Error. Since
the execution of this call is erroneous (any allocator from the pool
will have raised Storage_Error, so the nonnull access value must have
been allocated from a different pool or be a stack-allocated object),
we can't require any behavior — anything at all would be a legitimate
implementation.
NOTE 1 The rules here that refer
to Free apply to any instance of Unchecked_Deallocation.
NOTE 2 Unchecked_Deallocation cannot
be instantiated for an access-to-constant type. This is implied by the
rules of
12.5.4.
Wording Changes from Ada 95
{
AI95-00416-01}
The rules for coextensions are clarified (mainly by adding that term).
In theory, this reflects no change from Ada 95 (coextensions existed
in Ada 95, they just didn't have a name).
Wording Changes from Ada 2005
{
AI05-0033-1}
Correction: Added a rule that using an access-to-protected-subprogram
is erroneous if the associated object no longer exists. It is hard to
imagine an alternative meaning here, and this has no effect on correct
programs.
{
AI05-0107-1}
Correction: Moved the requirements on an implementation-generated
call to Deallocate to
13.11, in order to
put all of the rules associated with implementation-generated calls to
Allocate and Deallocate together.
{
AI05-0157-1}
Correction: Added wording so that calling an instance of Unchecked_Deallocation
is treated similarly to
allocators
for access types where
allocators
would be banned.
Inconsistencies With Ada 2012
{
AI12-0148-1}
Corrigendum: Defined
a "dangling reference", and specified that a dangling reference
might designate some other existing object. This allows simple implementations
of access values and reuse of object memory after deallocation. In prior
versions of Ada, "=" between a dangling reference and an access
to an existing object has to return False, even if the existing object
and the object designated by the dangling reference are allocated in
the same memory. A program that depended upon that could break with this
revised rule. However, as a practical matter, almost all Ada implementations
use simple implementations of access types that do not meet that requirement.
So such a program would not work (consistently) on most Ada implementations;
thus the change shouldn't break any existing programs - it just aligns
the Standard with actual practice.
{
AI12-0148-1}
A side effect of this change is to allow an Ada
implementation to detect dangling references in more places. This does
not require any Ada implementation to change, and if the implementation
does change, it just means that errors will be detected earlier.
Wording Changes from Ada 2012
{
AI12-0148-1}
Corrigendum: Clarified that deallocated
objects cease to exist after finalization but before Deallocate is called.
This is necessary to prevent erroneous execution from being triggered
by the rules in 13.11 in the time between
the end of finalization and the end of the call to the instance of Unchecked_Deallocation.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe