3.7 Discriminants
{
AI95-00251-01}
{
AI95-00326-01}
[
A composite
type (other than an array or interface 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 view of a type specifies that the discriminants
of the type are unknown for the given view; all subtypes of such a view
are indefinite subtypes.]
Term entry: discriminant —
parameter for a composite type, which can control, for example, the bounds
of a component that is an array
Note: A discriminant for a task type can be used to pass data to a task
of the type upon its creation.
Discussion: {
AI95-00114-01}
A view of a type, and all subtypes
of the view, have
unknown discriminants when the number or names
of the discriminants, if any, are unknown at the point of the type declaration
for the view. A
discriminant_part
of (<>) is used to indicate unknown discriminants.
Language Design Principles
{
AI95-00402-01}
When an access discriminant is initialized at the time of object creation
with an allocator of an anonymous type, the allocated object and the
object with the discriminant are tied together for their lifetime. They
should be allocated out of the same storage pool, and then at the end
of the lifetime of the enclosing object, finalized and reclaimed together.
In this case, the allocated object is called a coextension (see
3.10.2).
Discussion: The above principle when
applied to a nonlimited type implies that such an object may be copied
only to a shorter-lived object, because attempting to assign it to a
longer-lived object would fail because the access discriminants would
not match. In a copy, the lifetime connection between the enclosing object
and the allocated object does not exist. The allocated object is tied
in the above sense only to the original object. Other copies have only
secondary references to it.
Note that when an
allocator
appears as a constraint on an access discriminant in a
subtype_indication
that is elaborated independently from object creation, no such connection
exists. For example, if a named constrained subtype is declared via "
subtype
Constr
is Rec(Acc_Discrim =>
new T);" or if such
an
allocator
appears in the
subtype_indication
for a component, the allocator is evaluated when the
subtype_indication
is elaborated, and hence its lifetime is typically longer than the objects
or components that will later be subject to the constraint. In these
cases, the allocated object should not be reclaimed until the
subtype_indication
goes out of scope.
Syntax
unknown_discriminant_part ::= (<>)
Discussion: {
AI12-0398-1}
Only implementation-defined aspects are allowed on discriminants in Ada
2022. Implementers are cautioned that any aspect allowed on a discriminant
will need conformance rules. If, for instance, an aspect changed the
representation of a discriminant, rules would be needed to ensure that
the representation is the same for all views of the type (partial and
full).
Name Resolution Rules
Legality Rules
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.
Reason: 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.
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.
{
AI05-0214-1}
Defaults for discriminants of tagged types are disallowed so that every
object of a nonlimited 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. We don't need this rule for limited tagged types, as the
discriminants of such objects cannot be changed after the object is created
in any case — no full-object assignment is supported, and that
is required to change discriminant values. 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.
Discussion: This rule implies that a
type can have a default for an access discriminant if the type is limited,
but not if the only reason it's limited is because of a limited component.
Compare the definition of limited type and immutably limited type in
7.5.
Ramification: A (nonformal) limited private
type can always have a default for an access discriminant, because having
the default itself makes the type immutably limited. Such a private type
must necessarily have a full type with the same access discriminant with
a default, and thus the full type will always be immutably limited (if
legal).
Reason: {
AI95-00230-01}
We considered the following rules for access discriminants:
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).
A type with an access discriminant shall be
limited. This is equivalent to the rule we actually chose for Ada 95,
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”.
{
AI05-0063-1}
A type may have an access discriminant if it is an immutably limited
type. This was the rule chosen for Ada 95.
Any type may have an access discriminant.
For nonlimited type, there is no special accessibility for access discriminants;
they're the same as any other anonymous access component. For a limited
type, they have the special accessibility of Ada 95. However, this doesn't
work because a limited partial view can have a nonlimited full view --
giving the two views different accessibility.
{
AI05-0063-1}
Any type may have an access discriminant, as above. However, special
accessibility rules only apply to types that are immutably limited (task,
protected, and explicitly limited records). However, this breaks privacy;
worse, Legality Rules depend on the definition of accessibility.
{
AI05-0063-1}
Any type may have an access discriminant, as above. Limited types have
special accessibility, while nonlimited types have normal accessibility.
However, a limited partial view with an access discriminant can only
be completed by an immutably limited type. That prevents accessibility
from changing. A runtime accessibility check is required on generic formal
types with access discriminants. However, changing between limited and
nonlimited types would have far-reaching consequences for access discriminants
— which is uncomfortable.
Any type may have an access discriminant.
All types have special accessibility. This was considered early during
the Ada 9X process, but was dropped for “unpleasant complexities”,
which unfortunately aren't recorded. It does seem that an accessibility
check would be needed on assignment of such a type, to avoid copying
an object with a discriminant pointing to a local object into a more
global object (and thus creating a dangling pointer).
Any type may have an access discriminant,
but access discriminants cannot have defaults. All types have special
accessibility. This gets rid of the problems on assignment (you couldn't
change such a discriminant), but it would be horribly incompatible with
Ada 95.
{
AI05-0063-1}
Any type may have an access discriminant, but access discriminants may
have defaults only if they are of an immutably limited type. This is
the rule chosen for Ada 2005, as it is not incompatible, and it doesn't
require weird accessibility checks.
The parent subtype shall be constrained;
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;
Implementation Note: This ensures that
the new discriminant can share storage with an existing discriminant.
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.
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.
This paragraph
was deleted.
Static Semantics
[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.]
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.
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.
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.
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.
It is declared in a
variant_part
that is governed by the discriminant; or
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.
It is a subcomponent of a component that depends
on the discriminant.
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.
A
type declared with a
known_discriminant_part
is said to have
known discriminants; its first subtype is unconstrained.
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.
[Any subtype of
a type with unknown discriminants is an unconstrained and indefinite
subtype (see
3.2 and
3.3).]
Discussion: {
AI95-00114-01}
An
unknown_discriminant_part
“(<>)” is only permitted in the declaration of a (generic
or nongeneric) private type, private extension, incomplete type, 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.
{
AI95-00287-01}
A subtype with unknown discriminants is indefinite, and hence an object
of such a subtype needs explicit initialization. A limited private type
with unknown discriminants is “extremely” limited; objects
of such a type can be initialized only by subprograms (either procedures
with a parameter of the type, or a function returning the type) declared
in the package. Subprograms declared elsewhere can operate on and even
return the type, but they can only initialize the object by calling (ultimately)
a subprogram in the package declaring the type. 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
Ramification: {
AI95-00231-01}
{
AI95-00416-01}
The conversion of the
expression
defining the access discriminant to the anonymous access type raises
Program_Error 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.
NOTE 1 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.
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.)
NOTE 2 The
default_expression
for a discriminant of a type is evaluated when an object of an unconstrained
subtype of the type is created.
NOTE 3 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: {
AI95-00114-01}
An
unknown_discriminant_part
is permitted only in the declaration of a private type (including generic
formal private), private extension, incomplete type, 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.
NOTE 4 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
Examples of discriminated
types:
type Buffer(Size : Buffer_Size := 100)
is --
see 3.5.4
record
Pos : Buffer_Size := 0;
Value : String(1 .. Size);
end record;
type Matrix_Rec(Rows, Columns : Integer)
is
record
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
record
Left : Square(Number);
Right : Square(Number);
end record;
{
AI95-00433-01}
{
AI05-0229-1}
task type Worker(Prio : System.Priority; Buf :
access Buffer)
with Priority => Prio
is --
see D.1
--
discriminants used to parameterize the task type (see 9.1)
entry Fill;
entry Drain;
end Worker;
Extensions to Ada 83
{
AI95-00251-01}
Discriminants are allowed on all composite types other than array and
interface types.
Discriminants may be of an access type.
Wording Changes from Ada 83
Extensions to Ada 95
{
AI95-00230-01}
{
AI95-00402-01}
{
AI95-00416-01}
Access discriminants (anonymous access types used as a discriminant)
can be used on any type allowing discriminants. Defaults aren't allowed
on discriminants of nonlimited types, however, so that accessibility
problems don't happen on assignment.
Wording Changes from Ada 95
{
AI95-00251-01}
Added wording to prevent interfaces from having discriminants. We don't
want interfaces to have any components.
{
AI95-00254-01}
Removed wording which implied or required an access discriminant to have
an access-to-object type (anonymous access types can now be access-to-subprogram
types as well).
{
AI95-00326-01}
{
AI05-0299-1}
Fixed the wording of the introduction to this subclause to reflect that
both incomplete and partial views can have unknown discriminants. That
was always true, but for some reason this wording specified partial views.
{
AI95-00419-01}
Changed the wording to use the new term “explicitly limited record”,
which makes the intent much clearer (and eliminates confusion with derived
types that happen to contain the reserved word
limited).
Incompatibilities With Ada 2005
{
AI05-0063-1}
Correction: Changed the rules for when access
discriminants can have defaults to depend on the new definition for immutably
limited types; this will help ensure that unusual corner cases are properly
handled. Note that the Ada 2005 rule was unintentionally incompatible
with the Ada 95 rule (as enforced by the ACATS); this change brings it
back into alignment with actual practice. So there should be no practical
incompatibility.
Extensions to Ada 2005
{
AI05-0214-1}
A limited tagged type may now have defaults for its
discriminants.
Wording Changes from Ada 2005
{
AI05-0102-1}
Correction: Moved implicit conversion Legality Rule to
8.6.
Extensions to Ada 2012
{
AI12-0398-1}
Discriminants now can have an
aspect_specification,
allowing the specification of (implementation-defined) aspects for individual
discriminants.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe