Contents   Index   Search   Previous   Next

3.7 Discriminants

   [{discriminant} {type parameter: See discriminant} {parameter: See also discriminant} A composite type (other than an array type) can have discriminants, which parameterize the type. A known_discriminant_part specifies the discriminants of a composite type. A discriminant of an object is a component of the object, and is either of a discrete type or an access type. An unknown_discriminant_part in the declaration of a partial view of a type specifies that the discriminants of the type are unknown for the given view; all subtypes of such a partial view are indefinite subtypes.]
Glossary entry: {Discriminant} A discriminant is a parameter of a composite type. It can control, for example, the bounds of a component of the type if that type is an array type. A discriminant of a task type can be used to pass data to a task of the type upon creation.
Discussion: {unknown discriminants [partial]} {discriminants (unknown) [partial]} A type, and all of its subtypes, have unknown discriminants when the number or names of the discriminants, if any, are unknown at the point of the type declaration. A discriminant_part of (<>) is used to indicate unknown discriminants.


discriminant_part ::= unknown_discriminant_part | known_discriminant_part
unknown_discriminant_part ::= (<>)
known_discriminant_part ::=
   (discriminant_specification {; discriminant_specification})
discriminant_specification ::=
   defining_identifier_list : subtype_mark [:= default_expression]
 | defining_identifier_list : access_definition [:= default_expression]
default_expression ::= expression

Name Resolution Rules

   {expected type (discriminant default_expression) [partial]} The expected type for the default_expression of a discriminant_specification is that of the corresponding discriminant.

Legality Rules

     {8652/0007} A discriminant_part known_discriminant_part is only permitted in a declaration for a composite type that is not an array type [(this includes generic formal types)]. A ; a type declared with a known_discriminant_part is called a discriminated type,{discriminated type} as is a type that inherits (known) discriminants.
Implementation Note: Discriminants on array types were considered, but were omitted to ease (existing) implementations.
Discussion: Note that the above definition for ``discriminated type'' does not include types declared with an unknown_discriminant_part. This seems consistent with Ada 83, where such types (in a generic formal part) would not be considered discriminated types. Furthermore, the full type for a type with unknown discriminants need not even be composite, much less have any discriminants.
{8652/0007} On the other hand, unknown_discriminant_parts cannot be applied to type declarations that cannot have a known_discriminant_part. There is no point in having unknown discriminants on a type that can never have discriminants (for instance, a formal modular type), even when these are allowed syntactically.
   The subtype of a discriminant may be defined by a subtype_mark, in which case the subtype_mark shall denote a discrete or access subtype, or it may be defined by an access_definition [(in which case the subtype_mark of the access_definition may denote any kind of subtype)]. {access discriminant} A discriminant that is defined by an access_definition is called an access discriminant and is of an anonymous general access-to-variable type whose designated subtype is denoted by the subtype_mark of the access_definition.
Reason: In an early version of Ada 9X, we allowed access discriminants on nonlimited types, but this created unpleasant complexities. It turned out to be simpler and more uniform to allow discriminants of a named access type on any discriminated type, and keep access discriminants just for limited types.
Note that discriminants of a named access type are not considered ``access discriminants.'' Similarly, ``access parameter'' only refers to a formal parameter defined by an access_definition.
    A discriminant_specification for an access discriminant shall appear only in the declaration for a task or protected type, or for a type with the reserved word limited in its [(full)] definition or in that of one of its ancestors. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit.
Discussion: This rule implies that a type can have an access discriminant if the type is limited, but not if the only reason it's limited is because of a limited component. Compare with the definition of limited type in 7.5.
Ramification: It is a consequence of this rule that only a return-by-reference type can have an access discriminant (see 6.5). This is important to avoid dangling references to local variables.
Reason: We also considered the following rules:
    Default_expressions shall be provided either for all or for none of the discriminants of a known_discriminant_part. No default_expressions are permitted in a known_discriminant_part in a declaration of a tagged type [or a generic formal type].
Reason: The all-or-none rule is related to the rule that a discriminant constraint shall specify values for all discriminants. One could imagine a different rule that allowed a constraint to specify only some of the discriminants, with the others provided by default. Having defaults for discriminants has a special significance -- it allows objects of the type to be unconstrained, with the discriminants alterable as part of assigning to the object.
Defaults for discriminants of tagged types are disallowed so that every object of a tagged type is constrained, either by an explicit constraint, or by its initial discriminant values. This substantially simplifies the semantic rules and the implementation of inherited dispatching operations. For generic formal types, the restriction simplifies the type matching rules. If one simply wants a "default" value for the discriminants, a constrained subtype can be declared for future use.
    For a type defined by a derived_type_definition, if a known_discriminant_part is provided in its declaration, then:
