Next: , Previous: Biased Representation, Up: Representation Clauses and Pragmas


6.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
           at 0  range 0 .. Natural'Size - 1;
           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 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 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 erroneous to convert from one access subtype to the other.

At the implementation level, Esize stores the Object_Size and the RM_Size field stores the Value_Size (and hence the value of the Size attribute, which, as noted above, is equivalent to Value_Size).

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

                                            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
     
     subtype x5 is x2 range 0 .. dynamic;       16               3*
     
     subtype x6 is x2'base range 0 .. dynamic;   8               3*
     

Note: the entries marked “3*” are not actually specified by the Ada Reference Manual, but it seems in the spirit of the RM rules to allocate the minimum number of bits (here 3, given the range for x2) known to be large enough to hold the given range of values.

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:

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 be four bytes, and require four-byte alignment, and the Y component will be one byte. In this case R'Value_Size will be 40 (bits) since this is the minimum size required to store a value of this type, and for example, it is permissible to have a component of type R in an outer record whose component size is specified to be 48 bits. However, R'Object_Size will be 64 (bits), since it must be rounded up so that this value is a multiple of the alignment (4 bytes = 32 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.