!standard 03.04 (02) 05-12-02 AI95-00251/24 !standard 02.09 (02) !standard 03.02 (04) !standard 03.02.01 (02) !standard 03.04 (03) !standard 03.04 (08) !standard 03.04 (23) !standard 03.04 (35) !standard 03.04.01 (02) !standard 03.04.01 (10) !standard 03.07 (01) !standard 03.07 (08) !standard 03.09.03 (04) !standard 03.09.03 (05) !standard 03.09.04 (01) !standard 04.05.02 (03) !standard 04.06 (08-10) !standard 04.06 (11/1) !standard 04.06 (12/1) !standard 04.06 (12.1/1) !standard 04.06 (13-24) !standard 07.03 (03) !standard 08.03 (12) !standard 12.05 (03) !standard 12.05.01 (03) !standard 12.05.01 (15) !standard 12.05.05 (01) !standard 13.1 (13) !standard 13.13.2(9) !standard 13.13.2(27) !standard 13.13.2(36/1) !standard 13.14 (07) !class amendment 00-12-04 !status Amendment 200Y 04-03-29 !status WG9 Approved 04-06-18 !status ARG Approved 7-0-1 04-03-07 !status work item 00-12-04 !status received 00-12-04 !priority High !difficulty Hard !subject Abstract Interfaces to provide multiple inheritance !summary This proposal adds "interface" types to the standard as a kind of componentless abstract tagged type. A tagged type may be derived from one or more such interfaces as well as possibly from one tagged type; this provides a limited form of multiple inheritance. Dispatching calls through the primitives of an interface dispatch to code bodies associated with specific tagged types that are derived from that interface. This proposal also clarifies that it is always legal to convert between two types if they have a common ancestor and they meet the restrictions of 4.6(22-23). !problem A number of recent language designs have adopted a compromise between full multiple inheritance and strict single inheritance, using a concept called "interface" types. An interface consists solely of a set of operation specifications -- the interface type has no data components and no operation implementations. In these designs a type may "implement" multiple interfaces, but can inherit code from only one parent type. This compromise has been found to have much of the power of multiple inheritance, without most of the implementation and semantic difficulties. This allows a single type to "masquerade" as a number of different types so that it may take advantage of existing abstractions that expect an object that provides a certain set of primitives. This kind of flexibility is possible in Ada currently only using generics, or relatively complicated mechanisms using access discriminants. The solutions using generics don't really work when the abstraction itself cannot be represented as a generic. For example, a graphical subsystem may have a linked list of "observer" objects, each of which must implement an operation to receive notification when the observed object changes. This is not amenable to a generic mix-in approach. You can't mix-in the ability to be on a linked list of observers. Access discriminants can accomplish this, at the expense of making the type limited, and requiring a fair amount of mechanism. These problems would be overcome by Ada adopting the interface concept in some way. Moreover, this would make interfacing with Java and the new ".net" infrastructure from Microsoft significantly smoother. !proposal An interface type is a kind of abstract tagged type without any components. All primitive subprograms must be abstract or null. (A null procedure is introduced by AI-348 and behaves as if it had a body consisting solely of a null statement.) So we might have package PkgI1 is type I1 is interface; procedure P (X: I1) is abstract; procedure Q (X: I1) is null; end PkgI1; We can now derive a tagged type from one or more interface types plus possibly one tagged type. In other words we can derive a tagged type from several other types (its ancestor types) but only one of these can be a normal tagged type (it has to be written first). If we have an existing normal tagged type T1 in a package PkgT1 with operations P1, P2 and so on we could now write with PkgI1, PkgT1; package PkgDT is type DT is new PkgT1.T1 and PkgI1.I1 with ...; procedure P(X: DT); -- possibly other ops of DT end PkgDT; We must of course provide a concrete procedure for P inherited from the interface I1. We could also provide an overriding for Q but if we don't then we simply inherit the null procedure of I1. We could also override the inherited operations P1 and P2 from T1 in the usual way. We can also derive a new interface from other interfaces thus type I1 is interface; ... type I2 is interface; ... type I3 is interface I1; ... type I4 is interface I1 and I2; ... When we derive interfaces in this way we can add new operations so that the new interface such as I4 will have all the operations of both I1 and I2 plus possibly some others declared specifically as operations of I4. All these operations must be abstract or null and there are fairly obvious rules regarding what happens if two or more of the ancestor interfaces have the same operation. Thus a null operation overrides an abstract one. More generally if we have tagged types T1, T2 and so on we can derive further tagged types thus type DT1 is new T1 and I1 and I2 with null record; -- no additional components ... type DT2 is new I3 and I4 with record ... end record; -- has additional components ... The first in the list of ancestor types is always known as the parent type. The parent type might or might not be an interface. All other ancestor types must be interfaces. Thus the following is not permitted type DT3 is new T1 and T2 with .... -- illegal Class-wide types also apply to interface types. The class-wide type I1'Class covers all the types derived from the interface I1 (both other interfaces as well as normal tagged types). We can then dispatch using an object of a concrete tagged type in that class in the usual way since we know that any abstract operation of I1 will have been overridden. So we might have type I1_Ref is access all I1'Class; DT1_Var : aliased DT1; Ref: I1_Ref := DT1_Var'Access; Observe that conversion is permitted between the class-wide type I1_Ref and any type which is derived from the interface type I1. We informally speak of a specific tagged type as implementing an interface from which it is derived (directly or indirectly). The phrase "implementing an interface" is not used in the wording but it is useful for purposes of discussion. Interfaces can also be used in private extensions and as generic parameters. Thus type PT1 is new T1 and I2 and I3 with private; ... private type PT1 is new DT1 and I2 and I3 with null record; An important rule regarding private extensions is that the full view and the partial view must agree with respect to the set of interfaces they implement. Thus although the parent in the full view need not be T1 but can be any type derived from T1, the same is not true of the interfaces which must be such that they both implement the same set exactly. Generic parameters take the form generic type FI is interface I1 and I2; package... and then the actual parameter must be an interface which implements all the ancestors I1, I2 etc. The formal could also just be "type FI is interface;" in which case the actual parameter can be any interface. (There might be subprograms passed as further parameters which would require that the actual has certain operations.) The interfaces I1 and I2 might themselves be formal parameters occuring earlier in the parameter list. Interfaces can also be limited. !wording In 2.9(2) add the following to the list of reserved words interface 3.2 Types and Subtypes Add "interface types" to the list in 3.2(4). 3.2.1 Type Declarations Add to syntax type_definition ::= ... | interface_type_definition 3.4 Derived Types and Classes Replace syntax section with interface_list ::= interface_subtype_mark {AND interface_subtype_mark} derived_type_definition ::= [ABSTRACT] NEW parent_subtype_indication [[AND interface_list] record_extension_part] Add at the end of paragraph 3: A derived type has one parent type and zero or more interface ancestor types. Replace paragraph 8 with: Each class of types that includes the parent type or an interface ancestor type also includes the derived type. Add after paragraph 23: If a type declaration names an interface type in an interface_list, then the declared type inherits any user-defined primitive subprograms of the interface type in the same way. Add after paragraph 35 (in the Notes section): 18 An interface type which has an interface ancestor "is derived from" that type, and therefore is a derived type. A derived_type_definition, however, never defines an interface type. 3.4.1 Derivation Classes Insert after the first sentence of paragraph 2: A derived type or interface type is also derived from each of its interface ancestor types, if any. Replace the last sentence of paragraph 10 with: An *ultimate ancestor* of a type is an ancestor of that type that is not itself a descendant of any other type. Every untagged type has a unique ultimate ancestor. 3.7 Discriminants In 3.7(1) and 3.7(8), change "array" to "array or interface". 3.9.3 Abstract Types and Subprograms Replace paragraphs 4-5 with If a type inherits a subprogram corresponding to an abstract subprogram or to a function with a controlling result, then: - If the inheriting type is abstract or untagged, the inherited subprogram is abstract. 3.9.4 Interface Types (This section is entirely new.) 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. Syntax interface_type_definition ::= [LIMITED] INTERFACE [interface_list] Static Semantics An interface type (also called an "interface") is a specific abstract tagged type that is defined by an interface_type_definition. [An interface type has no components.] AARM Note: This follows from the syntax. Legality Rules All user-defined primitive subprograms of an interface type shall be abstract subprograms or null procedures. The type of a subtype named in an interface_list shall be an interface type. If a type declaration names an interface type in an interface_list, then the accessibility level of the declared type shall not be statically deeper than that of the interface type; also, the declared type shall not be declared in a generic body if the interface type is declared outside that body. A descendant of an interface type shall be limited if and only if the interface type is limited. AARM Note: Reason: Without this rule, an implementation would no longer know statically whether a dispatching function call is a call to a function with a return-by-reference result. This could be implemented, but it is a substantial and unnecessary complication. The complications of AI-318 occur precisely because we're trying to avoid runtime choices between normal and return-by-reference functions. Besides, the language has, as a matter of general principle, steered away from constructs which would allow a limited view of an object of a non-limited tagged type. We may want to reconsider this if some form of AI-318 is included in the standard. End AARM Note. A full view shall be a descendant of an interface type if and only if the corresponding partial view (if any) is also a descendant of the interface type. AARM Note: Reason: Consider the following example: package P is package Pkg is type Ifc is interface; procedure Foo (X : Ifc) is abstract; end; type Parent_1 is tagged null record; type T1 is new Parent_1 with private; private type Parent_2 is new Parent_1 and Pkg.Ifc with null record; procedure Foo (X : Parent_2); -- Foo #1 type T1 is new Parent_2 with null record; end; with P; package P_Client is type T2 is new P.T1 and P.Pkg.Ifc with null record; procedure Foo (X : T2); -- Foo #2 X : T2; end P_Client; with P_Client; package body P is ... begin Pkg.Foo (Pkg.Ifc'Class (P_Client.X)); -- call Foo #2 Pkg.Foo (Pkg.Ifc'Class (T1 (P_Client.X))); -- call Foo #1 end P; If this example were legal (it is illegal because the completion of T1 is descended from an interface that the partial view is not descended from), then we would have two dispatching calls to Pkg.Foo with the two controlling operands having the same tag and yet different bodies would be executed. The two conversions to Pkg.Ifc'Class would map Pkg.Foo to different slots in the same dispatch table because the source types of the conversions are different. This would be bad. End AARM Note 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. Dynamic Semantics The elaboration of an interface_type_definition has no effect. 4.5.2 Relational Operators and Membership Tests Replace paragraph 3 with The tested type of a membership test is the type of the range or the type determined by the subtype_mark. If the tested type is tagged, then the simple_expression shall resolve to be of a type that is convertible (see 4.6) to the tested type; if untagged, the expected type for the simple_expression is the tested type. 4.6 Type Conversions Insert the following before paragraph 8: In a view conversion for an untagged type, the target type shall be convertible (back) to the operand type. If there is a type that is an ancestor of both the target type and the operand type, or both types are class-wide types, then at least one of the following rules shall apply: - The target type shall be untagged; or - The operand type shall be covered by or descended from the target type; or - The operand type shall be a class-wide type that covers the target type; or - The operand and target types shall both be class-wide types and the specific type associated with at least one of them shall be an interface type. If there is no type that is the ancestor of both the target type and the operand type, and they are not both class-wide types, one of the following rules shall apply: Existing paragraphs 8, 9-12.1/1, 13-17, and 18-20 become bullets under "If there is no type ...". Delete the paragraphs 21-24 (they're included above). [Editor's note: A somewhat different order of changes is used in the Amendment document, as we can't change formatting with our tools. The net effect is the same, however.] 7.3 Private Types and Private Extensions Replace private_extension_declaration ::= TYPE defining_identifier [discriminant_part] IS [ABSTRACT] NEW ancestor_subtype_indication WITH PRIVATE; with private_extension_declaration ::= TYPE defining_identifier [discriminant_part] IS [ABSTRACT] NEW ancestor_subtype_indication [AND interface_list] WITH PRIVATE; 8.3 Visibility Add a new bullet into paragraphs 9/1 - 13 (the entire group is given here to make it easier to evaluate the changes): Two homographs are not generally allowed immediately within the same declarative region unless one overrides the other (see Legality Rules below). The only declarations that are overridable are the implicit declarations for predefined operators and inherited primitive subprograms. A declaration overrides another homograph that occurs immediately within the same declarative region in the following cases: * A declaration that is not overridable overrides one that is overridable, regardless of which declaration occurs first; * The implicit declaration of an inherited operator overrides that of a predefined operator; * An implicit declaration of an inherited subprogram overrides a previous implicit declaration of an inherited subprogram. * If two or more homographs are implicitly declared at the same place: - If at least one is a subprogram that is neither a null procedure nor an abstract subprogram, and does not require overriding (see 3.9.3), then they override those that are null procedures, abstract subprograms, or require overriding. If more than one such homograph remains that is not thus overridden, then they are all hidden from all visibility. - Otherwise (all are null procedures, abstract subprograms, or require overriding), then any null procedure overrides all abstract subprograms and all subprograms that require overriding; if more than one such homograph remains that is not thus overridden, then if they are all fully conformant with one another, one is chosen arbitrarily; if not, they are all hidden from all visibility. AARM Note: Discussion: It is intended that in the case where the implementation arbitrarily chooses one overrider from among a group of inherited subprograms, users should be unable to determine which member was chosen. This rule is needed in order to allow package Outer is package P1 is type Ifc1 is interface; procedure Null_Procedure (X : Ifc1) is null; procedure Abstract_Subp (X : Ifc1) is abstract; end P1; package P2 is type Ifc2 is interface; procedure Null_Procedure (X : Ifc2) is null; procedure Abstract_Subp (X : Ifc2) is abstract; end P2; type T is new P1.Ifc1 and P2.Ifc2 with null record; end Outer; without requiring that T explicitly override any of its inherited operations. When the subprograms do not conform, we chose not to adopt the "use clause" rule which would make them all visible resulting in likely ambiguity. If we had used such a rule, any successful calls would be confusing; and the fact that there are no Beaujolais-like effect to worry about means we can consider other rules. The hidden-from-all-visibility homographs are still inherited by further derivations, which avoids order-of-declaration dependencies and other anomolies. We have to be careful to not include arbitrary selection if the routines have real bodies. (This can happen in generics, see the example in the incompatibilies section below.) We don't want the ability to sucessfully call routines where the body executed depends on the compiler or a phase of the moon. Note that if the type is concrete, abstract subprograms are inherited as subprograms that require overriding. We include functions that require overriding as well; these don't have real bodies, so they can use the more liberal rules. End AARM Note * For an implicit declaration of a primitive subprogram in a generic unit, there is a copy of this declaration in an instance. However, a whole new set of primitive subprograms is implicitly declared for each type declared within the visible part of the instance. These new declarations occur immediately after the type declaration, and override the copied ones. The copied ones can be called only from within the instance; the new ones can be called only from outside the instance, although for tagged types, the body of a new one can be executed by a call to an old one. 12.5 Formal Types Add to syntax formal_type_definition ::= ... | formal_interface_type_definition 12.5.1 Formal Private and Derived Types Replace formal_derived_type_definition ::= [ABSTRACT] NEW subtype_mark [WITH PRIVATE] with formal_derived_type_definition ::= [ABSTRACT] NEW subtype_mark [[AND interface_list] WITH PRIVATE] Add after paragraph 5: The actual type for a generic formal derived type shall be a descendant of every ancestor of the formal type. 12.5.5 Formal Interface Types (This section is entirely new.) The class determined for a formal interface type is the class of all tagged types. Syntax formal_interface_type_definition ::= interface_type_definition Legality Rules The actual type shall be an interface type. AARM Note: Reason: The class of all interface types includes non-interface descendants of interface types. Such types must not match a formal interface. End AARM Note. The actual type shall be a descendant of every ancestor of the formal type. The actual type shall be limited if and only if the formal type is limited. 13.1 Operational and Representation Items Add after 13.1(13): A type_declaration is illegal if it has one or more progenitors, and a representation item applies to an ancestor, and this representation item conflicts with the representation of some other ancestor. The cases that cause conflicts are implementation defined. 13.13.2 Stream-Oriented Attributes Change the last sentence of paragraph 9 (as modified by AI-195) to: For a limited type extension, if the attribute of the parent type or any progenitor type of T is available anywhere within the immediate scope of T, and the attribute of the parent type or the type of any of the extension components is not available at the freezing point of T, then the attribute of T shall be directly specified. Add after paragraph 27: If T is an abstract type, then S'Input is an abstract function. Add a sentence to the end of paragraph 36 (as modified by AI-195): Furthermore, if a stream-oriented attribute is specified for an interface type by an attribute_definition_clause, the subprogram name given in the clause shall statically denote a null procedure. 13.14 Freezing Rules Add a further bullet after paragraph 7: The declaration of a record extension, interface type, task unit, or protected unit causes freezing of any progenitor types specified in the declaration. !discussion All primitives of an interface are required to be abstract or null to minimize difficulty in resolving what happens when the "same" primitive is inherited from two (or more) ancestor types. If only one ancestor type is permitted to have non-abstract, non-null primitives, no question arises as to which implementation to use. For simplicity the first type in the list of ancestor types is called the parent type and this parent type is the only ancestor that can be a normal tagged type - note that the parent type can itself be an interface type. Languages that support full multiple inheritance need elaborate rules for resolving these kinds of conflicts and lead to confusion in the mind of user. Moreover, no data components are allowed in interfaces to avoid implementation complexity and inefficiency. If data components could be inherited from multiple ancestors, then the offset of a component of an interface could not be determined statically. It would make component access as complicated and expensive as calling a primitive (which is discussed below), or require recompilation of inherited primitives even when not overridden. Inheritance then becomes more like macro expansion. We considered avoiding the introduction of the new concept of an interface type by simply permitting inheritance from several tagged types provided only one had concrete operations and components. However, this approach would prevent the clean introduction of the interface concept into generics, and would be fragile: if a maintenance programmer added a component or concrete operation, other, possibly distant, code would become illegal. Moreover, this is probably less easy for the user to understand - particularly if the user is familiar with Java. Having decided that a new concept was required, many forms of syntax were considered. The new reserved word interface was chosen as best reflecting the nature of the concept. Observe that the reserved word interface is always followed by a list of interface identifiers but that this list may be null. Thus "interface" may be followed just by a semicolon or by the identifier of an interface. The rules are structured to ensure that an existing componentless abstract tagged type can be changed to being an interface type with minimal disruption to the existing code. Because an interface type is an abstract tagged type, it can be used anywhere an abstract tagged private (or null record) type is permitted. We disallow "private" inheriting from interfaces, because without allocating a complete separate set of dispatching operation "slots" for the corresponding operations, there is no good way to prevent these operations from being unintentionally overridden outside of the package by a descendant that explicitly implements the same interface. For example if I1 is an interface type with a primitive P and T1 is some tagged type as in the proposal section then we might otherwise write package Pkg1 is type PT1 is tagged private; ... private type PT1 is new T1 and I1 with ... -- illegal private inheritance of interface procedure P(X: PT1); -- overrides primitive P of interface I1 end Pkg1; ... type PT2 is new Pkg1.PT1 and I1 with ... procedure P(X: PT2); -- if primitive operations such as P of I1 are overridden for PT2, -- they will override operations associated with PT1's private -- inheritance of I1, violating the privateness of the inheritance. In the above case, the partial view of PT1 is allowed to be type PT1 is new I1 with private; This makes it visible that the interface I1 is inherited and thus that its primitives might well be overriden outside Pkg1. Possible Implementation Model As an implementation model, one can think of a type which inherits operations from an interface type as defining a mapping from the primitive operations of the interface type (perhaps represented as dispatch table slot numbers) to those of the inheriting type. A dispatching call to an operation of an interface type is then accomplished by mapping the called operation to the corresponding operation of the controlling operand's type. Alternatively, one might construct a separate dispatch table representing this alternative "view" of the "real" dispatch table, but that would require more work when deriving from a type which implements an interface. The slot-to-slot mapping remains valid and can be reused for the derived type, whereas a new alternative "view" of the dispatch table of the derived type would need to be constructed. A value of type Some_Interface_Type'Class can then be represented at runtime as a record address paired (perhaps only conceptually - see next paragraph) with a reference to this dispatch table permutation map. Consider the example of a call to Some_Interface_Type'Class'Input occurring as the controlling operand of a dispatching call. The implementation must be able to determine where to dispatch to in this case. In terms of the "slot-to-slot mapping" implementation model, this means that an implementation needs to provide a mapping at runtime which takes the tag of a specific tagged type and of an interface type (which is implemented by the specific type) and returns a reference to the associated dispatch table permutation map (the "slot-to-slot" map). Given this mapping, a "thin pointer" representation becomes feasible. In other words, a value of type Some_Interface_Type'Class could be represented as an unaugmented record address. Alternatively, a "fat pointer" implementation model is possible. An interface "view" of an object is represented as a pair of pointers, one to an interface-specific dispatch table (or permutation map), and the other to the tagged object of the implementing type. The interface-specific dispatch table would have a layout determined by the order in which the primitives of the interface were declared, while its content would be determined by the particular type implementing the interface. Intermediate approaches between these two extremes are also possible (e.g. use the thin pointer representation, but associate an implicit parameter with formal parameters). Note also that the thin/fat implementation decision is orthogonal to the decision about whether to store the details of how a type implements an interface (hereafter referred to as an "interface implementation descriptor") as a new dispatch table, as a slot-to-slot map, or in some other form. In any case, a dispatch table could contain a map of some kind (e.g. an array) mapping interface type tag values to these "interface implementation descriptors", with one entry in the map for each interface implemented by the type. This would meet the aforementioned need for a mapping from the tag of a specific type and the tag of an interface type which it implements to the corresponding interface implementation descriptor. The Some_Interface_Type'Class'Input example described above also illustrates some of the difficulties that an implementation might run into if two types which both implement a given interface store their tag components at different locations. In order to perform the dispatching call, the tag of the controlling operand must be located. It is expected that most implementations will solve this problem by requiring that every tag component must reside at the same offset (typically zero) for all tagged types. ---- Technical notes: 1) Inherited homographs must be overridden. If the homographs are not mode-conformant, then this may be impossible. Life's hard. 2) Nothing here (in particular, nothing in the changes to the visibility rules in 8.3) was *intended* to change the semantics of a program which declares no interface types. Consider, however: generic type T1 is private; type T2 is private; package G is type T is null record; procedure P (X : T; Y : T1); procedure P (X : T; Y : T2); end G; package I is new G (Integer, Integer); -- exports homographs type D is new I.T; -- formerly legal, now illegal. Disallowing this case does not seem like a bad thing, but it is a (very minor) change. 3) A type derived from an interface type should inherit the same subprograms as a type which is derived from both the interface type and an ancestor of the interface type. The two type definitions should be indistinguishable, in the sense that adding or deleting the explicit redundant derivation relationship should be a semantics-preserving transformation. 4) It is intended that there should be no real difference between the two forms of derivation from interface types. For example, replacing the declaration type T is new Interface_Type_1 and Interface_Type_2 with null record; with type T is new Interface_Type_2 and Interface_Type_1 with null record; should be semantics-preserving. 5) Note that an interface type declared in a visible part is not allowed to have primitive operations declared in the private part. A dispatching call to a primitive of an interface type will execute the body of a corresponding routine associated with the specific type of the controlling operand. Without this restriction, it is possible that the specific type might provide no such routine. It would be OK to follow the example of the rules in 3.9.3(10) and allow this in the case where the subprogram declared in the private part "is overriding an abstract subprogram implicitly declared in the visible part", but this doesn't seem to be worth the bother because this could only be used to override an abstract procedure with a null procedure. The changes to section 4.6 also include a solution for the problem originally identified in AI-219 (which is now folded into this AI). Originally, 4.6(21) included the following: "If the target type is not included in any of the above 4 cases, then ..." Thus, 4.6(21-23) only applied if the target type was not a numeric type, an array type, a general access type, or an access-to-subprogram type. This turned out to be a mistake. It might seem at first that this restriction would have no consequences - if the two types are known to have a common ancestor, then surely the characteristics mentioned in 4.6(8-20) would all match up and the conversion would be legal. This is not always true. Consider the following example: package P is type Target is private; private type Target is range 1 .. 10; ... ; end P; with P; package Q is type Source is new P.Target; function Source_Value return Source; end Q; with Q; package body P is Converted : Target := Target (Q.Source_Value); -- legal ... end P; As seen from the point of the type conversion, Target is a numeric type although Source is not. With the original wording, 4.6(21-23) does not apply and the conversion would be illegal. Thus, the Standard as written is incompatible with Ada 83, which allowed conversions between any two types related by derivation. Certainly no such incompatibility was intended; no such incompatibility is listed under the "Incompatibilities with Ada 83" heading in the AARM. This AI's reorganization of 4.6 addresses this problem. !example An example involving interface types: type T is tagged null record; T_Var : T; package P1 is type Ifc1 is interface; procedure Op (X : Ifc1) is abstract; procedure Op1_A (X : Ifc1) is abstract; procedure Op1_B (X : Ifc1) is abstract; type Ref is access all Ifc1'Class; Procedure Foo (X : Ref); end; package P2 is type Ifc2 is interface; procedure Op (X : Ifc2) is abstract; procedure Op2_A (X : Ifc2) is abstract; procedure Op2_B (X : Ifc2) is abstract; type Ref is access all Ifc2'Class; Procedure Foo (X : Ref); end; package body P1 is ... procedure Foo (X : Ref) is type Stream_Ptr is access all Ada.Streams.Root_Stream_Type'Class; The_Stream : constant Stream_Ptr := ...; begin Op1_A (X.all); Op1_B (X.all); Ifc1'Class'Output (The_Stream, X.all); if X.all in T'Class then T_Var := T (X.all); end if; end Foo; end P1; package body P2 is ... end P2; type D is new P1.Ifc1 and P2.Ifc2 with record F1, F2 : Integer; end record; procedure Op1_A (X : D); procedure Op1_B (X : D); procedure Op (X : D); procedure Op2_A (X : D); procedure Op2_B (X : D); ... X : aliased D; begin P1.Foo (X'Access); P2.Foo (X'Access); P1.Op (P1.Ifc1'Class (X)); P2.Op (P2.Ifc2'Class (X)); end; -------- A somewhat less artificial example: package Object_Monitoring is type Monitored_Object is interface; procedure Display (Object : Monitored_Object) is abstract; type Object_Reference is access all Monitored_Object'Class; for Object_Reference'Storage_Size use 0; procedure Register (Object : Object_Reference); procedure Unregister (Object : Object_Reference); procedure Display_Registered_Objects; end; package body Object_Monitoring is package Object_Reference_Sets is new ... ; Registered_Objects : Object_Reference_Sets.Set; procedure Display_One (Object : Object_Reference) is begin Display (Object.all); end; procedure Display_Registered_Objects is begin Object_Reference_Sets.Visit_All (The_Set => Registered_Objects, Visit_One => Display_One'Access); end; ... end Object_Monitoring; package Pkg1 is ... end Pkg1; with Object_Monitoring; package body Pkg1 is type T is tagged record F1, F2 : Integer; end record; type Monitored_T is new T and Object_Monitoring.Monitored_Object with ...; procedure Display (Object : Monitored_T) is begin ... end; X : aliased Monitored_T; ... begin Object_Monitoring.Register (X'access); end; !comment !corrigendum 2.9(02) !comment This is now done by AI-284-2. !comment !comment @dinsl !comment @b !corrigendum 3.2(4) @drepl The composite types are the @i types, @i, @i types, @i types, and @i types. A @i type or @i represents a partial view (see 7.3) of a type, providing support for data abstraction. A partial view is a composite type. @dby The composite types are the @i types, @i, @i types, @i types, @i types, and @i types. A @i type or @i represents a partial view (see 7.3) of a type, providing support for data abstraction. A partial view is a composite type. !corrigendum 3.2.1(4) @drepl @xcode<@fa> @dby @xcode<@fa> !corrigendum 3.4(2) @drepl @xcode<@fa@ft<@b>@fa<] >@ft<@b @i>@fa> @dby @xcode<@fa@ft<@i>@fa@ft<@b>@fa< >@ft<@i>@fa> @xcode<@fa@ft<@b>@fa<] >@ft<@b @i>@fa@ft<@b>@fa< interface_list] record_extension_part]>> !corrigendum 3.4(3) @drepl The @i@fa defines the parent subtype; its type is the parent type. @dby The @i@fa defines the parent subtype; its type is the parent type. A derived type has one parent type and zero or more interface ancestor types. !corrigendum 3.4(8) @drepl @xbullet @dby @xbullet !corrigendum 3.4(23) @dinsa If a primitive subprogram of the parent type is visible at the place of the @fa, then the corresponding inherited subprogram is implicitly declared immediately after the @fa. Otherwise, the inherited subprogram is implicitly declared later or not at all, as explained in 7.3.1. @dinst If a type declaration names an interface type in an @fa, then the declared type inherits any user-defined primitive subprograms of the interface type in the same way. !corrigendum 3.4(35) @dinsa @xindent<@s9<17 If the reserved word @b is given in the declaration of a type, the type is abstract (see 3.9.3).>> @dinst @xindent<@s9<18 An interface type which has an interface ancestor "is derived from" that type, and therefore is a derived type. A @fa, however, never defines an interface type.>> !corrigendum 3.4.1(02) @drepl A derived type is @i its parent type @i; it is derived @i from any type from which its parent type is derived. The derivation class of types for a type @i (also called the class @i at @i) is the set consisting of @i (the @i of the class) and all types derived from @i (directly or indirectly) plus any associated universal or class-wide types (defined below). @dby A derived type is @i its parent type @i; it is derived @i from any type from which its parent type is derived. A derived type or interface type is also derived from each of its interface ancestor types, if any. The derivation class of types for a type @i (also called the class @i at @i) is the set consisting of @i (the @i of the class) and all types derived from @i (directly or indirectly) plus any associated universal or class-wide types (defined below). !corrigendum 3.4.1(10) @drepl A specific type @i is defined to be a @i of a type @i if @i is the same as @i, or if @i is derived (directly or indirectly) from @i. A class-wide type @i'Class is defined to be a descendant of type @i if @i is a descendant of @i. Similarly, the universal types are defined to be descendants of the root types of their classes. If a type @i is a descendant of a type @i, then @i is called an @i of @i. The @i of a type is the ancestor of the type that is not a descendant of any other type. @dby A specific type @i is defined to be a @i of a type @i if @i is the same as @i, or if @i is derived (directly or indirectly) from @i. A class-wide type @i'Class is defined to be a descendant of type @i if @i is a descendant of @i. Similarly, the universal types are defined to be descendants of the root types of their classes. If a type @i is a descendant of a type @i, then @i is called an @i of @i. An @i of a type is an ancestor of that type that is not itself a descendant of any other type. Every untagged type has a unique ultimate ancestor. !corrigendum 3.7(1) @drepl A composite type (other than an array type) can have discriminants, which parameterize the type. A @fa specifies the discriminants of a composite type. A discriminant of an object is a component of the object, and is either of a discrete type or an access type. An @fa in the declaration of a partial view of a type specifies that the discriminants of the type are unknown for the given view; all subtypes of such a partial view are indefinite subtypes. @dby A composite type (other than an array or interface type) can have discriminants, which parameterize the type. A @fa specifies the discriminants of a composite type. A discriminant of an object is a component of the object, and is either of a discrete type or an access type. An @fa in the declaration of a partial view of a type specifies that the discriminants of the type are unknown for the given view; all subtypes of such a partial view are indefinite subtypes. !corrigendum 3.07(8) @drepl A @fa is only permitted in a declaration for a composite type that is not an array type (this includes generic formal types). A type declared with a @fa is called a @i type, as is a type that inherits (known) discriminants. @dby A @fa is only permitted in a declaration for a composite type that is not an array or interface type (this includes generic formal types). A type declared with a @fa is called a @i type, as is a type that inherits (known) discriminants. !corrigendum 3.9.3(04) @drepl For a derived type, if the parent or ancestor type has an abstract primitive subprogram, or a primitive function with a controlling result, then: @dby If a type inherits a subprogram corresponding to an abstract subprogram or to a function with a controlling result, then: !corrigendum 3.9.3(05) @drepl @xbullet @dby @xbullet !corrigendum 3.9.4(1) @dinsc 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. @i<@s8> @xcode<@fa@ft<@b>@fa<] >@ft<@b>@fa< [interface_list]>> @i<@s8> An interface type (also called an @i) is a specific abstract tagged type that is defined by an @fa. An interface type has no components. @i<@s8> All user-defined primitive subprograms of an interface type shall be abstract subprograms or null procedures. The type of a subtype named in an @fa shall be an interface type. If a type declaration names an interface type in an @fa, then the accessibility level of the declared type shall not be statically deeper than that of the interface type; also, the declared type shall not be declared in a generic body if the interface type is declared outside that body. A descendant of an interface type shall be limited if and only if the interface type is limited. A full view shall be a descendant of an interface type if and only if the corresponding partial view (if any) is also a descendant of the interface type. 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. @i<@s8> The elaboration of an @fa has no effect. !corrigendum 4.5.2(3) @drepl The @i of a membership test is the type of the @fa or the type determined by the @fa. If the tested type is tagged, then the @fa shall resolve to be of a type that covers or is covered by the tested type; if untagged, the expected type for the @fa is the tested type. @dby The @i of a membership test is the type of the @fa or the type determined by the @fa. If the tested type is tagged, then the @fa shall resolve to be of a type that is convertible (see 4.6) to the tested type; if untagged, the expected type for the @fa is the tested type. !corrigendum 4.6(8) @drepl If the target type is a numeric type, then the operand type shall be a numeric type. @dby In a view conversion for an untagged type, the target type shall be convertible (back) to the operand type. !corrigendum 4.6(9) @ddel If the target type is an array type, then the operand type shall be an array type. Further: !corrigendum 4.6(10) @ddel @xbullet !corrigendum 4.6(11/1) @ddel @xbullet !corrigendum 4.6(12/1) @ddel @xbullet !corrigendum 4.6(12.1/1) @ddel @xbullet !corrigendum 4.6(13) @ddel If the target type is a general access type, then the operand type shall be an access-to-object type. Further: !corrigendum 4.6(14) @ddel @xbullet !corrigendum 4.6(15) @ddel @xbullet !corrigendum 4.6(16) @ddel @xbullet !corrigendum 4.6(17) @ddel @xbullet !corrigendum 4.6(18) @ddel If the target type is an access-to-subprogram type, then the operand type shall be an access-to-subprogram type. Further: !corrigendum 4.6(19) @ddel @xbullet !corrigendum 4.6(20) @ddel @xbullet !corrigendum 4.6(21) @drepl If the target type is not included in any of the above four cases, there shall be a type that is an ancestor of both the target type and the operand type. Further, if the target type is tagged, then either: @dby If there is a type that is an ancestor of both the target type and the operand type, or both types are class-wide types, then at least one of the following rules shall apply: @xbullet !corrigendum 4.6(23) @drepl @xbullet @dby @xbullet @xbullet !corrigendum 4.6(24) @drepl In a view conversion for an untagged type, the target type shall be convertible (back) to the operand type. @dby If there is no type that is the ancestor of both the target type and the operand type, and they are not both class-wide types, one of the following rules shall apply: @xbullet @xbullet @xinbull @xinbull @xinbull @xinbull @xbullet @xinbull @xinbull @xinbull @xinbull @xbullet @xinbull @xinbull !corrigendum 7.3(03) @drepl @xcode<@fa@ft<@b>@fa< defining_identifier [discriminant_part] >@ft<@b>@fa< [>@ft<@b>@fa<] >@ft<@b>@fa< ancestor_subtype_indication >@ft<@b>@fa<;>> @dby @xcode<@fa@ft<@b>@fa< defining_identifier [discriminant_part] >@ft<@b>@fa< [>@ft<@b>@fa<] >@ft<@b>@fa< ancestor_subtype_indication [>@ft<@b>@fa< interface_list] >@ft<@b>@fa<;>> !corrigendum 8.3(12) @dinsa @xbullet @dinss @xbullet @xinbull