Ada allows programmers to provide explicit information on the final representation of an entity [AAR95, Section 13]. The point at which the final representation is fully determined is called a Freezing Point. Obviously, if no representation is explicitly given the compiler has to take some default decision. When an entity is frozen it causes freezing of all depending entities. Following table summarizes the list of dependent entities to be frozen when a construct is frozen.
|Construct being frozen||Causes freezing of|
|Static Expression||Entities in expression and their types,|
|plus the type of the expression|
|Non-static Expression||Type of expression|
|Allocators||Designated subtype of qualified expression or|
|Object||Corresponding subtype (except deferred constants)|
|Derived subtype||Parent subtype|
|Type with components||Component subtypes|
|Constrained Array||Index subtypes|
|Subprograms and entries||Parameter and result subtypes|
|Tagged type||Corresponding Class-wide type|
|Tagged type extension||Parent tagged type|
|Class-wide type||Corresponding Tagged type|
|Generic Instantiation||Parameter subtypes|
GNAT generates a Freeze_Entity node at the point where an entity is frozen, and sets a pointer from the defining entity to its corresponding freeze node. Freeze nodes are later processed by the expander to generate associated code and data which must be delayed until the representation of the type is frozen (for example, an initialization subprogram). They are also used by Gigi to determine the point at which the object must be elaborated. If the GNAT compiler were fully faithful to the Ada model, it would generate freeze nodes for all entities, but that is a bit heavy. Therefore it freezes at their point of declaration all the entities whose representation characteristics can be determined at declaration time (cf. Package Freeze).
Although the Ada Reference Manual defines the freezing points of the language, the compiler must have special care with some details. These details are discussed in the following sections.
All declared types have freeze nodes. This includes anonymous base types created for constrained type declarations, where the defining identifier is a first subtype of the anonymous type. An obvious exception to this rule are access types, because freezing an access type does not freeze its designated subtype. In addition, all first subtypes have freeze nodes. Other subtypes only need freeze nodes if the corresponding base type has not yet been frozen; if the base type has been frozen there is no need for a freeze node because no representation clauses can appear for the subtype in any case.
Types and subtypes are frozen by 1) object declarations, 2) bodies found in the same declarative region (for example, a subprogram body), and 3) the end of their corresponding declarative part. When a user-defined type is frozen generally forces freezing of all the entities on which it depends (default expressions have an special treatment, and will be described later). For example freezing a subtype definition forces freezing of all the derivations found from this derived type to the root type. Implicit types, types and subtypes created else by the semantic analyzer or by the expander to reflect the underlying Ada semantics, are frozen when the corresponding derived type is frozen. For example, constrained type definitions are internally converted by the GNAT compiler into an implicit anonymous base type definition plus a derived type definition:
In this example TB and TD are implicit types which will be frozen when their derived type T is frozen. Freezing of implicit types introduced by component declarations (i.e. component constraints) do not need to be delayed because the semantic analyzer verifies that the parents of the subtypes are frozen before the enclosing record is frozen (cf. Subprogram Build_Discriminated_Subtype).
In general, naming an entity in any expression causes freezing of the corresponding type [AAR95, Section 13.14]. However, the compiler must have special care with default expressions used in discriminants and constraints of record components; they must be frozen at the point of the corresponding object declarations (not at the point of the type definition). For example:
Similarly, default expressions used in subprogram parameters must be deferred until the subprogram is frozen. In case of procedures this occurs when a body is found, but in case of functions the compiler must also consider function calls found in object initializations.
Simple objects are frozen at their point of declaration. Special care must be taken with deferred constants, which can not be frozen until the point of the full constant declaration. Objects with dynamic address clauses have a delayed freeze. In the freezing point Gigi generates code to evaluate the initialization expression (if present).
Protected types must be handled with special care because the protected-type definition is expanded into a record-type definition. For example:
As a consequence, the subtypes of components of protected-type definitions (line 1) do not need freezing. Freezing actions correspond to the equivalent components in the record-type definition (line 2) and, according to the Ada freezing rules, this must be delayed until the point of the first object declaration of type poV.
Formal parameters are frozen when the associated subprogram specification is frozen, so there is never any need for them to have delayed freezing. Subprograms specifications are frozen at the point of declaration unless one or more of the formal types, ^oor the return type, have delayed freezing and are not yet frozen. This includes the case of a formal access type where the designated type is not frozen. Subprogram bodies freeze everything.
Freezing rules for packages are quite clear. The language specifies that the end of a library package causes freezing of each entity declared within it, except for incomplete and private types which must be deferred until the full-type declaration is found. Special care must be taken with incomplete types because it is legal to place it in the package body [AAR95, Section 3.10.1(3)]. In addition, because the semantic analyzer keeps the entities of the partial and full views, it must also propagate the freezing information from the full view to the partial view (cf. Find_Type_Name subprogram).
In the case of local packages, the end of the package body causes the same effect of a subprogram body; it causes freezing of the entities declared before it within the same declarative part [AAR95, Section 13.14(3)].
Obviously, because no code is generated for a generic unit, generic units do not have freeze nodes; the freeze nodes must be generated in the instances. Library-level instances freeze everything at the end of the corresponding body. However, local instances only cause freezing of: (1) its real parameters as well as their default-expressions (if any), and (2) the entities declared before it within the same declarative part. This behavior avoids a premature freezing of global types used in the generic. In addition, the compiler must have special care with early instantiation of local packages. For example:
At the point of the early instantiation, the Semantic Analyzer generates an instance of the specification (required to continue the analysis of the subprogram Local); the corresponding instance is generated in a latter pass (cf. described in Section 7.4). However, the semantic analyzer must add a freezing node to give GiGi the right order of elaboration of the packages (cf. Install_Body and Freeze_Subprogram_Body subprograms).
Ada allows programmers to provide explicit information on the final representation of an entity. The compiler generates a Freeze_Entity node at the point where an entity is frozen, and sets a pointer from the defining entity to its corresponding freeze node. Freeze nodes are later processed by the expander to generate associated code and data. Although the Ada Reference Manual defines the freezing points of the language, the compiler must have special care with 1) anonymous, implicit and incomplete types, 2) default expressions, 3) protected types, and 4) generic units. These details have been discussed in this chapter.