Contents Index Previous Next
3.4 Derived Types and Classes
1
{derived type} A
derived_type_definition defines
a new type (and its first subtype) whose characteristics are
derived
from those of a
parent type.
{inheritance: See
derived types and classes}
1.a
Glossary entry: {Derived
type} A derived type is a type defined in terms of another type,
which is the parent type of the derived type. Each class containing the
parent type also contains the derived type. The derived type inherits
properties such as components and primitive operations from the parent.
A type together with the types derived from it (directly or indirectly)
form a derivation class.
Syntax
2
derived_type_definition
::= [
abstract]
new parent_subtype_indication [
record_extension_part]
Legality Rules
3
{parent subtype} {parent
type} The
parent_subtype_indication
defines the
parent subtype; its type is the parent 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
{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.
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
Ramification: This rule
needs to be rechecked in the visible part of an instance of a generic
unit.
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''.
7
The characteristics
of the derived type are defined as follows:
8
- 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
- The derived type is limited if and only if the parent type
is limited.
15.a
To be honest: 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
- {inherited subprogram} For
each user-defined primitive subprogram (other than a user-defined equality
operator -- see below) of the parent 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 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
Ramification: Because user-defined
equality operators are not inherited by 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.
18
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 type, after systematic replacement of each subtype of its profile
(see 6.1) that is of the parent type with a corresponding
subtype of the derived type. {corresponding subtype}
For a given subtype of the parent 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
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 type by the derived type is handled as part of the
normal type conversion associated with parameter passing -- see 6.4.1.]
22.a
Reason: 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
is ultimately being called anyway.
23
If a primitive subprogram of the parent
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.
27
{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 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 is converted to the derived type.
{implicit subtype conversion (result of inherited function)
[partial]}
27.a
Discussion: If an inherited function
returns the derived type, and the type is a record extension, then the inherited
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).
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.
Contents Index Previous Next Legal