Version 1.6 of ais/ai-00251.txt

Unformatted version of ais/ai-00251.txt version 1.6
Other versions for file ais/ai-00251.txt

!standard 03.04 (02)          02-02-05 AI95-00251/03
!standard 03.09.01 (02)
!class amendment 00-12-04
!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 "abstract interface" types ("interface" for short) to the standard. A tagged type may "implement" one or more such interfaces. The classwide type associated with the interface "covers" all types that implement it. Dispatching calls through the primitives of the abstract interface type dispatch to code bodies associated with specific tagged types that implement the interface.
!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 specs -- the interface type has no data components and no operation implementations.
A type may "implement" multiple interfaces, but 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. Finally, interfacing with Java and the new ".net" infrastructure from Microsoft would be significantly smoother if this concept were supported in a natural way in Ada.
!proposal
The following syntactic changes are proposed to support abstract interface types:
type_definition ::= ... | abstract_interface_definition
abstract_interface_definition ::= abstract [limited] interface
derived_type_definition ::= [abstract] new parent_subtype_indication [and abstract_interface_list] [record_extension_part]
abstract_interface_list ::= abstract_interface_subtype_mark {and abstract_interface_subtype_mark}
private_extension_declaration ::= ... [abstract] new ancestor_subtype_indication [and abstract_interface_list] with private;
formal_type_definition ::= ... | formal_abstract_interface_definition
formal_abstract_interface_definition ::= abstract_interface_definition
formal_derived_type_definition ::= [abstract] new ancestor_subtype_indication [and abstract_interface_list] [with private];
NOTE: we plan to make "INTERFACE" an unreserved keyword, to minimize upward incompatibility. The current term "reserved word" will probably become something like "keyword", which can be either "reserved" or "unreserved." Most new users need not be aware of the distinction, and will probably treat all keywords as reserved.
An abstract interface type (or "interface" for short) is defined by an abstract_interface_definition or by a derived_type_definition where the word ABSTRACT appears, the parent type is an interface, and there is no record_extension_part. A formal abstract interface type is defined by a formal_abstract_interface_definition, or a formal_derived_type_definition with the word ABSTRACT but without the words "WITH PRIVATE." An abstract interface type may not have discriminants (though tagged types that implement it may of course have them).
All primitive operations of an abstract interface type must be declared abstract or "is null" (see below). Only abstract interface types may be mentioned in an abstract_interface_list.
A tagged type may declare that it "implements" an interface by mentioning it after the reserved word "NEW" or "AND". Note that a tagged type defined by a derived_type_definition must include a record_extension_part.
If an interface mentions other abstract interface types in its definition (after the reserved word NEW or AND), then any type that implements this new interface must also implement these other interfaces. The new interface is also said to "implement" the other interfaces that it mentions. An interface "implements" itself as well. Finally, a tagged type "implements" all of its ancestor tagged types, including itself.
Similarly, a private extension or formal derived type "implements" all of the types mentioned after the word NEW or AND.
Note that we allow an abstract interface type as the parent type of a record extension or a private extension to simplify the syntax, and to allow an abstract interface type and an abstract tagged type to be used in a very similar fashion. This allows one to switch from an abstract tagged type to an abstract interface type during maintenance without significant disruption.
If the reserved word LIMITED appears in an abstract_interface_definition, the interface is a limited interface, and assignment and equality are not available on its classwide type; otherwise it is a nonlimited interface. A nonlimited type may implement a limited interface, but not vice-versa. A derived type is limited if and only if its parent type is limited.
A formal derived type without the words "WITH PRIVATE" but with the word "ABSTRACT" is called a "formal derived interface".
In a generic instantiation, for a formal derived type, the actual must implement all of the types that the formal implements. Furthermore, if the formal is not abstract, then the actual must not be abstract (and hence not an interface). Finally, if the formal is a formal derived interface, then the actual must be an abstract interface type.
If the actual type is an interface, the formal must be an interface or a formal derived type with no non-interface ancestors. The formal may not be an abstract private tagged type, as this may create difficulties for shared generic implementation. This restriction does to some extent run counter to the goal of simplifying changing an abstract tagged type into being an abstract interface type during maintenance.
In a package with a private extension declaration in its visible part, the full type must be a record extension that implements all of the types implemented by the private extension.
A private extension or private tagged type must implement all the interfaces implemented by its full view. In other words, the full view may not implement additional "private" interfaces. This is to prevent the descendants of the partial view from unintentionally "stepping" on the implementations of the primitive operations of a privately inherited interface.
A type that implements (directly or indirectly) an interface inherits the interface's (abstract) primitive operations with the usual substitution of the new type for the abstract interface type in their profile. If a type inherits multiple primitive subprograms that are homographs of one another, they must be subtype conformant with one another. Also, a non-abstract inherited subprogram overrides any abstract ones, and if they are all abstract, they must be fully conformant with one another or be overridden [so the formal parameter names and defaults are well-defined]. If a type is non-abstract, and it inherits any abstract primitives that are not overridden by inherited non-abstract ones, it also must (as usual) override them.
The 'Class attribute is defined for abstract interface types. The classwide type associated with an interface "covers" all types that implement it, and their associated classwide types. Conversions are permitted between (the classwide type of) any descendant of a type that implements an interface and the classwide type of the interface. Converting to a covered type generally requires a tag check. A classwide type associated with an interface is considered a non-abstract, tagged, indefinite type, and matches generic formals in the same way as a classwide type of a "normal" tagged type.
Membership tests are permitted where the operand's type covers the tested type. The 'Tag attribute is defined for abstract interface types, and uniquely identifies the interface from any other type or interface, except that for an interface declared within a task body or subprogram, the same value for 'Tag may be used for all elaborations of the declaration.
In an extension aggregate, the name of the parent type may be used prior to the word "WITH" even if it is an interface. If it is an interface, there are necessarily no components to be default initialized. This facilitates moving from an abstract tagged type to an abstract interface type. Alternatively, a normal record aggregate may be used, taking advantage of the fact that the parent interface has no components.
"Null" Primitives
basic_declaration ::= ... | null_subprogram_declaration
null_subprogram_declaration ::= subprogram_specification is null;
As part of this proposal we include an ability to declare a primitive procedure using "is null" rather than "is abstract." This would be permitted for interfaces, as well as other types. The semantics of this would be that the body of the procedure is defined to be "is begin null; end;".
If an interface has a null (i.e. non-abstract) primitive, then an abstract generic formal extension of the interface is only matched by types that have non-abstract implementations of this primitive. This allows non-dispatching calls on the primitive of the generic formal type to be made inside the generic, which may be necessary to enable a pass-the-buck-to-the-parent paradigm when overriding the operation. This is the paradigm used for finalization, for example, and is quite common in type hierarchies in general.
Upon inheritance, a non-null inherited primitive overrides any null or abstract inherited primitives. If one or more null primitives are inherited, and no non-null, non-abstract primitives, then the null primitives must all be fully conforming (so they have the same formal parameter names and defaults), and the resulting inherited primitive is null. And of course, a non-dispatching call is permitted on a null, as opposed to abstract, primitive of an interface (which is really only useful in generics).
Note that only dispatching calls are permitted on the non-null primitives of an interface, since they are necessarily abstract.
!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 interfaces, or from an interface and the parent type. If only the parent type can have a non-abstract, non-null primitive, no question arises as to which implementation to use. Languages that support full multiple inheritance need elaborate rules for resolving these kinds of conflicts.
No data components are allowed in interfaces to avoid implementation complexity and inefficiency. If data components could be inherited from multiple interfaces, or from an interface and the parent type, 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.
The rules are structured to ensure that an existing abstract tagged type can be changed to being an abstract interface type with minimal disruption to the existing code. The intent is that essentially anywhere an abstract tagged private (or null record) type is permitted, an interface is similarly permitted. Of course, there are additional places where only interfaces are allowed, but hopefully none where abstract private/null-record types are permitted, but interfaces are not.
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:
package P is type T1 is tagged private; ... private type T1 is new Foo and I with ... procedure prim_of_I(X : T1); end P;
... type T2 is new P1.T1 and I with ... procedure prim_of_I(X : T2); -- if operations of I are overridden for T2 (like prim_of_I), they -- will override operations associated with T1's "private" -- inheritance of I, violating the "privateness" of the -- inheritance.
In the above case, the partial view of T1 would need to be:
type T1 is new I with private;
This would make it visible that I is inherited, and thereby making the primitives of "I" as fair game for overriding outside P.
"Null" Primitives Rationale
The ability to declare a primitive of an interface as "is null" enables existing abstract types that have null (non-abstract) default implementations of some of their primitives to be transitioned to being abstract interface types. An example of such a type is Ada.Finalization.Controlled. [Note that there are other considerations which might argue for leaving Controlled as a "normal" tagged type, but the capability of switching other existing abstract tagged types to be interfaces remains potential quite useful, in our view.]
The ability to have non-dispatching calls on the primitive of a generic formal interface type to be made inside the generic can be necessary to enable a pass-the-buck-to-the-parent paradigm when overriding the operation. This is the paradigm used for finalization, for example, and is quite common in type hierarchies in general.
For example:
type T is abstract interface; procedure Prim1(X : T) is null; procedure Prim2(X : T) is abstract;
generic type NT is abstract new T with private; -- NT guaranteed to have non-abstract implementation of Prim1 package P is type NN is new NT with private; procedure Prim1(X : NN); -- may "pass-the-buck" to Prim1 of NT as part -- of implementation of Prim1 of NN. procedure Prim2(X : NN); -- Prim2 must be overridden; -- cannot call Prim2 of NT since might be abstract end P;
Being able to declare "is null" in the spec of a primitive procedure has other advantages. For example, for code in a generic which always "passes the buck" to the corresponding parent primitive (as is done for Finalize, for example), the compiler can eliminate such a call when the parent's operation is known null at instantiation time. There are also obvious documentation advantages for someone extending a type to know that the default implementation of an operation is null. These advantages exist independently of this proposal for interfaces.
Possible Implementation Model
A possible implementation model for an interface "view" of an object is to use a pair of pointers, one to an interface-specific dispatch table, 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.
If a given type (including potentially an interface) implements one or more interfaces, appropriate dispatching tables for these other interfaces should be efficiently accessible via the given type's dispatch table (as well as the dispatch table of any descendant of the type). A suggested model is to have in the dispatch table a pointer to an array of interfaces implemented by the type (where an interface is represented by a pointer to its dispatch table). This interface array would be organized so that a given interface is at a compile-time known offset in the array, analogous to how an individual dispatch table of primitive subprograms is laid out.
This interface array allows an efficient view conversion from the given type to one of its interfaces, by fetching the desired interface (dispatch table) from the compile-time known slot in the interface array. Descendants of a type would add further interfaces to the end of this array of interfaces, so the offsets within the array for earlier interfaces remain the same. This model also applies if we define a "descendant" of an interface to be any interface or tagged type that names the interface immediately after the word "NEW" in its declaration (as opposed to naming it after an "AND").
Converting from an interface view to a non-interface view that implements it can be performed using an approach commonly used in Ada 95 to convert to a descendant tagged type, by checking a table of ancestors to see if the target type appears at the appropriate level in the table. On the other hand, converting from one interface view to another one that implements it will generally require more overhead, both for the view conversion and the associated run-time check. Essentially the same two approaches would be involved in a membership test.
One mechanism to support interface-to-interface view conversion is to do a run-time search through a directory (perhaps a hash table) of all implemented interfaces, indexed by a unique identifier of the interface (such as the value returned by 'Tag), and containing a reference to the corresponding dispatch table for the interface. Note that this directory is not the same thing as the "interface array" mentioned above, because the unique identifier of the associated interface is not included in that array. Furthermore, multiple interfaces can share the same slot in an interface array, but each needs their own entry in this directory. If the target interface is found in this directory, then the conversion would pick up the corresponding interface-specific dispatch table. If the target interface is not found, then the conversion would raise Constraint_Error.
If it is desired to share these directories between types that implement the same set of interfaces and have the same interface array layout, this directory could contain the slot number of the interface within the interface array, rather than a direct pointer to the dispatch table for the interface.
"Thin Pointer" Implementation Model
It is still possible to implement references to interfaces using single-word pointers, but there is additional overhead when a call is to be performed. Essentially the same data structures suggested above can be used, but the directory of interfaces is searched to find the dispatch table before dispatching. The result of this search can be saved and reused, so that if there are multiple calls using the same interface reference, the directory search need only be performed once. In fact, one could use "fat" pointers for some or all parameter passing, and "thin" pointers for access-to-interfaces, and thereby only incur the expense when dereferencing an access value rather than upon individual calls. This combination of fat/thin is similar to the approach used in many compilers for access-to-unconstrained-arrays, where the access values are thin but two words are used for parameter passing.
Shared Generics
For implementations that share code between generic instantiations, we need to ensure that the addition of interfaces to the languages does not impose distributed overhead on programs that make no use of interfaces. If there are cases where an actual type can be an interface when the formal is not, clearly a shared generic implementation will choose to represent the formal as a "normal" tagged type. In particular an access-to-formal would be "thin." What are the implications of this? If there is a formal access type with the formal as the designated type, if the actual is not also "thin" there would be significant overhead. This implies that either we disallow an interface "masquerading" as a "normal" tagged type, or use thin pointers for all access-to-interface types.
An alternative is to use fat pointers for "normal" tagged types that could match the same formal as that matched by an interface. For example, in the rules proposed above, an interface does not match an abstract private type, but does match an abstract formal extension that has only interfaces specified after "NEW." This would mean that any normal tagged type that implements one or more interfaces would also have to use fat pointers, even though both words would point to the same dispatch table.
!example
type Stack is abstract interface; -- An abstract interface type procedure Append (S : in out Stack; E : Elem) is abstract; function Length (S : Stack) return Natural is abstract; procedure Remove_Last (S : in out Stack; E : out Element) is abstract;
type Queue is abstract interface; -- Another interface. function Length (Q : Queue) return Natural is abstract; procedure Append (Q : in out Queue; E : Elem) is abstract; procedure Remove_First (Q : in out Queue; E : out Element) is abstract;
. . . type Deque is abstract new Queue and Stack; -- An interface which inherits from both -- the Queue and Stack interfaces. procedure Prepend (Q : in out Deque; E : Elem) is abstract; procedure Remove_Last (Q : in out Deque; E : out Elem) is abstract;
. . . type My_Deque is new Deque with private; -- A private extension that implements an interface private type My_Deque is new Blob and Deque with record -- Conventional type implementing an interface. . . . end record;
!ACATS test
!appendix

!from Tucker Taft
!date Saturday, November 25, 2000 1:51 PM

Gary Dismukes wrote:
>
> > Here is a first cut on the Multiple (interface) Inheritance AI.
> > Comments welcome!
> > -Tuck
> (0-line AI included :-)
>
> Tuck, looks okay for a first cut but could use a little more detail ;-)
>
> -- Gary

Oops.  Here it is for real... (now that I have built up the suspense ;-)
-Tuck
----------
!standard 03.04    (02)                               00-11-21  AI95-xxx/01
!standard 03.09.01 (02)
!class amendment 00-11-21
!priority High
!difficulty Hard
!subject Tagged Types, Abstract Interface, Multiple Inheritance

!summary

This amendment AI proposes that "abstract interface" types may be
defined, and that a tagged type may "implement" one or more
such abstract interfaces.  The classwide type associated with
the abstract interface "covers" all types that implement it.
Dispatching calls through the primitives of the abstract interface
type dispatch to code bodies associated with specific tagged types that
implement the interface.

!question


!recommendation


Here are the proposed syntactic changes to support abstract interfaces:

   type_definition ::= ... | abstract_interface_definition

   abstract_interface_definition ::= ABSTRACT [LIMITED]

   derived_type_definition ::=
     [ABSTRACT] NEW parent_subtype_indication
       [AND abstract_interface_list]
       [record_extension_part]

   abstract_interface_list ::=
     absract_interface_subtype_mark {AND abstract_interface_subtype_mark}

   private_extension_declaration ::=
     ... [ABSTRACT] NEW ancestor_subtype_indication
       [AND abstract_interface_list] WITH PRIVATE;

   formal_type_definition ::= ... | formal_abstract_interface_definition

   formal_abstract_interface_definition ::=
       abstract_interface_definition

   formal_derived_type_definition ::=
     [ABSTRACT] NEW ancestor_subtype_indication
       [AND abstract_interface_list] [WITH PRIVATE];

An abstract interface type (or "abstract interface" for short)
is defined by an abstract_interface_definition or by a
derived_type_definition where the word ABSTRACT appears, the
parent type is an abstract interface, and there is no
record_extension_part.
All primitive operations of an abstract interface type must be declared
abstract (or perhaps "is null"?).  Only abstract interface types
may be mentioned in an abstract_interface_list.

A tagged type may declare that it "implements" an abstract
interface by mentioning it after the reserved word "NEW" or "AND".
Note that a tagged type defined by a derived_type_definition must
include a
record_extension_part.

If an abstract interface mentions other abstract interface types
in its definition (after the reserved word NEW or AND), then any
type that implements this new interface must also implement these
other abstract interfaces.  The new abstract interface is also said to
"implement" the other interfaces that it mentions.  An abstract
interface "implements" itself as well.  Finally, a tagged type
"implements" all of its ancestor tagged types, including itself.

Note that we allow an abstract interface type
as the parent type of a record extension or a private extension
to simplify the syntax, and to allow an abstract interface type and
an abstract tagged type to be used in a very similar fashion.
This allows one to switch from an abstract tagged type to an
abstract interface type during maintenance without significant
disruption.  [One possible goal would be for us to change
Ada.Finalization.[Limited_]Controlled into abstract interface
types.  This might require us to define an alternative to "is abstract'
such as "procedure ... is null" whereby an abstract interface
could establish a "null" default for an operation, since that is
what Controlled provides fopr all of its operations.]

