Skip to main content

3.8 Record Types


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


A record object is a composite object consisting of named components. The value of a record object is a composite value consisting of the values of the components.



record_type_definition ::= [[abstract] tagged] [limited] record_definition


record_definition ::=
end record [record_identifier]
| null record


component_list ::=
component_item {component_item}
| {component_item} variant_part
| null;


{8652/0009} component_item ::= component_declaration | aspect_clause


component_declaration ::=
defining_identifier_list : component_definition [:= default_expression]


If a record_identifier appears at the end of the record_definition, it shall repeat the defining_identifier of the enclosing full_type_declaration.

Name Resolution Rules


The expected type for the default_expression, if any, in a component_declaration is the type of the component.

Legality Rules


This paragraph was deleted.


Each component_declaration declares a component of the record type. Besides components declared by component_declarations, the components of a record type include any components declared by discriminant_specifications of the record type declaration. [The identifiers of all components of a record type shall be distinct.]


The identifiers of all components of a record type have to be distinct because they are all declared immediately within the same declarative region. See Clause 8.


Within a type_declaration, a name that denotes a component, protected subprogram, or entry of the type is allowed only in the following cases:

  • A name that denotes any component, protected subprogram, or entry is allowed within an aspect_specification, an operational item, or a representation item that occurs within the declaration of the composite type.
  • 12/3
  • A name that denotes a noninherited discriminant is allowed within the declaration of the type, but not within the discriminant_part. If the discriminant is used to define the constraint of a component, the bounds of an entry family, or the constraint of the parent subtype in a derived_type_definition, then its name shall appear alone as a direct_name (not as part of a larger expression or expanded name). A discriminant shall not be used to define the constraint of a scalar component.

The penultimate restriction simplifies implementation, and allows the outer discriminant and the inner discriminant or bound to possibly share storage.


Other rules prevent such a discriminant from being an inherited one.


The last restriction is inherited from Ada 83. The restriction is not really necessary from a language design point of view, but we did not remove it, in order to avoid unnecessary changes to existing compilers.


Note that a discriminant can be used to define the constraint for a component that is of an access-to-composite type.


The above rules, and a similar one in 6.1 for formal parameters, are intended to allow initializations of components or parameters to occur in a (nearly) arbitrary order — whatever order is most efficient (subject to the restrictions of 3.3.1), since one default_expression cannot depend on the value of another one. They also prevent circularities.


Inherited discriminants are not allowed to be denoted, except within aspect_specifications and representation items. However, the discriminant_selector_name of the parent subtype_indication is allowed to denote a discriminant of the parent.


If the name of the current instance of a type (see 8.6) is used to define the constraint of a component, then it shall appear as a direct_name that is the prefix of an attribute_reference whose result is of an access type, and the attribute_reference shall appear alone.


This rule allows T'Access or T'Unchecked_Access, but disallows, for example, a range constraint (1..T'Size). Allowing things like (1..T'Size) would mean that a per-object constraint could affect the size of the object, which would be bad.

Static Semantics


If a record_type_definition includes the reserved word limited, the type is called an explicitly limited record type.


The component_definition of a component_declaration defines the (nominal) subtype of the component. If the reserved word aliased appears in the component_definition, then the component is aliased (see 3.10).


If the component_list of a record type is defined by the reserved word null and there are no discriminants, then the record type has no components and all records of the type are null records. A record_definition of null record is equivalent to record null; end record.


This shorthand is available both for declaring a record type and a record extension — see 3.9.1.

Dynamic Semantics


The elaboration of a record_type_definition creates the record type and its first subtype, and consists of the elaboration of the record_definition. The elaboration of a record_definition consists of the elaboration of its component_list, if any.


The elaboration of a component_list consists of the elaboration of the component_items and variant_part, if any, in the order in which they appear. The elaboration of a component_declaration consists of the elaboration of the component_definition.


