Contents Index Search Previous Next
3.9 Tagged Types and Type Extensions
1
[
{dispatching operation
[partial]} {polymorphism}
{dynamic binding: See dispatching
operation} {generic
unit: See also dispatching operation} {variant:
See also tagged type} Tagged types and
type extensions support object-oriented programming, based on inheritance
with extension and run-time polymorphism via
dispatching operations.
{object-oriented programming (OOP): See tagged types
and type extensions} {OOP
(object-oriented programming): See tagged types and type extensions}
{inheritance: See also tagged
types and type extension} ]
Language Design Principles
1.a
The intended implementation
model is for a tag to be represented as a pointer to a statically allocated
and link-time initialized type descriptor. The type descriptor contains
the address of the code for each primitive operation of the type. It
probably also contains other information, such as might make membership
tests convenient and efficient.
1.b
The primitive operations of
a tagged type are known at its first freezing point; the type descriptor
is laid out at that point. It contains linker symbols for each primitive
operation; the linker fills in the actual addresses.
1.c
Other implementation models
are possible.
1.d
The rules ensure that ``dangling
dispatching'' is impossible; that is, when a dispatching call is made,
there is always a body to execute. This is different from some other
object-oriented languages, such as Smalltalk, where it is possible to
get a run-time error from a missing method.
1.e
Dispatching calls should be
efficient, and should have a bounded worst-case execution time. This
is important in a language intended for real-time applications. In the
intended implementation model, a dispatching call involves calling indirect
through the appropriate slot in the dispatch table. No complicated "method
lookup" is involved.
1.f
The programmer should have the
choice at each call site of a dispatching operation whether to do a dispatching
call or a statically determined call (i.e. whether the body executed
should be determined at run time or at compile time).
1.g
The same body should be executed
for a call where the tag is statically determined to be T'Tag as for
a dispatching call where the tag is found at run time to be T'Tag. This
allows one to test a given tagged type with statically determined calls,
with some confidence that run-time dispatching will produce the same
behavior.
1.h
All views of a type should share
the same type descriptor and the same tag.
1.i
The visibility rules determine
what is legal at compile time; they have nothing to do with what bodies
can be executed at run time. Thus, it is possible to dispatch to a subprogram
whose declaration is not visible at the call site. In fact, this is one
of the primary facts that gives object-oriented programming its power.
The subprogram that ends up being dispatched to by a given call might
even be designed long after the call site has been coded and compiled.
1.j
Given that Ada has overloading,
determining whether a given subprogram overrides another is based both
on the names and the type profiles of the operations.
1.k
When a type extension is declared,
if there is any place within its immediate scope where a certain subprogram
of the parent is visible, then a matching subprogram should override.
If there is no such place, then a matching subprogram should be totally
unrelated, and occupy a different slot in the type descriptor. This is
important to preserve the privacy of private parts; when an operation
declared in a private part is inherited, the inherited version can be
overridden only in that private part, in the package body, and in any
children of the package.
1.l
If an implementation shares
code for instances of generic bodies, it should be allowed to share type
descriptors of tagged types declared in the generic body, so long as
they are not extensions of types declared in the specification of the
generic unit.
Static Semantics
2
{tagged type} A
record type or private type that has the reserved word
tagged
in its declaration is called a
tagged type. [When deriving from
a tagged type, additional components may be defined. As for any derived
type, additional primitive subprograms may be defined, and inherited
primitive subprograms may be overridden.]
{type extension}
{extension (of a type)}
The derived type is called an
extension of
the ancestor type, or simply a
type extension.
{extension
(of a record type)} {private
extension} {extension
(of a private type)} Every type extension
is also a tagged type, and is either a
record extension or a
private
extension of some other tagged type. A record extension is defined
by a
derived_type_definition with
a
record_extension_part. A private
extension, which is a partial view of a record extension, can be declared
in the visible part of a package (see
7.3)
or in a generic formal part (see
12.5.1).
2.a
Glossary entry: {Tagged
type} The objects of a tagged type have a run-time type tag, which
indicates the specific type with which the object was originally created.
An operand of a class-wide tagged type can be used in a dispatching call;
the tag indicates which subprogram body to invoke. Nondispatching calls,
in which the subprogram body to invoke is determined at compile time,
are also allowed. Tagged types may be extended with additional components.
2.b
Ramification: If a tagged
type is declared other than in a package_specification,
it is impossible to add new primitive subprograms for that type, although
it can inherit primitive subprograms, and those can be overridden. If
the user incorrectly thinks a certain subprogram is primitive when it
is not, and tries to call it with a dispatching call, an error message
will be given at the call site.
2.c
Note that the accessibility
rules imply that a tagged type declared in a library package_specification
cannot be extended in a nested subprogram or task body.
3
{tag of an object}
An object of a tagged type has an associated (run-time)
tag that identifies the specific tagged type used to create the
object originally. [ The tag of an operand of a class-wide tagged type
T'Class controls which subprogram body is to be executed when
a primitive subprogram of type
T is applied to the operand (see
3.9.2);
{dispatching}
using a tag to control which body to execute is called
dispatching.]
{type tag: See tag}
{run-time type: See tag}
{type: See also tag}
{class: See also tag}
4
The tag of a specific tagged type identifies the
full_type_declaration of the type.
If a declaration for a tagged type occurs within a generic_package_declaration,
then the corresponding type declarations in distinct instances of the
generic package are associated with distinct tags. For a tagged type
that is local to a generic package body, the language does not specify
whether repeated instantiations of the generic body result in distinct
tags.
4.a
Reason: This eases generic
code sharing.
4.b
Implementation Note: The
language does not specify whether repeated elaborations of the same full_type_declaration
correspond to distinct tags. In most cases, we expect that all elaborations
will correspond to the same tag, since the tag will frequently be the
address (or index) of a statically allocated type descriptor. However,
with shared generics, the type descriptor might have to be allocated
on a per-instance basis, which in some implementation models implies
per-elaboration of the instantiation.
5
The following language-defined
library package exists:
6
package Ada.Tags is
type Tag is private;
7
function Expanded_Name(T : Tag) return String;
function External_Tag(T : Tag) return String;
function Internal_Tag(External : String) return Tag;
8
9
private
... -- not specified by the language
end Ada.Tags;
9.a
Reason: Tag is a nonlimited,
definite subtype, because it needs the equality operators, so that tag
checking makes sense. Also, equality, assignment, and object declaration
are all useful capabilities for this subtype.
9.b
For an object X and a type T,
``X'Tag = T'Tag'' is not needed, because a membership test can be used.
However, comparing the tags of two objects cannot be done via membership.
This is one reason to allow equality for type Tag.
10
The function Expanded_Name returns the full expanded
name of the first subtype of the specific type identified by the tag,
in upper case, starting with a root library unit. The result is implementation
defined if the type is declared within an unnamed block_statement.
10.a
To be honest: This name,
as well as each prefix of it, does
not denote a renaming_declaration.
10.b
Implementation defined: The
result of Tags.Expanded_Name for types declared within an unnamed block_statement.
11
The function External_Tag returns a string to
be used in an external representation for the given tag. The call External_Tag(S'Tag)
is equivalent to the
attribute_reference
S'External_Tag (see
13.3).
11.a
Reason: It might seem
redundant to provide both the function External_Tag and the attribute
External_Tag. The function is needed because the attribute can't be applied
to values of type Tag. The attribute is needed so that it can be specifiable
via an attribute_definition_clause.
12
The function Internal_Tag returns the tag that
corresponds to the given external tag, or raises Tag_Error if the given
string is not the external tag for any specific type of the partition.
13
For every subtype S of a tagged type T
(specific or class-wide), the following attributes are defined:
14
- S'Class
-
S'Class denotes a subtype of
the class-wide type (called T'Class in this International Standard)
for the class rooted at T (or if S already denotes a class-wide
subtype, then S'Class is the same as S).
15
- {unconstrained
(subtype)} {constrained
(subtype)} S'Class is unconstrained. However,
if S is constrained, then the values of S'Class are only those that when
converted to the type T belong to S.
15.a
Ramification: This attribute
is defined for both specific and class-wide subtypes. The definition
is such that S'Class'Class is the same as S'Class.
15.b
Note that if S is constrained,
S'Class is only partially constrained, since there might be additional
discriminants added in descendants of T which are not constrained.
15.c
Reason: The Class attribute
is not defined for untagged subtypes (except for incomplete types and
private types whose full view is tagged -- see 3.10.1
and 7.3.1) so as to preclude implicit conversion
in the absence of run-time type information. If it were defined for untagged
subtypes, it would correspond to the concept of universal types provided
for the predefined numeric classes.
16
- S'Tag
-
S'Tag denotes the tag of the
type T (or if T is class-wide, the tag of the root type
of the corresponding class). The value of this attribute is of type Tag.
16.a
Reason: S'Class'Tag equals
S'Tag, to avoid generic contract model problems when S'Class is the actual
type associated with a generic formal derived type.
17
Given a prefix
X that is of a class-wide tagged type [(after any implicit dereference)],
the following attribute is defined:
18
- X'Tag
-
X'Tag denotes the tag of X. The
value of this attribute is of type Tag.
18.a
Reason: X'Tag is not
defined if X is of a specific type. This is primarily to avoid confusion
that might result about whether the Tag attribute should reflect the
tag of the type of X, or the tag of X. No such confusion is possible
if X is of a class-wide type.
Dynamic Semantics
19
The tag associated
with an object of a tagged type is determined as follows:
20
- {tag of an object
(stand-alone object, component, or aggregate) [partial]}
The tag of a stand-alone object, a component, or
an aggregate of a specific tagged
type T identifies T.
20.a
Discussion: The tag of
a formal parameter of type T is not necessarily the tag of T,
if, for example, the actual was a type conversion.
21
- {tag of an object
(object created by an allocator) [partial]} The
tag of an object created by an allocator for an access type with a specific
designated tagged type T, identifies T.
21.a
Discussion: The tag of
an object designated by a value of such an access type might not be T,
if, for example, the access value is the result of a type conversion.
22
- {tag of an object
(class-wide object) [partial]} The tag
of an object of a class-wide tagged type is that of its initialization
expression.
22.a
Ramification: The tag
of an object (even a class-wide one) cannot be changed after it is initialized,
since a ``class-wide'' assignment_statement
raises Constraint_Error if the tags don't match, and a ``specific'' assignment_statement
does not affect the tag.
23
- {tag of an object
(returned by a function) [partial]} The
tag of the result returned by a function whose result type is a specific
tagged type T identifies T.
23.a
Implementation Note: This
requires a run-time check for limited tagged types, since they are returned
"by-reference." For a nonlimited type, a new anonymous object
with the appropriate tag is created as part of the function return, and
then assigned the value of the return expression. See 6.5,
``Return Statements''.
24
- {tag of an object
(returned by a function) [partial]} The
tag of the result returned by a function with a class-wide result type
is that of the return expression.
25
{tag of an object (preserved
by type conversion and parameter passing) [partial]} The
tag is preserved by type conversion and by parameter passing. The tag
of a value is the tag of the associated object (see
6.2).
Implementation Permissions
26
The implementation of the functions in Ada.Tags
may raise Tag_Error if no specific type corresponding to the tag passed
as a parameter exists in the partition at the time the function is called.
26.a
Reason: In most implementations,
repeated elaborations of the same type_declaration
will all produce the same tag. In such an implementation, Tag_Error will
be raised in cases where the internal or external tag was passed from
a different partition. However, some implementations might create a new
tag value at run time for each elaboration of a type_declaration.
In that case, Tag_Error could also be raised if the created type no longer
exists because the subprogram containing it has returned, for example.
We don't require the latter behavior; hence the word ``may'' in this
rule.
27
62 A type declared with
the reserved word tagged should normally be declared in a package_specification,
so that new primitive subprograms can be declared for it.
28
63 Once an object has been
created, its tag never changes.
29
64 Class-wide types are
defined to have unknown discriminants (see 3.7).
This means that objects of a class-wide type have to be explicitly initialized
(whether created by an object_declaration
or an allocator), and that aggregates
have to be explicitly qualified with a specific type when their expected
type is class-wide.
30
65 If S denotes an untagged
private type whose full type is tagged, then S'Class is also allowed
before the full type definition, but only in the private part of the
package in which the type is declared (see 7.3.1).
Similarly, the Class attribute is defined for incomplete types whose
full type is tagged, but only within the library unit in which the incomplete
type is declared (see 3.10.1).
Examples
31
Examples of
tagged record types:
32
type Point is tagged
record
X, Y : Real := 0.0;
end record;
33
type Expression is tagged null record;
-- Components will be added by each extension
Extensions to Ada 83
33.a
{extensions to Ada 83}
Tagged types are a new concept.
Contents Index Search Previous Next Legal