Implementation Note: This ensures that the new discriminant can share storage with an existing discriminant.
Reason: This ensures that on conversion (or extension via an extension aggregate) to a distantly related type, if the discriminants satisfy the target type's requirements they satisfy all the intermediate types' requirements as well.
Ramification: There is no requirement that the new discriminant have the same (or any) default_expression as the parent's discriminant.
    The type of the default_expression, if any, for an access discriminant shall be convertible to the anonymous access type of the discriminant (see 4.6). {convertible (required) [partial]}
Ramification: This requires convertibility of the designated subtypes.

Static Semantics

    A discriminant_specification declares a discriminant; the subtype_mark denotes its subtype unless it is an access discriminant, in which case the discriminant's subtype is the anonymous access-to-variable subtype defined by the access_definition.
    [For a type defined by a derived_type_definition, each discriminant of the parent type is either inherited, constrained to equal some new discriminant of the derived type, or constrained to the value of an expression.] {corresponding discriminants} When inherited or constrained to equal some new discriminant, the parent discriminant and the discriminant of the derived type are said to correspond. Two discriminants also correspond if there is some common discriminant to which they both correspond. A discriminant corresponds to itself as well. {specified discriminant} If a discriminant of a parent type is constrained to a specific value by a derived_type_definition, then that discriminant is said to be specified by that derived_type_definition.
Ramification: The correspondence relationship is transitive, symmetric, and reflexive. That is, if A corresponds to B, and B corresponds to C, then A, B, and C each corresponds to A, B, and C in all combinations.
    {depend on a discriminant (for a constraint or component_definition)} A constraint that appears within the definition of a discriminated type depends on a discriminant of the type if it names the discriminant as a bound or discriminant value. A component_definition depends on a discriminant if its constraint depends on the discriminant, or on a discriminant that corresponds to it.
Ramification: A constraint in a task_body is not considered to depend on a discriminant of the task type, even if it names it. It is only the constraints in the type definition itself that are considered dependents. Similarly for protected types.
    {depend on a discriminant (for a component)} A component depends on a discriminant if:
Ramification: A component does not depend on a discriminant just because its default_expression refers to the discriminant.
Reason: When the parent subtype depends on a discriminant, the parent part of the derived type is treated like a discriminant-dependent component.
Ramification: Because of this rule, we don't really need to worry about ``corresponding'' discriminants, since all the inherited components will be discriminant-dependent if there is a new known_discriminant_part whose discriminants are used to constrain the old discriminants.
Reason: The concept of discriminant-dependent (sub)components is primarily used in various rules that disallow renaming or 'Access, or specify that certain discriminant-changing assignments are erroneous. The goal is to allow implementations to move around or change the size of discriminant-dependent subcomponents upon a discriminant-changing assignment to an enclosing object. The above definition specifies that all subcomponents of a discriminant-dependent component or parent part are themselves discriminant-dependent, even though their presence or size does not in fact depend on a discriminant. This is because it is likely that they will move in a discriminant-changing assignment if they are a component of one of several discriminant-dependent parts of the same record.
    Each value of a discriminated type includes a value for each component of the type that does not depend on a discriminant[; this includes the discriminants themselves]. The values of discriminants determine which other component values are present in the value of the discriminated type.
To be honest: Which values are present might depend on discriminants of some ancestor type that are constrained in an intervening derived_type_definition. That's why we say "values of discriminants" instead of "values of the discriminants" -- a subtle point.
    {known discriminants} {discriminants (known)} {constrained (subtype)} {unconstrained (subtype)} A type declared with a known_discriminant_part is said to have known discriminants; its first subtype is unconstrained. {unknown discriminants} {discriminants (unknown)} A type declared with an unknown_discriminant_part is said to have unknown discriminants. A type declared without a discriminant_part has no discriminants, unless it is a derived type; if derived, such a type has the same sort of discriminants (known, unknown, or none) as its parent (or ancestor) type. A tagged class-wide type also has unknown discriminants. {class-wide type} {indefinite subtype} [Any subtype of a type with unknown discriminants is an unconstrained and indefinite subtype (see 3.2 and 3.3).]
Discussion: An unknown_discriminant_part ``(<>)'' is only permitted in the declaration of a (generic or nongeneric) private type, private extension, or formal derived type. Hence, only such types, descendants thereof, and class-wide types can have unknown discriminants. An unknown_discriminant_part is used to indicate that the corresponding actual or full type might have discriminants without defaults, or be an unconstrained array subtype. Tagged class-wide types are also considered to have unknown discriminants because discriminants can be added by type extensions, so the total number of discriminants of any given value of a tagged class-wide type is not known at compile time.
A subtype with unknown discriminants is indefinite, and hence an object of such a subtype needs explicit initialization. If the subtype is limited, no (stand-alone) objects can be declared since initialization is not permitted (though formal parameters are permitted, and objects of the actual/full type will generally be declarable). A limited private type with unknown discriminants is ``extremely'' limited; such a type is useful for keeping complete control over object creation within the package declaring the type.
A partial view of a type might have unknown discriminants, while the full view of the same type might have known, unknown, or no discriminants. ,

