3.4 Derived Types and Classes
1/2
{
AI95-00401-01}
{
AI95-00419-01}
{derived type} A
derived_type_definition defines a
derived
type new type (and its first
subtype) whose characteristics are
derived from those of a
parent
type, and possibly from progenitor types parent
type.
{inheritance: See derived
types and classes}
1.a/2
Glossary entry: {Derived type}
A derived type is a type defined in terms of one
or more other types given in a derived type definition. The first of
those types another type, which is
the parent type of the derived type and any others
are progenitor types. Each class containing the parent type or
a progenitor type also contains the derived type. The derived
type inherits properties such as components and primitive operations
from the parent and progenitors. A type
together with the types derived from it (directly or indirectly) form
a derivation class.
1.1/2
{
AI95-00442-01}
{class (of types)}
{category (of types)}
A class of types is a set of types that is
closed under derivation; that is, if the parent or a progenitor type
of a derived type belongs to a class, then so does the derived type.
By saying that a particular group of types forms a class, we are saying
that all derivatives of a type in the set inherit the characteristics
that define that set. The more general term category of types
is used for a set of types whose defining characteristics are not necessarily
inherited by derivatives; for example, limited, abstract, and interface
are all categories of types, but not classes of types.
1.b/2
Ramification: A
class of types is also a category of types.
Syntax
2/2
{
AI95-00251-01}
{
AI95-00419-01}
derived_type_definition ::=
[
abstract]
[limited] new parent_subtype_indication [
[and interface_list] record_extension_part]
Legality Rules
3/2
{
AI95-00251-01}
{
AI95-00401-01}
{
AI95-00419-01}
{parent subtype} {parent
type} The
parent_subtype_indication
defines the
parent subtype; its type is the
parent
type parent type.
The
interface_list defines the progenitor types
(see 3.9.4). A derived type has one parent
type and zero or more progenitor types.
3.a/2
Glossary entry: {Parent}
The parent of a derived type is the first type given in the definition
of the derived type. The parent can be almost any kind of type, including
an interface type.
4
A type shall be completely defined (see
3.11.1)
prior to being specified as the parent type in a
derived_type_definition
— [the
full_type_declarations for the
parent type and any of its subcomponents have to precede the
derived_type_definition.]
4.a
Discussion: This restriction does not
apply to the ancestor type of a private extension — see
7.3;
such a type need not be completely defined prior to the
private_extension_declaration.
However, the restriction does apply to record extensions, so the ancestor
type will have to be completely defined prior to the
full_type_declaration
corresponding to the
private_extension_declaration.
4.b
Reason: We originally hoped we could
relax this restriction. However, we found it too complex to specify the
rules for a type derived from an incompletely defined limited type that
subsequently became nonlimited.
5/2
{
AI95-00401-01}
{record extension} If
there is a
record_extension_part, the derived
type is called a
record extension of the parent type. A
record_extension_part
shall be provided if and only if the parent type is a tagged type.
[An interface_list shall be provided only
if the parent type is a tagged type.]
5.a.1/2
Proof: {
AI95-00401-01}
The syntax only allows an interface_list
to appear with a record_extension_part, and
a record_extension_part can only be provided
if the parent type is a tagged type. We give the last sentence anyway
for completeness.
5.a
Implementation Note: We allow a record
extension to inherit discriminants; an early version of Ada 9X did not.
If the parent subtype is unconstrained, it can be implemented as though
its discriminants were repeated in a new known_discriminant_part
and then used to constrain the old ones one-for-one. However, in an extension
aggregate, the discriminants in this case do not appear in the component
association list.
5.b/2
Ramification:
{
AI95-00114-01}
This rule needs to be rechecked in the visible
part of an instance of a generic unit
because of
the “only if” part of the rule. For example:
5.c/2
generic
type T is private;
package P is
type Der is new T;
end P;
5.d/2
package I is new P (Some_Tagged_Type); -- illegal
5.e/2
{
AI95-00114-01}
The instantiation is illegal because a tagged type
is being extended in the visible part without a record_extension_part.
Note that this is legal in the private part or body of an instance, both
to avoid a contract model violation, and because no code that can see
that the type is actually tagged can also see the derived type declaration.
5.f/2
No recheck is needed for
derived types with a record_extension_part,
as that has to be derived from something that is known to be tagged (otherwise
the template is illegal).
5.1/2
{
AI95-00419-01}
If the reserved word limited appears in
a derived_type_definition, the parent type
shall be a limited type.
5.g/2
Reason: We allow
limited because we don't inherit limitedness from interfaces,
so we must have a way to derive a limited type from interfaces. The word
limited has to be legal when the parent could be an interface,
and that includes generic formal abstract types. Since we have to allow
it in this case, we might as well allow it everywhere as documentation,
to make it explicit that the type is limited.
5.h/2
However, we do not want
to allow limited when the parent is nonlimited: limitedness cannot
change in a derivation tree.
Static Semantics
6
{constrained (subtype)}
{unconstrained (subtype)}
The first subtype of the derived type is unconstrained
if a
known_discriminant_part is provided in
the declaration of the derived type, or if the parent subtype is unconstrained.
{corresponding constraint}
Otherwise, the constraint of the first subtype
corresponds
to that of the parent subtype in the following sense: it is the same
as that of the parent subtype except that for a range constraint (implicit
or explicit), the value of each bound of its range is replaced by the
corresponding value of the derived type.
6.a
Discussion: A
digits_constraint
in a
subtype_indication for a decimal fixed
point subtype always imposes a range constraint, implicitly if there
is no explicit one given. See
3.5.9, “
Fixed
Point Types”.
6.1/2
{
AI95-00231-01}
The first subtype of the derived type excludes
null (see 3.10) if and only if the parent
subtype excludes null.
7
The characteristics
of the derived type are defined as follows:
8/2
- {AI95-00251-01}
{AI95-00401-01}
{AI95-00442-01}
[If the parent type or a progenitor type belongs
to a class of types, then the derived type also belongs to that class.]
The following sets of types, as well as any higher-level sets composed
from them, are classes in this sense[, and hence the characteristics
defining these classes are inherited by derived types from their parent
or progenitor types]: signed integer, modular integer, ordinary fixed,
decimal fixed, floating point, enumeration, boolean, character, access-to-constant,
general access-to-variable, pool-specific access-to-variable, access-to-subprogram,
array, string, non-array composite, nonlimited, untagged record, tagged,
task, protected, and synchronized tagged Each
class of types that includes the parent type also includes the derived
type.
8.a
Discussion: This is inherent in our notion
of a “class” of types. It is not mentioned in the initial
definition of “class” since at that point type derivation
has not been defined. In any case, this rule ensures that every class
of types is closed under derivation.
9
- If the parent type is an elementary
type or an array type, then the set of possible values of the derived
type is a copy of the set of possible values of the parent type. For
a scalar type, the base range of the derived type is the same as that
of the parent type.
9.a
Discussion: The base range of a type
defined by an integer_type_definition or a
real_type_definition is determined by the
_definition, and is not necessarily the same
as that of the corresponding root numeric type from which the newly defined
type is implicitly derived. Treating numerics types as implicitly derived
from one of the two root numeric types is simply to link them into a
type hierarchy; such an implicit derivation does not follow all the rules
given here for an explicit derived_type_definition.
10
- If the parent type is a composite
type other than an array type, then the components, protected subprograms,
and entries that are declared for the derived type are as follows:
11
- The discriminants specified
by a new known_discriminant_part, if there
is one; otherwise, each discriminant of the parent type (implicitly declared
in the same order with the same specifications) — {inherited
discriminant} {inherited
component} in the latter case, the discriminants
are said to be inherited, or if unknown in the parent, are also
unknown in the derived type;
12
- Each nondiscriminant component,
entry, and protected subprogram of the parent type, implicitly declared
in the same order with the same declarations; {inherited
component} {inherited
protected subprogram} {inherited
entry} these components, entries, and
protected subprograms are said to be inherited;
12.a
Ramification: The profiles of entries
and protected subprograms do not change upon type derivation, although
the type of the “implicit” parameter identified by the prefix
of the name in a call does.
12.b
To be honest: Any name in the parent
type_declaration that denotes the current
instance of the type is replaced with a name denoting the current instance
of the derived type, converted to the parent type.
13
- Each component declared in a
record_extension_part, if any.
14
Declarations of components, protected subprograms,
and entries, whether implicit or explicit, occur immediately within the
declarative region of the type, in the order indicated above, following
the parent subtype_indication.
14.a
Discussion: The order of declarations
within the region matters for record_aggregates
and extension_aggregates.
14.b
Ramification: In most cases, these things
are implicitly declared
immediately following the parent
subtype_indication.
However,
7.3.1, “
Private
Operations” defines some cases in which they are implicitly
declared later, and some cases in which the are not declared at all.
14.c
Discussion: The place of the implicit
declarations of inherited components matters for visibility — they
are not visible in the known_discriminant_part
nor in the parent subtype_indication, but
are usually visible within the record_extension_part,
if any (although there are restrictions on their use). Note that a discriminant
specified in a new known_discriminant_part
is not considered “inherited” even if it has the same name
and subtype as a discriminant of the parent type.
15/2
- This
paragraph was deleted.{AI95-00419-01}
The derived type is limited if and only if the
parent type is limited.
15.a/2
This paragraph
was deleted.To be honest: {
AI95-00419-01}
The derived type can become nonlimited if the derivation
takes place in the visible part of a child package, and the parent type
is nonlimited as viewed from the private part of the child package —
see 7.5.
16
- [For each predefined operator of the
parent type, there is a corresponding predefined operator of the derived
type.]
16.a
Proof: This is a ramification of the
fact that each class that includes the parent type also includes the
derived type, and the fact that the set of predefined operators that
is defined for a type, as described in
4.5,
is determined by the classes to which it belongs.
16.b
Reason: Predefined operators are handled
separately because they follow a slightly different rule than user-defined
primitive subprograms. In particular the systematic replacement described
below does not apply fully to the relational operators for Boolean and
the exponentiation operator for Integer. The relational operators for
a type derived from Boolean still return Standard.Boolean. The exponentiation
operator for a type derived from Integer still expects Standard.Integer
for the right operand. In addition, predefined operators "reemerge"
when a type is the actual type corresponding to a generic formal type,
so they need to be well defined even if hidden by user-defined primitive
subprograms.
17/2
- {AI95-00401-01}
{inherited subprogram} For
each user-defined primitive subprogram (other than a user-defined equality
operator — see below) of the parent type or of a progenitor type that already exists at the place of the
derived_type_definition, there exists a corresponding
inherited primitive subprogram of the derived type with the same
defining name. {equality operator (special
inheritance rule for tagged types)} Primitive
user-defined equality operators of the parent type and any progenitor types are also inherited by the derived type,
except when the derived type is a nonlimited record extension, and the
inherited operator would have a profile that is type conformant with
the profile of the corresponding predefined equality operator; in this
case, the user-defined equality operator is not inherited, but is rather
incorporated into the implementation of the predefined equality operator
of the record extension (see 4.5.2). {type
conformance [partial]}
17.a
Ramification: We say “...already
exists...” rather than “is visible” or “has been
declared” because there are certain operations that are declared
later, but still exist at the place of the
derived_type_definition,
and there are operations that are never declared, but still exist. These
cases are explained in
7.3.1.
17.b
Note that nonprivate extensions can appear only
after the last primitive subprogram of the parent — the freezing
rules ensure this.
17.c
Reason: A special case is made for the
equality operators on nonlimited record extensions because their predefined
equality operators are already defined in terms of the primitive equality
operator of their parent type (and of the tagged components of the extension
part). Inheriting the parent's equality operator as is would be undesirable,
because it would ignore any components of the extension part. On the
other hand, if the parent type is limited, then any user-defined equality
operator is inherited as is, since there is no predefined equality operator
to take its place.
17.d/2
Ramification: {
AI-00114-01}
Because user-defined equality operators are not inherited by
nonlimited
record extensions, the formal parameter names of = and /= revert
to Left and Right, even if different formal parameter names were used
in the user-defined equality operators of the parent type.
17.e/2
Discussion: {
AI-00401-01}
This rule only describes what operations are inherited;
the rules that describe what happens when there are conflicting inherited
subprograms are found in 8.3.
18/2
{AI95-00401-01}
The profile of an inherited subprogram (including an inherited enumeration
literal) is obtained from the profile of the corresponding (user-defined)
primitive subprogram of the parent or progenitor
type, after systematic replacement of each subtype of its profile (see
6.1) that is of the parent or progenitor type with a corresponding subtype of the
derived type. {corresponding subtype}
For a given subtype of the parent or progenitor type, the corresponding subtype of the derived type
is defined as follows:
19
- If the declaration of the derived
type has neither a known_discriminant_part
nor a record_extension_part, then the corresponding
subtype has a constraint that corresponds (as defined above for the first
subtype of the derived type) to that of the given subtype.
20
- If the derived type is a record
extension, then the corresponding subtype is the first subtype of the
derived type.
21
- If the derived type has a new
known_discriminant_part but is not a record
extension, then the corresponding subtype is constrained to those values
that when converted to the parent type belong to the given subtype (see
4.6). {implicit
subtype conversion (derived type discriminants) [partial]}
21.a
Reason: An inherited subprogram of an
untagged type has an Intrinsic calling convention, which precludes the
use of the Access attribute. We preclude 'Access because correctly performing
all required constraint checks on an indirect call to such an inherited
subprogram was felt to impose an undesirable implementation burden.
22/2
{AI95-00401-01}
The same formal parameters have default_expressions
in the profile of the inherited subprogram. [Any type mismatch due to
the systematic replacement of the parent or progenitor
type by the derived type is handled as part of the normal type conversion
associated with parameter passing — see 6.4.1.]
22.a/2
Reason: {
AI95-00401-01}
We don't introduce the type conversion explicitly here since conversions
to record extensions or on access parameters are not generally legal.
Furthermore, any type conversion would just be "undone" since
the
parent's subprogram
of
the parent or progenitor is ultimately being called anyway.
(Null procedures can be inherited from a progenitor without being overridden,
so it is possible to call subprograms of an interface.)
23/2
{
AI95-00401-01}
If a primitive subprogram of the parent
or progenitor
type is visible at the place of the
derived_type_definition,
then the corresponding inherited subprogram is implicitly declared immediately
after the
derived_type_definition. Otherwise,
the inherited subprogram is implicitly declared later or not at all,
as explained in
7.3.1.
24
{derived type
[partial]} A derived type can also be defined
by a
private_extension_declaration (see
7.3)
or a
formal_derived_type_definition (see
12.5.1).
Such a derived type is a partial view of the corresponding full or actual
type.
25
All numeric types are derived types, in that they
are implicitly derived from a corresponding root numeric type (see
3.5.4
and
3.5.6).
Dynamic Semantics
26
{elaboration (derived_type_definition)
[partial]} The elaboration of a
derived_type_definition
creates the derived type and its first subtype, and consists of the elaboration
of the
subtype_indication and the
record_extension_part,
if any. If the
subtype_indication depends
on a discriminant, then only those expressions that do not depend on
a discriminant are evaluated.
26.a/2
Discussion: {
AI95-00251-01}
We don't mention the interface_list,
because it does not need elaboration (see 3.9.4.
This is consistent with the handling of discriminant_parts,
which aren't elaborated either.
27/2
{
AI95-00391-01}
{
AI95-00401-01}
{execution (call on an inherited subprogram)
[partial]} For the execution of a call on
an inherited subprogram, a call on the corresponding primitive subprogram
of the parent
or progenitor type is performed;
the normal conversion of each actual parameter to the subtype of the
corresponding formal parameter (see
6.4.1)
performs any necessary type conversion as well. If the result type of
the inherited subprogram is the derived type, the result of calling the
parent's subprogram
of the parent or progenitor is converted to the derived type
,
or in the case of a null extension, extended to the derived type using
the equivalent of an extension_aggregate with
the original result as the ancestor_part and
null record as the record_component_association_list.
{implicit subtype conversion (result
of inherited function) [partial]}
27.a/2
Discussion: {
AI95-00391-01}
If an inherited function returns the derived type, and the type is a
non-null record extension, then the inherited
function
shall be overridden, unless the type is
abstract (in which case the function is abstract, and (unless
overridden) cannot be called except via a dispatching call
).
See
3.9.3.
28
10
{closed
under derivation} Classes are closed under
derivation — any class that contains a type also contains its derivatives.
Operations available for a given class of types are available for the
derived types in that class.
29
11 Evaluating an inherited enumeration
literal is equivalent to evaluating the corresponding enumeration literal
of the parent type, and then converting the result to the derived type.
This follows from their equivalence to parameterless functions.
{implicit
subtype conversion (inherited enumeration literal) [partial]}
30
12 A generic subprogram is not a subprogram,
and hence cannot be a primitive subprogram and cannot be inherited by
a derived type. On the other hand, an instance of a generic subprogram
can be a primitive subprogram, and hence can be inherited.
31
13 If the parent type is an access type,
then the parent and the derived type share the same storage pool; there
is a
null access value for the derived type and it is the implicit
initial value for the type. See
3.10.
32
14 If the parent type is a boolean type,
the predefined relational operators of the derived type deliver a result
of the predefined type Boolean (see
4.5.2).
If the parent type is an integer type, the right operand of the predefined
exponentiation operator is of the predefined type Integer (see
4.5.6).
33
15 Any discriminants of the parent type
are either all inherited, or completely replaced with a new set of discriminants.
34
16 For an inherited subprogram, the subtype
of a formal parameter of the derived type need not have any value in
common with the first subtype of the derived type.
34.a
Proof: This
happens when the parent subtype is constrained to a range that does not
overlap with the range of a subtype of the parent type that appears in
the profile of some primitive subprogram of the parent type. For example:
34.b
type T1 is range 1..100;
subtype S1 is T1 range 1..10;
procedure P(X : in S1); -- P is a primitive subprogram
type T2 is new T1 range 11..20;
-- implicitly declared:
-- procedure P(X : in T2'Base range 1..10);
-- X cannot be in T2'First .. T2'Last
35
17 If the reserved word
abstract
is given in the declaration of a type, the type is abstract (see
3.9.3).
35.1/2
18 {
AI95-00251-01}
{
AI95-00401-01}
An interface type that has a progenitor type “is
derived from” that type. A derived_type_definition,
however, never defines an interface type.
35.2/2
19 {
AI95-00345-01}
It is illegal for the parent type of a derived_type_definition
to be a synchronized tagged type.
35.a/2
Proof: 3.9.1
prohibits record extensions whose parent type is a synchronized tagged
type, and this clause requires tagged types to have a record extension.
Thus there are no legal derivations. Note that a synchronized interface
can be used as a progenitor in an interface_definition
as well as in task and protected types, but we do not allow concrete
extensions of any synchronized tagged type.
Examples
36
Examples of derived
type declarations:
37
type Local_Coordinate
is new Coordinate; --
two different types
type Midweek
is new Day
range Tue .. Thu; --
see 3.5.1
type Counter
is new Positive; --
same range as Positive
38
type Special_Key
is new Key_Manager.Key; --
see 7.3.1
--
the inherited subprograms have the following specifications:
--
procedure Get_Key(K : out Special_Key);
--
function "<"(X,Y : Special_Key) return Boolean;
Inconsistencies With Ada 83
38.a
{
inconsistencies with Ada 83}
When
deriving from a (nonprivate, nonderived) type in the same visible part
in which it is defined, if a predefined operator had been overridden
prior to the derivation, the derived type will inherit the user-defined
operator rather than the predefined operator. The work-around (if the
new behavior is not the desired behavior) is to move the definition of
the derived type prior to the overriding of any predefined operators.
Incompatibilities With Ada 83
38.b
{
incompatibilities with Ada 83}
When
deriving from a (nonprivate, nonderived) type in the same visible part
in which it is defined, a primitive subprogram of the parent type declared
before the derived type will be inherited by the derived type. This can
cause upward incompatibilities in cases like this:
38.c
package P is
type T is (A, B, C, D);
function F( X : T := A ) return Integer;
type NT is new T;
-- inherits F as
-- function F( X : NT := A ) return Integer;
-- in Ada 95 only
...
end P;
...
use P; -- Only one declaration of F from P is use-visible in
-- Ada 83; two declarations of F are use-visible in
-- Ada 95.
begin
...
if F > 1 then ... -- legal in Ada 83, ambiguous in Ada 95
Extensions to Ada 83
38.d
{
extensions to Ada 83}
The
syntax for a
derived_type_definition is amended
to include an optional
record_extension_part
(see
3.9.1).
38.e
A derived type may override the discriminants
of the parent by giving a new discriminant_part.
38.f
The parent type in a derived_type_definition
may be a derived type defined in the same visible part.
38.g
When deriving from a type in the same visible
part in which it is defined, the primitive subprograms declared prior
to the derivation are inherited as primitive subprograms of the derived
type. See
3.2.3.
Wording Changes from Ada 83
38.h
We now talk about the classes to which a type
belongs, rather than a single class.
38.i
As explained in Section 13, the concept of "storage
pool" replaces the Ada 83 concept of "collection." These
concepts are similar, but not the same.
Extensions to Ada 95
38.j/2
38.k/2
{
AI95-00419-01}
A derived type may specify that it is a limited
type. This is required for interface ancestors (from which limitedness
is not inherited), but it is generally useful as documentation of limitedness.
Wording Changes from Ada 95
38.l/2
{
AI95-00391-01}
Defined the result of functions for null extensions
(which we no longer require to be overridden - see 3.9.3).
38.m/2
{
AI95-00442-01}
Defined the term “category of types”
and used it in wording elsewhere; also specified the language-defined
categories that form classes of types (this was never normatively specified
in Ada 95.