Indeterminate record types may be declared, provided that the type declaration meets all of the following conditions:
An example declaration of an indeterminate record type Foobar
with a discriminant field size
and an indeterminate field buffer
is shown below:
TYPE Foobar = RECORD a, b, c : Foo; (* other fields *) size : CARDINAL; (* discriminant field *) ~ buffer : ARRAY size OF OCTET; (* indeterminate field *) END; (* Foobar *)
The value returned by TSIZE
for an indeterminate record type is the value of its allocation size without the size of the indeterminate field.
Records of an indeterminate type may only be allocated dynamically at runtime using the NEW
statement (see issues). When the record is allocated, the discriminant value must be passed to NEW
as an additional parameter. Any attempt to allocate a record of indeterminate type without passing the discriminant value results in a compile time error.
The compiler replaces a macro call to NEW
for an indeterminate type with a code fragment that calculates the correct allocation size, calls ALLOCATE
and initialises the discriminant field of the new record with the discriminant value passed.
The allocation size is calculated by the following formula:
where T is the indeterminate record type, discriminant is the discriminant value passed to NEW
and baseType(T.indeterminateField) is the element type of the indeterminate field.
An example allocation of a new record of indeterminate type Foobar
with a discriminant value of 100 is shown below:
VAR foo : POINTER TO Foobar; BEGIN (* allocate a record of type Foobar with a buffer of 100 elements *) NEW foo OF 100;
This source fragment is then replaced by the compiler with the following code fragment:
ALLOCATE( foo, TSIZE(foo) + 100 * TSIZE(OCTET) ); IF foo # NIL THEN foo^.size := 100 END;
The discriminant field of a record of indeterminate type is automatically initialised when it is allocated. After initialisation the discriminant field becomes immutable and the compiler enforces its immutability as follows:
VAR
parameter
++
and --
operations may not be used on a discriminant field
The following statements all result in compile time errors due to immutability of foo^.size
:
INC(foo^.size, 10); (* discriminant fields may not be passed as VAR parameter *) foo^.size := newSize; (* discriminant fields may not be assigned to *) foo^.size++; (* discriminant fields may not be used with ++ or -- *)
Access to the indeterminate array field of a record of indeterminate type is bounds checked at runtime in the same manner as access to a determinate array is checked. The compiler automatically inserts the code to check array subscripts against the discriminant field. Any attempt to access the array with a subscript that is out of bounds results in a run-time error.
An example of an assignment to a variable size array field is shown below:
foo^.buffer[index] := 0;
generates target code equivalent to
IF index < foo^.size THEN foo^.buffer[index] := 0 ELSE Error(OutOfBounds) END;
The assignment compatibility of two records of indeterminate type cannot be verified at compile time. For this reason records of indeterminate type can only be copied field-wise, not record-wise.
The following example shows how one record of type Foobar
is copied to another:
VAR foo, bar : POINTER TO Foobar; BEGIN foo^.a := bar^.a; foo^.b := bar^.b; foo^.c := bar^.c; FOR index, value IN foo^.buffer DO value := bar^.buffer[index] END;
The following example will result in a compile time error:
foo^ := bar^; (* records of indeterminate type may not be copied record-wise *)
Since the compatibility of records of indeterminate types cannot be determined at compile time, it follows that indeterminate record types may not be formal types and records of indeterminate type may not be passed as parameters unless the formal type is CAST ARRAY OF OCTET
because this formal type is compatible with any type.
The following example declaration results in a compile time error:
PROCEDURE clear ( f : Foobar ); (* compile time error: indeterminate record types may not be formal types *)
The indeterminate field of a record of indeterminate type may be passed as a parameter whose formal type is an open array with the same element type as the indeterminate field's array type.
An example of passing the indeterminate field foo^.buffer
as a parameter is shown below:
PROCEDURE clear ( VAR buffer : ARRAY OF OCTET ); VAR index : CARDINAL; BEGIN FOR VALUE elem IN buffer DO elem := 0; END; END clear; BEGIN (* module initialisation *) clear( foo^.buffer );
Records of indeterminate type may only be deallocated using the RELEASE
statement (see issues).
An example deallocation of a record of type Foobar
is shown below:
RELEASE foo;
This statement is then transformed by the compiler to the following code fragment
DEALLOCATE( foo, TSIZE(foo) + foo^.size * TSIZE(OCTET) );
The following issues still need to be resolved
NEW
The use of ALLOCATE
with records of indeterminate types is insufficient because the determinant field of the allocated record is immutable and must be initialised during allocation.
How should an attempt to allocate a record of indeterminate type using ALLOCATE
be handled?
ALLOCATE
to initialise all allocated memory to zero (expensive, thus undesirable)
ALLOCATE
ALLOCATE
RELEASE
The use of DEALLOCATE
with records of indeterminate types is undesirable because of
TSIZE
, must be passed to DEALLOCATE
NEW
must be used for allocation, RELEASE
should be used for deallocation
How should an attempt to deallocate a record of indeterminate type using DEALLOCATE
be handled?
DEALLOCATE
DEALLOCATE
Do we need to reintroduce SIZE
to obtain the actual allocation size? Probably yes.