Contents Index Search Previous Next
13.14 Freezing Rules
1
[This clause defines a place in the program text
where each declared entity becomes ``frozen.'' A use of an entity, such
as a reference to it by name, or (for a type) an expression of the type,
causes freezing of the entity in some contexts, as described below. The
Legality Rules forbid certain kinds of uses of an entity in the region
of text where it is frozen.]
1.a
Reason: This concept
has two purposes: a compile-time one and a run-time one.
1.b
The compile-time purpose of
the freezing rules comes from the fact that the evaluation of static
expressions depends on overload resolution, and overload resolution sometimes
depends on the value of a static expression. (The dependence of static
evaluation upon overload resolution is obvious. The dependence in the
other direction is more subtle. There are three rules that require static
expressions in contexts that can appear in declarative places: The expression
in an attribute_designator shall
be static. In a record aggregate, variant-controlling discriminants shall
be static. In an array aggregate with more than one named association,
the choices shall be static. The compiler needs to know the value of
these expressions in order to perform overload resolution and legality
checking.) We wish to allow a compiler to evaluate static expressions
when it sees them in a single pass over the compilation_unit.
The freezing rules ensure that.
1.c
The run-time purpose of the
freezing rules is called the ``linear elaboration model.'' This means
that declarations are elaborated in the order in which they appear in
the program text, and later elaborations can depend on the results of
earlier ones. The elaboration of the declarations of certain entities
requires run-time information about the implementation details of other
entities. The freezing rules ensure that this information has been calculated
by the time it is used. For example, suppose the initial value of a constant
is the result of a function call that takes a parameter of type T.
In order to pass that parameter, the size of type T has to be
known. If T is composite, that size might be known only at run
time.
1.d
(Note that in these discussions,
words like ``before'' and ``after'' generally refer to places in the
program text, as opposed to times at run time.)
1.e
Discussion:
The ``implementation details'' we're talking about above are:
1.f
- For a tagged
type, the implementations of all the primitive subprograms of the type
-- that is (in the canonical implementation model), the contents of the
type descriptor, which contains pointers to the code for each primitive
subprogram.
1.g
- For a type, the
full type declaration of any parts (including the type itself) that are
private.
1.h
- For a deferred
constant, the full constant declaration, which gives the constant's value.
(Since this information necessarily comes after the constant's type and
subtype are fully known, there's no need to worry about its type or subtype.)
1.i
- For any entity,
representation information specified by the user via representation items.
Most representation items are for types or subtypes; however, various
other kinds of entities, such as objects and subprograms, are possible.
1.j
Similar issues arise for incomplete
types. However, we do not use freezing there; incomplete types have different,
more severe, restrictions. Similar issues also arise for subprograms,
protected operations, tasks and generic units. However, we do not use
freezing there either; 3.11 prevents problems
with run-time Elaboration_Checks.
Language Design Principles
1.k
An evaluable construct should
freeze anything that's needed to evaluate it.
1.l
However, if the construct is
not evaluated where it appears, let it cause freezing later, when it
is evaluated. This is the case for default_expressions
and default_names. (Formal parameters,
generic formal parameters, and components can have default_expressions
or default_names.)
1.m
The compiler should be allowed
to evaluate static expressions without knowledge of their context. (I.e.
there should not be any special rules for static expressions that happen
to occur in a context that requires a static expression.)
1.n
Compilers should be allowed
to evaluate static expressions (and record the results) using the run-time
representation of the type. For example, suppose Color'Pos(Red) = 1,
but the internal code for Red is 37. If the value of a static expression
is Red, some compilers might store 1 in their symbol table, and other
compilers might store 37. Either compiler design should be feasible.
1.o
Compilers should never be required
to detect erroneousness or exceptions at compile time (although it's
very nice if they do). This implies that we should not require code-generation
for a nonstatic expression of type T too early, even if we can
prove that that expression will be erroneous, or will raise an exception.
1.p
Here's
an example (modified from AI83-00039, Example 3):
1.q
type T is
record
...
end record;
function F return T;
function G(X : T) return Boolean;
Y : Boolean := G(F); -- doesn't force T in Ada 83
for T use
record
...
end record;
1.r
AI83-00039
says this is legal. Of course, it raises Program_Error because the function
bodies aren't elaborated yet. A one-pass compiler has to generate code
for an expression of type T before it knows the representation of T.
Here's a similar example, which AI83-00039 also says is legal:
1.s
package P is
type T is private;
function F return T;
function G(X : T) return Boolean;
Y : Boolean := G(F); -- doesn't force T in Ada 83
private
type T is
record
...
end record;
end P;
1.t
If T's size were dynamic, that
size would be stored in some compiler-generated dope; this dope would
be initialized at the place of the full type declaration. However, the
generated code for the function calls would most likely allocate a temp
of the size specified by the dope before checking for Program_Error.
That dope would contain uninitialized junk, resulting in disaster. To
avoid doing that, the compiler would have to determine, at compile time,
that the expression will raise Program_Error.
1.u
This is silly. If we're going
to require compilers to detect the exception at compile time, we might
as well formulate the rule as a legality rule.
1.v
Compilers should not be required
to generate code to load the value of a variable before the address of
the variable has been determined.
1.w
After an entity has been frozen,
no further requirements may be placed on its representation (such as
by a representation item or a full_type_declaration).
2
{freezing (entity) [distributed]}
{freezing points (entity)}
The
freezing of an entity occurs at one or
more places (
freezing points) in the program text where the representation
for the entity has to be fully determined. Each entity is frozen from
its first freezing point to the end of the program text (given the ordering
of compilation units defined in
10.1.4).
2.a
Ramification: The ``representation''
for a subprogram includes its calling convention and means for referencing
the subprogram body, either a ``link-name'' or specified address. It
does not include the code for the subprogram body itself, nor its address
if a link-name is used to reference the body.
3/1
{
8652/0014}
{freezing (entity caused by the end of an enclosing
construct)} The end of a
declarative_part,
protected_body, or a declaration
of a library package or generic library package, causes
freezing
of each entity declared within it, except for incomplete types.
{freezing
(entity caused by a body)} A noninstance
body
other than a renames-as-body causes freezing of each entity
declared before it within the same
declarative_part.
3.a
Discussion: This is worded
carefully to handle nested packages and private types. Entities declared
in a nested package_specification
will be frozen by some containing construct.
3.b
An incomplete type declared
in the private part of a library package_specification
can be completed in the body.
3.c
Ramification: The part
about bodies does not say immediately within. A renaming-as-body
does not have this property. Nor does a pragma
Import.
3.d
Reason: The reason bodies
cause freezing is because we want proper_bodies
and body_stubs to be interchangeable
-- one should be able to move a proper_body
to a subunit, and vice-versa, without
changing the semantics. Clearly, anything that should cause freezing
should do so even if it's inside a proper_body.
However, if we make it a body_stub,
then the compiler can't see that thing that should cause freezing. So
we make body_stubs cause freezing,
just in case they contain something that should cause freezing. But that
means we need to do the same for proper_bodies.
3.e
Another reason for bodies to
cause freezing, there could be an added implementation burden if an entity
declared in an enclosing declarative_part
is frozen within a nested body, since some compilers look at bodies after
looking at the containing declarative_part.
4/1
{
8652/0046}
{freezing (entity caused by a construct) [distributed]}
A construct that (explicitly or implicitly) references
an entity can cause the
freezing of the entity, as defined by
subsequent paragraphs.
{freezing (by a constituent
of a construct) [partial]} At the place
where a construct causes freezing, each
name,
expression, implicit_dereference expression[,
or
range] within the construct causes
freezing:
4.a
Ramification:
Note that in the sense of this paragraph, a subtype_mark
``references'' the denoted subtype, but not the type.
5
- {freezing
(generic_instantiation) [partial]} The
occurrence of a generic_instantiation
causes freezing; also, if a parameter of the instantiation is defaulted,
the default_expression or default_name
for that parameter causes freezing.
6
- {freezing
(object_declaration) [partial]} The occurrence
of an object_declaration that has
no corresponding completion causes freezing.
6.a
Ramification: Note that
this does not include a formal_object_declaration.
7
- {freezing (subtype
caused by a record extension) [partial]} The
declaration of a record extension causes freezing of the parent subtype.
7.a
Ramification: This combined
with another rule specifying that primitive subprogram declarations shall
precede freezing ensures that all descendants of a tagged type implement
all of its dispatching operations.
7.b
The declaration of a private
extension does not cause freezing. The freezing is deferred until the
full type declaration, which will necessarily be for a record extension.
8/1
{
8652/0046}
{freezing (by an expression) [partial]}
A static expression causes freezing where it occurs.
{freezing (by an object name) [partial]}
An object name or A nonstatic expression
causes freezing where it occurs, unless the
name or expression
is part of a
default_expression,
a
default_name, or a per-object
expression of a component's
constraint,
in which case, the freezing occurs later as part of another construct.
8.1/1
{
8652/0046}
{freezing (by an implicit call) [partial]}
An implicit call freezes the same entities that would
be frozen by an explicit call. This is true even if the implicit call
is removed via implementation permissions.
8.2/1
{
8652/0046}
{freezing (subtype caused by an implicit conversion)
[partial]} If an expression is implicitly
converted to a type or subtype T, then at the place where the
expression causes freezing, T is frozen.
9
The following rules
define which entities are frozen at the place where a construct causes
freezing:
10
- {freezing
(type caused by an expression) [partial]} At
the place where an expression causes freezing, the type of the expression
is frozen, unless the expression is an enumeration literal used as a
discrete_choice of the array_aggregate
of an enumeration_representation_clause.
10.a
Reason: We considered
making enumeration literals never cause freezing, which would be more
upward compatible, but examples like the variant record aggregate (Discrim
=> Red, ...) caused us to change our mind. Furthermore, an enumeration
literal is a static expression, so the implementation should be allowed
to represent it using its representation.
10.b
Ramification:
The following pathological example was legal in Ada 83, but is illegal
in Ada 95:
10.c
package P1 is
type T is private;
package P2 is
type Composite(D : Boolean) is
record
case D is
when False => Cf : Integer;
when True => Ct : T;
end case;
end record;
end P2;
X : Boolean := P2."="( (False,1), (False,1) );
private
type T is array(1..Func_Call) of Integer;
end;
10.d
In Ada 95, the declaration of
X freezes Composite (because it contains an expression of that type),
which in turn freezes T (even though Ct does not exist in this particular
case). But type T is not completely defined at that point, violating
the rule that a type shall be completely defined before it is frozen.
In Ada 83, on the other hand, there is no occurrence of the name T, hence
no forcing occurrence of T.
11
- {freezing
(entity caused by a name) [partial]} At
the place where a name causes freezing,
the entity denoted by the name is
frozen, unless the name is a prefix
of an expanded name; {freezing (nominal subtype caused
by a name) [partial]} at the place where
an object name causes freezing,
the nominal subtype associated with the name
is frozen.
11.a
Ramification: This only
matters in the presence of deferred constants or access types; an object_declaration
other than a deferred_constant_declaration
causes freezing of the nominal subtype, plus all component junk.
11.b/1
This paragraph
was deleted.{8652/0046}
Implicit_dereferences are covered
by expression.
11.1/1
- {8652/0046}
{freezing (subtype caused by an implicit dereference)
[partial]} At the place where an implicit_dereference
causes freezing, the nominal subtype associated with the implicit_dereference
is frozen.
12
- [{freezing
(type caused by a range) [partial]} At
the place where a range causes freezing,
the type of the range is frozen.]
12.a
Proof: This is consequence
of the facts that expressions freeze their type, and the Range attribute
is defined to be equivalent to a pair of expressions separated by ``..''.}
13
- {freezing
(designated subtype caused by an allocator) [partial]} At
the place where an allocator causes
freezing, the designated subtype of its type is frozen. If the type of
the allocator is a derived type,
then all ancestor types are also frozen.
13.a
Ramification: Allocators
also freeze the named subtype, as a consequence of other rules.
13.b
The
ancestor types are frozen to prevent things like this:
13.c
type Pool_Ptr is access System.Storage_Pools.Root_Storage_Pool'Class;
function F return Pool_Ptr;
13.d
package P is
type A1 is access Boolean;
type A2 is new A1;
type A3 is new A2;
X : A3 := new Boolean; -- Don't know what pool yet!
for A1'Storage_Pool use F.all;
end P;
13.e
This is necessary because derived
access types share their parent's pool.
14
- {freezing
(subtypes of the profile of a callable entity) [partial]}
At the place where a callable entity is frozen, each
subtype of its profile is frozen. If the callable entity is a member
of an entry family, the index subtype of the family is frozen. {freezing
(function call) [partial]} At the place
where a function call causes freezing, if a parameter of the call is
defaulted, the default_expression
for that parameter causes freezing.
14.a
Discussion: We don't
worry about freezing for procedure calls or entry calls, since a body
freezes everything that precedes it, and the end of a declarative part
freezes everything in the declarative part.
15
- {freezing
(type caused by the freezing of a subtype) [partial]} At
the place where a subtype is frozen, its type is frozen. {freezing
(constituents of a full type definition) [partial]} {freezing
(first subtype caused by the freezing of the type) [partial]}
At the place where a type is frozen, any expressions
or names within the full type definition
cause freezing; the first subtype, and any component subtypes, index
subtypes, and parent subtype of the type are frozen as well. {freezing
(class-wide type caused by the freezing of the specific type) [partial]}
{freezing (specific type caused
by the freezing of the class-wide type) [partial]} For
a specific tagged type, the corresponding class-wide type is frozen as
well. For a class-wide type, the corresponding specific type is frozen
as well.
15.a
Ramification: Freezing
a type needs to freeze its first subtype in order to preserve the property
that the subtype-specific aspects of statically matching subtypes are
the same.
15.b
Freezing an access type does
not freeze its designated subtype.
Legality Rules
16
[The explicit declaration
of a primitive subprogram of a tagged type shall occur before the type
is frozen (see
3.9.2).]
16.a
Reason: This rule is
needed because (1) we don't want people dispatching to things that haven't
been declared yet, and (2) we want to allow tagged type descriptors to
be static (allocated statically, and initialized to link-time-known symbols).
Suppose T2 inherits primitive P from T1, and then overrides P. Suppose
P is called before the declaration of the overriding P. What should
it dispatch to? If the answer is the new P, we've violated the first
principle above. If the answer is the old P, we've violated the second
principle. (A call to the new one necessarily raises Program_Error, but
that's beside the point.)
16.b
Note that a call upon a dispatching
operation of type T will freeze T.
16.c
We considered applying this
rule to all derived types, for uniformity. However, that would be upward
incompatible, so we rejected the idea. As in Ada 83, for an untagged
type, the above call upon P will call the old P (which is arguably confusing).
17
[A type shall be
completely defined before it is frozen (see
3.11.1
and
7.3).]
18
[The completion
of a deferred constant declaration shall occur before the constant is
frozen (see
7.4).]
19/1
{
8652/0009}
An operational or A representation item that directly specifies
an aspect of an entity shall appear before the entity is frozen (see
13.1).
19.a/1
Discussion: {8652/0009}
From RM83-13.1(7). The wording here forbids freezing within the aspect_clause representation_clause
itself, which was not true of the Ada 83 wording. The wording of this
rule is carefully written to work properly for type-related representation
items. For example, an enumeration_representation_clause
is illegal after the type is frozen, even though the _clause
refers to the first subtype.
19.b
Proof: The above Legality
Rules are stated ``officially'' in the referenced clauses.
19.c
Discussion:
Here's an example that illustrates when freezing occurs in the presence
of defaults:
19.d
type T is ...;
function F return T;
type R is
record
C : T := F;
D : Boolean := F = F;
end record;
X : R;
19.e
Since the elaboration of R's
declaration does not allocate component C, there is no need to freeze
C's subtype at that place. Similarly, since the elaboration of R does
not evaluate the default_expression
``F = F'', there is no need to freeze the types involved at that point.
However, the declaration of X does need to freeze these things.
Note that even if component C did not exist, the elaboration of the declaration
of X would still need information about T -- even though D is not of
type T, its default_expression requires
that information.
19.f
Ramification: Although
we define freezing in terms of the program text as a whole (i.e. after
applying the rules of Section 10), the freezing rules actually have no
effect beyond compilation unit boundaries.
19.g
Reason: That is important,
because Section 10 allows some implementation definedness in the order
of things, and we don't want the freezing rules to be implementation
defined.
19.h
Ramification: These rules
also have no effect in statements
-- they only apply within a single declarative_part,
package_specification, task_definition,
protected_definition, or protected_body.
19.i
Implementation Note: An
implementation may choose to generate code for default_expressions
and default_names in line at the
place of use. {thunk} Alternatively, an implementation
may choose to generate thunks (subprograms implicitly generated by the
compiler) for evaluation of defaults. Thunk generation cannot, in general,
be done at the place of the declaration that includes the default. Instead,
they can be generated at the first freezing point of the type(s) involved.
(It is impossible to write a purely one-pass Ada compiler, for various
reasons. This is one of them -- the compiler needs to store a representation
of defaults in its symbol table, and then walk that representation later,
no earlier than the first freezing point.)
19.j
In implementation terms, the
linear elaboration model can be thought of as preventing uninitialized
dope. For example, the implementation might generate dope to contain
the size of a private type. This dope is initialized at the place where
the type becomes completely defined. It cannot be initialized earlier,
because of the order-of-elaboration rules. The freezing rules prevent
elaboration of earlier declarations from accessing the size dope for
a private type before it is initialized.
19.k
2.8
overrides the freezing rules in the case of unrecognized pragmas.
19.l/1
{8652/0009}
An aspect_clause A representation_clause
for an entity should most certainly not be a freezing point for
the entity.
Incompatibilities With Ada 83
19.m
{incompatibilities with Ada
83} RM83 defines a forcing occurrence of a type
as follows: ``A forcing occurrence is any occurrence [of the name of
the type, subtypes of the type, or types or subtypes with subcomponents
of the type] other than in a type or subtype declaration, a subprogram
specification, an entry declaration, a deferred constant declaration,
a pragma, or a representation_clause
for the type itself. In any case, an occurrence within an expression
is always forcing.''
19.n
It
seems like the wording allows things like this:
19.o
type A is array(Integer range 1..10) of Boolean;
subtype S is Integer range A'Range;
-- not forcing for A
19.p
Occurrences within pragmas
can cause freezing in Ada 95. (Since such pragmas
are ignored in Ada 83, this will probably fix more bugs than it causes.)
Extensions to Ada 83
19.q
{extensions
to Ada 83} In Ada 95, generic_formal_parameter_declarations
do not normally freeze the entities from which they are defined. For
example:
19.r
package Outer is
type T is tagged limited private;
generic
type T2 is
new T with private; -- Does not freeze T
-- in Ada 95.
package Inner is
...
end Inner;
private
type T is ...;
end Outer;
19.s
This is important for the usability
of generics. The above example uses the Ada 95 feature of formal derived
types. Examples using the kinds of formal parameters already allowed
in Ada 83 are well known. See, for example, comments 83-00627 and 83-00688.
The extensive use expected for formal derived types makes this issue
even more compelling than described by those comments. Unfortunately,
we are unable to solve the problem that explicit_generic_actual_parameters
cause freezing, even though a package equivalent to the instance would
not cause freezing. This is primarily because such an equivalent package
would have its body in the body of the containing program unit, whereas
an instance has its body right there.
Wording Changes from Ada 83
19.t
The concept of freezing is based
on Ada 83's concept of ``forcing occurrences.'' The first freezing point
of an entity corresponds roughly to the place of the first forcing occurrence,
in Ada 83 terms. The reason for changing the terminology is that the
new rules do not refer to any particular ``occurrence'' of a name of
an entity. Instead, we refer to ``uses'' of an entity, which are sometimes
implicit.
19.u
In Ada 83, forcing occurrences
were used only in rules about representation_clauses.
We have expanded the concept to cover private types, because the rules
stated in RM83-7.4.1(4) are almost identical to the forcing occurrence
rules.
19.v
The
Ada 83 rules are changed in Ada 95 for the following reasons:
19.w
- The Ada 83 rules
do not work right for subtype-specific aspects. In an earlier version
of Ada 95, we considered allowing representation items to apply to subtypes
other than the first subtype. This was part of the reason for changing
the Ada 83 rules. However, now that we have dropped that functionality,
we still need the rules to be different from the Ada 83 rules.
19.x
- The Ada 83 rules
do not achieve the intended effect. In Ada 83, either with or without
the AIs, it is possible to force the compiler to generate code that references
uninitialized dope, or force it to detect erroneousness and exception
raising at compile time.
19.y
- It was a goal
of Ada 83 to avoid uninitialized access values. However, in the case
of deferred constants, this goal was not achieved.
19.z
- The Ada 83 rules
are not only too weak -- they are also too strong. They allow loopholes
(as described above), but they also prevent certain kinds of default_expressions
that are harmless, and certain kinds of generic_declarations
that are both harmless and very useful.
19.aa
- Ada 83 had a
case where a representation_clause
had a strong effect on the semantics of the program -- 'Small. This caused
certain semantic anomalies. There are more cases in Ada 95, because the
attribute_representation_clause
has been generalized.
Contents Index Search Previous Next Legal