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:
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.
Object_Size
of a subtype is the same as the
Object_Size
of
the type from which it is obtained.
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:
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).
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".)
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:
Size
for 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 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
.