In Ada 95 and Ada 2005,
T'Size for a type
T is the minimum
number of bits required to hold values of type
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,
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
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
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.
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
discrete types are as follows:
Object_Sizefor 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.
Object_Sizeof a subtype is the same as the
Object_Sizeof the type from which it is obtained.
Object_Sizeof a derived base type is copied from the parent base type, and the
Object_Sizeof a derived first subtype is copied from the parent first subtype.
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
Value_Size values differ generate a warning, and are potentially
The default rules for the value of
Value_Size are as follows:
Value_Sizefor 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).
Value_Sizeas 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”.)
Value_Sizecorresponding 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
Size attribute may be defined for a first-named subtype. This sets
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
Value_Size is affected only for the first subtype, any
dynamic subtypes obtained from it directly, and any statically matching
Value_Size of any other static subtypes is not affected.
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
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
which, as noted above, is equivalent to
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
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:
Sizefor a type
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
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
and Value_Size are the same (and equivalent to the RM attribute
Size may be specified for such types.