Skip to main content

3.9 Tagged Types and Type Extensions

danger

This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue

1

[ Tagged types and type extensions support object-oriented programming, based on inheritance with extension and run-time polymorphism via dispatching operations. ]

Language Design Principles

1.a/2

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

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

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

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

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

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 for any derived type, additional primitive subprograms may be defined, and inherited primitive subprograms may be overridden.] The derived type is called an extension of its ancestor types, or simply a type extension.

2.1/2

Every type extension is also a tagged type, and is a record extension or a private extension of some other tagged type, or a noninterface 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/5

Term entry: tagged type — type whose objects each have a run-time type tag, which indicates the specific type for which the object was originally created
Note: Tagged types can be extended with additional components.

2.b/2
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. 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.

3

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); using a tag to control which body to execute is called dispatching.]

4/2

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.

4.a/2
This paragraph was deleted.
4.a.1/2
implementation note

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

For types that are not type extensions (even for ones declared in nested scopes), we do not require that 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.

4.c/2

To be honest: 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/5

package Ada.Tags with Preelaborate, Nonblocking, Global => in out synchronized is type Tag is private with Preelaborable_Initialization; 6.1/2

No_Tag : constant Tag; 7/2

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

function Descendant_Tag(External : String; Ancestor : Tag) return Tag; function Is_Descendant_At_Same_Level(Descendant, Ancestor : Tag) return Boolean; 7.2/2

function Parent_Tag (T : Tag) return Tag; 7.3/2

type Tag_Array is array (Positive range <>) of Tag; 7.4/2

function Interface_Ancestor_Tags (T : Tag) return Tag_Array; 7.5/3

function Is_Abstract (T : Tag) return Boolean; 8 Tag_Error : exception; 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

No_Tag is the default initial value of type Tag.

9.c/2
reason

This is similar to the requirement that all access values be initialized to null.

10/2

The function Wide_Wide_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 for types declared within an unnamed block_statement.

10.1/2

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 via an attribute_definition_clause.

11.1/2

The string returned by the functions Expanded_Name, Wide_Expanded_Name, Wide_Wide_Expanded_Name, and External_Tag has lower bound 1.

12/2

The function Internal_Tag returns a 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/3
reason

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 might 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/3

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, or if the given external tag identifies more than one type that has the appropriate Ancestor and accessibility level.

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.b.1/3

Rules for specifying external tags will usually prevent an external tag from identifying more than one type. However, an external tag can identify multiple types if a generic body contains a derivation of a tagged type declared outside of the generic, and there are multiple instances at the same accessibility level as the type. (The Reference Manual allows default external tags to not be unique in this case.)

12.2/2

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/3

For the purposes of the dynamic semantics of functions Descendant_Tag and Is_Descendant_At_Same_Level, a tagged type T2 is a descendant of a type T1 if it is the same as T1, or if its parent type or one of its progenitor types is a descendant of type T1 by this rule[, even if at the point of the declaration of T2, one of the derivations in the chain is not visible].

12.c.1/3
discussion

In other contexts, “descendant” is dependent on visibility, and the particular view a derived type has of its parent type. See 7.3.1.

12.4/4

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 defined by a derived_type_definition), then No_Tag is returned.

12.d/4
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 privacy is OK.

12.5/3

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.]

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.

12.6/3

