Skip to main content

13.5 Record Layout

danger

This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue

1

The (record) layout aspect of representation consists of the storage places for some or all components, that is, storage place attributes of the components. The layout can be specified with a record_representation_clause.

13.5.1 Record Representation Clauses

1

[A record_representation_clause specifies the storage representation of records and record extensions, that is, the order, position, and size of components (including discriminants, if any). ]

Language Design Principles

1.a/2

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 in record_representation_clauses.

Syntax

2/5

record_representation_clause ::=
for first_subtype_local_name use
record [mod_clause]
{component_clause}
end record [local_name];

3

component_clause ::=
component_local_name at position range first_bit .. last_bit;

4
position ::= static_expression
5
first_bit ::= static_simple_expression
6
last_bit ::= static_simple_expression
6.a
reason

First_bit and last_bit need to be simple_expression instead of expression for the same reason as in range (see 3.5, “Scalar Types”).

6.1/5

If a local_name appears at the end of the record_representation_clause, it shall repeat the first_subtype_local_name.

Name Resolution Rules

7

Each position, first_bit, and last_bit is expected to be of any integer type.

7.a
ramification

These need not have the same integer type.

Legality Rules

8/2

The first_subtype_local_name of a record_representation_clause shall denote a specific record or record extension subtype.

8.a
ramification

As for all type-related representation items, the local_name is required to denote a first subtype.

9

If the component_local_name is a direct_name, the local_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, the direct_name 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.

10

The position, first_bit, and last_bit shall be static expressions. The value of position and first_bit shall be nonnegative. The value of last_bit shall be no less than first_bit – 1.

10.a
ramification

A component_clause such as “X at 4 range 0..–1;” is allowed if X can fit in zero bits.

10.1/2

If the nondefault bit ordering applies to the type, then either:

10.2/2
  • the value of last_bit shall be less than the size of the largest machine scalar; or
  • 10.3/2
  • the value of first_bit shall be zero and the value of last_bit + 1 shall be a multiple of System.Storage_Unit.
11

At most one component_clause is allowed for each component of the type, including for each discriminant (component_clauses may be given for some, all, or none of the components). Storage places within a component_list shall not overlap, unless they are for components in distinct variants of the same variant_part.

12

A name that denotes a component of a type is not allowed within a record_representation_clause for the type, except as the component_local_name of a component_clause.

12.a
reason

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

12.b

type T is record X : Integer; end record; X : constant := 31; -- Same defining name as the component. for T use record X at 0 range 0..X; -- Illegal! end record;

12.c

The component X hides the named number X throughout the record_representation_clause.

Static Semantics

13/2

A record_representation_clause (without the mod_clause) specifies the layout.

13.a/3

Aspect Description for Layout (record): Layout of record components. Specified by a record_representation_clause, not by an aspect_specification.

13.b/3

Aspect Description for Record layout: See Layout.

13.1/2

If the default bit ordering applies to the type, the position, first_bit, and last_bit of each component_clause directly specify the position and size of the corresponding component.

13.2/3

If the nondefault bit ordering applies to the type, then the layout is determined as follows:

13.3/2
13.c/2
This paragraph was deleted.
13.d
ramification

A component_clause also determines the value of the Size attribute of the component, since this attribute is related to First_Bit and Last_Bit.

14

[A record_representation_clause 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 record extension.

Implementation Permissions

15

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_references. An implementation may allow such implementation-defined names to be used in record_representation_clauses. An implementation can restrict such component_clauses in any manner it sees fit.

15.a
implementation defined

Implementation-defined components.

15.b
ramification

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_clauses, for example. The visibility rules for such names are up to the implementation.

15.c

We do not allow such component names to be normal identifiers — that would constitute blanket permission to do all kinds of evil things.

15.d
discussion

Such implementation-defined components are known in the vernacular as “dope”. Their main purpose is for storing offsets of components that depend on discriminants.

16

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.

16.a
reason

This is clearly necessary, since the whole record may need to be laid out differently.

Implementation Advice

17

The recommended level of support for record_representation_clauses is:

17.1/2
  • An implementation should support machine scalars that correspond to all of the integer, floating point, and address formats supported by the machine.
  • 18
  • 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.
  • 19
  • 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.
  • 20/2
  • For a component with a subtype whose Size is less than the word size, any storage place that does not cross an aligned word boundary should be supported.
20.a
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.

21
  • An implementation may reserve a storage place for the tag field of a tagged type, and disallow other components from overlapping that place.
21.a
ramification

Similar permission for other dope is not granted.

22/5
  • An implementation is not required to 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.
22.a
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 required.

22.b/2
implementation advice

The recommended level of support for record_representation_clauses should be followed.

23

NOTE 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_clauses are given for all components, the record_representation_clause completely specifies the representation of the type and will be obeyed exactly by the implementation.

23.a
ramification

The visibility rules prevent the name of a component of the type from appearing in a record_representation_clause at any place except for the component_local_name of a component_clause. However, since the record_representation_clause is part of the declarative region of the type declaration, the component names hide outer homographs throughout.

23.b/1

{8652/0009} A record_representation_clause 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, 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 way.

Examples

24

Example of specifying the layout of a record type:

25

Word : constant := 4; -- storage element is byte, 4 bytes per word 26 type State is (A,M,W,P); type Mode is (Fix, Dec, Exp, Signif); 27/5

type Byte_Mask is array (0..7) of Boolean with Component_Size => 1; type State_Mask is array (State) of Boolean with Component_Size => 1; type Mode_Mask is array (Mode) of Boolean with Component_Size => 1; 28 type Program_Status_Word is record 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; end record; 29 for Program_Status_Word use record 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; end record; 30 for Program_Status_Word'Size use 8*System.Storage_Unit; for Program_Status_Word'Alignment use 8;

31/5

The record_representation_clause 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

32.a

The alignment_clause has been renamed to mod_clause and moved to Annex J, “Obsolescent Features”.

32.b

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.

32.c

The RM83-13.4(7) wording incorrectly allows components in nonvariant records to overlap. We have corrected that oversight.

Incompatibilities With Ada 95

32.d/2
correction

Amendment 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

32.e/2
correction

Amendment The undocumented (and likely unintentional) incompatibility with Ada 83 caused by not allowing record_representation_clauses on limited record types is removed.

Extensions to Ada 2012

32.f/5

The local_name following end record is new.

13.5.2 Storage Place Attributes

Static Semantics

1

For a component C of a composite, non-array object R, the storage place attributes are defined:

1.a
ramification

The storage place attributes are not (individually) specifiable, but the user may control their values by giving a record_representation_clause.

2/2

R.C'Position
If the nondefault bit ordering applies to the composite type, and if a component_clause specifies the placement of C, denotes the value given for the position of the component_clause; otherwise, denotes the same value as R.C'Address – R'Address. The value of this attribute is of the type universal_integer.
2.a/2
ramification

Thus, for the default bit order, R.C'Position is the offset of C in storage elements from the beginning of the object, where the first storage element of an object is numbered zero. R'Address + R.C'Position = R.C'Address. For record extensions, the offset is not measured from the beginning of the extension part, but from the beginning of the whole object, as usual.

2.b

In “R.C'Address – R'Address”, the "–" operator is the one in System.Storage_Elements that takes two Addresses and returns a Storage_Offset.

3/2

R.C'First_Bit
If the nondefault bit ordering applies to the composite type, and if a component_clause specifies the placement of C, denotes the value given for the first_bit of the component_clause; otherwise, denotes the offset, from the start of the first of the storage elements occupied by C, of the first bit occupied by C. This offset is measured in bits. The first bit of a storage element is numbered zero. The value of this attribute is of the type universal_integer.
4/2

R.C'Last_Bit
If the nondefault bit ordering applies to the composite type, and if a component_clause specifies the placement of C, denotes the value given for the last_bit of the component_clause; otherwise, denotes the offset, from the start of the first of the storage elements occupied by C, of the last bit occupied by C. This offset is measured in bits. The value of this attribute is of the type universal_integer.
4.a/2
ramification

The ordering of bits in a storage element is defined in 13.5.3, “Bit Ordering”.

4.b

R.C'Size = R.C'Last_Bit – R.C'First_Bit + 1. (Unless the implementation chooses an indirection representation.)

4.c

If a component_clause applies to a component, then that component will be at the same relative storage place in all objects of the type. Otherwise, there is no such requirement.

Implementation Advice

5

If a component is represented using some form of pointer (such as an offset) to the actual data of the component, and this data is contiguous with the rest of the object, then the storage place attributes should reflect the place of the actual data, not the pointer. If a component is allocated discontiguously from the rest of the object, then a warning should be generated upon reference to one of its storage place attributes.

5.a
reason

For discontiguous components, these attributes make no sense. For example, an implementation might allocate dynamic-sized components on the heap. For another example, an implementation might allocate the discriminants separately from the other components, so that multiple objects of the same subtype can share discriminants. Such representations cannot happen if there is a component_clause for that component.

5.b/2
implementation advice

If a component is represented using a pointer to the actual data of the component which is contiguous with the rest of the object, then the storage place attributes should reflect the place of the actual data. If a component is allocated discontiguously from the rest of the object, then a warning should be generated upon reference to one of its storage place attributes.

Incompatibilities With Ada 95

5.c/2
correction

Amendment The meaning of the storage place attributes for the nondefault bit order is now clearly defined, and can be different than that given by strictly following the Ada 95 wording. Any code which depends on the original Ada 95 values for a type using the nondefault bit order where they are different will break.

13.5.3 Bit Ordering

1

[The Bit_Order attribute specifies the interpretation of the storage place attributes.]

1.a
reason

The intention is to provide uniformity in the interpretation of storage places across implementations on a particular machine by allowing the user to specify the Bit_Order. It is not intended to fully support data interoperability across different machines, although it can be used for that purpose in some situations.

1.b/2

We can't require all implementations on a given machine to use the same bit ordering by default; if the user cares, a Bit_Order attribute_definition_clause can be used to force all implementations to use the same bit ordering.

Static Semantics

2

A bit ordering is a method of interpreting the meaning of the storage place attributes. High_Order_First [(known in the vernacular as “big endian”)] means that the first bit of a storage element (bit 0) is the most significant bit (interpreting the sequence of bits that represent a component as an unsigned integer value). Low_Order_First [(known in the vernacular as “little endian”)] means the opposite: the first bit is the least significant.

3/5

For every specific record subtype S, the following representation attribute is defined:

4

S'Bit_Order
Denotes the bit ordering for the type of S. The value of this attribute is of type System.Bit_Order. Bit_Order may be specified for specific record types via an attribute_definition_clause; the expression of such a clause shall be static.
4.a/3

Aspect Description for Bit_Order: Order of bit numbering in a record_representation_clause.

5

If Word_Size = Storage_Unit, the default bit ordering is implementation defined. If Word_Size > Storage_Unit, the default bit ordering is the same as the ordering of storage elements in a word, when interpreted as an integer.

5.a
implementation defined

If Word_Size = Storage_Unit, the default bit ordering.

5.b
ramification

Consider machines whose Word_Size = 32, and whose Storage_Unit = 8. Assume the default bit ordering applies. On a machine with big-endian addresses, the most significant storage element of an integer is at the address of the integer. Therefore, bit zero of a storage element is the most significant bit. On a machine with little-endian addresses, the least significant storage element of an integer is at the address of the integer. Therefore, bit zero of a storage element is the least significant bit.

6

The storage place attributes of a component of a type are interpreted according to the bit ordering of the type.

6.a
ramification

This implies that the interpretation of the position, first_bit, and last_bit of a component_clause of a record_representation_clause obey the bit ordering given in a representation item.

Implementation Advice

7

The recommended level of support for the nondefault bit ordering is:

8/2
  • The implementation should support the nondefault bit ordering in addition to the default bit ordering.
8.a/2
ramification

The implementation should support both bit orderings. Implementations are required to support storage positions that cross storage element boundaries when Word_Size > Storage_Unit but the definition of the storage place attributes for the nondefault bit order ensures that such storage positions will not be split into two or three pieces. Thus, there is no significant implementation burden to supporting the nondefault bit order, given that the set of machine scalars is implementation-defined.

8.b/2
implementation advice

The recommended level of support for the nondefault bit ordering should be followed.

9/2

NOTE Bit_Order clauses make it possible to write record_representation_clauses that can be ported between machines having different bit ordering. They do not guarantee transparent exchange of data between such machines.

Extensions to Ada 83

9.a

The Bit_Order attribute is new to Ada 95.

Wording Changes from Ada 95

9.b/2

We now suggest that all implementations support the nondefault bit order.