The Size of Discriminated Records with Default Discriminants (GNAT Reference Manual)

### 16.5 The Size of Discriminated Records with Default Discriminants ¶

If a discriminated type `T` has discriminants with default values, it is possible to declare an object of this type without providing an explicit constraint:

    type Size is range 1..100;

    type Rec (D : Size := 15) is record
       Name : String (1..D);
    end T;

    Word : Rec;

Such an object is said to be ‘unconstrained’. The discriminant of the object can be modified by a full assignment to the object, as long as it preserves the relation between the value of the discriminant, and the value of the components that depend on it:

    Word := (3, "yes");

    Word := (5, "maybe");

    Word := (5, "no"); -- raises Constraint_Error

In order to support this behavior efficiently, an unconstrained object is given the maximum size that any value of the type requires. In the case above, `Word` has storage for the discriminant and for a `String` of length 100. It is important to note that unconstrained objects do not require dynamic allocation. It would be an improper implementation to place on the heap those components whose size depends on discriminants. (This improper implementation was used by some Ada83 compilers, where the `Name` component above would have been stored as a pointer to a dynamic string). Following the principle that dynamic storage management should never be introduced implicitly, an Ada compiler should reserve the full size for an unconstrained declared object, and place it on the stack.

This maximum size approach has been a source of surprise to some users, who expect the default values of the discriminants to determine the size reserved for an unconstrained object: “If the default is 15, why should the object occupy a larger size?” The answer, of course, is that the discriminant may be later modified, and its full range of values must be taken into account. This is why the declaration:

    type Rec (D : Positive := 15) is record
       Name : String (1..D);
    end record;

    Too_Large : Rec;

is flagged by the compiler with a warning: an attempt to create `Too_Large` will raise `Storage_Error`, because the required size includes `Positive'Last` bytes. As the first example indicates, the proper approach is to declare an index type of ‘reasonable’ range so that unconstrained objects are not too large.

One final wrinkle: if the object is declared to be `aliased`, or if it is created in the heap by means of an allocator, then it is ‘not’ unconstrained: it is constrained by the default values of the discriminants, and those values cannot be modified by full assignment. This is because in the presence of aliasing all views of the object (which may be manipulated by different tasks, say) must be consistent, so it is imperative that the object, once created, remain invariant.