3.4.1 Derivation Classes
1
In addition to the various language-defined classes
of types, types can be grouped into derivation classes.
Static Semantics
2/2
{
AI95-00251-01}
{
AI95-00401-01}
{derived from (directly or indirectly)}
A derived type is
derived from its parent
type
directly; it is derived
indirectly from any type from
which its parent type is derived.
A derived type,
interface type, type extension, task type, protected type, or formal
derived type is also derived from every ancestor of each of its progenitor
types, if any. {derivation class
(for a type)} {root
type (of a class)} {rooted
at a type} The derivation class of types
for a type
T (also called the class
rooted at
T)
is the set consisting of
T (the
root type of the class)
and all types derived from
T (directly or indirectly) plus any
associated universal or class-wide types (defined below).
2.a
Discussion: Note that the definition
of “derived from” is a recursive definition. We don't define
a root type for all interesting language-defined classes, though presumably
we could.
2.b
To be honest: By the class-wide type
“associated” with a type T, we mean the type T'Class.
Similarly, the universal type associated with root_integer, root_real,
and root_fixed are universal_integer, universal_real,
and universal_fixed, respectively.
3/2
{
AI95-00230-01}
Every type is either a
specific type, a
class-wide type,
or a
universal type.
{specific
type} A specific type is one defined by
a
type_declaration, a
formal_type_declaration,
or a full type definition embedded in
another construct a
declaration for an object. Class-wide and universal types are
implicitly defined, to act as representatives for an entire class of
types, as follows:
3.a
To be honest: The root types root_integer,
root_real, and root_fixed are also specific types. They
are declared in the specification of package Standard.
4
{class-wide
type} Class-wide types
Class-wide types are defined for [(and belong to)] each derivation class
rooted at a tagged type (see
3.9). Given a
subtype S of a tagged type
T, S'Class is the
subtype_mark
for a corresponding subtype of the tagged class-wide type
T'Class.
Such types are called “class-wide” because when a formal
parameter is defined to be of a class-wide type
T'Class, an actual
parameter of any type in the derivation class rooted at
T is acceptable
(see
8.6).
5
{first subtype}
The set of values for a class-wide type
T'Class
is the discriminated union of the set of values of each specific type
in the derivation class rooted at
T (the tag acts as the implicit
discriminant — see
3.9). Class-wide types
have no primitive subprograms of their own. However, as explained in
3.9.2, operands of a class-wide type
T'Class
can be used as part of a dispatching call on a primitive subprogram of
the type
T. The only components [(including discriminants)] of
T'Class that are visible are those of
T. If S is a first
subtype, then S'Class is a first subtype.
5.a
Reason: We want S'Class to be a first
subtype when S is, so that an attribute_definition_clause
like “for S'Class'Output use ...;” will be
legal.
6/2
Universal types are defined for [(and belong to)] the integer, real,
and fixed point
, and
access classes, and are referred to in this standard as respectively,
universal_integer,
universal_real,
and
universal_fixed, and universal_access.
These are analogous to class-wide types for these language-defined
elementary numeric
classes. As with class-wide types, if a formal parameter is of a universal
type, then an actual parameter of any type in the corresponding class
is acceptable. In addition, a value of a universal type (including an
integer or real
numeric_literal,
or the literal null) is “universal” in that
it is acceptable where some particular type in the class is expected
(see
8.6).
7
The set of values of a universal type is the
undiscriminated union of the set of values possible for any definable
type in the associated class. Like class-wide types, universal types
have no primitive subprograms of their own. However, their “universality”
allows them to be used as operands with the primitive subprograms of
any type in the corresponding class.
7.a
Discussion: A class-wide type is only
class-wide in one direction, from specific to class-wide, whereas a universal
type is class-wide (universal) in both directions, from specific to universal
and back.
7.b/2
{
AI95-00230-01}
We considered defining class-wide or perhaps universal types for all
derivation classes, not just tagged classes and these
four
elementary three numeric classes.
However, this was felt to overly weaken the strong-typing model in some
situations. Tagged types preserve strong type distinctions thanks to
the run-time tag. Class-wide or universal types for untagged types would
weaken the compile-time type distinctions without providing a compensating
run-time-checkable distinction.
7.c
We considered defining standard names for the
universal numeric types so they could be used in formal parameter specifications.
However, this was felt to impose an undue implementation burden for some
implementations.
7.d
To be honest: Formally, the set of values
of a universal type is actually a copy of the undiscriminated
union of the values of the types in its class. This is because we want
each value to have exactly one type, with explicit or implicit conversion
needed to go between types. An alternative, consistent model would be
to associate a class, rather than a particular type, with a value, even
though any given expression would have a particular type. In that case,
implicit type conversions would not generally need to change the value,
although an associated subtype conversion might need to.
8
{root_integer
[partial]} {root_real
[partial]} The integer and real numeric classes
each have a specific root type in addition to their universal type, named
respectively
root_integer and
root_real.
9
{cover (a type)}
A class-wide or universal type is said to
cover
all of the types in its class. A specific type covers only itself.
10/2
{
AI95-00230-01}
{
AI95-00251-01}
{descendant (of a type)} A
specific type
T2 is defined to be a
descendant of a type
T1 if
T2 is the same as
T1, or if
T2 is derived
(directly or indirectly) from
T1. A class-wide type
T2'Class
is defined to be a descendant of type
T1 if
T2 is a descendant
of
T1. Similarly, the
numeric universal
types are defined to be descendants of the root types of their classes.
{ancestor (of a type)} If
a type
T2 is a descendant of a type
T1, then
T1
is called an
ancestor of
T2.
{ultimate
ancestor (of a type)} {ancestor
(ultimate)} An The
ultimate ancestor of a type is
an the
ancestor of
that the
type that is not
itself a descendant of
any other type.
Every untagged type has a unique
ultimate ancestor.
10.a
Ramification: A specific type is a descendant
of itself. Class-wide types are considered descendants of the corresponding
specific type, and do not have any descendants of their own.
10.b
A specific type is an ancestor of itself. The
root of a derivation class is an ancestor of all types in the class,
including any class-wide types in the class.
10.c
Discussion:
The terms root, parent, ancestor, and ultimate ancestor are all related.
For example:
10.d/2
- {AI95-00251-01}
Each type has at most one parent, and one or more ancestor types; each
untagged type has exactly one ultimate ancestor.
In Ada 83, the term “parent type” was sometimes used more
generally to include any ancestor type (e.g. RM83-9.4(14)). In Ada 95,
we restrict parent to mean the immediate ancestor.
10.e
- A class of types has at most
one root type; a derivation class has exactly one root type.
10.f
- The root of a class is an ancestor
of all of the types in the class (including itself).
10.g
- The type root_integer
is the root of the integer class, and is the ultimate ancestor of all
integer types. A similar statement applies to root_real.
10.h/2
Glossary entry: {Ancestor}
An ancestor of a type is the type itself or, in the case of a type derived
from other types, its parent type or one of its progenitor types or one
of their ancestors. Note that ancestor and descendant are inverse relationships.
10.i/2
Glossary entry: {Descendant}
A type is a descendant of itself, its parent and progenitor types, and
their ancestors. Note that descendant and ancestor are inverse relationships.
11
{inherited (from
an ancestor type)} An inherited component
[(including an inherited discriminant)] of a derived type is inherited
from a given ancestor of the type if the corresponding component
was inherited by each derived type in the chain of derivations going
back to the given ancestor.
12
20 Because operands of a universal type
are acceptable to the predefined operators of any type in their class,
ambiguity can result. For
universal_integer and
universal_real,
this potential ambiguity is resolved by giving a preference (see
8.6)
to the predefined operators of the corresponding root types (
root_integer
and
root_real, respectively). Hence, in an apparently ambiguous
expression like
13
1 + 4 < 7
14
where each of the literals is of type universal_integer,
the predefined operators of root_integer will be preferred over
those of other specific integer types, thereby resolving the ambiguity.
14.a
Ramification: Except for this preference,
a root numeric type is essentially like any other specific type in the
associated numeric class. In particular, the result of a predefined operator
of a root numeric type is not “universal” (implicitly convertible)
even if both operands were.
Wording Changes from Ada 95
14.b/2
{
AI95-00230-01}
Updated the wording to define the universal_access
type. This was defined to make null for anonymous access types
sensible.
14.c/2
{
AI95-00251-01}
{
AI95-00401-01}
The definitions of ancestors and descendants were
updated to allow multiple ancestors (necessary to support interfaces).