If the reserved word LIMITED appears in an abstract_interface_definition,
the abstract interface is an abstract limited interface, and assignment
and equality are not available on its classwide type; otherwise
it is an abstract nonlimited interface.  A nonlimited type may implement
an abstract limited interface, but not vice-versa.  A derived type is
limited if and only if its parent type is limited.

A type that implements (directly or indirectly) an abstract interface inherits
the interface's (abstract) primitive operations with the usual substitution
of the new type for the abstract interface type in their profile.
If a type inherits multiple primitive subprograms that are
homographs of one another, they must be subtype conformant with one
another.  Also, a non-abstract inherited subprogram overrides any
abstract ones, and if they are all abstract, they must be fully conformant
with one another or be overridden [so the formal parameter names and
defaults are well-defined].  If a type is non-abstract,
and it inherits any abstract primitives that are not overridden
by inherited non-abstract ones, it also must (as usual) override them.

The 'Class attribute is defined for abstract interface types.
The classwide type associated with an abstract interface "covers"
all types that implement it, and their associated classwide types.
Conversions are permitted between (the classwide type of) any
descendant of a type that implements an abstract interface and
the classwide type of the abstract interface.  Converting to a
covered type generally requires a tag check.

Membership tests are permitted where the operand's type covers the
tested type.

Note that only dispatching calls are permitted on the primitives of
an abstract interface, since they are all abstract.

A tagged type matches a generic formal private extension so long as it
implements all the types mentioned in the formal_derived_type_definition.
Similarly, if the record extension that completes a private extension
declaration must implement all the types mentioned
in the private extension declaration.

An abstract interface matches a formal derived type without the words
"WITH PRIVATE" so long as the word ABSTRACT appears and
it implements all the abstract interfaces mentioned in the
formal_derived_type_definition.

        Implementation Model

A possible implementation model for an abstract interface reference
is to use a pair of pointers, one to an interface-specific dispatch
table,
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 abstract interface
were declared, while its content would be determined by the
particular type implementing the interface.

If a given type (including potentially an abstract interface) implements one
or more abstract interfaces, appropriate dispatching tables for these
other interfaces must be pointed-to from the given type's dispatch
table, at
known offsets.  This allows a conversion from the given type to
one of these interfaces to be performed efficiently, by
fetching the desired interface-specific pointer from the appropriate
slot
in the given type's dispatch table.

Converting from an abstract interface to a non-interface
type that implements it can be performed using the same approach used
in Ada 95 to convert to a descendant tagged type, by checking a table
of ancestors to see if the target type appears at the appropriate level
in the table.  On the other hand, converting from one abstract interface
to another one that implements it will generally require more overhead,
both for the conversion and the associated run-time check (essentially
the same logic would be involved in a membership test).

One mechanism to support interfact-to-interface conversion is for every
tagged type to have in its dispatch table a pointer to an array of all
the interfaces it implements, as well as a pointer to a parallel array
with
the corresponding interface-specific dispatch tables.  (This pair of
parallel arrays could of course be combined into a single array
of pairs.)  To convert from one interface to another, one must
be sure that the type implements the target interface.
This can be done by scanning down this array.  If the target
interface is found, then the conversion would pick up the corresponding
interface-specific dispatch table from the parallel array to
form the two-word interface reference.

Note that this same array of interface-specific dispatch tables can
be used to support the conversion from a tagged classwide type
to an interface it is known to implement, by ensuring the array is
in the same order in all descendants of a given tagged type, and
it is only added to on the end as more interfaces are implemented
by lower-down descendants.  This conversion would not require
any searching, since the relevant offsets would be known
at compile-time by suitable ordering of the arrays.

****************************************************************

From: Tucker Taft
Sent: Monday, December 04, 2000 9:44 AM
Subject: Multiple inheritance bug bites ESA