The function Is_Abstract returns True if the type whose tag is T is abstract, and False otherwise.

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 document) for the class rooted at T (or if S already denotes a class-wide subtype, then S'Class is the same as S).
15
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

The Class attribute is not defined for untagged subtypes (except for incomplete types and private types whose full view is tagged — see J.11 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

The following language-defined generic function exists:

18.2/5

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 with Preelaborate, Convention => Intrinsic, Nonblocking, Global => in out synchronized;

18.3/2

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/5

Note that almost any tagged type can be used in an instance of Generic_Dispatching_Constructor. Using a tagged incomplete view or a tagged partial view before the completion of the type in such an instance would be illegal; all other tagged types can be used in an instance of Generic_Dispatching_Constructor.

Dynamic Semantics

19

The tag associated with an object of a tagged type is determined as follows:

20
  • 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/5
  • 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
  • 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
  • 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

For a limited tagged type, the return object is “built in place” in the ultimate result object with the appropriate tag. For a nonlimited type, a new anonymous object with the appropriate tag is created as part of the function return. See 6.5, “Return Statements”.

24/2
  • The tag of the result returned by a function with a class-wide result type is that of the return object.
25

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/3

Tag_Error is raised by a call of Descendant_Tag, Expanded_Name, External_Tag, Interface_Ancestor_Tags, Is_Abstract, Is_Descendant_At_Same_Level, Parent_Tag, Wide_Expanded_Name, or Wide_Wide_Expanded_Name if any tag passed is No_Tag.

25.2/2

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

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

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 ever 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

The implementation of Internal_Tag and Descendant_Tag may raise Tag_Error if no specific type corresponding to the string External 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

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; hence the word “may” in this rule.

Implementation Advice

26.1/3

Internal_Tag should return the tag of a type, if one exists, whose innermost master is a master of the point of the function call.

26.b/3
implementation advice

Tags.Internal_Tag should return the tag of a type, if one exists, whose innermost master is a master of the point of the function call.

26.c/2
reason

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.

26.d/3
discussion

There is no Advice for the result of Internal_Tag if no such type exists. In most cases, the Implementation Permission can be used to raise Tag_Error, but some other tag can be returned as well.

27/5

NOTE 1 A type declared with the reserved word tagged is normally declared in a package_specification, so that new primitive subprograms can be declared for it.

28

NOTE 2 Once an object has been created, its tag never changes.

29/5

NOTE 3 Class-wide types are defined to have unknown discriminants (see 3.7). This means that, by the rules in 3.7 for objects with unknown discriminants, objects of a class-wide type are illegal unless they are explicitly initialized (whether created by an object_declaration or an allocator), and that aggregates are illegal unless they are explicitly qualified with a specific type when their expected type is class-wide.

30/2

NOTE 4 The capability provided by Tags.Generic_Dispatching_Constructor is sometimes known as a 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

Tagged types are a new concept.

Inconsistencies With Ada 95

33.b/2
correction

Amendment 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

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/3

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 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

Ada.Tags is now defined to be preelaborated.

33.f/2

Generic function Tags.Generic_Dispatching_Constructor is new.

Wording Changes from Ada 95

33.g/2

We talk about return objects rather than return expressions, as functions can return using an extended_return_statement.

33.h/2

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.

Inconsistencies With Ada 2005

33.i/3
correction

Added wording specifying that Dependent_Tag must raise Tag_Error if there is more than one type which matches the requirements. If an implementation had returned a random tag of the matching types, a program may have worked properly. However, such a program would not be portable (another implementation may return a different tag) and the conditions that would cause the problem are unlikely (most likely, a tagged type extension declared in a generic body with multiple instances in the same scope).

Incompatibilities With Ada 2005

33.j/3

Function Is_Abstract is added to Ada.Tags. If Ada.Tags is referenced in a use_clause, and an entity E with the defining_identifier Is_Abstract 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.

Wording Changes from Ada 2005

33.k/3
correction

We explicitly define the meaning of "descendant" at runtime, so that it does not depend on visibility as does the usual meaning.

3.9.1 Type Extensions

1/2

[ 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

1.a

We want to make sure that we can extend a generic formal tagged type, without knowing its discriminants.

1.b

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

2
record_extension_part ::= withrecord_definition

Legality Rules

3/2

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.

3.a
reason

If the parent is a limited formal type, then the actual might be nonlimited.

3.b/2

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.

3.c/2

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.

4/2

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.

4.a
reason

This paragraph ensures that a dispatching call will never attempt to execute an inaccessible subprogram body.

4.a.1/2

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.

4.b/2

This rule is necessary in order to preserve the contract model.

4.c/2

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:

4.d/2

package P is type T is tagged null record; function F return T; -- Inherited versions will require overriding. end P; 4.e 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; 4.f/2 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; 4.g package I is new Gp(TT => P.T);

4.h/2

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.

4.h.1/2

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.

4.i

If TT were declared as abstract, then we could have the same problem with abstract procedures.

4.j

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.

4.k

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.

4.l/2
This paragraph was deleted.
4.m/2
ramification

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

4.1/2

A record extension is a null extension if its declaration has no known_discriminant_part and its record_extension_part includes no component_declarations.

4.2/5

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.

4.n/5
ramification

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.

4.o/5
discussion

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:

4.p/5

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;

4.q/5

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

5

The elaboration of a record_extension_part consists of the elaboration of the record_definition.

6

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.

6.a
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”).

7/2

NOTE 2 When an extension is declared immediately within a body, primitive subprograms are inherited and are overridable, but new primitive subprograms cannot be added.

8/5

NOTE 3 By the rules given in 3.8, a name that denotes a component (including a discriminant) of the parent type is illegal within the record_extension_part. Similarly, a name that denotes a component defined within the record_extension_part is illegal within the record_extension_part. A name that denotes a discriminant of the record extension is legal, providing that it refers to a discriminant defined in a new known_discriminant_part in the enclosing type declaration.

8.a
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.

9/5

NOTE 4 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

10

Examples of record extensions (of types defined above in 3.9):

11

type Painted_Point is new Point with record Paint : Color := White; end record; -- Components X and Y are inherited 12 Origin : constant Painted_Point := (X | Y => 0.0, Paint => Black); 13 type Literal is new Expression with record -- a leaf in an Expression tree Value : Real; end record; 14/5

type Expr_Ptr is access all Expression'Class; -- see 3.9 15 type Binary_Operation is new Expression with record -- an internal node in an Expression tree Left, Right : Expr_Ptr; end record; 16 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 17 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

17.a

Type extension is a new concept.

Extensions to Ada 95

17.b/2

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

17.c/2

Added wording to prevent extending synchronized tagged types.

17.d/2

Defined null extension for use elsewhere.

Wording Changes from Ada 2012

17.e/5

Defined the term “components of the nominal type” to remove a confusion as to how components are described in Dynamic Semantics rules.

3.9.2 Dispatching Operations of Tagged Types

1/5

The primitive subprograms of a tagged type, the subprograms declared by formal_abstract_subprogram_declarations, the Put_Image attribute (see 4.10) of a specific tagged type, and the stream attributes of a specific tagged type that are available (see 13.13.2) at the end of the declaration list where the type is declared are called dispatching operations. [A dispatching operation can be called using a statically determined controlling tag, in which case the body to be executed is determined at compile time. Alternatively, the controlling tag can be dynamically determined, in which case the call dispatches to a body that is determined at run time;] such a call is termed a dispatching call. [As explained below, the properties of the operands and the context of a particular call on a dispatching operation determine how the controlling tag is determined, and hence whether or not the call is a dispatching call. Run-time polymorphism is achieved when a dispatching operation is called by a dispatching call.]

1.a.1/2
reason

For the stream attributes of a type declared immediately within a package_specification that has a partial view, the declaration list to consider is the visible part of the package. Stream attributes that are not available in the same declaration list are not dispatching as there is no guarantee that descendants of the type have available attributes (there is such a guarantee for visibly available attributes). If we allowed dispatching for any available attribute, then for attributes defined in the private part we could end up executing a nonexistent body.

Language Design Principles

1.a

The controlling tag determination rules are analogous to the overload resolution rules, except they deal with run-time type identification (tags) rather than compile-time type resolution. As with overload resolution, controlling tag determination may depend on operands or result context.

Static Semantics

2/3

A call on a dispatching operation is a call whose name or prefix denotes the declaration of a dispatching operation. A controlling operand in a call on a dispatching operation of a tagged type T is one whose corresponding formal parameter is of type T or is of an anonymous access type with designated type T; the corresponding formal parameter is called a controlling formal parameter. If the controlling formal parameter is an access parameter, the controlling operand is the object designated by the actual parameter, rather than the actual parameter itself. If the call is to a (primitive) function with result type T (a function with a controlling result), then the call has a controlling result — the context of the call can control the dispatching. Similarly, if the call is to a function with an access result type designating T (a function with a controlling access result), then the call has a controlling access result, and the context can similarly control dispatching.

2.a
ramification

This definition implies that a call through the dereference of an access-to-subprogram value is never considered a call on a dispatching operation. Note also that if the prefix denotes a renaming_declaration, the place where the renaming occurs determines whether it is primitive; the thing being renamed is irrelevant.

3/5

A name or expression of a tagged type is either statically tagged, dynamically tagged, or tag indeterminate, according to whether, when used as a controlling operand, the tag that controls dispatching is determined statically by the operand's (specific) type, dynamically by its tag at run time, or from context. A qualified_expression or parenthesized expression is statically, dynamically, or indeterminately tagged according to its operand. A conditional_expression is statically, dynamically, or indeterminately tagged according to rules given in 4.5.7. A declare_expression is statically, dynamically, or indeterminately tagged according to its body_expression. For other kinds of names and expressions, this is determined as follows:

4/2
  • The name or expression is statically tagged if it is of a specific tagged type and, if it is a call with a controlling result or controlling access result, it has at least one statically tagged controlling operand;
4.a
discussion

It is illegal to have both statically tagged and dynamically tagged controlling operands in the same call -- see below.

5/2
  • The name or expression is dynamically tagged if it is of a class-wide type, or it is a call with a controlling result or controlling access result and at least one dynamically tagged controlling operand;
  • 6/2
  • The name or expression is tag indeterminate if it is a call with a controlling result or controlling access result, all of whose controlling operands (if any) are tag indeterminate.
7/1

{8652/0010} [A type_conversion is statically or dynamically tagged according to whether the type determined by the subtype_mark is specific or class-wide, respectively.] For an object that is designated by an expression whose expected type is an anonymous access-to-specific tagged type, the object is dynamically tagged if the expression, ignoring enclosing parentheses, is of the form X'Access, where X is of a class-wide type, or is of the form new T'(...), where T denotes a class-wide subtype. Otherwise, the object is statically or dynamically tagged according to whether the designated type of the type of the expression is specific or class-wide, respectively.

7.a
ramification

A type_conversion is never tag indeterminate, even if its operand is. A designated object is never tag indeterminate.

7.a.1/1

{8652/0010} Allocators and access attributes of class-wide types can be used as the controlling parameters of dispatching calls.

Legality Rules

8

A call on a dispatching operation shall not have both dynamically tagged and statically tagged controlling operands.

8.a
reason

This restriction is intended to minimize confusion between whether the dynamically tagged operands are implicitly converted to, or tag checked against the specific type of the statically tagged operand(s).

9/1

{8652/0010} If the expected type for an expression or name is some specific tagged type, then the expression or name shall not be dynamically tagged unless it is a controlling operand in a call on a dispatching operation. Similarly, if the expected type for an expression is an anonymous access-to-specific tagged type, then the object designated by the expression shall not be dynamically tagged unless it is a controlling operand in a call on a dispatching operation.

9.a
reason

This prevents implicit "truncation" of a dynamically-tagged value to the specific type of the target object/formal. An explicit conversion is required to request this truncation.

9.b/4
ramification

This rule applies to all expressions or names with a specific expected type, not just those that are actual parameters to a dispatching call. This rule does not apply to a membership test whose tested_simple_expression is class-wide, since any type that covers the tested type is explicitly allowed. See 4.5.2. This rule also doesn't apply to a selected_component whose selector_name is a subprogram, since the rules explicitly say that the prefix may be class-wide (see 4.1.3).

10/2

{8652/0011} In the declaration of a dispatching operation of a tagged type, everywhere a subtype of the tagged type appears as a subtype of the profile (see 6.1), it shall statically match the first subtype of the tagged type. If the dispatching operation overrides an inherited subprogram, it shall be subtype conformant with the inherited subprogram. The convention of an inherited dispatching operation is the convention of the corresponding primitive operation of the parent or progenitor type. The default convention of a dispatching operation that overrides an inherited primitive operation is the convention of the inherited operation; if the operation overrides multiple inherited operations, then they shall all have the same convention. An explicitly declared dispatching operation shall not be of convention Intrinsic.

10.a
reason

These rules ensure that constraint checks can be performed by the caller in a dispatching call, and parameter passing conventions match up properly. A special rule on aggregates prevents values of a tagged type from being created that are outside of its first subtype.

11/2

The default_expression for a controlling formal parameter of a dispatching operation shall be tag indeterminate.

11.a/2
reason

This rule ensures that the default_expression always produces the "correct" tag when called with or without dispatching, or when inherited by a descendant. If it were statically tagged, the default would be useless for a dispatching call; if it were dynamically tagged, the default would be useless for a nondispatching call.

11.1/2

If a dispatching operation is defined by a subprogram_renaming_declaration or the instantiation of a generic subprogram, any access parameter of the renamed subprogram or the generic subprogram that corresponds to a controlling access parameter of the dispatching operation, shall have a subtype that excludes null.

12

A given subprogram shall not be a dispatching operation of two or more distinct tagged types.

12.a
reason

This restriction minimizes confusion since multiple dispatching is not provided. The normal solution is to replace all but one of the tagged types with their class-wide types.

12.a.1/1
ramification

{8652/0098} This restriction applies even if the partial view (see 7.3) of one or both of the types is untagged. This follows from the definition of dispatching operation: the operation is a dispatching operation anywhere the full views of the (tagged) types are visible.

13

The explicit declaration of a primitive subprogram of a tagged type shall occur before the type is frozen (see 13.14). [For example, new dispatching operations cannot be added after objects or values of the type exist, nor after deriving a record extension from it, nor after a body.]

13.a/2
reason

This rule is needed because (1) we don't want people dispatching to things that haven't been declared yet, and (2) we want to allow the static part of tagged type descriptors to be static (allocated statically, and initialized to link-time-known symbols). Suppose T2 inherits primitive P from T1, and then overrides P. Suppose P is called before the declaration of the overriding P. What should it dispatch to? If the answer is the new P, we've violated the first principle above. If the answer is the old P, we've violated the second principle. (A call to the new one necessarily raises Program_Error, but that's beside the point.)

13.b

Note that a call upon a dispatching operation of type T will freeze T.

13.c

We considered applying this rule to all derived types, for uniformity. However, that would be upward incompatible, so we rejected the idea. As in Ada 83, for an untagged type, the above call upon P will call the old P (which is arguably confusing).

13.d/2
implementation note

Because of this rule, the type descriptor can be created (presumably containing linker symbols pointing at the not-yet-compiled bodies) at the first freezing point of the type. It also prevents, for a (nonincomplete) tagged type declared in a package_specification, overriding in the body or by a child subprogram.

13.e/2
ramification

A consequence is that for a tagged type declaration in a declarative_part, only the last (overriding) primitive subprogram can be declared by a subprogram_body. (Other overridings must be provided by subprogram_declarations.)

13.f/3

To be honest: This rule applies only to "original" declarations and not to the completion of a primitive subprogram, even though a completion is technically an explicit declaration, and it may declare a primitive subprogram.

Dynamic Semantics

14

For the execution of a call on a dispatching operation of a type T, the controlling tag value determines which subprogram body is executed. The controlling tag value is defined as follows:

15
  • If one or more controlling operands are statically tagged, then the controlling tag value is statically determined to be the tag of T.
  • 16
  • If one or more controlling operands are dynamically tagged, then the controlling tag value is not statically determined, but is rather determined by the tags of the controlling operands. If there is more than one dynamically tagged controlling operand, a check is made that they all have the same tag. If this check fails, Constraint_Error is raised unless the call is a function_call whose name denotes the declaration of an equality operator (predefined or user defined) that returns Boolean, in which case the result of the call is defined to indicate inequality, and no subprogram_body is executed. This check is performed prior to evaluating any tag-indeterminate controlling operands.
16.a
reason

Tag mismatch is considered an error (except for "=" and "/=") since the corresponding primitive subprograms in each specific type expect all controlling operands to be of the same type. For tag mismatch with an equality operator, rather than raising an exception, "=" returns False and "/=" returns True. No equality operator is actually invoked, since there is no common tag value to control the dispatch. Equality is a special case to be consistent with the existing Ada 83 principle that equality comparisons, even between objects with different constraints, never raise Constraint_Error.

17/2
  • If all of the controlling operands (if any) are tag-indeterminate, then:
18/2
  • If the call has a controlling result or controlling access result and is itself, or designates, a (possibly parenthesized or qualified) controlling operand of an enclosing call on a dispatching operation of a descendant of type T, then its controlling tag value is determined by the controlling tag value of this enclosing call;
18.a/2
discussion

For code that a user can write explicitly, the only contexts that can control dispatching of a function with a controlling result of type T are those that involve controlling operands of the same type T: if the two types differ there is an illegality and the dynamic semantics are irrelevant.

18.b/2

In the case of an inherited subprogram however, if a default expression is a function call, it may be of type T while the parameter is of a type derived from T. To cover this case, we talk about "a descendant of T" above. This is safe, because if the type of the parameter is descended from the type of the function result, it is guaranteed to inherit or override the function, and this ensures that there will be an appropriate body to dispatch to. Note that abstract functions are not an issue here because the call to the function is a dispatching call, so it is guaranteed to always land on a concrete body.

18.1/2
  • If the call has a controlling result or controlling access result and (possibly parenthesized, qualified, or dereferenced) is the expression of an assignment_statement whose target is of a class-wide type, then its controlling tag value is determined by the target;
  • 19
  • Otherwise, the controlling tag value is statically determined to be the tag of type T.
19.a
ramification

This includes the cases of a tag-indeterminate procedure call, and a tag-indeterminate function_call that is used to initialize a class-wide formal parameter or class-wide object.

20/3

For the execution of a call on a dispatching operation, the action performed is determined by the properties of the corresponding dispatching operation of the specific type identified by the controlling tag value:

20.1/3
  • if the corresponding operation is explicitly declared for this type, [even if the declaration occurs in a private part], then the action comprises an invocation of the explicit body for the operation;
  • 20.2/3
  • if the corresponding operation is implicitly declared for this type and is implemented by an entry or protected subprogram (see 9.1 and 9.4), then the action comprises a call on this entry or protected subprogram, with the target object being given by the first actual parameter of the call, and the actual parameters of the entry or protected subprogram being given by the remaining actual parameters of the call, if any;
  • 20.3/3
  • if the corresponding operation is a predefined operator then the action comprises an invocation of that operator;
  • 20.4/3
  • otherwise, the action is the same as the action for the corresponding operation of the parent type or progenitor type from which the operation was inherited except that additional invariant checks (see 7.3.2) and class-wide postcondition checks (see 6.1.1) may apply. If there is more than one such corresponding operation, the action is that for the operation that is not a null procedure, if any; otherwise, the action is that of an arbitrary one of the operations.
20.a/3
This paragraph was deleted.
20.a.1/3
ramification

“Corresponding dispatching operation” refers to the inheritance relationship between subprograms. Primitive operations are always inherited for a type T, but they might not be declared if the primitive operation is never visible within the immediate scope of the type T. If no corresponding operation is declared, the last bullet is used and the corresponding operation of the parent type is executed (an explicit body that happens to have the same name and profile is not called in that case).

20.a.2/3

We have to talk about progenitors in the last bullet in case the corresponding operation is a null procedure inherited from an interface. In that case, the parent type might not even have the operation in question.

20.a.3/3

For the last bullet, if there are multiple corresponding operations for the parent and progenitors, all but one of them have to be a null procedure. (If the progenitors declared abstract routines, there would have to be an explicit overriding of the operation, and then the first bullet would apply.) We call the nonnull routine if one exists.

20.a.4/3

Any explicit declaration for an inherited corresponding operation has to be an overriding routine. These rules mean that a dispatching call executes the overriding routine (if any) for the specific type.

20.b/3
reason

The wording of the above rules is intended to ensure that the same body is executed for a given tag, whether that tag is determined statically or dynamically. For a type declared in a package, it doesn't matter whether a given subprogram is overridden in the visible part or the private part, and it doesn't matter whether the call is inside or outside the package. For example:

20.c

package P1 is type T1 is tagged null record; procedure Op_A(Arg : in T1); procedure Op_B(Arg : in T1); end P1; 20.d with P1; use P1; package P2 is type T2 is new T1 with null record; procedure Op_A(Param : in T2); private procedure Op_B(Param : in T2); end P2; 20.e/1 with P1; with P2; procedure Main is X : P2.T2; Y : P1.T1'Class := X; begin P2.Op_A(Param => X); -- Nondispatching call to a dispatching operation. P1.Op_A(Arg => Y); -- Dispatching call. P2.Op_B(Arg => X); -- Nondispatching call to a dispatching operation. P1.Op_B(Arg => Y); -- Dispatching call. end Main;

20.f

The two calls to Op_A both execute the body of Op_A that has to occur in the body of package P2. Similarly, the two calls to Op_B both execute the body of Op_B that has to occur in the body of package P2, even though Op_B is overridden in the private part of P2. Note, however, that the formal parameter names are different for P2.Op_A versus P2.Op_B. The overriding declaration for P2.Op_B is not visible in Main, so the name in the call actually denotes the implicit declaration of Op_B inherited from T1.

20.g

If a call occurs in the program text before an overriding, which can happen only if the call is part of a default expression, the overriding will still take effect for that call.

20.h
implementation note

Even when a tag is not statically determined, a compiler might still be able to figure it out and thereby avoid the overhead of run-time dispatching.

21

NOTE 1 The body to be executed for a call on a dispatching operation is determined by the tag; it does not matter whether that tag is determined statically or dynamically, and it does not matter whether the subprogram's declaration is visible at the place of the call.

22/2

NOTE 2 This subclause covers calls on dispatching subprograms of a tagged type. Rules for tagged type membership tests are described in 4.5.2. Controlling tag determination for an assignment_statement is described in 5.2.

23

NOTE 3 A dispatching call can dispatch to a body whose declaration is not visible at the place of the call.

24

NOTE 4 A call through an access-to-subprogram value is never a dispatching call, even if the access value designates a dispatching operation. Similarly a call whose prefix denotes a subprogram_renaming_declaration cannot be a dispatching call unless the renaming itself is the declaration of a primitive subprogram.

Extensions to Ada 83

24.a

The concept of dispatching operations is new.

Incompatibilities With Ada 95

24.b/2

If a dispatching operation is defined by a subprogram_renaming_declaration, and it has a controlling access parameter, Ada 2005 requires the subtype of the parameter to exclude null. The same applies to instantiations. This is required so that all calls to the subprogram operate the same way (controlling access parameters have to exclude null so that dispatching calls will work). Since Ada 95 didn't have the notion of access subtypes that exclude null, and all access parameters excluded null, it had no such rules. These rules will require the addition of an explicit not null on nondispatching operations that are later renamed to be dispatching, or on a generic that is used to define a dispatching operation.

Extensions to Ada 95

24.c/2

Functions that have an access result type can be dispatching in the same way as a function that returns a tagged object directly.

Wording Changes from Ada 95

24.d/3

{8652/0010} Corrigendum: Allocators and access attributes of objects of class-wide types can be used as the controlling parameter in a dispatching calls. This was an oversight in the definition of Ada 95. (See 3.10.2 and 4.8).

24.e/2

{8652/0011} Corrigendum: Corrected the conventions of dispatching operations. This is extended in Ada 2005 to cover operations inherited from progenitors, and to ensure that the conventions of all inherited operations are the same.

24.f/2

Clarified the wording to ensure that functions with no controlling operands are tag-indeterminate, and to describe that the controlling tag can come from the target of an assignment_statement.

24.g/2

Fixed the wording to cover default expressions inherited by derived subprograms. A literal reading of the old wording would have implied that operations would be called with objects of the wrong type.

24.h/2

An abstract formal subprogram is a dispatching operation, even though it is not a primitive operation. See 12.6, “Formal Subprograms”.

24.i/2

Dispatching calls include operations implemented by entries and protected operations, so we have to update the wording to reflect that.

24.j/2

A stream attribute of a tagged type is usually a dispatching operation, even though it is not a primitive operation. If they weren't dispatching, T'Class'Input and T'Class'Output wouldn't work.

Wording Changes from Ada 2005

24.k/3
correction

Defined “function with a controlling result”, as it is used in 3.9.3.

24.l/3
correction

Corrected holes in the definition of dynamic dispatching: the behavior for operations that are never declared and/or inherited from a progenitor were not specified.

Wording Changes from Ada 2012

24.m/5

Added a special rule for taggedness of declare_expressions, and added a pointer to the existing special rules for conditional_expressions, as the list of exceptions to the usual rules appear to be exhaustive.

24.n/5

Added wording to clarify that the Put_Image attribute is a dispatching operation. Put_Image follows the model of stream-oriented attributes, and thus need to be mentioned in the same place.

3.9.3 Abstract Types and Subprograms

1/2

[ An abstract type is a tagged type intended for use as an ancestor of other types, but which is not allowed to have objects of its own. An abstract subprogram is a subprogram that has no body, but is intended to be overridden at some point when inherited. Because objects of an abstract type cannot be created, a dispatching call to an abstract subprogram always dispatches to some overriding body.]

1.a.1/5

Term entry: abstract type — tagged type intended for use as an ancestor of other types, but which is not allowed to have objects of its own

Language Design Principles

1.a/3

An abstract subprogram has no body, so the rules in this subclause are designed to ensure (at compile time) that the body will never be invoked. We do so primarily by disallowing the creation of values of the abstract type. Therefore, since type conversion and parameter passing don't change the tag, we know we will never get a class-wide value with a tag identifying an abstract type. This means that we only have to disallow nondispatching calls on abstract subprograms (dispatching calls will never reach them).

Syntax

1.1/3

abstract_subprogram_declaration ::=
[overriding_indicator]
subprogram_specification is abstract
[aspect_specification];

Static Semantics

1.2/2

Interface types (see 3.9.4) are abstract types. In addition, a tagged type that has the reserved word abstract in its declaration is an abstract type. The class-wide type (see 3.4.1) rooted at an abstract type is not itself an abstract type.

Legality Rules

2/2

Only a tagged type shall have the reserved word abstract in its declaration.

2.a
ramification

Untagged types are never abstract, even though they can have primitive abstract subprograms. Such subprograms cannot be called, unless they also happen to be dispatching operations of some tagged type, and then only via a dispatching call.

2.b

Class-wide types are never abstract. If T is abstract, then it is illegal to declare a stand-alone object of type T, but it is OK to declare a stand-alone object of type T'Class; the latter will get a tag from its initial value, and this tag will necessarily be different from T'Tag.

3/2

A subprogram declared by an abstract_subprogram_declaration or a formal_abstract_subprogram_declaration (see 12.6) is an abstract subprogram. If it is a primitive subprogram of a tagged type, then the tagged type shall be abstract.

3.a
ramification

Note that for a private type, this applies to both views. The following is illegal:

3.b

package P is type T is abstract tagged private; function Foo (X : T) return Boolean is abstract; -- Illegal! private type T is tagged null record; -- Illegal! X : T; Y : Boolean := Foo (T'Class (X)); end P;

3.c

The full view of T is not abstract, but has an abstract operation Foo, which is illegal. The two lines marked "-- Illegal!" are illegal when taken together.

3.d/2
reason

We considered disallowing untagged types from having abstract primitive subprograms. However, we rejected that plan, because it introduced some silly anomalies, and because such subprograms are harmless. For example:

3.e/1

package P is type Field_Size is range 0..100; type T is abstract tagged null record; procedure Print(X : in T; F : in Field_Size := 0) is abstract; . . . package Q is type My_Field_Size is new Field_Size; -- implicit declaration of Print(X : T; F : My_Field_Size := 0) is abstract; end Q;

3.f

It seemed silly to make the derivative of My_Field_Size illegal, just because there was an implicitly declared abstract subprogram that was not primitive on some tagged type. Other rules could be formulated to solve this problem, but the current ones seem like the simplest.

3.g/2

In Ada 2005, abstract primitive subprograms of an untagged type may be used to “undefine” an operation.

3.h/2
ramification

Note that the second sentence does not apply to abstract formal subprograms, as they are never primitive operations of a type.

4/3

If a type has an implicitly declared primitive subprogram that is inherited or is a predefined operator, and the corresponding primitive subprogram of the parent or ancestor type is abstract or is a function with a controlling access result, or if a type other than a nonabstract null extension inherits a function with a controlling result, then:

4.a/3
ramification

These rules apply to each view of the type individually. That is necessary to preserve privacy. For instance, in the following example:

4.b/3

package P is type I is interface; procedure Op (X : I) is abstract; end P; 4.c/3 with P; package Q is type T is abstract new P.I with private; -- Op inherited here. private type T is abstract new P.I with null record; procedure Op (X : T) is null; end Q; 4.d/3 with Q; package R is type T2 is new Q.T with null record; -- Illegal. Op inherited here, but requires overriding. end R;

4.e/3

If this did not depend on the view, this would be legal. But in that case, the fact that Op is overridden in the private part would be visible; package R would have to be illegal if no overriding was in the private part.

4.f/3

Note that this means that whether an inherited subprogram is abstract or concrete depends on where it inherited. In the case of Q, Q.Op in the visible part is abstract, while Q.Op in the private part is concrete. That is, R is illegal since it is an unrelated unit (and thus it cannot see the private part), but if R had been a private child of Q, it would have been legal.

5/2
  • If the type is abstract or untagged, the implicitly declared subprogram is abstract.
5.a
ramification

Note that it is possible to override a concrete subprogram with an abstract one.

6/5
  • Otherwise, the subprogram shall be overridden with a nonabstract subprogram or, in the case of a private extension inheriting a nonabstract function with a controlling result, have a full type that is a null extension[; for a type declared in the visible part of a package, the overriding may be either in the visible or the private part]. Such a subprogram is said to require overriding. However, if the type is a generic formal type, the subprogram is allowed to be inherited as is, without being overridden for the formal type itself; [a nonabstract version will necessarily be provided by the actual type.]
6.a/2
reason

A function that returns the parent type requires overriding for a type extension (or becomes abstract for an abstract type) because conversion from a parent type to a type extension is not defined, and function return semantics is defined in terms of conversion (other than for a null extension; see below). (Note that parameters of mode in out or out do not have this problem, because the tag of the actual is not changed.)

6.b

Note that the overriding required above can be in the private part, which allows the following:

6.c

package Pack1 is type Ancestor is abstract ...; procedure Do_Something(X : in Ancestor) is abstract; end Pack1; 6.d with Pack1; use Pack1; package Pack2 is type T1 is new Ancestor with record ...; -- A concrete type. procedure Do_Something(X : in T1); -- Have to override. end Pack2; 6.e with Pack1; use Pack1; with Pack2; use Pack2; package Pack3 is type T2 is new Ancestor with private; -- A concrete type. private type T2 is new T1 with -- Parent different from ancestor. record ... end record; -- Here, we inherit Pack2.Do_Something. end Pack3;

6.f/2

T2 inherits an abstract Do_Something, but T2 is not abstract, so Do_Something has to be overridden. However, it is OK to override it in the private part. In this case, we override it by inheriting a concrete version from a different type. Nondispatching calls to Pack3.Do_Something are allowed both inside and outside package Pack3, as the client “knows” that the subprogram was necessarily overridden somewhere.

6.g/2

For a null extension, the result of a function with a controlling result is defined in terms of an extension_aggregate with a null record extension part (see 3.4). This means that these restrictions on functions with a controlling result do not have to apply to null extensions.

6.h/2

However, functions with controlling access results still require overriding. Changing the tag in place might clobber a preexisting object, and allocating new memory would possibly change the pool of the object, leading to storage leaks. Moreover, copying the object isn't possible for limited types. We don't need to restrict functions that have an access return type of an untagged type, as derived types with primitive subprograms have to have the same representation as their parent type.

7/5

A call on an abstract subprogram shall be a dispatching call; nondispatching calls to an abstract subprogram are not allowed. In addition to the places where Legality Rules normally apply (see 12.3), these rules also apply in the private part of an instance of a generic unit.

7.a/5
ramification

If an abstract subprogram is not a dispatching operation of some tagged type, then it cannot be called at all. In Ada 2005, such subprograms are not even considered by name resolution (see 6.4). However, this rule is still needed for cases that can arise in the instance of a generic specification where Name Resolution Rules are not reapplied, but Legality Rules are, when the equality operator for an untagged record type is abstract, while the operator for the formal type is not abstract (see 12.5).

7.1/5

If the name or prefix given in an iterator_procedure_call (see 5.5.3) denotes an abstract subprogram, the subprogram shall be a dispatching subprogram.

8/5

The type of an aggregate, of an object created by an object_declaration or an allocator, or of a generic formal object of mode in, shall not be abstract. The type of the target of an assignment operation (see 5.2) shall not be abstract. The type of a component shall not be abstract. If the result type of a function is abstract, then the function shall be abstract. If a function has an access result type designating an abstract type, then the function shall be abstract. The type denoted by a return_subtype_indication (see 6.5) shall not be abstract. A generic function shall not have an abstract result type or an access result type designating an abstract type.

8.a
reason

This ensures that values of an abstract type cannot be created, which ensures that a dispatching call to an abstract subprogram will not try to execute the nonexistent body.

8.b

Generic formal objects of mode in are like constants; therefore they should be forbidden for abstract types. Generic formal objects of mode in out are like renamings; therefore, abstract types are OK for them, though probably not terribly useful.

8.c/3

Generic functions returning a formal abstract type are illegal because any instance would have to be instantiated with a nonabstract type in order to avoid violating the function rule (generic functions cannot be declared abstract). But that would be an implied contract; it would be better for the contract to be explicit by the formal type not being declared abstract. Moreover, the implied contract does not add any capability.

9

If a partial view is not abstract, the corresponding full view shall not be abstract. If a generic formal type is abstract, then for each primitive subprogram of the formal that is not abstract, the corresponding primitive subprogram of the actual shall not be abstract.

9.a
discussion

By contrast, we allow the actual type to be nonabstract even if the formal type is declared abstract. Hence, the most general formal tagged type possible is "type T(<>) is abstract tagged limited private;".

9.b

For an abstract private extension declared in the visible part of a package, it is only possible for the full type to be nonabstract if the private extension has no abstract dispatching operations.

9.c/2

To be honest: In the sentence about primitive subprograms above, there is some ambiguity as to what is meant by “corresponding” in the case where an inherited operation is overridden. This is best explained by an example, where the implicit declarations are shown as comments:

9.d/2

package P1 is type T1 is abstract tagged null record; procedure P (X : T1); -- (1) end P1; 9.e/2 package P2 is type T2 is abstract new P1.T1 with null record; -- procedure P (X : T2); -- (2) procedure P (X : T2) is abstract; -- (3) end P2; 9.f/2 generic type D is abstract new P1.T1 with private; -- procedure P (X : D); -- (4) procedure G (X : D); 9.g/2 procedure I is new G (P2.T2); -- Illegal.

9.h/2

Type T2 inherits a nonabstract procedure P (2) from the primitive procedure P (1) of T1. P (2) is overridden by the explicitly declared abstract procedure P (3). Type D inherits a nonabstract procedure P (4) from P (1). In instantiation I, the operation corresponding to P (4) is the one which is not overridden, that is, P (3): the overridden operation P (2) does not “reemerge”. Therefore, the instantiation is illegal.

10/3

For an abstract type declared in a visible part, an abstract primitive subprogram shall not be declared in the private part, unless it is overriding an abstract subprogram implicitly declared in the visible part. For a tagged type declared in a visible part, a primitive function with a controlling result or a controlling access result shall not be declared in the private part, unless it is overriding a function implicitly declared in the visible part.

10.a
reason

The “visible part” could be that of a package or a generic package. This rule is needed because a nonabstract type extension declared outside the package would not know about any abstract primitive subprograms or primitive functions with controlling results declared in the private part, and wouldn't know that they need to be overridden with nonabstract subprograms. The rule applies to a tagged record type or record extension declared in a visible part, just as to a tagged private type or private extension. The rule applies to explicitly and implicitly declared abstract subprograms:

10.b

package Pack is type T is abstract new T1 with private; private type T is abstract new T2 with record ... end record; ... end Pack;

10.c

The above example would be illegal if T1 has a nonabstract primitive procedure P, but T2 overrides P with an abstract one; the private part should override P with a nonabstract version. On the other hand, if the P were abstract for both T1 and T2, the example would be legal as is.

11/2

A generic actual subprogram shall not be an abstract subprogram unless the generic formal subprogram is declared by a formal_abstract_subprogram_declaration. The prefix of an attribute_reference for the Access, Unchecked_Access, or Address attributes shall not denote an abstract subprogram.

11.a
ramification

An abstract_subprogram_declaration is not syntactically a subprogram_declaration. Nonetheless, an abstract subprogram is a subprogram, and an abstract_subprogram_declaration is a declaration of a subprogram.

11.b/2

The part about generic actual subprograms includes those given by default. Of course, an abstract formal subprogram's actual subprogram can be abstract.

Dynamic Semantics

11.1/2

The elaboration of an abstract_subprogram_declaration has no effect.

12/5

NOTE 1 Abstractness is not inherited; a type is abstract only if the reserved word abstract is used in the declaration of the type extension.

12.a
ramification

A derived type can be abstract even if its parent is not. Similarly, an inherited concrete subprogram can be overridden with an abstract subprogram.

13

NOTE 2 A class-wide type is never abstract. Even if a class is rooted at an abstract type, the class-wide type for the class is not abstract, and an object of the class-wide type can be created; the tag of such an object will identify some nonabstract type in the class.

Examples

14

Example of an abstract type representing a set of natural numbers:

15

package Sets is subtype Element_Type is Natural; type Set is abstract tagged null record; function Empty return Set is abstract; function Union(Left, Right : Set) return Set is abstract; function Intersection(Left, Right : Set) return Set is abstract; function Unit_Set(Element : Element_Type) return Set is abstract; procedure Take(Element : out Element_Type; From : in out Set) is abstract; end Sets;

16/5

Given the above abstract type, one can derive various (nonabstract) extensions of the type, representing alternative implementations of a set. One possibility is to use a bit vector, but impose an upper bound on the largest element representable, while another possible implementation is a hash table, trading off space for flexibility.

17.a
discussion

One way to export a type from a package with some components visible and some components private is as follows:

17.b

package P is type Public_Part is abstract tagged record ... end record; type T is new Public_Part with private; ... private type T is new Public_Part with record ... end record; end P;

17.c

The fact that Public_Part is abstract tells clients they have to create objects of type T instead of Public_Part. Note that the public part has to come first; it would be illegal to declare a private type Private_Part, and then a record extension T of it, unless T were in the private part after the full declaration of Private_Part, but then clients of the package would not have visibility to T.

Extensions to Ada 95

17.d/2

It is not necessary to override functions with a controlling result for a null extension. This makes it easier to derive a tagged type to complete a private type.

Wording Changes from Ada 95

17.e/2

Updated the wording to reflect the addition of interface types (see 3.9.4).

17.f/2

Updated the wording to reflect the addition of abstract formal subprograms (see 12.6).

17.g/2

The wording of shall-be-overridden was clarified so that it clearly applies to abstract predefined equality.

17.h/2

Moved the syntax and elaboration rule for abstract_subprogram_declaration here, so the syntax and most of the semantics are together (which is consistent with null procedures).

17.i/2

We define the term require overriding to make other wording easier to understand.

Incompatibilities With Ada 2005

17.j/3
correction

Added rules to eliminate holes with controlling access results and generic functions that return abstract types. While these changes are technically incompatible, it is unlikely that they could be used in a program without violating some other rule of the use of abstract types.

17.k/3
correction

Corrected a minor glitch having to do with abstract null extensions. The Ada 2005 rule allowed such extensions to inherit concrete operations in some rare cases. It is unlikely that these cases exist in user code.

Extensions to Ada 2005

17.l/3

An optional aspect_specification can be used in an abstract_subprogram_declaration. This is described in 13.1.1.

Wording Changes from Ada 2005

17.m/3
correction

Clarified that the predefined operator corresponding to an inherited abstract operator is also abstract. The Ada 2005 rules caused the predefined operator and the inherited operator to override each other, which is weird. But the effect is the same either way (the operator is not considered for resolution).

17.n/3
correction

Added wording to disallow abstract return objects. These were illegal in Ada 2005 by other rules; the extension to support class-wide type better opened a hole which has now been plugged.

Incompatibilities With Ada 2012

17.o/5
correction

Clarified that a recheck is needed in the case of an actual that is a record type with an abstract equality. This is an incompatibility as the generic boilerplate was previously omitted, meaning that such a recheck should not have been performed in the private part of an instance. Usually, this would just change an elaboration time raise of Program_Error into an error (a good thing, as the instance will never be useful), but could break a working instance if the equality usage is in a default expression that appears in the private part of the generic unit and it is never used in a call. In that case, Ada 2022 will reject the instance while it would have worked in Ada 2012. As a practical matter, it's more likely that a compiler already does the recheck in the entire instance spec, or does not do it at all; thus for many implementations there will be no practical incompatibility.

3.9.4 Interface Types

1/2

[An interface type is an abstract tagged type that provides a restricted form of multiple inheritance. A tagged type, task type, or protected type may have one or more interface types as ancestors.]

1.a/5

Term entry: interface type — abstract tagged type that has no components or concrete operations except possibly null procedures
Note: Interface types are used for composing other interfaces and tagged types and thereby provide multiple inheritance. Only an interface type can be used as a progenitor of another type.

Language Design Principles

1.b/2

The rules are designed so that an interface can be used as either a parent type or a progenitor type without changing the meaning. That's important so that the order that interfaces are specified in a derived_type_definition is not significant. In particular, we want:

1.c/2

type Con1 is new Int1 and Int2 with null record; type Con2 is new Int2 and Int1 with null record;

1.d/2

to mean exactly the same thing.

Syntax

2/2

interface_type_definition ::=
[limited | task | protected | synchronized] interface [and interface_list]

3/2
interface_list ::= interface_subtype_mark{andinterface_subtype_mark}

Static Semantics

4/2

An interface type (also called an interface) is a specific abstract tagged type that is defined by an interface_type_definition.

5/2

An interface with the reserved word limited, task, protected, or synchronized in its definition is termed, respectively, a limited interface, a task interface, a protected interface, or a synchronized interface. In addition, all task and protected interfaces are synchronized interfaces, and all synchronized interfaces are limited interfaces.

5.a/5

Term entry: synchronized — can be safely operated on by multiple tasks concurrently
Note: Synchronized is used to qualify entities, as in a synchronized interface.

6/2

[A task or protected type derived from an interface is a tagged type.] Such a tagged type is called a synchronized tagged type, as are synchronized interfaces and private extensions whose declaration includes the reserved word synchronized.

6.a/2
proof

The full definition of tagged types given in 3.9 includes task and protected types derived from interfaces.

6.b/2
ramification

The class-wide type associated with a tagged task type (including a task interface type) is a task type, because “task” is one of the language-defined classes of types (see 3.2). However, the class-wide type associated with an interface is not an interface type, as “interface” is not one of the language-defined classes (as it is not closed under derivation). In this sense, “interface” is similar to “abstract”. The class-wide type associated with an interface is a concrete (nonabstract) indefinite tagged composite type.

6.c/2

“Private extension” includes generic formal private extensions, as explained in 12.5.1.

7/2

A task interface is an [abstract] task type. A protected interface is an [abstract] protected type.

7.a/2
proof

The “abstract” follows from the definition of an interface type.

7.b/2
reason

This ensures that task operations (like abort and the Terminated attribute) can be applied to a task interface type and the associated class-wide type. While there are no protected type operations, we apply the same rule to protected interfaces for consistency.

8/2

[An interface type has no components.]

8.a/2
proof

This follows from the syntax and the fact that discriminants are not allowed for interface types.

9/2

An interface_subtype_mark in an interface_list names a progenitor subtype; its type is the progenitor type. An interface type inherits user-defined primitive subprograms from each progenitor type in the same way that a derived type inherits user-defined primitive subprograms from its progenitor types (see 3.4).

9.a/5

Term entry: progenitor — type given in the interface list, if any, of an interface, task, protected, or derived type definition
Note: A progenitor is always an interface type.

Legality Rules

10/2

All user-defined primitive subprograms of an interface type shall be abstract subprograms or null procedures.

11/2

The type of a subtype named in an interface_list shall be an interface type.

12/2

A type derived from a nonlimited interface shall be nonlimited.

13/2

An interface derived from a task interface shall include the reserved word task in its definition; any other type derived from a task interface shall be a private extension or a task type declared by a task declaration (see 9.1).

14/2

An interface derived from a protected interface shall include the reserved word protected in its definition; any other type derived from a protected interface shall be a private extension or a protected type declared by a protected declaration (see 9.4).

15/2

An interface derived from a synchronized interface shall include one of the reserved words task, protected, or synchronized in its definition; any other type derived from a synchronized interface shall be a private extension, a task type declared by a task declaration, or a protected type declared by a protected declaration.

15.a/2
reason

We require that an interface descendant of a task, protected, or synchronized interface repeat the explicit kind of interface it will be, rather than simply inheriting it, so that a reader is always aware of whether the interface provides synchronization and whether it may be implemented only by a task or protected type. The only place where inheritance of the kind of interface might be useful would be in a generic if you didn't know the kind of the actual interface. However, the value of that is low because you cannot implement an interface properly if you don't know whether it is a task, protected, or synchronized interface. Hence, we require the kind of the actual interface to match the kind of the formal interface (see 12.5.5).

16/2

No type shall be derived from both a task interface and a protected interface.

16.a
reason

This prevents a single private extension from inheriting from both a task and a protected interface. For a private type, there can be no legal completion. For a generic formal derived type, there can be no possible matching type (so no instantiation could be legal). This rule provides early detection of the errors.

17/2

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.

17.a/3
ramification

This paragraph is intended to apply to all of the Legality Rules in this subclause. We cannot allow interface types which do not obey these rules, anywhere. Luckily, deriving from a formal type (which might be an interface) is not allowed for any tagged types in a generic body. So checking in the private part of a generic covers all of the cases.

Dynamic Semantics

18/3

The elaboration of an interface_type_definition creates the interface type and its first subtype.

18.a/3
discussion

There is no other effect. An interface_list is made up of subtype_marks, which do not need to be elaborated, so the interface_list does not either. This is consistent with the handling of discriminant_parts.

19/5

NOTE Nonlimited interface types have predefined nonabstract equality operators. These can be overridden with user-defined abstract equality operators. Such operators will then require an explicit overriding for any nonabstract descendant of the interface.

Examples

20/2

Example of a limited interface and a synchronized interface extending it:

21/2

type Queue is limited interface; procedure Append(Q : in out Queue; Person : in Person_Name) is abstract; procedure Remove_First(Q : in out Queue; Person : out Person_Name) is abstract; function Cur_Count(Q : in Queue) return Natural is abstract; function Max_Count(Q : in Queue) return Natural is abstract; -- See 3.10.1 for Person_Name. 22/3

Queue_Error : exception; -- Append raises Queue_Error if Cur_Count(Q) = Max_Count(Q) -- Remove_First raises Queue_Error if Cur_Count(Q) = 0 23/2 type Synchronized_Queue is synchronized interface and Queue; -- see 9.11 procedure Append_Wait(Q : in out Synchronized_Queue; Person : in Person_Name) is abstract; procedure Remove_First_Wait(Q : in out Synchronized_Queue; Person : out Person_Name) is abstract; 24/2 ... 25/2 procedure Transfer(From : in out Queue'Class; To : in out Queue'Class; Number : in Natural := 1) is Person : Person_Name; begin for I in 1..Number loop Remove_First(From, Person); Append(To, Person); end loop; end Transfer;

26/5

This defines a Queue interface defining a queue of people. (A similar design is possible to define any kind of queue simply by replacing Person_Name by an appropriate type.) The Queue interface has four dispatching operations, Append, Remove_First, Cur_Count, and Max_Count. The body of a class-wide operation, Transfer is also shown. Every nonabstract extension of Queue will provide implementations for at least its four dispatching operations, as they are abstract. Any object of a type derived from Queue can be passed to Transfer as either the From or the To operand. The two operands can be of different types in a given call.

27/5

The Synchronized_Queue interface inherits the four dispatching operations from Queue and adds two additional dispatching operations, which wait if necessary rather than raising the Queue_Error exception. This synchronized interface can only be implemented by a task or protected type, and as such ensures safe concurrent access.

28/2

Example use of the interface:

29/3

type Fast_Food_Queue is new Queue with record ...; procedure Append(Q : in out Fast_Food_Queue; Person : in Person_Name); procedure Remove_First(Q : in out Fast_Food_Queue; Person : out Person_Name); function Cur_Count(Q : in Fast_Food_Queue) return Natural; function Max_Count(Q : in Fast_Food_Queue) return Natural; 30/2 ... 31/2 Cashier, Counter : Fast_Food_Queue; 32/5

... -- Add Casey (see 3.10.1) to the cashier's queue: Append (Cashier, Casey); -- After payment, move Casey to the sandwich counter queue: Transfer (Cashier, Counter); ...

33/5

An interface such as Queue can be used directly as the parent of a new type (as shown here), or can be used as a progenitor when a type is derived. In either case, the primitive operations of the interface are inherited. For Queue, the implementation of the four inherited routines will necessarily be provided. Inside the call of Transfer, calls will dispatch to the implementations of Append and Remove_First for type Fast_Food_Queue.

34/2

Example of a task interface:

35/2

type Serial_Device is task interface; -- see 9.1 procedure Read (Dev : in Serial_Device; C : out Character) is abstract; procedure Write(Dev : in Serial_Device; C : in Character) is abstract;

36/2

The Serial_Device interface has two dispatching operations which are intended to be implemented by task entries (see 9.1).

Extensions to Ada 95

36.a/2

Interface types are new. They provide multiple inheritance of interfaces, similar to the facility provided in Java and other recent language designs.

Wording Changes from Ada 2005

36.b/3
correction

Corrected the definition of elaboration for an interface_type_definition to match that of other type definitions.