3.9.1 Type Extensions
{
AI95-00345-01}
[
Every
type extension is a tagged type, and is a
record extension or
a
private extension of some other tagged type, or a noninterface
synchronized tagged type.]
Language Design Principles
We want to make sure that we can extend a generic
formal tagged type, without knowing its discriminants.
We don't want to allow components in an extension
aggregate to depend on discriminants inherited from the parent value,
since such dependence requires staticness in aggregates, at least for
variants.
Syntax
Legality Rules
{
AI95-00344-01}
{
AI95-00345-01}
{
AI95-00419-01}
The parent type of a record extension shall not be a class-wide type
nor shall it be a synchronized tagged type (see
3.9.4).
If the parent type or any progenitor is nonlimited, then each of the
components of the
record_extension_part
shall be nonlimited.
In addition to the places where
Legality Rules normally apply (see
12.3),
these rules apply also in the private part of an instance of a generic
unit.
Reason: If the parent is a limited formal
type, then the actual might be nonlimited.
{
AI95-00344-01}
Ada 95 required the record extensions to be the same level as the parent
type. Now we use accessibility checks on class-wide
allocators
and return statements to prevent objects from living longer than their
type.
{
AI95-00345-01}
Synchronized tagged types cannot be extended. We have this limitation
so that all of the data of a task or protected type is defined within
the type. Data defined outside of the type wouldn't be subject to the
mutual exclusion properties of a protected type, and couldn't be used
by a task, and thus doesn't seem to be worth the potential impact on
implementations.
{
AI95-00344-01}
Within the body of a generic unit, or the body of any of its descendant
library units, a tagged type shall not be declared as a descendant of
a formal type declared within the formal part of the generic unit.
Reason: This paragraph ensures that a
dispatching call will never attempt to execute an inaccessible subprogram
body.
{
AI95-00344-01}
The convoluted wording (“formal type declared within the formal
part”) is necessary to include tagged types that are formal parameters
of formal packages of the generic unit, as well as formal tagged and
tagged formal derived types of the generic unit.
{
AI95-00344-01}
This rule is necessary in order to preserve the contract model.
{
AI05-0005-1}
{
AI95-00344-01}
If an ancestor is a formal of the generic unit , we have a problem because
it might have an unknown number of subprograms that require overriding,
as in the following example:
package P is
type T is tagged null record;
function F return T; -- Inherited versions will require overriding.
end P;
generic
type TT is tagged private;
package Gp is
type NT is abstract new TT with null record;
procedure Q(X : in NT) is abstract;
end Gp;
package body Gp is
type NT2 is new NT with null record; -- Illegal!
procedure Q(X : in NT2) is begin null; end Q;
-- Is this legal or not? Can't decide because
-- we don't know whether TT had any functions that require
-- overriding on extension.
end Gp;
package I is new Gp(TT => P.T);
I.NT is an abstract type with two abstract subprograms:
F (inherited as abstract) and Q (explicitly declared as abstract). But
the generic body doesn't know about F, so we don't know that it needs
to be overridden to make a nonabstract extension of NT. Hence, we have
to disallow this case.
Similarly, since the actual type for a formal
tagged limited private type can be a nonlimited type, we would have a
problem if a type extension of a limited private formal type could be
declared in a generic body. Such an extension could have a task component,
for example, and an object of that type could be passed to a dispatching
operation of a nonlimited ancestor type. That operation could try to
copy the object with the task component. That would be bad. So we disallow
this as well.
If TT were declared as abstract, then we could
have the same problem with abstract procedures.
We considered disallowing all tagged types in
a generic body, for simplicity. We decided not to go that far, in order
to avoid unnecessary restrictions.
We also considered trying
make the accessibility level part of the contract; i.e. invent some way
of saying (in the
generic_declaration)
“all instances of this generic unit will have the same accessibility
level as the
generic_declaration”.
Unfortunately, that doesn't solve the part of the problem having to do
with abstract types.
This paragraph
was deleted.
Ramification: {
AI95-00344}
This rule applies to types with ancestors (directly or indirectly) of
formal interface types (see
12.5.5), formal
tagged private types (see
12.5.1), and formal
derived private types whose ancestor type is tagged (see
12.5.1).
Static Semantics
{
AI12-0191-1}
In the case
where the (compile-time) view of an object
X is of a tagged type
T1 or T1'Class and the (run-time) tag of
X is T2'Tag, only the
components (if any) of
X that are components of T1 (or that are
discriminants which correspond to a discriminant of T1) are said to be
components of the nominal type of
X. Similarly, only parts
(respectively, subcomponents) of T1 are parts (respectively, subcomponents)
of the nominal type of
X.
Ramification: {
AI12-0191-1}
For example, if T2 is an undiscriminated extension of T1 which declares
a component named Comp, then
X.Comp is not a component of the
nominal type of
X.
Discussion:
{
AI12-0191-1}
For example, there is a Dynamic Semantics rule that finalization of an
object includes finalization of its components (see
7.6.1).
In the following case:
type T1 is tagged null record;
type T2 is new T1 with record
Comp : Some_Controlled_Type;
end record;
function Func return T1'Class is (T2'(others => <>));
X : T1'Class := Func;
the rule that “every component of the
object is finalized” (as opposed to something like “every
component of the nominal type of the object is finalized”) means
that the finalization of X will include finalization of X.Comp. For another
example, see the rule about accessibility checking of access discriminants
of parts of function results in
6.5. In contrast,
the rules in
7.3.2 explicitly state that
type invariant checks are only performed for parts which are of the type-invariant
bearing type and which are parts of the nominal type of the object (as
opposed to for all parts, whether part of the nominal type or not, which
are of the invariant-bearing type). Similarly, the rule in
13.13.2
governing which components of a composite value are read and written
by the default implementations of Read and Write for a composite type
states that only the components of the object which are components of
the nominal type of the object are read or written.
Dynamic Semantics
NOTE 1 The term “type extension”
refers to a type as a whole. The term “extension part” refers
to the piece of text that defines the additional components (if any)
the type extension has relative to its specified ancestor type.
Discussion: We considered other terminology,
such as “extended type”. However, the terms “private
extended type” and “record extended type” did not convey
the proper meaning. Hence, we have chosen to uniformly use the term “extension”
as the type resulting from extending a type, with “private extension”
being one produced by privately extending the type, and “record
extension” being one produced by extending the type with an additional
record-like set of components. Note also that the term “type extension”
refers to the result of extending a type in the language Oberon as well
(though there the term “extended type” is also used, interchangeably,
perhaps because Oberon doesn't have the concept of a “private extension”).
NOTE 2 {
AI95-00344-01}
When an extension is declared immediately within a body, primitive subprograms
are inherited and are overridable, but new primitive subprograms cannot
be added.
Reason: The restriction against depending
on discriminants of the parent is to simplify the definition of extension
aggregates. The restriction against using parent components in other
ways is methodological; it presumably simplifies implementation as well.
NOTE 4 {
AI12-0447-1}
By the rules given in
8.3, each visible component
of a record extension will have a unique name, whether the component
is (visibly) inherited from the parent type or declared in the
record_extension_part.
Examples
Examples of record
extensions (of types defined above in 3.9):
type Painted_Point is new Point with
record
Paint : Color := White;
end record;
-- Components X and Y are inherited
Origin : constant Painted_Point := (X | Y => 0.0, Paint => Black);
type Literal is new Expression with
record -- a leaf in an Expression tree
Value : Real;
end record;
{
AI12-0404-1}
type Expr_Ptr
is access all Expression'Class;
--
see 3.9
type Binary_Operation is new Expression with
record -- an internal node in an Expression tree
Left, Right : Expr_Ptr;
end record;
type Addition is new Binary_Operation with null record;
type Subtraction is new Binary_Operation with null record;
-- No additional components needed for these extensions
Tree : Expr_Ptr := -- A tree representation of “5.0 + (13.0–7.0)”
new Addition'(
Left => new Literal'(Value => 5.0),
Right => new Subtraction'(
Left => new Literal'(Value => 13.0),
Right => new Literal'(Value => 7.0)));
Extensions to Ada 83
Type extension is a new
concept.
Extensions to Ada 95
{
AI95-00344-01}
Type extensions now can be declared in more nested
scopes than their parent types. Additional accessibility checks on
allocators
and return statements prevent objects from outliving their type.
Wording Changes from Ada 95
{
AI95-00345-01}
Added wording to prevent extending synchronized tagged types.
Wording Changes from Ada 2012
{
AI12-0191-1}
Defined the term “components of the nominal type” to remove
a confusion as to how components are described in Dynamic Semantics rules.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe