3.4 Derived Types and Classes
This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue
A derived_type_definition
defines a derived type (and its first subtype) whose characteristics are derived from those of a parent type, and possibly from progenitor 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.
Syntax
3/2derived_type_definition
::=
[abstract] [limited] new parent_subtype_indication
[[and interface_list
] record_extension_part
]
Legality Rules
4/2The parent_subtype_indication
defines the parent subtype; its type is the 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.
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_declaration
s for the parent type and any of its subcomponents have to precede the derived_type_definition
.]
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
. 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.]
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. 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. generic
type T is private;
package P is
type Der is new T;
end P;
7.d/2package I is new P (Some_Tagged_Type); -- illegal
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.record_extension_part
, as that has to be derived from something that is known to be tagged (otherwise the template is illegal). If the reserved word limited appears in a derived_type_definition
, the parent type shall be a limited type. If the parent type is a tagged formal type, then 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.
Static Semantics
8The 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. 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.
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”. The first subtype of the derived type excludes null (see 3.10) if and only if the parent subtype excludes null.
The characteristics and implicitly declared primitive subprograms of the derived type are defined as follows:
- [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.
- 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.
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
. - 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:
- 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) — in the latter case, the discriminants are said to be inherited, or if unknown in the parent, are also unknown in the derived type; 14 - Each nondiscriminant component, entry, and protected subprogram of the parent type, implicitly declared in the same order with the same declarations; these components, entries, and protected subprograms are said to be inherited;
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.- Each component declared in a
record_extension_part
, if any.
- 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
.
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. 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. - This paragraph was deleted.
- [For each predefined operator of the parent type, there is a corresponding predefined operator of the derived type.]
- 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. 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).
derived_type_definition
, and there are operations that are never declared, but still exist. These cases are explained in 7.3.1.- 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, other than those subtypes found in the designated profile of an
access_definition
, with a corresponding subtype of the derived type. For a given subtype of the parent or progenitor type, the corresponding subtype of the derived type is defined as follows:
- If the declaration of the derived type has neither a
known_discriminant_part
nor arecord_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. 22 - If the derived type is a record extension, then the corresponding subtype is the first subtype of the derived type.
- 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).
- The same formal parameters have
default_expression
s 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.]
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.
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.
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
28The 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.
interface_list
, because it does not need elaboration (see 3.9.4). This is consistent with the handling of discriminant_part
s, which aren't elaborated either. 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 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
.
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
derived_type_definition
, however, never defines an interface type.derived_type_definition
to be a synchronized tagged type. interface_type_definition
as well as in task and protected types, but we do not allow concrete extensions of any synchronized tagged type. Examples
38Examples of derived type declarations:
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
40type 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
Incompatibilities With Ada 83
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
derived_type_definition
is amended to include an optional record_extension_part
(see 3.9.1).discriminant_part
.derived_type_definition
may be a derived type defined in the same visible part.Wording Changes from Ada 83
Extensions to Ada 95
Wording Changes from Ada 95
Incompatibilities With Ada 2005
Wording Changes from Ada 2005
3.4.1 Derivation Classes
1In addition to the various language-defined classes of types, types can be grouped into derivation classes.
Static Semantics
2/2A 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. 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).
Every type is one of a specific type, a class-wide type, or a universal type. A specific type is one defined by a type_declaration
, a formal_type_declaration
, or a full type definition embedded in another construct. Class-wide and universal types are implicitly defined, to act as representatives for an entire class of types, as follows:
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 - 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.
attribute_definition_clause
like “for S'Class'Output use ...;” will be legal. Universal types
- Universal types are defined for [(and belong to)] the integer, real, fixed point, and access classes, and are referred to in this document as respectively, universal_integer, universal_real, universal_fixed, and universal_access. These are analogous to class-wide types for these language-defined elementary 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.
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.
A class-wide or universal type is said to cover all of the types in its class. In addition, universal_integer covers a type that has a specified Integer_Literal aspect, while universal_real covers a type that has a specified Real_Literal aspect (see 4.2.1). A specific type covers only itself.
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. If a type T2 is a descendant of a type T1, then T1 is called an ancestor of T2. An ultimate ancestor of a type is an ancestor of that type that is not itself a descendant of any other type. Every untagged type has a unique ultimate ancestor.
- 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.
- A class of types has at most one root type; a derivation class has exactly one root type.
- The root of a class is an ancestor of all of the types in the class (including itself).
- 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.
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.