If the defining_identifier_list has more than one defining_identifier, we presume here that the transformation explained in 3.3.1 has already taken place. Alternatively, we could say that the component_definition is elaborated once for each defining_identifier in the list.


{8652/0002} Within the definition of a composite type, if a component_definition or discrete_subtype_definition (see 9.5.2) includes a name that denotes a discriminant of the type, or that is an attribute_reference whose prefix denotes the current instance of the type, the expression containing the name is called a per-object expression, and the constraint or range being defined is called a per-object constraint. For the elaboration of a component_definition of a component_declaration or the discrete_subtype_definition of an entry_declaration for an entry family (see 9.5.2), if the component subtype is defined by an access_definition or if the constraint or range of the subtype_indication or discrete_subtype_definition is not a per-object constraint, then the access_definition, subtype_indication, or discrete_subtype_definition is elaborated. On the other hand, if the constraint or range is a per-object constraint, then the elaboration consists of the evaluation of any included expression that is not part of a per-object expression. Each such expression is evaluated once unless it is part of a named association in a discriminant constraint, in which case it is evaluated once for each associated discriminant.


{8652/0002} When a per-object constraint is elaborated [(as part of creating an object)], each per-object expression of the constraint is evaluated. For other expressions, the values determined during the elaboration of the component_definition or entry_declaration are used. Any checks associated with the enclosing subtype_indication or discrete_subtype_definition are performed[, including the subtype compatibility check (see 3.2.2),] and the associated subtype is created.


The evaluation of other expressions that appear in component_definitions and discrete_subtype_definitions is performed when the type definition is elaborated. The evaluation of expressions that appear as default_expressions is postponed until an object is created. Expressions in representation items that appear within a composite type definition are evaluated according to the rules of the particular representation item.


NOTE 1 A component_declaration with several identifiers is equivalent to a sequence of single component_declarations, as explained in 3.3.1.


NOTE 2 The default_expression of a record component is only evaluated upon the creation of a default-initialized object of the record type (presuming the object has the component, if it is in a variant_part — see 3.3.1).


NOTE 3 The subtype defined by a component_definition (see 3.6) has to be a definite subtype.


NOTE 4 If a record type does not have a variant_part, then the same components are present in all values of the type.


NOTE 5 A record type is limited if it has the reserved word limited in its definition, or if any of its components are limited (see 7.5).


NOTE 6 The predefined operations of a record type include membership tests, qualification, and explicit conversion. If the record type is nonlimited, they also include assignment and the predefined equality operators.


NOTE 7 A component of a record can be named with a selected_component. A value of a record can be specified with a record_aggregate.



Examples of record type declarations:


type Date is record Day : Integer range 1 .. 31; Month : Month_Name; -- see 3.5.1 Year : Integer range 0 .. 4000; end record; 28/5

type Complex is record Re : Real := 0.0; Im : Real := 0.0; end record Complex;


Examples of record variables:


Tomorrow, Yesterday : Date; A, B, C : Complex; 31 -- both components of A, B, and C are implicitly initialized to zero

Extensions to Ada 83


The syntax rule for component_declaration is modified to use component_definition (instead of component_subtype_definition). The effect of this change is to allow the reserved word aliased before the component_subtype_definition.


A short-hand is provided for defining a null record type (and a null record extension), as these will be more common for abstract root types (and derived types without additional components).


The syntax rule for record_type_definition is modified to allow the reserved words tagged and limited. Tagging is new. Limitedness is now orthogonal to privateness. In Ada 83 the syntax implied that limited private was sort of more private than private. However, limitedness really has nothing to do with privateness; limitedness simply indicates the lack of assignment capabilities, and makes perfect sense for nonprivate types such as record types.

Wording Changes from Ada 83


{8652/0009} The syntax rules now allow aspect_clauses to appear in a record_definition. This is not a language extension, because Legality Rules prevent all language-defined representation clauses from appearing there. However, an implementation-defined attribute_definition_clause could appear there. The reason for this change is to allow the rules for aspect_clauses and representation pragmas to be as similar as possible.

