13.5.1 Record Representation Clauses
specifies the storage representation of records and record extensions,
that is, the order, position, and size of components (including discriminants,
Language Design Principles
It should be feasible for an implementation to use negative offsets in
the representation of composite types. However, no implementation should
be forced to support negative offsets. Therefore,
in the interest of uniformity
, negative offsets should be disallowed
Name Resolution Rules
Ramification: These need not have the
same integer type.
As for all type-related
representation items, the local_name
is required to denote a first subtype.
If the component_local_name
is a direct_name
shall denote a component of the type. For a record extension, the component
shall not be inherited, and shall not be a discriminant that corresponds
to a discriminant of the parent type. If the component_local_name
has an attribute_designator
of the local_name
shall denote either the declaration of the type or a component of the
type, and the attribute_designator
shall denote an implementation-defined implicit component of the type.
such as “X at
0..–1;” is allowed
if X can fit in zero bits.
If the nondefault bit ordering applies to the type,
the value of last_bit
shall be less than the size of the largest machine scalar; or
the value of first_bit
shall be zero and the value of last_bit
+ 1 shall be a multiple of System.Storage_Unit.
might seem strange to make the record_representation_clause
part of the declarative region, and then disallow mentions of the components
within almost all of the record_representation_clause
The alternative would be to treat the component_local_name
like a formal parameter name in a subprogram call (in terms of visibility).
However, this rule would imply slightly different semantics, because
(given the actual rule) the components can hide other declarations. This
was the rule in Ada 83, and we see no reason to change it. The following,
for example, was and is illegal:
type T is
X : Integer;
X : constant := 31; -- Same defining name as the component.
for T use
X at 0 range 0..X; -- Illegal!
for Record layout: See
If the nondefault bit ordering applies to the type, then the layout is determined as follows:
for which the value of last_bit
is greater than or equal to the size of the largest machine scalar directly
specify the position and size of the corresponding component;
For example, if Storage_Unit is 8, then “C
at 0 range 24..31;” defines C'Position = 3, C'First_Bit
= 0, and C'Last_Bit = 7. This is true of machines with either bit ordering.
also determines the value of the Size attribute of the component, since
this attribute is related to First_Bit and Last_Bit.
for a record extension does not override the layout of the parent part;]
if the layout was specified for the parent type, it is inherited by the
An implementation may generate implementation-defined
components (for example, one containing the offset of another component).
An implementation may generate names that denote such implementation-defined
components; such names shall be implementation-defined attribute_reference
An implementation may allow such implementation-defined names to be used
An implementation can restrict such component_clause
in any manner it sees fit.
Implementation defined: Implementation-defined
Of course, since the semantics
of implementation-defined attributes is implementation defined, the implementation
need not support these names in all situations. They might be purely
for the purpose of component_clause
for example. The visibility rules for such names are up to the implementation.
We do not allow such component names to be normal
identifiers — that would constitute blanket permission to do all
kinds of evil things.
implementation-defined components are known in the vernacular as “dope.”
Their main purpose is for storing offsets of components that depend on
If a record_representation_clause
is given for an untagged derived type, the storage place attributes for
all of the components of the derived type may differ from those of the
corresponding components of the parent type, even for components whose
storage place is not specified explicitly in the record_representation_clause
Reason: This is clearly necessary, since
the whole record may need to be laid out differently.
An implementation should support machine scalars
that correspond to all of the integer, floating point, and address formats
supported by the machine.
An implementation should support storage places
that can be extracted with a load, mask, shift sequence of machine code,
and set with a load, shift, mask, store sequence, given the available
machine instructions and run-time model.
A storage place should be supported if its size
is equal to the Size of the component subtype, and it starts and ends
on a boundary that obeys the Alignment of the component subtype.
For If the default
bit ordering applies to the declaration of a given type, then for
a component with a subtype
Size is less than the word size, any storage place that does not
cross an aligned word boundary should be supported.
Reason: The above recommendations are
sufficient to define interfaces to most interesting hardware. This causes
less implementation burden than the definition in ACID, which requires
arbitrary bit alignments of arbitrarily large components. Since the ACID
definition is neither enforced by the ACVC, nor supported by all implementations,
it seems OK for us to weaken it.
An implementation may reserve a storage place for
the tag field of a tagged type, and disallow other components from overlapping
Ramification: Similar permission for
other dope is not granted.
An implementation need not support a component_clause
for a component of an extension part if the storage place is not after
the storage places of all components of the parent type, whether or not
those storage places had been specified.
Reason: These restrictions are probably
necessary if block equality operations are to be feasible for class-wide
types. For block comparison to work, the implementation typically has
to fill in any gaps with zero (or one) bits. If a “gap” in
the parent type is filled in with a component in a type extension, then
this won't work when a class-wide object is passed by reference, as is
15 If no component_clause
is given for a component, then the choice of the storage place for the
component is left to the implementation. If component_clause
are given for all components, the record_representation_clause
completely specifies the representation of the type and will be obeyed
exactly by the implementation.
cannot be given for a protected type, even though protected types, like
record types, have components. The primary reason for this rule is that
there is likely to be too much dope in a protected type — entry
queues, bit maps for barrier values, etc. In order to control the representation
of the user-defined components, simply declare a record type, give it
a record_representation_clause representation_clause
and give the protected type one component whose type is the record type.
Alternatively, if the protected object is protecting something like a
device register, it makes more sense to keep the thing being protected
outside the protected object (possibly with a pointer to it in the protected
object), in order to keep implementation-defined components out of the
Example of specifying
the layout of a record type:
Word : constant := 4; -- storage element is byte, 4 bytes per word
type State is (A,M,W,P);
type Mode is (Fix, Dec, Exp, Signif);
type Byte_Mask is array (0..7) of Boolean;
type State_Mask is array (State) of Boolean;
type Mode_Mask is array (Mode) of Boolean;
type Program_Status_Word is
System_Mask : Byte_Mask;
Protection_Key : Integer range 0 .. 3;
Machine_State : State_Mask;
Interrupt_Cause : Interruption_Code;
Ilc : Integer range 0 .. 3;
Cc : Integer range 0 .. 3;
Program_Mask : Mode_Mask;
Inst_Address : Address;
for Program_Status_Word use
System_Mask at 0*Word range 0 .. 7;
Protection_Key at 0*Word range 10 .. 11; -- bits 8,9 unused
Machine_State at 0*Word range 12 .. 15;
Interrupt_Cause at 0*Word range 16 .. 31;
Ilc at 1*Word range 0 .. 1; -- second word
Cc at 1*Word range 2 .. 3;
Program_Mask at 1*Word range 4 .. 7;
Inst_Address at 1*Word range 8 .. 31;
for Program_Status_Word'Size use 8*System.Storage_Unit;
for Program_Status_Word'Alignment use 8;
16 Note on the example:
defines the record layout. The Size clause guarantees that (at least)
eight storage elements are used for objects of the type. The Alignment
clause guarantees that aliased, imported, or exported objects of the
type will have addresses divisible by eight.
Wording Changes from Ada 83
We have clarified that implementation-defined
component names have to be in the form of an attribute_reference
of a component or of the first subtype itself; surely Ada 83 did not
intend to allow arbitrary identifiers.
The RM83-13.4(7) wording incorrectly allows
components in nonvariant records to overlap. We have corrected that oversight.
Incompatibilities With Ada 95
The meaning of a record_representation_clause
for the nondefault bit order is now clearly defined. Thus, such clauses
can be portably written. In order to do that though, the equivalence
of bit 1 in word 1 to bit 9 in word 0 (for a machine with Storage_Unit
= 8) had to be dropped for the nondefault bit order. Any record_representation_clauses
which depends on that equivalence will break (although such code would
imply a noncontiguous representation for a component, and it seems unlikely
that compilers were supporting that anyway).
Extensions to Ada 95
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe