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/2
{
AI95-00251-01}
The intended implementation model is for
the static
portion of 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.
Tags
for nested type extensions must also have a dynamic part that identifies
the particular elaboration of the type.
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.b.1/2
{
AI95-00251-01}
Primitive operations of type extensions that are
declared at a level deeper than the level of the ultimate ancestor from
which they are derived can be represented by wrappers that use the dynamic
part of the tag to call the actual primitive operation. The dynamic part
would generally be some way to represent the static link or display necessary
for making a nested call. One implementation strategy would be to store
that information in the extension part of such nested type extensions,
and use the dynamic part of the tag to point at it. (That way, the “dynamic”
part of the tag could be static, at the cost of indirect access.)
1.b.2/2
{
AI95-00251-01}
If the tagged type is descended from any interface
types, it also will need to include “subtags” (one for each
interface) that describe the mapping of the primitive operations of the
interface to the primitives of the type. These subtags could directly
reference the primitive operations (for faster performance), or simply
provide the tag “slot” numbers for the primitive operations
(for easier derivation). In either case, the subtags would be used for
calls that dispatch through a class-wide type of the interface.
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/2
{
AI95-00251-01}
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
although a call which is dispatching on an interface may require a lookup
of the appropriate interface subtag.
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/2
{
AI-00401-01}
When a type extension is declared, if there is any place within its immediate
scope where a certain subprogram of the parent
or
progenitor 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/2
{
AI95-00345-01}
{tagged type} A
record type or private type that has the reserved word
tagged
in its declaration is called a
tagged type.
In addition, an interface type is a tagged type, as is a task or protected
type derived from an interface (see 3.9.4).
[When deriving from a tagged type,
as 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
its the
ancestor
types 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.1/2
{
AI95-00345-01}
{extension (of
a record type)} {private
extension} {extension
(of a private type)} Every type extension
is also a tagged type, and is a record extension or a private
extension of some other tagged type, or a non-interface synchronized
tagged type (see 3.9.4). A record extension
is defined by a derived_type_definition with
a record_extension_part (see 3.9.1)[,
which may include the definition of additional components]. A private
extension, which is a partial view of a record extension or of a synchronized
tagged type, 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/2
Ramification: {
AI95-00218-03}
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.
Similarly, by using
an overriding_indicator (see 6.1),
the user can declare that a subprogram is intended to be overriding,
and get an error message when they made a mistake. The use of overriding_indicators
is highly recommended in new code that does not need to be compatible
with Ada 95.
2.c/2
This paragraph
was deleted.{
AI95-00344-01}
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/2
{
AI95-00344-01}
The tag of a specific tagged type identifies the
full_type_declaration
of the type
, and for a type extension, is sufficient
to uniquely identify the type among all descendants of the same ancestor.
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
and with
all of its ancestors (if any) also local to the generic body,
the language does not specify whether repeated instantiations of the
generic body result in distinct tags.
{Unspecified
[partial]}
4.a/2
This paragraph
was deleted.Reason: {
AI95-00344-01}
This eases generic code sharing.
4.a.1/2
Implementation Note:
{
AI95-00344-01}
In most cases, a tag need only identify a particular
tagged type declaration, and can therefore be a simple link-time-known
address. However, for tag checks (see 3.9.2)
it is essential that each descendant (that currently exists) of a given
type have a unique tag. Hence, for types declared in shared generic bodies
where an ancestor comes from outside the generic, or for types declared
at a deeper level than an ancestor, the tag needs to be augmented with
some kind of dynamic descriptor (which may be a static link, global display,
instance descriptor pointer, or combination). This implies that type
Tag may need to be two words, the second of which is normally null, but
in these identified special cases needs to include a static link or equivalent.
Within an object of one of these types with a two-word tag, the two parts
of the tag would typically be separated, one part as the first word of
the object, the second placed in the first extension part that corresponds
to a type declared more nested than its parent or declared in a shared
generic body when the parent is declared outside. Alternatively, by using
an extra level of indirection, the type Tag could remain a single-word.
4.b/2
{
AI95-00344-01}
For types that are not type extensions (even for
ones declared in nested scopes), we do not require that The
language does not specify whether repeated elaborations of the
same
full_type_declaration correspond to distinct
tags.
This was done so that Ada 2005 implementations
of tagged types could maintain representation compatibility with Ada
95 implementations. Only type extensions that were not allowed in Ada
95 require additional information with the tag. 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.
4.c/2
To be honest: {
AI95-00344-01}
The wording “is sufficient to uniquely identify
the type among all descendants of the same ancestor” only applies
to types that currently exist. It is not necessary to distinguish between
descendants that currently exist, and descendants of the same type that
no longer exist. For instance, the address of the stack frame of the
subprogram that created the tag is sufficient to meet the requirements
of this rule, even though it is possible, after the subprogram returns,
that a later call of the subprogram could have the same stack frame and
thus have an identical tag.
5
The following language-defined
library package exists:
6/2
{
AI95-00362-01}
package Ada.Tags
is
pragma Preelaborate(Tags);
type Tag
is private;
pragma Preelaborable_Initialization(Tag);
6.1/2
7/2
{
AI95-00400-01}
function Expanded_Name(T : Tag)
return String;
function Wide_Expanded_Name(T : Tag) return Wide_String;
function Wide_Wide_Expanded_Name(T : Tag) return Wide_Wide_String;
function External_Tag(T : Tag)
return String;
function Internal_Tag(External : String)
return Tag;
7.1/2
{
AI95-00344-01}
function Descendant_Tag(External : String; Ancestor : Tag) return Tag;
function Is_Descendant_At_Same_Level(Descendant, Ancestor : Tag)
return Boolean;
7.2/2
7.3/2
{
AI95-00405-01}
type Tag_Array is array (Positive range <>) of Tag;
7.4/2
{
AI95-00405-01}
function Interface_Ancestor_Tags (T : Tag) return Tag_Array;
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.
9.1/2
9.c/2
Reason: {
AI95-00260-02}
This is similar to the requirement that all access
values be initialized to null.
10/2
{
AI95-00400-01}
The function
Wide_Wide_Expanded_Name 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/2
Implementation defined: The result of
Tags.Wide_Wide_Expanded_Name Tags.Expanded_Name
for types declared within an unnamed block_statement.
10.1/2
{
AI95-00400-01}
The function Expanded_Name (respectively, Wide_Expanded_Name)
returns the same sequence of graphic characters as that defined for Wide_Wide_Expanded_Name,
if all the graphic characters are defined in Character (respectively,
Wide_Character); otherwise, the sequence of characters is implementation
defined, but no shorter than that returned by Wide_Wide_Expanded_Name
for the same value of the argument.
10.c/2
Implementation defined:
The sequence of characters of the value
returned by Tags.Expanded_Name (respectively, Tags.Wide_Expanded_Name)
when some of the graphic characters of Tags.Wide_Wide_Expanded_Name are
not defined in Character (respectively, Wide_Character).
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 specified specifiable
via an attribute_definition_clause.
11.1/2
{
AI95-00417-01}
The string returned by the functions Expanded_Name,
Wide_Expanded_Name, Wide_Wide_Expanded_Name, and External_Tag has lower
bound 1.
12/2
{
AI95-00279-01}
The function Internal_Tag returns
a 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.
Tag_Error is also raised if the specific
type identified is a library-level type whose tag has not yet been created
(see 13.14).
12.a/2
Reason: {
AI95-00279-01}
The check for uncreated library-level types prevents
a reference to the type before execution reaches the freezing point of
the type. This is important so that T'Class'Input or an instance of Tags.Generic_Dispatching_Constructor
do not try to create an object of a type that hasn't been frozen (which
may not have yet elaborated its constraints). We don't require this behavior
for non-library-level types as the tag can be created multiple times
and possibly multiple copies can exist at the same time, making the check
complex.
12.1/2
{
AI95-00344-01}
The function Descendant_Tag returns the (internal)
tag for the type that corresponds to the given external tag and is both
a descendant of the type identified by the Ancestor tag and has the same
accessibility level as the identified ancestor. Tag_Error is raised if
External is not the external tag for such a type. Tag_Error is also raised
if the specific type identified is a library-level type whose tag has
not yet been created.
12.b/2
Reason: Descendant_Tag
is used by T'Class'Input to identify the type identified by an external
tag. Because there can be multiple elaborations of a given type declaration,
Internal_Tag does not have enough information to choose a unique such
type. Descendant_Tag does not return the tag for types declared at deeper
accessibility levels than the ancestor because there could be ambiguity
in the presence of recursion or multiple tasks. Descendant_Tag can be
used in constructing a user-defined replacement for T'Class'Input.
12.2/2
{
AI95-00344-01}
The function Is_Descendant_At_Same_Level returns
True if the Descendant tag identifies a type that is both a descendant
of the type identified by Ancestor and at the same accessibility level.
If not, it returns False.
12.c/2
Reason: Is_Descendant_At_Same_Level
(or something similar to it) is used by T'Class'Output to determine whether
the item being written is at the same accessibility level as T. It may
be used to determine prior to using T'Class'Output whether Tag_Error
will be raised, and also can be used in constructing a user-defined replacement
for T'Class'Output.
12.3/2
{
AI95-00260-02}
The function Parent_Tag returns the tag of the
parent type of the type whose tag is T. If the type does not have a parent
type (that is, it was not declared by a derived_type_declaration), then
No_Tag is returned.
12.d/2
Ramification: The
parent type is always the parent of the full type; a private extension
appears to define a parent type, but it does not (only the various forms
of derivation do that). As this is a run-time operation, ignoring privateness
is OK.
12.4/2
{
AI95-00405-01}
The function Interface_Ancestor_Tags returns an
array containing the tag of each interface ancestor type of the type
whose tag is T, other than T itself. The lower bound of the returned
array is 1, and the order of the returned tags is unspecified. Each tag
appears in the result exactly once.[ If the type whose tag is T has no
interface ancestors, a null array is returned.] {Unspecified
[partial]}
12.e/2
Ramification: The
result of Interface_Ancestor_Tags includes the tag of the parent type,
if the parent is an interface.
12.f/2
Indirect interface ancestors
are included in the result of Interface_Ancestor_Tags. That's because
where an interface appears in the derivation tree has no effect on the
semantics of the type; the only interesting property is whether the type
has an interface as an ancestor.
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/2
Reason: {
AI95-00326-01}
The Class attribute is not defined for untagged subtypes (except for
incomplete types and private types whose full view is tagged —
see
J.11 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.
18.1/2
18.2/2
generic
type T (<>) is abstract tagged limited private;
type Parameters (<>) is limited private;
with function Constructor (Params : not null access Parameters)
return T is abstract;
function Ada.Tags.Generic_Dispatching_Constructor
(The_Tag : Tag;
Params : not null access Parameters) return T'Class;
pragma Preelaborate(Generic_Dispatching_Constructor);
pragma Convention(Intrinsic, Generic_Dispatching_Constructor);
18.3/2
{
AI95-00260-02}
Tags.Generic_Dispatching_Constructor provides a
mechanism to create an object of an appropriate type from just a tag
value. The function Constructor is expected to create the object given
a reference to an object of type Parameters.
18.b/2
Discussion: This
specification is designed to make it easy to create dispatching constructors
for streams; in particular, this can be used to construct overridings
for T'Class'Input.
18.c/2
Note that any tagged type
will match T (see 12.5.1).
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/2
Implementation Note: {
AI95-00318-02}
For a limited tagged type, the return object is
“built in place” in the ultimate result object with the appropriate
tag. 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/2
- {AI95-00318-02}
{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 object 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).
25.1/2
{
AI95-00260-02}
{
AI95-00344-01}
{
AI95-00405-01}
Tag_Error is raised by a call of Descendant_Tag,
Expanded_Name, External_Tag, Interface_Ancestor_Tag, Is_Descendant_At_Same_Level,
or Parent_Tag if any tag passed is No_Tag.
25.2/2
{
AI95-00260-02}
An instance of Tags.Generic_Dispatching_Constructor
raises Tag_Error if The_Tag does not represent a concrete descendant
of T or if the innermost master (see 7.6.1)
of this descendant is not also a master of the instance. Otherwise, it
dispatches to the primitive function denoted by the formal Constructor
for the type identified by The_Tag, passing Params, and returns the result.
Any exception raised by the function is propagated.
25.a/2
Ramification: The
tag check checks both that The_Tag is in T'Class, and that it is not
abstract. These checks are similar to the ones required by streams for
T'Class'Input (see 13.13.2). In addition,
there is a check that the tag identifies a type declared on the current
dynamic call chain, and not a more nested type or a type declared by
another task. This check is not necessary for streams, because the stream
attributes are declared at the same dynamic level as the type used.
Erroneous Execution
25.3/2
{
AI95-00260-02}
{erroneous execution
(cause) [partial]} If an internal tag
provided to an instance of Tags.Generic_Dispatching_Constructor or to
any subprogram declared in package Tags identifies either a type that
is not library-level and whose tag has not been created (see 13.14),
or a type that does not exist in the partition at the time of the call,
then execution is erroneous.
25.b/2
Ramification: One
reason that a type might not exist in the partition is that the tag refers
to a type whose declaration was elaborated as part of an execution of
a subprogram_body which has been left (see
7.6.1).
25.c/2
We exclude tags of library-level
types from the current execution of the partition, because misuse of
such tags should always be detected. T'Tag freezes the type (and thus
creates the tag), and Internal_Tag and Descendant_Tag cannot return the
tag of a library-level type that has not been created. All ancestors
of a tagged type must be frozen no later than the (full) declaration
of a type that uses them, so Parent_Tag and Interface_Ancestor_Tags cannot
return a tag that has not been created. Finally, library-level types
never cease to exist while the partition is executing. Thus, if the tag
comes from a library-level type, there cannot be erroneous execution
(the use of Descendant_Tag rather than Internal_Tag can help ensure that
the tag is of a library-level type). This is also similar to the rules
for T'Class'Input (see 13.13.2).
25.d/2
Discussion: {
AI95-00344-01}
Ada 95 allowed Tag_Error in this case, or expected
the functions to work. This worked because most implementations used
tags constructed at link-time, and each elaboration of the same type_declaration
produced the same tag. However, Ada 2005 requires at least part of the
tags to be dynamically constructed for a type derived from a type at
a shallower level. For dynamically constructed tags, detecting the error
can be expensive and unreliable. To see this, consider a program containing
two tasks. Task A creates a nested tagged type, passes the tag to task
B (which saves it), and then terminates. The nested tag (if dynamic)
probably will need to refer in some way to the stack frame for task A.
If task B later tries to use the tag created by task A, the tag's reference
to the stack frame of A probably is a dangling pointer. Avoiding this
would require some sort of protected tag manager, which would be a bottleneck
in a program's performance. Moreover, we'd still have a race condition;
if task A terminated after the tag check, but before the tag was used,
we'd still have a problem. That means that all of these operations would
have to be serialized. That could be a significant performance drain,
whether or not nested tagged types are every used. Therefore, we allow
execution to become erroneous as we do for other dangling pointers. If
the implementation can detect the error, we recommend that Tag_Error
be raised.
Implementation Permissions
26/2
{
AI95-00260-02}
{
AI95-00279-01}
The implementation of
Internal_Tag and Descendant_Tag the
functions in Ada.Tags may raise Tag_Error if no specific type
corresponding to the
string External tag
passed as a parameter exists in the partition at the time the function
is called
, or if there is no such type whose innermost
master is a master of the point of the function call.
26.a/2
Reason: {
AI95-00260-02}
{
AI95-00279-01}
{
AI95-00344-01}
Locking would be required to ensure that the mapping
of strings to tags never returned tags of types which no longer exist,
because types can cease to exist (because they belong to another task,
as described above) during the execution of these operations. Moreover,
even if these functions did use locking, that would not prevent the type
from ceasing to exist at the instant that the function returned. Thus,
we do not require the overhead of locking; 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.
Implementation Advice
26.1/2
{
AI95-00260-02}
Internal_Tag should return the tag of a type whose
innermost master is the master of the point of the function call.
26.b/2
Implementation Advice:
Tags.Internal_Tag should return the
tag of a type whose innermost master is the master of the point of the
function call..
26.c/2
Reason: {
AI95-00260-02}
{
AI95-00344-01}
It's not helpful if Internal_Tag returns the tag
of some type in another task when one is available in the task that made
the call. We don't require this behavior (because it requires the same
implementation techniques we decided not to insist on previously), but
encourage it.
27
64 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
65 Once an object has been created, its
tag never changes.
29
66 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/2
This paragraph was
deleted.67 {
AI95-00326-01}
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).
30.1/2
68 {
AI95-00260-02}
The capability provided by Tags.Generic_Dispatching_Constructor
is sometimes known as a factory.{factory}
{class factory}
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.
Inconsistencies With Ada 95
33.b/2
{
AI95-00279-01}
{inconsistencies with Ada 95} Amendment
Correction: Added wording specifying that Internal_Tag must raise
Tag_Error if the tag of a library-level type has not yet been created.
Ada 95 gave an Implementation Permission to do this; we require it to
avoid erroneous execution when streaming in an object of a library-level
type that has not yet been elaborated. This is technically inconsistent;
a program that used Internal_Tag outside of streaming and used a compiler
that didn't take advantage of the Implementation Permission would not
have raised Tag_Error, and may have returned a useful tag. (If the tag
was used in streaming, the program would have been erroneous.) Since
such a program would not have been portable to a compiler that did take
advantage of the Implementation Permission, this is not a significant
inconsistency.
33.c/2
{
AI95-00417-01}
We now define the lower bound of the string returned
from [[Wide_]Wide_]Expanded_Name and External_Name. This makes working
with the returned string easier, and is consistent with many other string-returning
functions in Ada. This is technically an inconsistency; if a program
depended on some other lower bound for the string returned from one of
these functions, it could fail when compiled with Ada 2005. Such code
is not portable even between Ada 95 implementations, so it should be
very rare.
Incompatibilities With Ada 95
33.d/2
{
AI95-00260-02}
{
AI95-00344-01}
{
AI95-00400-01}
{
AI95-00405-01}
{incompatibilities with Ada 95} Constant
No_Tag, and functions Parent_Tag, Interface_Ancestor_Tags, Descendant_Tag,
Is_Descendant_At_Same_Level, Wide_Expanded_Name, and Wide_Wide_Expanded_Name
are newly added to Ada.Tags. If Ada.Tags is referenced in a use_clause,
and an entity E with the same defining_identifier
as a new entity in Ada.Tags is defined in a package that is also referenced
in a use_clause, the entity E may no
longer be use-visible, resulting in errors. This should be rare and is
easily fixed if it does occur.
Extensions to Ada 95
33.e/2
{
AI95-00362-01}
{extensions to Ada 95} Ada.Tags
is now defined to be preelaborated.
33.f/2
{
AI95-00260-02}
Generic function Tags.Generic_Dispatching_Constructor
is new.
Wording Changes from Ada 95
33.g/2
{
AI95-00318-02}
We talk about return objects rather than return
expressions, as functions can return using an extended_return_statement.
33.h/2
{
AI95-00344-01}
Added wording to define that tags for all descendants
of a tagged type must be distinct. This is needed to ensure that more
nested type extensions will work properly. The wording does not require
implementation changes for types that were allowed in Ada 95.