A variable having an indirect type (called an indirect variable) is a pointer. The value of an indirect variable is either nil
or a reference to some dynamic variable (i.e., it 'points' to the dynamic variable). For example, in
TYPE t : PTR STRING(ASCII) (5);
VAR x,y : t;
The variables x and y are indirect variables that can either have the value nil or can point to some dynamic variable
with subtype STRING(ASCII) (5). All indirect variables are automatically initialized to have value nil.
There is also a literal for the value nil. For example,
x := NIL;
sets the value of x to be nil.
A dynamic variable is created by elaboration of an allocation statement. For example, elaboration
of
ALLOC y PTR := "ABCDE";
creates a new dynamic variable with subtype STRING(ASCII) (5), initializes it to have the value "ABCDE", and sets y to point to this dynamic variable.
Note that dynamic variables, unlike other variables, are not defined or named.
Dynamic variables are referenced via indirect variables. As with direct types, .ALL qualification
and component selection operations (if the underlying dynamic variable has components) are
automatically defined. These operations are permitted only if the value of the indirect variable is not
nil. The operations provide access to the referenced dynamic variable or its components (i.e., they
are 'dereferencing' operations). For example,
The lifetime of a dynamic variable is different than that of other variables. A dynamic variable exists as long as there
is some way of accessing it. This means that the lifetime of a dynamic variable is not coupled to the elaboration of a scope.
As is the case for direct types, assignment (:=) is also automatically defined for indirect types. The assignment operation for
indirects, however, is a "sharing" assignment. For example,
The equality operators (=, /=) are also automatically defined for indirect types. For example,
As is the case for all data types, the subtype of a dynamic variable need not be known until the dynamic variable is created.
Constraints on a dynamic variable, which are to be resolved at creation time, are specified via an allocation statement. For example,
Dynamic variables can contain components having indirect types which reference other dynamic variables. This means that
recursive data structures, and data structures having cycles, can be created. For example,
ln addition to indirect variables, it is also possible to define indirect constants. Like all constants,
an indirect constant must be initialized and its value may not be changed. The value of the dynamic
variable which it references may, however, be changed.
When creating an abstract data type and its subtype, the programmer must ensure that the
implementation of the abstract type is invisible to the user. This permits the implementation to be
changed without affecting those parts of a program which use the abstract type. The programmer
who implements an abstract type should be able to change the underlying type from a direct type to
an indirect type (and vice versa), without affecting the users of the abstract type. For example, an
abstract stack data type could be implemented using either an array (a direct type) or a linked list
(achieved via an indirect type). For this reason, it is important that when a type is exported from a
capsule used to realize an abstract data type, it should not be possible to detect outside the capsule
whether the exported type was a direct or an indirect type.
As mentioned above, a dynamic variable exists as long as there is some way to access it.
Detecting when there is no longer any way to access a dynamic variable, and reclaiming the storage
that was used for it, usually involves a process called garbage collection. in some cases, the overhead
of full garbage collection can be avoided and a simpler, and less costly, strategy used. For cases
where this is impossible, the user can avoid garbage collection costs by the use of the FREE
procedure. If v is an indirect variable that points to some dynamic variable, and there are no other
pointers to that dynamic variable, then
FREE(v):
reclaims the storage for that dynamic variable and sets the value of v to nil. If there were other
pointers to the dynamic variable, the X_FREE exception is raised (this prevents the problem of
dangling pointers). Although this avoids the cost of garbage collection, it introduces some cost in
checking that there are no other pointers. For those cases where even this cost is unacceptable, it is
possible to inhibit the generation of code for doing this checking by suppressing the X_FREE
exception (see Appendix B).
RULES
Indirect Type Declaration
The identifier in an indirect type declaration is defined to be a type name in the scope ln which
the indirect type declaration appears. Indirect types are referenced using the same rules (both
syntax and semantics) as for direct types (see Section 4.4.2).
Only CONST formal parameters may be used.
Indirect types are invoked using the same syntax as direct types (see Section 4.4.2). Elaboration
of a user-defined subtype consists of elaborating the actual parameters and binding the actual
parameters to the formal parameters 1 of the named type (see Section 7.3).
The result of a user-defined subtype is a subtype of the invoked type, whose constraint property
list is the actual parameter list of the invocation.
The value of an indirect variable or constant is either nil or a reference to some underlying
dynamic variable. All indirect variables are automatically initialized to have the value nil.
The following operations are automatically defined for each indirect type:
- Assignment (:=) is a sharing assignment. If the indirect subtypes of left hand side and right
hand side are not equal, the X_SUBTYPE exception is raised.
- Equality (=) is defined to produce true if both of its actual parameters are nil, or if both
reference the same dynamic variable.
- Component selection, if the underlying type has components. If the value of the variable or
constant is nil when component selection is applied to the variable or constant, the X_NIL
exception ls raised.
- .ALL qualification which gives access to the underlying dynamic variable. If the value of the
variable or constant is nil when .ALL qualification is applied to the variable or constant, the
X_NIL exception is raised.
Types are assumed to be normal (see Section 7.2.1).
Allocation Statement
The variable must have an indirect type on which .ALL qualification is available.
Elaboration of the allocation statement consists of:
- elaborating the variable;
- elaborating the actual parameters;
- binding the actual parameters to the formal parameters 2 in the indirect type declaration
associated with the type of the variable;
- elaborating the subtype in the indirect type declaration;
- allocating a dynamic variable having that subtype;
- if initialization is specified, initializing the newly created dynamic variable (using :=); and
- setting the variable named in the allocation statement to be a reference to the newly created
dynamic variable.
The dynamic variable must have an assignable type if initialization is specified in an allocation statement.
NOTES
Because indirect types are always new types, and therefore named, the type equivalence rules are simplified
(see Section 4.1.5).
An indirect type declaration is a closed scope.
The formal parameter names (of both formal parameter lists) are defined
in this scope. Since a type declaration cannot have an imports list, no variable names may be used
within the subtype.
Representation specifications are used for machine-dependent programs and
are described in Section 12.2.
EXAMPLES
1) Indirect string types
2) Defines symbol tables which can hold symbols of different lengths
4.5 TYPE AND SUBTYPE INQUIRY, PREDICATES AND ASSERTIONS
Since a formal parameter can specify a type, rather than a subtype, a deferred unit with a formal
parameter can be invoked with actual parameters having any subtype belonging to that type. Although
this flexibility is often quite useful, there are cases where it is desirable to further limit either the
values or subtypes that actual parameters are permitted to have (in order to exclude values which are
not meaningful for the deferred unit). In many cases, these limitations also allow the translator to
produce more efficient code.
Some limitations can be achieved by specifying a subtype (rather than a type) for a formal
parameter. A finer degree of control can be achieved by including an assertion at the beginning of the
body of the deferred unit. Assertions concerning subtypes are supported by language facilities for
inquiring about the type, subtype and subtype properties of a data item. These features are discussed
in the following subsections.
Inquiry is also useful for several other purposes, including specifying the subtype of local data
items of a procedure or function and accessing array index bounds.
4.5.1 SUBTYPE INQUIRY
RULES
If exp is any expression, then the result of elaborating
SUBTYPE(exp)
is the subtype of that expression.
If exp is an expression for an n-dimensional array, and i is a manifest integer expression whose value is between one and n,
then the result of elaborating
INDEXOF(exp, 1)
is the 1'th index subtype of the array. The form
TYPE bits(n : INT) : ARRAY INT(1..n) OF BOOL;
is equivalent to
INDEXOF(exp, 1)
EXAMPLES
4.5.2 TYPE INQUIRY
RULES
If exp is an expression, then the result of elaborating
TYPE(st)
is the type to which that subtype belongs (see Section 4.1.3).
NOTES
Elaborations of TYPEOF takes place during translation.
4.5.3 ATTRIBUTES
In addition to inquiry of an entire subtype, it is also possible to inquire about specific subtype
constraints, called attributes.
RULES
The attributes of built-in types are listed in Appendix C.
Each formal parameter of a user-defined type is an attribute of that type. The identifier which is
the name of the formal parameter is used as the attribute name.