Dynamic Semantics

    An access_definition is elaborated when the value of a corresponding access discriminant is defined, either by evaluation of its default_expression or by elaboration of a discriminant_constraint. [The elaboration of an access_definition creates the anonymous access type. When the expression defining the access discriminant is evaluated, it is converted to this anonymous access type (see 4.6).] {implicit subtype conversion (access discriminant) [partial]}
Ramification: This conversion raises Constraint_Error if the initial value is null, or, for an object created by an allocator of an access type T, if the initial value is an access parameter that designates a view whose accessibility level is deeper than that of T.
50  If a discriminated type has default_expressions for its discriminants, then unconstrained variables of the type are permitted, and the values of the discriminants can be changed by an assignment to such a variable. If defaults are not provided for the discriminants, then all variables of the type are constrained, either by explicit constraint or by their initial value; the values of the discriminants of such a variable cannot be changed after initialization.
Discussion: This connection between discriminant defaults and unconstrained variables can be a source of confusion. For Ada 95, we considered various ways to break the connection between defaults and unconstrainedness, but ultimately gave up for lack of a sufficiently simple and intuitive alternative.
{mutable} An unconstrained discriminated subtype with defaults is called a mutable subtype, and a variable of such a subtype is called a mutable variable, because the discriminants of such a variable can change. There are no mutable arrays (that is, the bounds of an array object can never change), because there is no way in the language to define default values for the bounds. Similarly, there are no mutable class-wide subtypes, because there is no way to define the default tag, and defaults for discriminants are not allowed in the tagged case. Mutable tags would also require a way for the maximum possible size of such a class-wide subtype to be known. (In some implementations, all mutable variables are allocated with the maximum possible size. This approach is appropriate for real-time applications where implicit use of the heap is inappropriate.)
51  The default_expression for a discriminant of a type is evaluated when an object of an unconstrained subtype of the type is created.
52  Assignment to a discriminant of an object (after its initialization) is not allowed, since the name of a discriminant is a constant; neither assignment_statements nor assignments inherent in passing as an in out or out parameter are allowed. Note however that the value of a discriminant can be changed by assigning to the enclosing object, presuming it is an unconstrained variable.
Discussion: An unknown_discriminant_part is permitted only in the declaration of a private type (including generic formal private), private extension, or generic formal derived type. These are the things that will have a corresponding completion or generic actual, which will either define the discriminants, or say there are none. The (<>) indicates that the actual/full subtype might be an indefinite subtype. An unknown_discriminant_part is not permitted in a normal untagged derived type declaration, because there is no separate full type declaration for such a type. Note that (<>) allows unconstrained array bounds; those are somewhat like undefaulted discriminants.
For a derived type, either the discriminants are inherited as is, or completely respecified in a new discriminant_part. In this latter case, each discriminant of the parent type shall be constrained, either to a specific value, or to equal one of the new discriminants. Constraining a parent type's discriminant to equal one of the new discriminants is like a renaming of the discriminant, except that the subtype of the new discriminant can be more restrictive than that of the parent's one. In any case, the new discriminant can share storage with the parent's discriminant.
53  A discriminant that is of a named access type is not called an access discriminant; that term is used only for discriminants defined by an access_definition.


    Examples of discriminated types:
type Buffer(Size : Buffer_Size := 100)  is        -- see 3.5.4
      Pos   : Buffer_Size := 0;
      Value : String(1 .. Size);
   end record;
type Matrix_Rec(Rows, Columns : Integer) is
      Mat : Matrix(1 .. Rows, 1 .. Columns);       -- see 3.6
   end record;
type Square(Side : Integer) is new
   Matrix_Rec(Rows => Side, Columns => Side);
type Double_Square(Number : Integer) is
      Left  : Square(Number);
      Right : Square(Number);
   end record;
type Item(Number : Positive) is
      Content : Integer;
      --  no component depends on the discriminant
   end record;

Extensions to Ada 83

{extensions to Ada 83} The syntax for a discriminant_specification is modified to allow an access discriminant, with a type specified by an access_definition (see 3.10).
Discriminants are allowed on all composite types other than array types.
Discriminants may be of an access type.

Wording Changes from Ada 83

Discriminant_parts are not elaborated, though an access_definition is elaborated when the discriminant is initialized.

Contents   Index   Search   Previous   Next   Legal