For whatever reason, it sounds like ESA now believes multiple
inheritance is essential in some situations.  Groan.
Perhaps the multiple inheritance AI needs a slightly higher profile ;-)
-Tuck
--------------------
From team-ada@ACM.ORG Mon Dec  4 08:33 EST 2000
MIME-Version: 1.0
Date:         Mon, 4 Dec 2000 15:31:46 +0200
From: Soeren Henssel <Soeren.Henssel-Rasmussen@NOKIA.COM>
Subject:      ESA now also "prefer" C++ instead of Ada on some projects

European Space Agency has designed a prototype for component-based software
framework for a satellite Attitude and Orbit Control System (AOCS). Their
home page is at =>
http://www.softwareresearch.net/AocsFrameworkProject/ProjectHomePage.html
It has been programmed in C++ in preference to Ada 95 - read inter alia
http://www.softwareresearch.net/AocsFrameworkProject/DesignPrinciples.html
paragraph "Language Compatibility" for the reasons behind the decission. The
main reason is lack of multiple inheritance in Ada 95.

****************************************************************

From: Randy Brukardt
Sent: Monday, December 04, 2000 10:31 PM

While editing Tucker's proposal, I was struck by the following:

> Note that we allow an abstract interface type
> as the parent type of a record extension or a private extension
> to simplify the syntax, and to allow an abstract interface type and
> an abstract tagged type to be used in a very similar fashion.
> This allows one to switch from an abstract tagged type to an
> abstract interface type during maintenance without significant
> disruption.  [One possible goal would be for us to change
> Ada.Finalization.[Limited_]Controlled into abstract interface
> types.  This might require us to define an alternative to "is abstract'
> such as "procedure ... is null" whereby an abstract interface
> could establish a "null" default for an operation, since that is
> what Controlled provides fopr all of its operations.]

This "goal" seems to me to be a good way to kill off this proposal. The effect
of this would be to require a significant change in the way that finalization is
implemented. For instance, if the implementation literally uses a list of
Ada.Finalization.Controlled'Class to implement this, the pointer size would
change (assuming the use of the implementation model given in the AI).
Similarly, the implementation would need to handle any additional data needed
for finalization by some compiler magic, rather than by inheritance as is
currently done (assuming that abstract interface types may not have components).

I note that the proposal never seems to state that abstract interface types must
not define any components. Certainly, the implementation model and the entire
discussion seem to assume that is the case. (An "normal" abstract type can have
components defined, they'll be part of any extension; but I don't think we want
that here.)

****************************************************************

From: Tucker Taft
Sent: Wednesday, December 20, 2000 5:51 PM

Randy Brukardt wrote:

> While editing Tucker's proposal, I was struck by the following:
> ... [One possible goal would be for us to change
> > Ada.Finalization.[Limited_]Controlled into abstract interface
> > types.  This might require us to define an alternative to "is abstract'
> > such as "procedure ... is null" whereby an abstract interface
> > could establish a "null" default for an operation, since that is
> > what Controlled provides fopr all of its operations.]
>
> This "goal" seems to me to be a good way to kill off this proposal. The
> effect of this would be to require a significant change in the way that
> finalization is implemented. For instance, if the implementation literally
> uses a list of Ada.Finalization.Controlled'Class to implement this, the
> pointer size would change (assuming the use of the implementation model
> given in the AI). Similarly, the implementation would need to handle any
> additional data needed for finalization by some compiler magic, rather than
> by inheritance as is currently done (assuming that abstract interface types
> may not have components).

I realized this was a possibility, but I wasn't sure what
current implementation strategies actually are.  Comments
from vendors about specific implementation problems
with this possible change would certainly be of interest.

In any case, I certainly hope that the
decision whether to use abstract interfaces for
Ada.Finalization.Controlled can be viewed as a completely separate
issue.  I would suspect that there are other similar situations,
even if Finalization turns out not to work for other reasons, where
one would want to move from abstract tagged type to abstract
interface or vice-versa during maintenance or enhancement.
That is one of the main advantages of the
proposed syntax, and is why I changed the suggested syntax
from what I presented in Baltimore.

> I note that the proposal never seems to state that abstract interface types
> must not define any components. Certainly, the implementation model and the
> entire discussion seem to assume that is the case. (An "normal" abstract
> type can have components defined, they'll be part of any extension; but I
> don't think we want that here.)

I improperly presumed the reader knew what was meant by an
abstract "interface" type.  It definitely doesn't have any
data components.  That is one of the things that significantly simplifies
multiple inheritance of interfaces.

I also notice that an example is missing.  I will try to provide
one over the next couple of days.

****************************************************************

Questions? Ask the ACAA Technical Agent