From Modula-2 Reloaded

Spec: Flexible Array Fields In Records

Terminology

Determinate and Indeterminate Types
Array Bounds
Record Types and Fields

Declaration of Indeterminate Record Types

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 *)

Allocation

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:

allocSize(T) = TSIZE(T) + discriminant * TSIZE( baseType(T.indeterminateField) )

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;

Immutability of the Discriminant Field

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:

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 -- *)

Run-time Bounds Checking

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;

Assignment Compatibility

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 *)

Parameter passing

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 );

Deallocation

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) );

Outstanding Issues

The following issues still need to be resolved

Attempt to allocate without the use of 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?

Attempt to deallocate without the use of RELEASE

The use of DEALLOCATE with records of indeterminate types is undesirable because of

How should an attempt to deallocate a record of indeterminate type using DEALLOCATE be handled?

Obtaining the actual allocation size of records of indeterminate type

Do we need to reintroduce SIZE to obtain the actual allocation size? Probably yes.

Retrieved from http://modula-2.net/m2r10/pmwiki.php?n=Spec.FlexibleArrayFieldsInRecords
Page last modified on 2015-09-20 18:16