Extensions to Ada 95


Record components can have an anonymous access type.


Limited components can be initialized, so long as the expression is one that allows building the object in place (such as an aggregate or function_call).

Wording Changes from Ada 95


{8652/0002} Corrigendum: Improved the description of the elaboration of per-object constraints.


{8652/0009} Corrigendum: Changed representation clauses to aspect clauses to reflect that they are used for more than just representation.


Defined explicitly limited record type to use in other rules.

Extensions to Ada 2005


An optional aspect_specification can be used in a component_declaration. This is described in 13.1.1.

Extensions to Ada 2012


The record_identifier following end record is new.

3.8.1 Variant Parts and Discrete Choices


A record type with a variant_part specifies alternative lists of components. Each variant defines the components for the value or values of the discriminant covered by its discrete_choice_list.


Discrete_choice_lists and discrete_choices are said to cover values as defined below; which discrete_choice_list covers a value determines which of various alternatives is chosen. These are used in variant_parts, array_aggregates, and case_statements.

Language Design Principles


The definition of “cover” in this subclause and the rules about discrete choices are designed so that they are also appropriate for array aggregates and case statements.


The rules of this subclause intentionally parallel those for case statements.



variant_part ::=
case discriminant_direct_name is
end case;


variant ::=
when discrete_choice_list =>


discrete_choice_list ::= discrete_choice {'|' discrete_choice}


discrete_choice ::=
choice_expression | discrete_subtype_indication | range | others

Name Resolution Rules


The discriminant_direct_name shall resolve to denote a discriminant (called the discriminant of the variant_part) specified in the known_discriminant_part of the full_type_declaration that contains the variant_part. The expected type for each discrete_choice in a variant is the type of the discriminant of the variant_part.


A full_type_declaration with a variant_part has to have a (new) known_discriminant_part; the discriminant of the variant_part cannot be an inherited discriminant.

Legality Rules


The discriminant of the variant_part shall be of a discrete type.


It shall not be of an access type, named or anonymous.


The choice_expressions, subtype_indications, and ranges given as discrete_choices in a variant_part shall be static. The discrete_choice others shall appear alone in a discrete_choice_list, and such a discrete_choice_list, if it appears, shall be the last one in the enclosing construct.


A discrete_choice is defined to cover a value in the following cases:


A dynamic predicate is never allowed in this case (for variants, case_statements, and case_expressions, a subtype with a dynamic predicate isn't static and thus isn't allowed in a discrete_choice, and for a choice in an array_aggregate, a dynamic predicate is explicitly disallowed — see 3.2.4).


For case_statements, this includes values outside the range of the static subtype (if any) to be covered by the choices. It even includes values outside the base range of the case expression's type, since values of numeric types (and undefined values of any scalar type?) can be outside their base range.


A discrete_choice_list covers a value if one of its discrete_choices covers the value.


The possible values of the discriminant of a variant_part shall be covered as follows:

  • If the discriminant is of a static constrained scalar subtype then, except within an instance of a generic unit, each non-others discrete_choice shall cover only values in that subtype that satisfy its predicates, and each value of that subtype that satisfies its predicates shall be covered by some discrete_choice [(either explicitly or by others)];

The exemption for a discriminated type declared in an instance allows the following example:


generic type T is new Integer; package G is type Rec (Discrim : T) is record case Discrim is when -10 .. -1 => Foo : Float; when others => null; end case; end record; end G; 15.c/3 package I is new G (Natural); -- Legal

  • If the type of the discriminant is a descendant of a generic formal scalar type, then the variant_part shall have an others discrete_choice;

The base range is not known statically in this case.

  • Otherwise, each value of the base range of the type of the discriminant shall be covered [(either explicitly or by others)].

Two distinct discrete_choices of a variant_part shall not cover the same value.

