Contents Index Previous Next
3.7 Discriminants
1
[
{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.]
1.a
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.
1.b
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.
Syntax
2
discriminant_part
::= unknown_discriminant_part |
known_discriminant_part
3
unknown_discriminant_part
::= (<>)
4
known_discriminant_part
::=
(
discriminant_specification {;
discriminant_specification})
5
discriminant_specification
::=
defining_identifier_list :
subtype_mark [:=
default_expression]
|
defining_identifier_list :
access_definition [:=
default_expression]
6
default_expression
::= expression
Name Resolution Rules
7
{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
8/1
{
8652/0007}
A
discriminant_partknown_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.
8.a
Implementation Note: Discriminants
on array types were considered, but were omitted to ease (existing) implementations.
8.b
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.
8.b.1/1
{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.
9
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.
9.a
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.
9.b
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.
10
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.
10.a
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.
10.b
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.
10.c
Reason:
We also considered the following rules:
10.d
- If a type has an access discriminant, this automatically
makes it limited, just like having a limited component automatically
makes a type limited. This was rejected because it decreases program
readability, and because it seemed error prone (two bugs in a previous
version of the RM9X were attributable to this rule).
10.e
- A type with an access discriminant shall be limited. This
is equivalent to the rule we actually chose, except that it allows a
type to have an access discriminant if it is limited just because of
a limited component. For example, any record containing a task would
be allowed to have an access discriminant, whereas the actual rule requires
``limited record''. This rule was also rejected due to
readability concerns, and because would interact badly with the rules
for limited types that ``become nonlimited''.
11
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].
11.a
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.
11.b
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.
12
For a type defined
by a derived_type_definition, if
a known_discriminant_part is provided
in its declaration, then:
13
- The parent subtype shall be constrained;
14
- If the parent type is not a tagged type, then each discriminant
of the derived type shall be used in the constraint defining the parent
subtype;
14.a
Implementation Note: This
ensures that the new discriminant can share storage with an existing
discriminant.
15
- If a discriminant is used in the constraint defining the parent
subtype, the subtype of the discriminant shall be statically compatible (see
4.9.1) with the subtype of the corresponding parent
discriminant.
15.a
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.
15.b
Ramification: There is
no requirement that the new discriminant have the same (or any) default_expression
as the parent's discriminant.
16
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]}
16.a
Ramification: This requires
convertibility of the designated subtypes.
Static Semantics
17
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.
18
[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.
18.a
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.
19
{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.
19.a
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.
20
{depend
on a discriminant (for a component)} A
component
depends on a discriminant if:
21
- Its component_definition
depends on the discriminant; or
21.a
Ramification: A component
does not depend on a discriminant just because its default_expression
refers to the discriminant.
22
- It is declared in a variant_part
that is governed by the discriminant; or
23
- It is a component inherited as part of a derived_type_definition,
and the constraint of the parent_subtype_indication
depends on the discriminant; or
23.a
Reason: When the parent
subtype depends on a discriminant, the parent part of the derived type
is treated like a discriminant-dependent component.
23.b
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.
24
- It is a subcomponent of a component that depends on the
discriminant.
24.a
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.
25
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.
25.a
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.
26
{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).]
26.a
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.
26.b
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.
26.c
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
27
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]}
27.a
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.
28
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.
28.a
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.
28.b
{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.)
29
51 The default_expression
for a discriminant of a type is evaluated when an object of an unconstrained
subtype of the type is created.
30
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.
30.a
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.
30.b
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.
31
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
32
Examples of
discriminated types:
33
type Buffer(Size : Buffer_Size := 100) is -- see 3.5.4
record
Pos : Buffer_Size := 0;
Value : String(1 .. Size);
end record;
34
type Matrix_Rec(Rows, Columns : Integer) is
record
Mat : Matrix(1 .. Rows, 1 .. Columns); -- see 3.6
end record;
35
type Square(Side : Integer) is new
Matrix_Rec(Rows => Side, Columns => Side);
36
type Double_Square(Number : Integer) is
record
Left : Square(Number);
Right : Square(Number);
end record;
37
type Item(Number : Positive) is
record
Content : Integer;
-- no component depends on the discriminant
end record;
Extensions to Ada 83
37.a
{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).
37.b
Discriminants are allowed on all
composite types other than array types.
37.c
Discriminants may be of an access
type.
Wording Changes from Ada 83
37.d
Discriminant_parts
are not elaborated, though an access_definition
is elaborated when the discriminant is initialized.
Contents Index Previous Next Legal