Value_Size and Object_Size Clauses (GNAT Reference Manual)

### 10.6 Value_Size and Object_Size Clauses ¶

In Ada 95 and Ada 2005, `T'Size` for a type `T` is the minimum number of bits required to hold values of type `T`. Although this interpretation was allowed in Ada 83, it was not required, and this requirement in practice can cause some significant difficulties. For example, in most Ada 83 compilers, `Natural'Size` was 32. However, in Ada 95 and Ada 2005, `Natural'Size` is typically 31. This means that code may change in behavior when moving from Ada 83 to Ada 95 or Ada 2005. For example, consider:

    type Rec is record
       A : Natural;
       B : Natural;
    end record;

    for Rec use record
       A at 0 range 0 .. Natural'Size - 1;
       B at 0 range Natural'Size .. 2 * Natural'Size - 1;
    end record;

In the above code, since the typical size of `Natural` objects is 32 bits and `Natural'Size` is 31, the above code can cause unexpected inefficient packing in Ada 95 and Ada 2005, and in general there are cases where the fact that the object size can exceed the size of the type causes surprises.

To help get around this problem GNAT provides two implementation defined attributes, `Value_Size` and `Object_Size`. When applied to a type, these attributes yield the size of the type (corresponding to the RM defined size attribute), and the size of objects of the type respectively.

The `Object_Size` is used for determining the default size of objects and components. This size value can be referred to using the `Object_Size` attribute. The phrase ‘is used’ here means that it is the basis of the determination of the size. The backend is free to pad this up if necessary for efficiency, e.g., an 8-bit stand-alone character might be stored in 32 bits on a machine with no efficient byte access instructions such as the Alpha.

The default rules for the value of `Object_Size` for discrete types are as follows:

- The `Object_Size` for base subtypes reflect the natural hardware size in bits (run the compiler with ‘-gnatS’ to find those values for numeric types). Enumeration types and fixed-point base subtypes have 8, 16, 32, or 64 bits for this size, depending on the range of values to be stored.
- The `Object_Size` of a subtype is the same as the `Object_Size` of the type from which it is obtained.
- The `Object_Size` of a derived base type is copied from the parent base type, and the `Object_Size` of a derived first subtype is copied from the parent first subtype.

The `Value_Size` attribute is the (minimum) number of bits required to store a value of the type. This value is used to determine how tightly to pack records or arrays with components of this type, and also affects the semantics of unchecked conversion (unchecked conversions where the `Value_Size` values differ generate a warning, and are potentially target dependent).

The default rules for the value of `Value_Size` are as follows:

- The `Value_Size` for a base subtype is the minimum number of bits required to store all values of the type (including the sign bit only if negative values are possible).
- If a subtype statically matches the first subtype of a given type, then it has by default the same `Value_Size` as the first subtype. (This is a consequence of RM 13.1(14): “if two subtypes statically match, then their subtype-specific aspects are the same”.)
- All other subtypes have a `Value_Size` corresponding to the minimum number of bits required to store all values of the subtype. For dynamic bounds, it is assumed that the value can range down or up to the corresponding bound of the ancestor

The RM defined attribute `Size` corresponds to the `Value_Size` attribute.

The `Size` attribute may be defined for a first-named subtype. This sets the `Value_Size` of the first-named subtype to the given value, and the `Object_Size` of this first-named subtype to the given value padded up to an appropriate boundary. It is a consequence of the default rules above that this `Object_Size` will apply to all further subtypes. On the other hand, `Value_Size` is affected only for the first subtype, any dynamic subtypes obtained from it directly, and any statically matching subtypes. The `Value_Size` of any other static subtypes is not affected.

`Value_Size` and `Object_Size` may be explicitly set for any subtype using an attribute definition clause. Note that the use of these attributes can cause the RM 13.1(14) rule to be violated. If two access types reference aliased objects whose subtypes have differing `Object_Size` values as a result of explicit attribute definition clauses, then it is illegal to convert from one access subtype to the other. For a more complete description of this additional legality rule, see the description of the `Object_Size` attribute.

To get a feel for the difference, consider the following examples (note that in each case the base is `Short_Short_Integer` with a size of 8):

Type or subtype declaration | Object_Size | Value_Size
--- | --- | ---
`type X1 is range 0 .. 5;` | 8 | 3
`type X2 is range 0 .. 5; for X2'Size use 12;` | 16 | 12
`subtype X3 is X2 range 0 .. 3;` | 16 | 2
`subtype X4 is X2'Base range 0 .. 10;` | 8 | 4
`Dynamic : X2'Base range -64 .. +63;` |  |
`subtype X5 is X2 range 0 .. Dynamic;` | 16 | 3*
`subtype X6 is X2'Base range 0 .. Dynamic;` | 8 | 7*

Note: the entries marked ‘*’ are not actually specified by the Ada Reference Manual, which has nothing to say about size in the dynamic case. What GNAT does is to allocate sufficient bits to accommodate any possible dynamic values for the bounds at run-time.

So far, so good, but GNAT has to obey the RM rules, so the question is under what conditions must the RM `Size` be used. The following is a list of the occasions on which the RM `Size` must be used:

- Component size for packed arrays or records
- Value of the attribute `Size` for a type
- Warning about sizes not matching for unchecked conversion

For record types, the `Object_Size` is always a multiple of the alignment of the type (this is true for all types). In some cases the `Value_Size` can be smaller. Consider:

    type R is record
      X : Integer;
      Y : Character;
    end record;

On a typical 32-bit architecture, the X component will occupy four bytes and the Y component will occupy one byte, for a total of 5 bytes. As a result `R'Value_Size` will be 40 (bits) since this is the minimum size required to store a value of this type. For example, it is permissible to have a component of type R in an array whose component size is specified to be 40 bits.

However, `R'Object_Size` will be 64 (bits). The difference is due to the alignment requirement for objects of the record type. The X component will require four-byte alignment because that is what type Integer requires, whereas the Y component, a Character, will only require 1-byte alignment. Since the alignment required for X is the greatest of all the components’ alignments, that is the alignment required for the enclosing record type, i.e., 4 bytes or 32 bits. As indicated above, the actual object size must be rounded up so that it is a multiple of the alignment value. Therefore, 40 bits rounded up to the next multiple of 32 yields 64 bits.

For all other types, the `Object_Size` and `Value_Size` are the same (and equivalent to the RM attribute `Size`). Only `Size` may be specified for such types.

Note that `Value_Size` can be used to force biased representation for a particular subtype. Consider this example:

    type R is (A, B, C, D, E, F);
    subtype RAB is R range A .. B;
    subtype REF is R range E .. F;

By default, `RAB` has a size of 1 (sufficient to accommodate the representation of `A` and `B`, 0 and 1), and `REF` has a size of 3 (sufficient to accommodate the representation of `E` and `F`, 4 and 5). But if we add the following `Value_Size` attribute definition clause:

    for REF'Value_Size use 1;

then biased representation is forced for `REF`, and 0 will represent `E` and 1 will represent `F`. A warning is issued when a `Value_Size` attribute definition clause forces biased representation. This warning can be turned off using `-gnatw.B`.