Static Semantics


If the component_list of a variant is specified by null, the variant has no components.


The discriminant of a variant_part is said to govern the variant_part and its variants. In addition, the discriminant of a derived type governs a variant_part and its variants if it corresponds (see 3.7) to the discriminant of the variant_part.

Dynamic Semantics


A record value contains the values of the components of a particular variant only if the value of the discriminant governing the variant is covered by the discrete_choice_list of the variant. This rule applies in turn to any further variant that is, itself, included in the component_list of the given variant.


When an object of a discriminated type T is initialized by default, Constraint_Error is raised if no discrete_choice_list of any variant of a variant_part of T covers the value of the discriminant that governs the variant_part. When a variant_part appears in the component_list of another variant V, this test is only applied if the value of the discriminant governing V is covered by the discrete_choice_list of V.

implementation note

This is not a “check”; it cannot be suppressed. However, in most cases it is not necessary to generate any code to raise this exception. A test is needed (and can fail) in the case where the discriminant subtype has a Static_Predicate specified, it also has predicate checking disabled, and the discriminant governs a variant_part which lacks a when others choice.


The test also could fail for a static discriminant subtype with range checking suppressed and the discriminant governs a variant_part which lacks a when others choice. But execution is erroneous if a range check that would have failed is suppressed (see 11.5), so an implementation does not have to generate code to check this case. (An unchecked failed predicate does not cause erroneous execution, so the test is required in that case.)


Like the checks associated with a per-object constraint, this test is not made during the elaboration of a subtype_indication.


The elaboration of a variant_part consists of the elaboration of the component_list of each variant in the order in which they appear.



Example of record type with a variant part:


type Device is (Printer, Disk, Drum); type State is (Open, Closed); 25 type Peripheral(Unit : Device := Disk) is record Status : State; case Unit is when Printer => Line_Count : Integer range 1 .. Page_Size; when others => Cylinder : Cylinder_Index; Track : Track_Number; end case; end record;


Examples of record subtypes:


subtype Drum_Unit is Peripheral(Drum); subtype Disk_Unit is Peripheral(Disk);


Examples of constrained record variables:


Writer : Peripheral(Unit => Printer); Archive : Disk_Unit;

Extensions to Ada 83


In Ada 83, the discriminant of a variant_part is not allowed to be of a generic formal type. This restriction is removed in Ada 95; an others discrete_choice is required in this case.

Wording Changes from Ada 83


The syntactic category choice is removed. The syntax rules for variant, array_aggregate, and case_statement now use discrete_choice_list or discrete_choice instead. The syntax rule for record_aggregate now defines its own syntax for named associations.


We have added the term Discrete Choice to the title since this is where they are talked about. This is analogous to the name of the subclause "Index Constraints and Discrete Ranges" in the subclause on Array Types.


The rule requiring that the discriminant denote a discriminant of the type being defined seems to have been left implicit in RM83.

Incompatibilities With Ada 2005


Membership tests are no longer allowed as a discrete_choice, in order that those tests can be expanded to allow multiple tests in a single expression without ambiguity. Since a membership test has a boolean type, they are very unlikely to be used as a discrete_choice.

Extensions to Ada 2005


Subtypes with static predicates can be used in discrete_choices, and the coverage rules are modified to respect the predicates.


Variants in generic specifications are no longer rejected if the subtype of the actual type does not include all of the case choices. This probably isn't useful, but it is consistent with the treatment of case_expressions.

Wording Changes from Ada 2005


Added a test that some variant covers the value of a discriminant that governs a variant_part. This is similar to the test that some case limb covers the value of the Selecting_expression of a case_statement. This test cannot change the behavior of any nonerroneous Ada 2005 program, so it is not an inconsistency.

Wording Changes from Ada 2012


Corrigendum: Updated wording of the coverage rules to use the new term "satisfies the predicates" (see 3.2.4).