3.7. Record Types


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 its components.

    record_type_definition ::=
       end record 

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

    component_declaration ::=
       identifier_list : component_subtype_definition [:= expression]; 

    component_subtype_definition ::=  subtype_indication 

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 must be distinct. The use of a name that denotes a record component other than a discriminant is not allowed within the record type definition that declares the component.

A component declaration with several identifiers is equivalent to a sequence of single component declarations, as explained in section 3.2. Each single component declaration declares a record component whose subtype is specified by the component subtype definition.

If a component declaration includes the assignment compound delimiter followed by an expression, the expression is the default expression of the record component; the default expression must be of the type of the component. Default expressions are not allowed for components that are of a limited type.

If a record type does not have a discriminant part, the same components are present in all values of the type. If the component list of a record type is defined by the reserved word null and there is no discriminant part, then the record type has no components and all records of the type are null records.

The elaboration of a record type definition creates a record type; it consists of the elaboration of any corresponding (single) component declarations, in the order in which they appear, including any component declaration in a variant part. The elaboration of a component declaration consists of the elaboration of the component subtype definition.

For the elaboration of a component subtype definition, if the constraint does not depend on a discriminant (see 3.7.1), then the subtype indication is elaborated. If, on the other hand, the constraint depends on a discriminant, then the elaboration consists of the evaluation of any included expression that is not a discriminant.

Examples of record type declarations:

    type DATE is
          DAY   : INTEGER range 1 .. 31;
          MONTH : MONTH_NAME;
          YEAR  : INTEGER range 0 .. 4000;
       end record; 

    type COMPLEX is
          RE : REAL := 0.0;
          IM : REAL := 0.0;
       end record; 

Examples of record variables:

    A, B, C : COMPLEX; 

    -- both components of A, B, and C are implicitly initialized to zero 


The default expression of a record component is implicitly evaluated by the elaboration of the declaration of a record object, in the absence of an explicit initialization (see 3.2.1). If a component declaration has several identifiers, the expression is evaluated once for each such component of the object (since the declaration is equivalent to a sequence of single component declarations).

Unlike the components of an array, the components of a record need not be of the same type.

References: assignment compound delimiter, component, composite value, constraint, declaration, depend on a discriminant, discriminant, discriminant part, and 3.7.1, elaboration, expression, identifier, identifier list, limited type, name, object, subtype, type, type mark, variant part.


3.7.1. Discriminants


A discriminant part specifies the discriminants of a type. A discriminant of a record is a component of the record. The type of a discriminant must be discrete.

    discriminant_part ::=
       (discriminant_specification {; discriminant_specification}) 

    discriminant_specification ::=
       identifier_list : type_mark [:= expression]   

A discriminant part is only allowed in the type declaration for a record type, in a private type declaration or an incomplete type declaration (the corresponding full declaration must then declare a record type), and in the generic parameter declaration for a formal private type.

A discriminant specification with several identifiers is equivalent to a sequence of single discriminant specifications, as explained in section 3.2. Each single discriminant specification declares a discriminant. If a discriminant specification includes the assignment compound delimiter followed by an expression, the expression is the default expression of the discriminant; the default expression must be of the type of the discriminant. Default expressions must be provided either for all or for none of the discriminants of a discriminant part.

The use of the name of a discriminant is not allowed in default expressions of a discriminant part if the specification of the discriminant is itself given in the discriminant part.

Within a record type definition the only allowed uses of the name of a discriminant of the record type are: in the default expressions for record components; in a variant part as the discriminant name; and in a component subtype definition, either as a bound in an index constraint, or to specify a discriminant value in a discriminant constraint. A discriminant name used in these component subtype definitions must appear by itself, not as part of a larger expression. Such component subtype definitions and such constraints are said to depend on a discriminant.

A component is said to depend on a discriminant if it is a record component declared in a variant part, or a record component whose component subtype definition depends on a discriminant, or finally, one of the subcomponents of a component that itself depends on a discriminant.

Each record value includes a value for each discriminant specified for the record type; it also includes a value for each record component that does not depend on a discriminant. The values of the discriminants determine which other component values are in the record value.

Direct assignment to a discriminant of an object is not allowed; furthermore a discriminant is not allowed as an actual parameter of mode in out or out, or as a generic actual parameter of mode in out. The only allowed way to change the value of a discriminant of a variable is to assign a (complete) value to the variable itself. Similarly, an assignment to the variable itself is the only allowed way to change the constraint of one of its components, if the component subtype definition depends on a discriminant of the variable.

The elaboration of a discriminant part has no other effect.


    type BUFFER(SIZE : BUFFER_SIZE := 100)  is        -- see 3.5.4
          POS   : BUFFER_SIZE := 0;
          VALUE : STRING(1 .. SIZE);
       end record; 

    type SQUARE(SIDE : INTEGER) is
          MAT : MATRIX(1 .. SIDE, 1 .. SIDE);       -- see 3.6
       end record;  

          LEFT  : SQUARE(NUMBER);
       end record;                                                                            

          CONTENT : INTEGER;
          --  no component depends on the discriminant
       end record;   

References: assignment, assignment compound delimiter, bound of a range, component, component declaration, component of a record, declaration, discrete type, discriminant, discriminant constraint, elaboration, expression, generic formal type, generic parameter declaration, identifier, identifier list, incomplete type declaration, index constraint, name, object, private type, private type declaration, record type, scope, simple name, subcomponent, subtype indication, type declaration, type mark, variant part.

3.7.2. Discriminant Constraints


A discriminant constraint is only allowed in a subtype indication, after a type mark. This type mark must denote either a type with discriminants, or an access type whose designated type is a type with discriminants. A discriminant constraint specifies the values of these discriminants.

    discriminant_constraint ::=
       (discriminant_association {, discriminant_association}) 

    discriminant_association ::=
       [discriminant_simple_name {| discriminant_simple_name} =>] expression 

Each discriminant association associates an expression with one or more discriminants. A discriminant association is said to be named if the discriminants are specified explicitly by their names; it is otherwise said to be positional. For a positional association, the (single) discriminant is implicitly specified by position, in textual order. Named associations can be given in any order, but if both positional and named associations are used in the same discriminant constraint, then positional associations must occur first, at their normal position. Hence once a named association is used, the rest of the discriminant constraint must use only named associations.

For a named discriminant association, the discriminant names must denote discriminants of the type for which the discriminant constraint is given. A discriminant association with more than one discriminant name is only allowed if the named discriminants are all of the same type. Furthermore, for each discriminant association (whether named or positional), the expression and the associated discriminants must have the same type. A discriminant constraint must provide exactly one value for each discriminant of the type.

A discriminant constraint is compatible with the type denoted by a type mark, if and only if each discriminant value belongs to the subtype of the corresponding discriminant. In addition, for each subcomponent whose component subtype specification depends on a discriminant, the discriminant value is substituted for the discriminant in this component subtype specification and the compatibility of the resulting subtype indication is checked.

A composite value satisfies a discriminant constraint if and only if each discriminant of the composite value has the value imposed by the discriminant constraint.

The initial values of the discriminants of an object of a type with discriminants are determined as follows:

For the elaboration of a discriminant constraint, the expressions given in the discriminant associations are evaluated in some order that is not defined by the language; the expression of a named association is evaluated once for each named discriminant.

Examples (using types declared in the previous section): 01

  LARGE   : BUFFER(200);  --  constrained, always 200 characters (explicit
                              discriminant value)
  MESSAGE : BUFFER;       --  unconstrained, initially 100 characters
                              (default discriminant value)
  BASIS   : SQUARE(5);    --  constrained, always 5 by 5
  ILLEGAL : SQUARE;       --  illegal, a SQUARE must be constrained


The above rules and the rules defining the elaboration of an object declaration (see 3.2) ensure that discriminants always have a value. In particular, if a discriminant constraint is imposed on an object declaration, each discriminant is initialized with the value specified by the constraint. Similarly, if the subtype of a component has a discriminant constraint, the discriminants of the component are correspondingly initialized.

References: access type, access type definition, access value, actual parameter, allocator, array type definition, bound of a range, compatible, component, component declaration, component subtype indication, composite value, constant, constrained subtype, constraint, declaration, default expression for a discriminant, depend on a discriminant, designate, designated type, discriminant, elaboration, entry, evaluation, expression, formal parameter, generic actual parameter, generic formal parameter, and 12.3, mode in, mode in out, name, object, object declaration, renaming declaration, reserved word, satisfy, simple name, subcomponent, subprogram, subtype, subtype indication, type, type mark, variable.

3.7.3. Variant Parts


A record type with a variant part specifies alternative lists of components. Each variant defines the components for the corresponding value or values of the discriminant.

    variant_part ::=
       case discriminant_simple_name is
       end case; 

    variant ::=
       when choice {| choice} =>

    choice ::= simple_expression
       | discrete_range | others | component_simple_name 

Each variant starts with a list of choices which must be of the same type as the discriminant of the variant part. The type of the discriminant of a variant part must not be a generic formal type. If the subtype of the discriminant is static, then each value of this subtype must be represented once and only once in the set of choices of the variant part, and no other value is allowed. Otherwise, each value of the (base) type of the discriminant must be represented once and only once in the set of choices.

The simple expressions and discrete ranges given as choices in a variant part must be static. A choice defined by a discrete range stands for all values in the corresponding range (none if a null range). The choice others is only allowed for the last variant and as its only choice; it stands for all values (possibly none) not given in the choices of previous variants. A component simple name is not allowed as a choice of a variant (although it is part of the syntax of choice).

A record value contains the values of the components of a given variant if and only if the discriminant value is equal to one of the values specified by the choices of the variant. This rule applies in turn to any further variant that is, itself, included in the component list of the given variant. If the component list of a variant is specified by null, the variant has no components.

Example of record type with a variant part:

    type STATE  is (OPEN, CLOSED); 

          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:


Examples of constrained record ariables:



Choices with discrete values are also used in case statements and in array aggregates. Choices with component simple names are used in record aggregates.

References: array aggregates, base type, component, component list, discrete range, discriminant, generic formal type, null range, record aggregate, range, record type, simple expression, simple name, static discrete range, static expression, static subtype, subtype.

3.7.4. Operations of Record Types


The basic operations of a record type include the operations involved in assignment and aggregates (unless the type is limited), membership tests, selection of record components, qualifications, and type conversion (for derived types)

For any object A of a type with discriminants, the basic operations also include the following attribute:

In addition, the attributes T'BASE and T'SIZE are defined for a record type or subtype T (see 3.3.3); the attributes A'SIZE and A'ADDRESS are defined for a record object A (see 13.7.2).

Besides the basic operations, the operations of a record type include the predefined comparison for equality and inequality, unless the type is limited.


A record type is limited if the type of any of its components is limited (see 7.4.2).

References: actual parameter, appregate, assignment, attribute, basic operation, boolean type, constant, conversion, derived type, discriminant, discriminant constraint, formal parameter, generic actual parameter, generic formal parameter, and 12.3, limited type, membership test, and 4.5.2, mode, object, operation, predefined operator, type, qualified expression, record type, relational operator, and 4.5.2, selected component, subcomponent, subtype, type.