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 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|
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 accomodate 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:
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 array 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.
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.