7.6 Assignment and FinalizationUser-Defined Assignment and Finalization
1
[
Three kinds
of actions are fundamental to the manipulation of objects: initialization,
finalization, and assignment. Every object is initialized, either explicitly
or by default, after being created (for example, by an
object_declaration
or
allocator).
Every object is finalized before being destroyed (for example, by leaving
a
subprogram_body
containing an
object_declaration,
or by a call to an instance of Unchecked_Deallocation). An assignment
operation is used as part of
assignment_statements,
explicit initialization, parameter passing, and other operations.
2
Default definitions for these three fundamental operations
are provided by the language, but
a
controlled
type gives the user additional control over parts of these operations.
In particular,
the user can define, for a controlled type, an Initialize procedure which
is invoked immediately after the normal default initialization of a controlled
object, a Finalize procedure which is invoked immediately before finalization
of any of the components of a controlled object, and an Adjust procedure
which is invoked as the last step of an assignment to a (nonlimited)
controlled object.]
2.a
Glossary entry: A controlled type supports
user-defined assignment and finalization. Objects are always finalized
before being destroyed.
2.b/2
Ramification: {
AI95-00114-01}
{
AI95-00287-01}
Here's the basic idea of initialization, value adjustment, and finalization,
whether or not user defined: When an object is created, if it is explicitly
assigned an initial value,
the object is either
built-in-place from an aggregate
or function call (in which case neither Adjust nor Initialize is applied),
or the assignment copies and adjusts the initial value. Otherwise,
Initialize is applied to it (except in the case of an
aggregate
as a whole). An
assignment_statement
finalizes the target before copying in and adjusting the new value. Whenever
an object goes away, it is finalized. Calls on Initialize and Adjust
happen bottom-up; that is, components first, followed by the containing
object. Calls on Finalize
happen happens
top-down; that is, first the containing object, and then its components.
These ordering rules ensure that any components will be in a well-defined
state when Initialize, Adjust, or Finalize is applied to the containing
object.
Static Semantics
3
The following language-defined
library package exists:
4/3
5/2
{
AI95-00161-01}
type Controlled
is abstract tagged private;
pragma Preelaborable_Initialization(Controlled);
6/2
{
AI95-00348-01}
procedure Initialize (Object :
in out Controlled)
is null;
procedure Adjust (Object :
in out Controlled)
is null;
procedure Finalize (Object :
in out Controlled)
is null;
7/2
{
AI95-00161-01}
type Limited_Controlled
is abstract tagged limited private;
pragma Preelaborable_Initialization(Limited_Controlled);
8/2
{
AI95-00348-01}
procedure Initialize (Object :
in out Limited_Controlled)
is null;
procedure Finalize (Object :
in out Limited_Controlled)
is null;
private
... --
not specified by the language
end Ada.Finalization;
9/2
{
AI95-00348-01}
A controlled type is a descendant of Controlled or
Limited_Controlled.
The (default) implementations
of Initialize, Adjust, and Finalize have no effect. The predefined
"=" operator of type Controlled always returns True, [since
this operator is incorporated into the implementation of the predefined
equality operator of types derived from Controlled, as explained in
4.5.2.]
The type Limited_Controlled is like Controlled, except that it is limited
and it lacks the primitive subprogram Adjust.
9.a
Discussion: We say “nonlimited
controlled type” (rather than just “controlled type”;)
when we want to talk about descendants of Controlled only.
9.b
Reason: We considered making Adjust and
Finalize abstract. However, a reasonable coding convention is e.g. for
Finalize to always call the parent's Finalize after doing whatever work
is needed for the extension part. (Unlike CLOS, we have no way to do
that automatically in Ada 95.) For this to work, Finalize cannot be abstract.
In a generic unit, for a generic formal abstract derived type whose ancestor
is Controlled or Limited_Controlled, calling the ancestor's Finalize
would be illegal if it were abstract, even though the actual type might
have a concrete version.
9.c
Types Controlled and Limited_Controlled are
abstract, even though they have no abstract primitive subprograms. It
is not clear that they need to be abstract, but there seems to be no
harm in it, and it might make an implementation's life easier to know
that there are no objects of these types — in case the implementation
wishes to make them “magic” in some way.
9.d/2
{
AI95-00251-01}
For Ada 2005, we considered making these types
interfaces. That would have the advantage of allowing them to be added
to existing trees. But that was rejected both because it would cause
massive disruptions to existing implementations, and because it would
be very incompatible due to the "no hidden interfaces" rule.
The latter rule would prevent a tagged private type from being completed
with a derivation from Controlled or Limited_Controlled — a very
common idiom.
9.1/2
9.2/2
it is a controlled type,
a task type or a protected type; or
9.3/3
{
AI05-0092-1}
it has a component whose
type that
needs finalization; or
9.4/3
{
AI05-0013-1}
it is a class-wide type; or it
is a limited type that has an access discriminant whose designated type
needs finalization; or
9.5/3
{
AI05-0026-1}
it is a partial view whose full view needs finalization;
or
9.6/2
it is one of a number of
language-defined types that are explicitly defined to need finalization.
9.e/2
Ramification: The
fact that a type needs finalization does not require it to be implemented
with a controlled type. It just has to be recognized by the No_Nested_Finalization
restriction.
9.f/2
This property is defined
for the type, not for a particular view. That's necessary as restrictions
look in private parts to enforce their restrictions; the point is to
eliminate all controlled parts, not just ones that are visible.
Dynamic Semantics
10/2
{
AI95-00373-01}
During the elaboration
or evaluation of a construct that causes an object
to be initialized by default of an object_declaration,
for every controlled subcomponent of the object that is not assigned
an initial value (as defined in
3.3.1), Initialize
is called on that subcomponent. Similarly, if the object
that
is initialized by default as a whole is controlled
and is not assigned an initial value, Initialize is called on
the object.
The same applies to the evaluation
of an allocator,
as explained in 4.8.
11/2
{
8652/0021}
{
AI95-00182-01}
{
AI95-00373-01}
For an
extension_aggregate
whose
ancestor_part
is a
subtype_mark denoting a, for
each controlled subcomponent of the ancestor part, either Initialize
is called, or its initial value is assigned, as appropriate Initialize
is called on all controlled subcomponents of the ancestor part;
if the type of the ancestor part is itself controlled
subtype, the Initialize procedure of the ancestor type is called,
unless that Initialize procedure is abstract.
11.a
Discussion:
Example:
11.b
type T1 is new Controlled with
record
... -- some components might have defaults
end record;
11.c
type T2 is new Controlled with
record
X : T1; -- no default
Y : T1 := ...; -- default
end record;
11.d
A : T2;
B : T2 := ...;
11.e
As part of the elaboration of A's declaration,
A.Y is assigned a value; therefore Initialize is not applied to A.Y.
Instead, Adjust is applied to A.Y as part of the assignment operation.
Initialize is applied to A.X and to A, since those objects are not assigned
an initial value. The assignment to A.Y is not considered an assignment
to A.
11.f
For the elaboration of B's declaration, Initialize
is not called at all. Instead the assignment adjusts B's value; that
is, it applies Adjust to B.X, B.Y, and B.
11.f.1/2
12
Initialize and other initialization operations are
done in an arbitrary order, except as follows.
Initialize
is applied to an object after initialization of its subcomponents, if
any [(including both implicit initialization and Initialize calls)].
If an object has a component with an access discriminant constrained
by a per-object expression, Initialize is applied to this component after
any components that do not have such discriminants. For an object with
several components with such a discriminant, Initialize is applied to
them in order of their
component_declarations.
For an
allocator,
any task activations follow all calls on Initialize.
12.a
Reason: The fact that Initialize is done
for subcomponents first allows Initialize for a composite object to refer
to its subcomponents knowing they have been properly initialized.
12.b
The fact that Initialize is done for components
with access discriminants after other components allows the Initialize
operation for a component with a self-referential access discriminant
to assume that other components of the enclosing object have already
been properly initialized. For multiple such components, it allows some
predictability.
13
When
a target object with any controlled parts is assigned a value, [either
when created or in a subsequent
assignment_statement,]
the
assignment operation proceeds as follows:
14
The value of the target becomes the assigned value.
15
The value
of the target is
adjusted.
15.a
Ramification: If any parts of the object
are controlled, abort is deferred during the assignment operation.
16/3
{
AI05-0067-1}
To adjust the value of a
[(nonlimited)]
composite object, the values of the components of the object are
first adjusted in an arbitrary order, and then, if the object is
nonlimited
controlled, Adjust is called.
Adjusting the
value of an elementary object has no effect[, nor does adjusting the
value of a composite object with no controlled parts.]
16.a/3
Ramification: {
AI05-0067-1}
Adjustment is never
actually performed for
values of
an immutably a
by-reference limited type, since
all assignment
operations for such types are required to be built-in-place. Even so,
we still define adjustment for all types in order that the canonical
semantics is well-defined these types do
not support copying.
16.b/3
Reason: {
AI05-0005-1}
The verbiage in the Initialize rule about access discriminants constrained
by per-object expressions is not necessary here, since such types are
either limited
or
do not have defaults, so the discriminant can only be changed by an assignment
to an outer object. Such an assignment could happen only before any adjustments
or (if part of an outer Adjust) only after any inner (component) adjustments
have completed., and therefore are never
adjusted.
17
For an
assignment_statement,
[ after the
name
and
expression
have been evaluated, and any conversion (including constraint checking)
has been done,] an anonymous object is created, and the value is assigned
into it; [that is, the assignment operation is applied]. [(Assignment
includes value adjustment.)] The target of the
assignment_statement
is then finalized. The value of the anonymous object is then assigned
into the target of the
assignment_statement.
Finally, the anonymous object is finalized. [As explained below, the
implementation may eliminate the intermediate anonymous object, so this
description subsumes the one given in
5.2,
“
Assignment Statements”.]
17.a
Reason: An
alternative design for user-defined assignment might involve an Assign
operation instead of Adjust:
17.b
procedure Assign(Target : in out Controlled; Source : in out Controlled);
17.c
Or perhaps even
a syntax like this:
17.d
procedure ":="(Target : in out Controlled; Source : in out Controlled);
17.e
Assign (or ":=")
would have the responsibility of doing the copy, as well as whatever
else is necessary. This would have the advantage that the Assign operation
knows about both the target and the source at the same time — it
would be possible to do things like reuse storage belonging to the target,
for example, which Adjust cannot do. However, this sort of design would
not work in the case of unconstrained discriminated variables, because
there is no way to change the discriminants individually. For example:
17.f
type Mutable(D : Integer := 0) is
record
X : Array_Of_Controlled_Things(1..D);
case D is
when 17 => Y : Controlled_Thing;
when others => null;
end D;
end record;
17.g
An assignment to an unconstrained variable of
type Mutable can cause some of the components of X, and the component
Y, to appear and/or disappear. There is no way to write the Assign operation
to handle this sort of case.
17.h
Forbidding such cases is not an option —
it would cause generic contract model violations.
17.1/3
{
AI05-0067-1}
When a function call or aggregate
is used to initialize an object, the result of the function call or aggregate
is an anonymous object, which is assigned into the newly-created object.
For such an assignment, the anonymous object might be built in place.
Under certain circumstances, the anonymous object is required to be built
in place, in which case the assignment does not
involve any copying. In particular:
17.i/3
Discussion: {
AI05-0067-1}
We say assignment to built-in-place objects does
not involve copying, which matches the intended implementation (see below).
Of course, the implementation can do any copying it likes, if it can
make such copying semantically invisible (by patching up access values
to point to the copy, and so forth).
17.2/3
If the full type of any part
of the object is immutably limited, the anonymous object is built in
place.
17.j/3
Reason: {
AI05-0067-1}
We talk about the full types being immutably limited,
as this is independent of the view of a type (in the same way that it
is for determining the technique of parameter passing). That is, privacy
is ignored for this purpose.
17.k/3
{
AI05-0067-1}
For function calls, we only require building in
place for immutable types. These are the types that would have been return-by-reference
types in Ada 95. We limited the requirement because we want to minimize
disruption to Ada 95 implementations and users.
17.l/3
To be honest: {
AI05-0232-1}
This is a dynamic property and is determined by
the specific type of the parts of the actual object. In particular, if
a part has a class-wide type, the tag of the object might need to be
examined in order to determine if build-in-place is required. However,
we expect that most Ada implementations will determine this property
at compile-time using some assume-the-worst algorithm in order to chose
the appropriate method to implement a given call or aggregate. In addition,
there is no attribute or other method for a program to determine if a
particular object has this property (or not), so there is no value to
a more careful description of this rule.
17.3/3
In the case of an aggregate,
if the full type of any part of the newly-created object is controlled,
the anonymous object is built in place.
17.m/3
Reason:
{
AI05-0067-1}
This is necessary to prevent elaboration problems
with deferred constants of controlled types. Consider:
17.m.1/3
package P is
type Dyn_String is private;
Null_String : constant Dyn_String;
...
private
type Dyn_String is new Ada.Finalization.Controlled with ...
procedure Finalize(X : in out Dyn_String);
procedure Adjust(X : in out Dyn_String);
Null_String : constant Dyn_String :=
(Ada.Finalization.Controlled with ...);
...
end P;
17.m.2/3
When Null_String is elaborated,
the bodies of Finalize and Adjust clearly have not been elaborated. Without
this rule, this declaration would necessarily raise Program_Error (unless
the permissions given below are used by the implementation).
17.n/3
Ramification: An
aggregate
with a controlled part used in the return expression of a simple_return_statement
has to be built in place in the anonymous return object, as this is similar
to an object declaration. (This is a change from Ada 95, but it is not
an inconsistency as it only serves to restrict implementation choices.)
But this only covers the aggregate;
a separate anonymous return object can still be used unless it too is
required to be built in place.
17.o/3
Similarly, an aggregate
that has a controlled part but is not itself controlled and that is used
to initialize an object also has to be built in place. This is also a
change from Ada 95, but it is not an inconsistency as it only serves
to restrict implementation choices. This avoids problems if a type like
Dyn_String (in the example above) is used as a component in a type used
as a deferred constant in package P.
17.4/3
In other cases, it is unspecified
whether the anonymous object is built in place.
17.p/3
Reason: This is
left unspecified so the implementation can use any appropriate criteria
for determining when to build in place. That includes making the decision
on a call-by-call basis. Reasonable programs will not care what decision
is made here anyway.
17.5/3
{
AI05-0067-1}
Notwithstanding what this International
Standard says elsewhere, if an object is built in place:
17.6/3
Upon successful completion
of the return statement or aggregate,
the anonymous object mutates into the newly-created object; that
is, the anonymous object ceases to exist, and the newly-created object
appears in its place.
17.7/3
Finalization is not performed
on the anonymous object.
17.8/3
Adjustment is not performed
on the newly-created object.
17.9/3
All access values that designate
parts of the anonymous object now designate the corresponding parts of
the newly-created object.
17.10/3
All renamings of parts of
the anonymous object now denote views of the corresponding parts of the
newly-created object.
17.11/3
Coextensions of the anonymous
object become coextensions of the newly-created object.
17.q/3
To be honest: This
“mutating” does not necessarily happen atomically with respect
to abort and other tasks. For example, if a function call is used as
the parent part of an extension_aggregate,
then the tag of the anonymous object (the function result) will be different
from the tag of the newly-created object (the parent part of the extension_aggregate).
In implementation terms, this involves modifying the tag field. If the
current task is aborted during this modification, the object might become
abnormal. Likewise, if some other task accesses the tag field during
this modification, it constitutes improper use of shared variables, and
is erroneous.
17.r/3
Implementation Note:
The intended implementation is that the anonymous object is allocated
at the same address as the newly-created object. Thus, no run-time action
is required to cause all the access values and renamings to point to
the right place. They just point to the newly-created object, which is
what the return object has magically “mutated into”.
17.s/3
There is no requirement
that 'Address of the return object is equal to 'Address of the newly-created
object, but that will be true in the intended implementation.
17.t/3
For a function call, if
the size of the newly-created object is known at the call site, the object
is allocated there, and the address is implicitly passed to the function;
the return object is created at that address. Otherwise, a storage pool
is implicitly passed to the function; the size is determined at the point
of the return statement, and passed to the Allocate procedure. The address
returned by the storage pool is returned from the function, and the newly-created
object uses that same address. If the return statement is left without
returning (via an exception or a goto, for example), then Deallocate
is called. The storage pool might be a dummy pool that represents “allocate
on the stack”.
17.u/3
The Tag of the newly-created
object may be different from that of the result object. Likewise, the
master and accessibility level may be different.
17.v/3
An alternative implementation
model might allow objects to move around to different addresses. In this
case, access values and renamings would need to be modified at run time.
It seems that this model requires the full power of tracing garbage collection.
Implementation Requirements
17.12/3
17.v.1/3
This
paragraph was deleted.Reason: {
AI95-00318-02}
{
AI05-0067-1}
This build-in-place requirement is necessary to prevent elaboration problems with deferred constants
of controlled types. Consider:
17.v.2/3
This paragraph
was deleted.package P is
type Dyn_String is private;
Null_String : constant Dyn_String;
...
private
type Dyn_String is new Ada.Finalization.Controlled with ...
procedure Finalize(X : in out Dyn_String);
procedure Adjust(X : in out Dyn_String);
Null_String : constant Dyn_String :=
(Ada.Finalization.Controlled with ...);
...
end P;
17.v.3/3
This paragraph
was deleted.When
Null_String is elaborated, the bodies of Finalize and Adjust clearly
have not been elaborated. Without this rule, this declaration would necessarily
raise Program_Error (unless the permissions given below are used by the
implementation).
17.v.4/3
This paragraph
was deleted.Ramification: An
aggregate
of a controlled type used in the return expression of a simple_return_statement
has to be built-in-place in the anonymous return object, as this is similar
to an object declaration. (This is a change from Ada 95, but it is not
an inconsistency as it only serves to restrict implementation choices.)
But this only covers the aggregate;
a separate anonymous return object can still be used unless it too is
required to be built-in-place (see 7.5).
Implementation Permissions
18/3
18.a/3
This paragraph
was deleted.Proof: {
AI05-0067-1}
The phrase “for nonlimited controlled types”
follows from the fact that all of the following permissions apply to
cases involving assignment. It is important because the programmer can
count on a stricter semantics for limited controlled types.
18.b/3
Ramification: {
AI05-0067-1}
The relaxations apply only to nonlimited types,
as assignment_statements
are not allowed for limited types. This is important so that the programmer
can count on a stricter semantics for limited controlled types.
19/3
19.a
Ramification: In other words, even if
an object is controlled and a combination of Finalize and Adjust on the
object might have a net side effect, they need not be performed.
20/3
{
AI05-0067-1}
For
assignment of an
assignment_statement
for a noncontrolled type, the implementation may finalize and
assign each component of the variable separately (rather than finalizing
the entire variable and assigning the entire new value) unless a discriminant
of the variable is changed by the assignment.
20.a
Reason: For example, in a slice assignment,
an anonymous object is not necessary if the slice is copied component-by-component
in the right direction, since array types are not controlled (although
their components may be). Note that the direction, and even the fact
that it's a slice assignment, can in general be determined only at run
time.
20.b/3
Ramification: {
AI05-0005-1}
This potentially breaks a single assignment operation
into many, and thus abort deferral (see 9.8)
needs to last only across an individual component assignment when the
component has a controlled part. It is only important that the copy step
is not separated (by an abort) from the adjust step, so aborts between
component assignments is not harmful.
21/3
{
AI95-00147-01}
{
AI05-0067-1}
The For an aggregate
or function call whose value is assigned into a target object, the implementation
need not create a separate anonymous object if it can safely create the
value of the aggregate
or function call directly in the target object. Similarly, for an assignment_statement,
the implementation need not create an anonymous object if the
value being assigned is the result of evaluating a
name
denoting an object (the source object) whose storage cannot overlap with
the target. If the source object might overlap with the target object,
then the implementation can avoid the need for an intermediary anonymous
object by exercising one of the above permissions and perform the assignment
one component at a time (for an overlapping array assignment), or not
at all (for an assignment where the target and the source of the assignment
are the same object).
Even if an anonymous object
is created, the implementation may move its value to the target object
as part of the assignment without re-adjusting so long as the anonymous
object has no aliased subcomponents.
21.a/3
Ramification: {
AI05-0005-1}
If the anonymous object is eliminated by this permission,
In the aggregate
case, only one value adjustment is necessary, and there is no
anonymous object to be finalized
and thus the Finalize
call on it is eliminated.
21.b/3
{
AI95-00147-01}
{
AI05-0005-1}
Note that if the anonymous
object is eliminated but the new value is not built in place in the target
object Similarly,
in the function call case, the anonymous object can be eliminated. Note,
however, that Adjust must be called In
the assignment_statement
case as well, no finalization of the anonymous object is needed. On the
other hand, if the target has aliased subcomponents, then an adjustment
takes place directly on the target object as the last step of
the assignment, since some of the subcomponents may be self-referential
or otherwise position-dependent.
This Adjust can
be eliminated only by using one of the following permissions.
22/2
{
AI95-00147-01}
Furthermore, an implementation is permitted to
omit implicit Initialize, Adjust, and Finalize calls and associated assignment
operations on an object of a nonlimited controlled type provided that:
23/2
any omitted Initialize call
is not a call on a user-defined Initialize procedure, and
23.a/2
To be honest: This
does not apply to any calls to a user-defined Initialize routine that
happen to occur in an Adjust or Finalize routine. It is intended that
it is never necessary to look inside of an Adjust or Finalize routine
to determine if the call can be omitted.
23.b/2
Reason: We don't
want to eliminate objects for which the Initialize might have side effects
(such as locking a resource).
24/2
any usage of the value of
the object after the implicit Initialize or Adjust call and before any
subsequent Finalize call on the object does not change the external effect
of the program, and
25/2
after the omission of such
calls and operations, any execution of the program that executes an Initialize
or Adjust call on an object or initializes an object by an aggregate
will also later execute a Finalize call on the object and will always
do so prior to assigning a new value to the object, and
26/2
the assignment operations
associated with omitted Adjust calls are also omitted.
27/2
This permission applies to
Adjust and Finalize calls even if the implicit calls have additional
external effects.
27.a/2
Reason: The goal
of the above permissions is to allow typical dead assignment and dead
variable removal algorithms to work for nonlimited controlled types.
We require that “pairs” of Initialize/Adjust/Finalize operations
are removed. (These aren't always pairs, which is why we talk about “any
execution of the program”.)
Extensions to Ada 83
27.b
Controlled types and user-defined
finalization are new to Ada 95. (Ada 83 had finalization semantics only
for masters of tasks.)
Extensions to Ada 95
27.c/2
{
AI95-00161-01}
Amendment Correction:
Types Controlled and Limited_Controlled now have Preelaborable_Initialization,
so that objects of types derived from these types can be used in preelaborated
packages.
Wording Changes from Ada 95
27.d/2
27.e/2
{
8652/0021}
{
AI95-00182-01}
Corrigendum: Added wording to clarify that
the default initialization (whatever it is) of an ancestor part is used.
27.f/2
{
8652/0022}
{
AI95-00083-01}
Corrigendum: Clarified that Adjust is never
called on an aggregate
used for the initialization of an object or subaggregate, or passed as
a parameter.
27.g/2
{
AI95-00147-01}
Additional optimizations are allowed for nonlimited
controlled types. These allow traditional dead variable elimination to
be applied to such types.
27.h/2
{
AI95-00318-02}
Corrected the build-in-place requirement for controlled
aggregates
to be consistent with the requirements for limited types.
27.i/2
{
AI95-00348-01}
The operations of types Controlled and Limited_Controlled
are now declared as null procedures (see 6.7)
to make the semantics clear (and to provide a good example of what null
procedures can be used for).
27.j/2
27.k/2
{
AI95-00373-01}
Generalized the description of objects that have
Initialize called for them to say that it is done for all objects that
are initialized by default. This is needed so that all of the new cases
are covered.
Extensions to Ada 2005
27.l/3
{
AI05-0212-1}
Package Ada.Finalization now
has Pure categorization, so it can be mentioned for any package. Note
that this does not change the preelaborability of objects descended from
Controlled and Limited_Controlled.
Wording Changes from Ada 2005
27.m/3
{
AI05-0013-1}
Correction: Eliminated coextensions from
the “needs finalization” rules, as this cannot be determined
in general in the compilation unit that declares the type. (The designated
type of the coextension may have been imported as a limited view.) Uses
of “needs finalization” need to ensure that coextensions
are handled by other means (such as in No_Nested_Finalization –
see D.7) or that coextensions cannot happen.
27.n/3
{
AI05-0013-1}
Correction: Corrected the “needs finalization”
rules to include class-wide types, as a future extension can include
a part that needs finalization.
27.o/3
{
AI05-0026-1}
Correction: Corrected the “needs finalization”
rules to clearly say that they ignore privacy.
27.p/3
{
AI05-0067-1}
Correction: Changed “built in place”
to Dynamic Semantics and centralized the rules here. This eliminates
the fiction that built in place is just a combination of a permission
and a requirement; it clearly has noticeable semantic effects. This wording
change is not intended to change the semantics of any correct Ada program.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe