Version 1.35 of ais/ai-00251.txt

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

!standard 03.04 (02)          05-09-20 AI95-00251/23
!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 (02)
!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 a progenitor or ancestor, and this representation item conflicts with the representation of some other progenitor or 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<interface>
!corrigendum 3.2(4)
Replace the paragraph:
The composite types are the record types, record extensions, array types, task types, and protected types. A private type or private extension represents a partial view (see 7.3) of a type, providing support for data abstraction. A partial view is a composite type.
by:
The composite types are the record types, record extensions, array types, interface types, task types, and protected types. A private type or private extension 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)
Replace the paragraph:
type_definition ::= enumeration_type_definition | integer_type_definition | real_type_definition | array_type_definition | record_type_definition | access_type_definition | derived_type_definition
by:
type_definition ::= enumeration_type_definition | integer_type_definition | real_type_definition | array_type_definition | record_type_definition | access_type_definition | derived_type_definition | interface_type_definition
!corrigendum 3.4(2)
Replace the paragraph:
derived_type_definition ::= [abstract] new parent_subtype_indication [record_extension_part]
by:
interface_list ::= interface_subtype_mark {and interface_subtype_mark}
derived_type_definition ::= [abstract] new parent_subtype_indication [[and interface_list] record_extension_part]
!corrigendum 3.4(3)
Replace the paragraph:
The parent_subtype_indication defines the parent subtype; its type is the parent type.
by:
The parent_subtype_indication 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)
Replace the paragraph:
by:
!corrigendum 3.4(23)
Insert after the paragraph:
If a primitive subprogram of the parent type is visible at the place of the derived_type_definition, then the corresponding inherited subprogram is implicitly declared immediately after the derived_type_definition. Otherwise, the inherited subprogram is implicitly declared later or not at all, as explained in 7.3.1.
the new paragraph:
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.
!corrigendum 3.4(35)
Insert after the paragraph:
17 If the reserved word abstract is given in the declaration of a type, the type is abstract (see 3.9.3).
the new paragraph:
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.
!corrigendum 3.4.1(02)
Replace the paragraph:
A derived type is derived from its parent type directly; it is derived indirectly from any type from which its parent type is derived. The derivation class of types for a type T (also called the class rooted at T) is the set consisting of T (the root type of the class) and all types derived from T (directly or indirectly) plus any associated universal or class-wide types (defined below).
by:
A derived type is derived from its parent type directly; it is derived indirectly 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 T (also called the class rooted at T) is the set consisting of T (the root type of the class) and all types derived from T (directly or indirectly) plus any associated universal or class-wide types (defined below).
!corrigendum 3.4.1(10)
Replace the paragraph:
A specific type T2 is defined to be a descendant of a type T1 if T2 is the same as T1, or if T2 is derived (directly or indirectly) from T1. A class-wide type T2'Class is defined to be a descendant of type T1 if T2 is a descendant of T1. Similarly, the universal types are defined to be descendants of the root types of their classes. If a type T2 is a descendant of a type T1, then T1 is called an ancestor of T2. The ultimate ancestor of a type is the ancestor of the type that is not a descendant of any other type.
by:
A specific type T2 is defined to be a descendant of a type T1 if T2 is the same as T1, or if T2 is derived (directly or indirectly) from T1. A class-wide type T2'Class is defined to be a descendant of type T1 if T2 is a descendant of T1. Similarly, the universal types are defined to be descendants of the root types of their classes. If a type T2 is a descendant of a type T1, then T1 is called an ancestor of T2. 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.
!corrigendum 3.7(1)
Replace the paragraph:
A composite type (other than an array type) can have discriminants, which parameterize the type. A known_discriminant_part 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 unknown_discriminant_part 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.
by:
A composite type (other than an array or interface type) can have discriminants, which parameterize the type. A known_discriminant_part 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 unknown_discriminant_part 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)
Replace the paragraph:
A discriminant_part 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 known_discriminant_part is called a discriminated type, as is a type that inherits (known) discriminants.
by:
A discriminant_part 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 known_discriminant_part is called a discriminated type, as is a type that inherits (known) discriminants.
!corrigendum 3.9.3(04)
Replace the paragraph:
For a derived type, if the parent or ancestor type has an abstract primitive subprogram, or a primitive function with a controlling result, then:
by:
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)
Replace the paragraph:
by:
!corrigendum 3.9.4(1)
Insert new clause:
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.
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.
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.
Dynamic Semantics
The elaboration of an interface_type_definition has no effect.
!corrigendum 4.5.2(3)
Replace the paragraph:
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 covers or is covered by the tested type; if untagged, the expected type for the simple_expression is the tested type.
by:
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.
!corrigendum 4.6(8)
Replace the paragraph:
If the target type is a numeric type, then the operand type shall be a numeric type.
by:
In a view conversion for an untagged type, the target type shall be convertible (back) to the operand type.
!corrigendum 4.6(9)
Delete the paragraph:
If the target type is an array type, then the operand type shall be an array type. Further:
!corrigendum 4.6(10)
Delete the paragraph:
!corrigendum 4.6(11/1)
Delete the paragraph:
!corrigendum 4.6(12/1)
Delete the paragraph:
!corrigendum 4.6(12.1/1)
Delete the paragraph:
!corrigendum 4.6(13)
Delete the paragraph:
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)
Delete the paragraph:
!corrigendum 4.6(15)
Delete the paragraph:
!corrigendum 4.6(16)
Delete the paragraph:
!corrigendum 4.6(17)
Delete the paragraph:
!corrigendum 4.6(18)
Delete the paragraph:
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)
Delete the paragraph:
!corrigendum 4.6(20)
Delete the paragraph:
!corrigendum 4.6(21)
Replace the paragraph:
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:
by:
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:
!corrigendum 4.6(23)
Replace the paragraph:
by:
!corrigendum 4.6(24)
Replace the paragraph:
In a view conversion for an untagged type, the target type shall be convertible (back) to the operand type.
by:
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:
!corrigendum 7.3(02)
Replace the paragraph:
private_extension_declaration ::= type defining_identifier [discriminant_part] is [abstract] new ancestor_subtype_indication with private;
by:
private_extension_declaration ::= type defining_identifier [discriminant_part] is [abstract] new ancestor_subtype_indication [and interface_list] with private;
!corrigendum 8.3(12)
Insert after the paragraph:
the new paragraphs:
!corrigendum 12.5(03)
Replace the paragraph:
formal_type_definition ::= formal_private_type_definition | formal_derived_type_definition | formal_discrete_type_definition | formal_signed_integer_type_definition | formal_modular_type_definition | formal_floating_point_definition | formal_ordinary_fixed_point_definition | formal_decimal_fixed_point_definition | formal_array_type_definition | formal_access_type_definition
by:
formal_type_definition ::= formal_private_type_definition | formal_derived_type_definition | formal_discrete_type_definition | formal_signed_integer_type_definition | formal_modular_type_definition | formal_floating_point_definition | formal_ordinary_fixed_point_definition | formal_decimal_fixed_point_definition | formal_array_type_definition | formal_access_type_definition | formal_interface_type_definition
!corrigendum 12.5.1(03)
Replace the paragraph:
formal_derived_type_definition ::= [abstract] new subtype_mark [with private]
by:
formal_derived_type_definition ::= [abstract] new subtype_mark [[and interface_list] with private]
!corrigendum 12.5.1(05)
Insert after the paragraph:
The ancestor subtype of a formal derived type is the subtype denoted by the subtype_mark of the formal_derived_type_definition. For a formal derived type declaration, the reserved words with private shall appear if and only if the ancestor type is a tagged type; in this case the formal derived type is a private extension of the ancestor type and the ancestor shall not be a class-wide type. Similarly, the optional reserved word abstract shall appear only if the ancestor type is a tagged type.
the new paragraph:
The actual type for a generic formal derived type shall be a descendant of every ancestor of the formal type.
!corrigendum 12.5.5(01)
Insert new clause:
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.
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.
!corrigendum 13.1(13)
Insert after the paragraph:
A representation or operational item that is not supported by the implementation is illegal, or raises an exception at run time.
the new paragraph:
A type_declaration is illegal if it has one or more progenitors, and a representation item applies to a progenitor or ancestor, and this representation item conflicts with the representation of some other progenitor or ancestor. The cases that cause conflicts are implementation defined.
!corrigendum 13.13.2(9/1)
Replace the paragraph:
For elementary types, the representation in terms of stream elements is implementation defined. For composite types, the Write or Read attribute for each component is called in canonical order, which is last dimension varying fastest for an array, and positional aggregate order for a record. Bounds are not included in the stream if T is an array type. If T is a discriminated type, discriminants are included only if they have defaults. If T is a tagged type, the tag is not included. For type extensions, the Write or Read attribute for the parent type is called, followed by the Write or Read attribute of each component of the extension part, in canonical order. For a limited type extension, if the attribute of any ancestor type of T has been directly specified and the attribute of any ancestor type of the type of any of the extension components which are of a limited type has not been specified, the attribute of T shall be directly specified.
by:
For elementary types, the representation in terms of stream elements is implementation defined. For composite types, the Write or Read attribute for each component is called in canonical order, which is last dimension varying fastest for an array, and positional aggregate order for a record. Bounds are not included in the stream if T is an array type. If T is a discriminated type, discriminants are included only if they have defaults. If T is a tagged type, the tag is not included. For type extensions, the Write or Read attribute for the parent type is called, followed by the Write or Read attribute of each component of the extension part, in canonical order. 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.
!corrigendum 13.13.2(27)
Insert after the paragraph:
S'Output then calls S'Write to write the value of Item to the stream. S'Input then creates an object (with the bounds or discriminants, if any, taken from the stream), initializes it with S'Read, and returns the value of the object.
the new paragraph:
If T is an abstract type, then S'Input is an abstract function.
!corrigendum 13.13.2(36/1)
!comment This is mostly a conflict-creator.
@drepl The stream-oriented attributes may be specified for any type via an @fa<attribute_definition_clause>. All nonlimited types have default implementations for these operations. An @fa<attribute_reference> for one of these attributes is illegal if the type is limited, unless the attribute has been specified by an @fa<attribute_definition_clause> or (for a type extension) the attribute has been specified for an ancestor type. For an @fa<attribute_definition_clause> specifying one of these attributes, the subtype of the Item parameter shall be the base subtype if scalar, and the first subtype otherwise. The same rule applies to the result of the Input function. @dby The stream-oriented attributes may be specified for any type via an @fa<attribute_definition_clause>. The subprogram name given in such a clause shall not denote an abstract subprogram. Furthermore, if a stream-oriented attribute is specified for an interface type by an @fa<attribute_definition_clause>, the subprogram name given in the clause shall statically denote a null procedure.
!corrigendum 13.14(7)
Insert after the paragraph:
the new paragraph:
!ACATS test
(Many) ACATS Tests should be constructed to test this feature.
!appendix

!topic Type conversions of derived types
!reference RM-4.6(8-23), 7.3.1
!date 1999-05-14
!from Bob Duff
!keywords type conversion, derived type
!discussion

Question:

Is it always legal to convert between two untagged types if they have a
common ancestor?  Is it always legal to convert between two tagged types
if they have a common ancestor, and obey the additional restrictions of
4.6(22-23)?

The answers should be "Yes", but a literal reading of the RM implies
"No" in some cases.

Example:

    package Type_Conversion is
        type Root is private;
    private
        type Root is access all Integer;
    end Type_Conversion;

    package Type_Conversion.Son is
        type Son_Type is new Root;
    end Type_Conversion.Son;

    with Type_Conversion.Son; use Type_Conversion.Son;
    package Type_Conversion.Daughter is

        type Daughter_Type is new Root;
        Son_Var: Son_Type;
        Daughter_Var: Daughter_Type;

        A: Daughter_Type := Daughter_Type(Son_Var); -- Legal.
        B: Son_Type := Son_Type(Daughter_Var);

    private

        -- Here, Daughter_Type becomes a general access type.

        C: Daughter_Type := Daughter_Type(Son_Var); -- Illegal?!
        D: Son_Type := Son_Type(Daughter_Var);

    end Type_Conversion.Daughter;

In the declaration of C, the target type is a general access type (by
7.3.1) so 4.6(13) applies.  The source type is not an access-to-object
type (when viewed from that place), so it's illegal.  4.6(21) does not
apply, because the target type *is* included in one of the "above four
cases".

That's clearly not the intent of the language designers.

Reasons:

First of all, I think it would be incompatible with Ada 83 (although I'm
not sure -- I don't have my Ada 83 RM at hand, and I've largely
forgotten that language ;-)).  Certainly no such incompatibility was
intended; no such incompatibility is listed under the "Incompatibilities
with Ada 83" heading in the AARM.

Second, consider the declaration of A.  It is clearly legal.  Why should
moving it into the private part make it illegal?  Normally moving into a
private part or body *increases* the things you're allowed to do.

Third, if we remove "all" from the full declaration of Root, then the
declaration of C is clearly legal, because then 4.6(21) applies; it
would be very strange for the "all" to have this effect.

Fourth, the intent is that 4.6(21-23) should always be applicable, and
that's the only sensible rule; this problem is merely an accident of
wording.

Note that the same wording problem arises for the special cases
mentioned in 4.6(8,9,18) -- numerics, arrays, and, access-to-subprogram.
I hesitate to suggest wording changes, but here's a timid attempt: Move
4.6(21-23) earlier, before all the special cases, and make it clear that
the special cases only apply when the "common ancestor" case doesn't.

Current behavior of some compilers: As of yesterday, the Averstar front
end finds it illegal, but I've changed it to make it legal, in
anticipation of the expected ARG ruling.  The Rational compiler finds it
legal.  The GNAT compiler (version 3.11p) doesn't complain about the
declaration of C, but complains on the declaration of D that Son_Type is
not visible.  If I remove D, GNAT is happy, so that's apparently a bug
not directly related to this issue.

Note: I tripped over this problem in real code I was modifying (in our
run-time system); I changed an "access" to "access all", and some type
conversions suddenly became illegal (according to our compiler).  The
comments in the compiler sent me to 4.6(13), and I was surprised to find
that it was doing exactly what the RM literally says.

- Bob

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

!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 class-wide 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.

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

From: Steve Baird
Sent: Thursday, May 16, 2002  2:38 PM

This is an initial proposal for wording for AI-00251 and some
related issues (null procedures and unreserved keywords).
    -- Steve

----------------------------------------------------------------
----------------------------------------------------------------

Null procedures:

    3.1

    Add
        basic_declaration ::= ... | null_procedure_declaration

    --------

   6.1

   Replace
       subprogram_specification ::= PROCEDURE defining_program_unit_name
                                              parameter_profile
   with
       procedure_specification ::= PROCEDURE defining_program_unit_name
                                             parameter_profile
       subprogram_specification ::= procedure_specification

   --------

   6.7  Null Procedures

                                  Syntax

        null_procedure_declaration ::= procedure_specification IS NULL;

                                  Static Semantics

        A null_procedure_declaration declares a null procedure.

                                  Dynamic Semantics

        The execution of a null procedure is invoked by a subprogram call.
        This execution has no effect.

   --------

Discussion:
   1) Like an instantiation, a null procedure is not allowed as a
completion.
      Allowing this would double the amount of RM text needed.
   2) A null procedure may have OUT-mode parameters, the same as a
      conventional "begin null; end;" procedure.
   3) Change Ada.Finalization spec to declare Initialize/Finalize/Adjust
      as null procedures?

----------------------------------------------------------------
----------------------------------------------------------------

Unreserved keywords (see AI-284):

    2.2(1)
      Replace "a reserved word," with "a keyword,"

    2.3(4)
      Replace
         An identifier shall not be a reserved word.
      with
         An identifier shall not be a reserved keyword.

    Replace 2.9 with:

        2.9 Keywords

                                  Syntax
        The following are the reserved keywords (ignoring upper/lower case
        distinctions):
            ABORT ... XOR [same list as before]

        The following are the unreserved keywords:
            INTERFACE

    Throughout the RM:
        Replace "reserved word" with "keyword" (e.g. 3.9.3(2)).

----------------------------------------------------------------
----------------------------------------------------------------

Abstract Interface Types

    3.2.1

    Add
        type_definition ::= ... | abstract_interface_definition

    --------

    3.4(2)

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

    --------

    3.4

    Add after paragraph 23:
        A type which implements an abstract interface (see 3.9.4) inherits
        subprograms from the interface type, in the same way as for a type
        derived from the interface type.

    --------

    3.9.1

    Add between sentences 3 and 4 of paragraph 3:
         The accessibilty level (see 3.10.2) of a type which implements an
         abstract interface (see 3.9.4) shall not be statically deeper than
         that of the interface type.

    Add after sentence 1 of paragraph 4:
         An abstract interface shall not be
         implemented in a generic body if the interface type is declared
         outside that body.

    --------

    3.9.3

     Add after paragraph 6:

         Corresponding rules apply for a type which inherits an abstract
         subprogram by implementing an abstract interface (see 3.9.4, 3.4).

    --------

    3.9.4 Abstract Interface Types

        An abstract interface type is an abstract tagged type intended for use
        in providing a restricted form of multiple inheritance.
        A tagged type may "implement" multiple interfaces, thereby allowing
        multiple views of objects of the type.

                                 Syntax

        abstract_interface_definition ::= ABSTRACT [LIMITED] INTERFACE

        abstract_interface_list ::=
            abstract_interface_subtype_mark
            {AND abstract_interface_subtype_mark}

                                 Legality Rules

        An abstract interface type (also called an "interface type" or
        "interface") is a specific abstract tagged type that is defined by an
        abstract_interface_definition, or by a derived_type_definition or
        formal_derived_type_definition where the keyword ABSTRACT
        appears and the parent type is an interface type.

        An interface type shall have no components.

        All primitive operations of an interface type shall be abstract
        subprograms or null procedures.

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

        A descendant of a type which names an interface type in an
        abstract_interface_list, or which is derived from an interface type,
        is said to "implement" the interface and all of its ancestors.
        A class-wide type implements the interfaces implemented by the
        corresponding specific type. The corresponding full view of a
        partial view of a type implements all interfaces implemented by
        the partial view.

        A type which implements an interface shall be limited if and only
        if the interface type is limited.

        If a partial view defines an interface type, then the corresponding
        full view shall define an interface type.

        If a full view implements an interface, then the corresponding
        partial view (if any) shall implement the interface.

        For an interface type declared in a visible part, a primitive
        subprogram shall not be declared in the private part.

        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.

    --------

    4.5.2

    In paragraph 3, replace:

        If the tested type is tagged, then the simple_expression shall
resolve
        to be of a type that covers or is covered by the tested type;

    with

        If the tested type is tagged, then the simple_expression shall
resolve
        to be of a type that covers or is covered by the tested type, or of
a
        class-wide type which covers an abstract interface type that is
        implemented by the tested type.

    --------

    4.6

    Add after paragraph 20:

        If the target type is tagged and there exists no type that is an
        ancestor of both the target type and the operand type, then either:

            The target type shall cover or be an ancestor of an abstract
            interface type which is implemented by the operand type; or

            The operand type shall be a class-wide type that covers an
abstract interface type which is
            implemented by the target type.


    In paragraph 21, replace "four" with "five".

    --------

    7.3

    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 abstract_interface_list] WITH PRIVATE];

    --------

    8.3

    Add after paragraph 12:

        An implicit declaration of a subprogram which is neither abstract
nor
        a null procedure overrides that of a subprogram simultaneously
        inherited by implementing an abstract interface.

        If one subprogram overrides another and a type simultaneously
inherits
        subprograms corresponding to both, then the inherited subprogram
        corresponding to the overriding subprogram overrides the other
        inherited subprogram.

        If an inherited subprogram corresponds to another subprogram and a
        type simultaneously inherits subprograms corresponding to both, then
        the subprogram corresponding to the inherited subprogram overrides
the
        other inherited subprogram.

    Add after paragraph 23:

        If two non-overridden homographs are simultaneously inherited as
        primitive operations of a tagged type which implements an abstract
        interface, then they shall be fully conformant and either both shall
        be abstract subprograms or both shall be null procedures.

    --------

    12.5.1

    Replace
        formal_derived_type_definition ::= [ABSTRACT] NEW subtype_mark
                                           [WITH PRIVATE]
    with
        formal_derived_type_definition ::= [ABSTRACT] NEW subtype_mark
                                           [[AND abstract_interface_list]
                                           WITH PRIVATE]


    Add after paragraph 18:

        If the formal type is an abstract interface type, then the actual
type
        shall be an abstract interface type.

        The actual type shall implement any interfaces that are implemented
by
        the formal type.

    --------

    13.14

    Add after paragraph 7:

        The implementation of an abstract interface causes freezing of the
        abstract interface type.

    --------

Discussion:

   1) The "no components for interface types" rule does not conflict with
the
      "every tagged object has a tag" rule.
      13.5.1(15) might be interpreted to mean that a tag is an
      "implementation-defined component" of a tagged type, but apparently an
      "implementation-defined component" is not a "component" (see, for
      example, the definition of "needed components" for record aggregates).

   2) An abstract interface type is an abstract tagged type; this point was
      unclear in the AI.
      The class-wide type associated with an interface type is not an
      interface type.

   3) It is intended that explicitly specifying that a type implements an
      interface which it already implements should have no semantic effect.

   4) A non-interface type may be derived from an interface type.

   5) An abstract non-interface type cannot be directly derived from an
      interface type. The syntax given in the AI for doing this
      would violate the rule that an extension part is given as part of
      a derivation if and only if the parent type is tagged.

      I feel that preserving this rule is important. I don't want to take a
      clean, simple rule which users encounter frequently and amend
      it with an obscure exception.

      If it is felt that direct derivation of an abstract non-interface type
      from an an interface is an important capability that must be
supported,
      then alternative syntax should be considered. Even an interminable
      string of keywords like

          type T is abstract new Some_Interface_Type
                    and not interface with null record;

      would be preferable to the AI's proposal; perhaps the
      "and not Interface" part could be omitted in the case where
      a non-empty list of components is given.

   6) It is intended that if two homographs are inherited as primitive
      operations of a tagged type, then they can share one slot in the
      dispatch table.

   7) No formal abstract interface types. Use formal abstract tagged private
      (or formal derived) instead. A type declared in the generic package
spec
      can't implement a formal tagged private, but that's ok. The situation
      today with deriving a non-abstract type from a formal abstract
      type is already quite ugly (the constract model does not include any
way
      to specify the set of abstract operations that must be overridden, so
      that check is deferred until the instantiation) - we don't want to
shine
      a spotlight on this corner of the language.

   8) The rule that only a tagged type may implement interfaces is not
      stated explicitly; it is implicit in the syntax.

   9) There are interactions between this AI and AI-279. In particular, it
is
      ok for Some_Abstract_Interface_Type'Class'Input to read in the tag of
a
      type which implements the interface but is not derived from it. The
rule
      given in AI-279 requires that Constraint_Error be raised in this case.
      This check should be relaxed.

   10) A primitive subprogram is inherited or it is not; it does not matter
if
       there is more than one reason for its inheritance - only one
inherited
       subprogram results.
       Does this need to be stated explicitly?

   11) The term "implement" is defined differently than in the AI. An
interface
       does not implicitly implement itself (because then it would inherit
       operators from itself).

   12) Nothing here (in particular, nothing in the 8.3 stuff) is intended to
       change the semantics of a program which declares no interface types.

   13) An example to illustrate some of the 8.3 stuff:

           package P1 is
               type T1 is abstract interface;
               procedure P (X : T1) is abstract; -- P'1
           end P1;

           package P2 is
               type T2 is abstract new P1.T1; -- P'2 (implicit)
               procedure P (X : T2) is abstract; -- P'3
           end P2;

           type D is new Some_Tagged_Type and P1.T1 and P2.T2 with null
record;

       D inherits procedures P'4, P'5, and P'6, corresponding to
       P'1, P'2, and P'3, respectively.

       Since P'3 overides P'2, P'6 overrides P'5 by the "overriding is
       preserved by inheritance" rule.

       Since P'2 corresponds to P'1,  P'5 overrides P'4 by the "inherited
       copies of your parent's ops hide inherited copies of your
grandparent's
       ops" rule.

       The word "simultaneous" is used in contradistinction to the word
       "previous" in 8.3(12). The word "corresponds" is used as
       "corresponding" is used in 3.4(17).


   14) Is this section of the proposed 8.3 addition,

           If one subprogram overrides another and a type simultaneously
           inherits subprograms corresponding to both, then the inherited
           subprogram corresponding to the overriding subprogram overrides
           the other inherited subprogram.

       , necessary, or can it be derived from existing language rules?

   15) Allowing implementation of a limited interface by a non-limited
       type might be feasible, but it would introduce some complications:

           A (dispatching) caller of a primitive function of a
           limited interface type returning that type would not know
           statically whether the (non-abstract) callee has a
           return-by-reference result type. For replicated-generic
           implementations, this would be something new.

           The caller of such a function would need to
           finalize the function result if and only if the function call
           introduces an anonymous object (see 7.6.1(13/1))); this
           would no longer be known statically.

       Strictly from a user's perspective (i.e. ignoring any implementation
       problems), would support for this construct even be desirable?

       The problems associated with disallowing this construct in the
       face of limited views of non-limited types (i.e. a limited
       formal type where the corresponding actual type is non-limited)
       are no worse than for 3.7(1)'s rule prohibiting access discriminants
       for non-limited types.

   16) Does the list of legality rules which need to be checked in
       the private part of an instance (see 12.3(11), AARM 12.3(11.y)) need
       to be extended? (The change to 3.9.1(3) is one such extension;
       the last paragrah of 3.9.4 is another).

   17) Recommended level of support for representation items for
       interface types = "confirmation only" ? Or perhaps no
       representation items at all for interface types - only
       operational items; would this be a legality rule or just the
       recommended level of support?

   18) Should the AI include a sample implementation model for
       My_Interface_Type'Class'Input ?

   19) Are there interactions between interface types and
       unknown discriminants that need to be addressed?
       I don't see any.

   20) Yes, it really is illegal if the parent type of a "with private"
       type's completion happens to implement some interface that the
       private view did not. This may turn out to be a pain.

       Relaxing the rule that a partial view must implement all interfaces
       implemented by the completion is not out of the question, but it
could
       open the door for some very peculiar situations. If this example,

        package P is
          packge Pkg is
              type Ifc is abstract 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 P2_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 P2;

      , were legal (it is illegal because the completion of T1 implements
      an interface that is not implemented by the the partial view), 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.

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

From: Randy Brukardt
Sent: Thursday, May 16, 2002  11:31 PM

> Unreserved keywords (see AI-284):

Are you taking over this AI? It needs a discussion section at least.


>     Throughout the RM:
>         Replace "reserved word" with "keyword" (e.g. 3.9.3(2)).

NO, NO, NO! At the meeting, Bob pointed out that there are 96 occurrences of
this term in the standard. We can't make 94 text changes in the Amendment,
people will reject it as being too large without even seeing what's new.
Besides, it would take me at least 6 hours to do this, and I think I'd
demand extra pay to compensate for the extreme boredom involved. (Each
paragraph would have to be cut from the standard and properly formatted.)
Plus the tools would need to be updated to handle many, many changes from a
single AI.

At the very least, you have to enumerate every place that needs to be
changed. That would be necessary to help ensure that I don't miss any.

The minutes say "Tucker suggests the age-old solution of defining "reserved
word" to be equivalent to "reserved keyword". In the more general case, we
simply say "keyword".

I'm not adverse to making this change in paragraphs that we are changing
anyway (there are already five such uses, four of which are new). But,
please, lets stick with what we decided. We simply don't have the budget to
spend on unnecessary massaging.

...
>     --------
>
>     3.9.3
>
>      Add after paragraph 6:
>
>          Corresponding rules apply for a type which inherits an abstract
>          subprogram by implementing an abstract interface (see 3.9.4,
3.4).

Oh-oh! Do we have to define "corresponding" here? :-) :-)

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

From: Steve Baird
Sent: Friday, May 17, 2002   2:25 PM

> Are you taking over this AI?

No (or at least I didn't think that I was).

I just wanted to provide enough context so that I
could talk about the keyword INTERFACE in the
AI-00251 writeup.

> The minutes say "Tucker suggests the age-old solution of
> defining "reserved word" to be equivalent to
> "reserved keyword". In the more general case, we
> simply say "keyword".

Fine with me.

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

From: Tucker Taft
Sent: Friday, May 17, 2002  10:52 AM

Looks good.  I would suggest we define "implement" more generally,
so that a derived type "implements" its parent type.  Then we can
define "cover", etc. in terms of "implement" without any special cases.

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

From: Steve Baird
Sent: Monday, May 20, 2002   1:32 PM

This sounds like a good approach.

This could solve a problem with the initial proposal,
where explicit conversion is required in cases where
we probably want to allow implicit conversion.

Given this example

    type Ifc is abstract interface;
    type T is new Some_Tagged_Type and Ifc with null record;

    type Ifc_Ref is access all Ifc'Class;

    X : aliased T;
    Ptr1 : Ifc_Ref := Ifc'Class (X)'Access; -- legal
    Ptr2 : Ifc_Ref := X'Access; -- legal ?

, I believe that the initial proposal would  allow the
first use of the Access attribute, but not the second.

I think that we want to allow the second use.

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

From: Tucker Taft
Sent: Monday, May 20, 2002  1:40 PM

Yes, I agree that Ifc'Class should "cover" T and its derivatives.

I believe that the current AI implies that, but that your
proposed wording does not.

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

From: Steve Baird
Sent: Wednesday, June 5, 2002   3:49 PM

Tuck says:
> I would suggest we define "implement" more generally,
> so that a derived type "implements" its parent type.  Then we can
> define "cover", etc. in terms of "implement" without any special cases.

I agree that if type T implements some interface type Ifc, then we want to
define "cover" so that Ifc'Class covers T.

I'm not so sure about changing the definition of "implement".

It would be nice to use a generalized form of "implementation" for
determining operator inheritance - a type inherits operators from the types
that it implements, and from no others.

This rule would imply that a derived type inherits operators not only from
its parent, but from all of its parent's ancestors.

Typically this extra inheritance would make no difference (all the extra
inherited operators would be overridden), but this would be an incompatible
change if an ancestor has primitive subprograms that the parent lacks (this
can only happen in the untagged case):

    package Pkg is
      type T1 is null record;
      type T2 is new T1;
      procedure P (X : T1);
      type T3 is new T2; -- T3 should not inherit a P procedure
    end Pkg;

With that in mind, here is a revision of my previous attempt at wording
for AI-251.

  -- Steve

----------------------------------------------------------------
----------------------------------------------------------------

    3.4.1(9)

    Add after the first sentence:
      If a type T1 implements an abstract interface type T2 (see 3.9.4),
      then T2'Class also covers all types covered by T1'Class.

    --------

    4.5.2: Ignore the changes I proposed earlier; revert to the current RM
text.

    --------

    4.6:

    Ignore the changes I proposed earlier.

    Replace paragraphs 21-23 with:

      If the target type is tagged, then either:

        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.

      If the target type is not included in any of the above five cases,
      there shall be a type that is an ancestor of both the target type and the
      operand type.

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

From: Steve Baird
Sent: Thursday, June 13, 2002   3:52 PM

I intended that the "implements" relation be transitive, but I didn't word
the definition correctly.

In this example,

     type Ifc1 is abstract interface;

     type Ifc2 is abstract interface;

     type Ifc3 is abstract new Ifc1 and Ifc2 with null record;

     type T1 is tagged null record;

     type T2 is new T1 and Ifc3 with null record;

, T2 implements Ifc3 and Ifc3 implements Ifc2, so therefore T2 should
implement Ifc2.

I believe that the proposed wording should be fixed to handle this case.

In the new 3.9.4 section, replace
        A descendant of a type which names an interface type in an
        abstract_interface_list, or which is derived from an interface type,
        is said to "implement" the interface and all of its ancestors.
with
        A descendant of a type which names an interface type in an
        abstract_interface_list, or which is derived from an interface type,
        is said to "implement" the interface and any other interfaces
        implemented by that interface.


Comments?

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

From: Steve Baird
Sent: Wednesday, September 25, 2002   1:12 PM

This is my attempt at incorporating the comments of the Vienna meeting
into AI-251.
Changes include:
   1) "abstract interface" => "interface" in both terminology and syntax.
   2) A derived type is never an interface type, as described in the
      Vienna meeting minutes. To get the effect of derivation, declare
      a second interface type which implements the first one.
   3) The notion of implicit declarations being declared "simultaneously"
      is replaced with declarations occurring implicitly "at the same point".
   4) Formal interface types are added.
   5) A different way of expressing the notion that if a routine inherits
      two null homographs or two abstract homographs, then there is
      really no ambiguity.
   6) Expand the definition of "cover" so that Some_Interface_Type'Class
      covers all types which implement the interface.
   7) Define the "implements" relation to be transitive.

I'm not at all sure that these are all improvements, but I believe/hope that
they reflect the consensus of the group.
See also the discussion points at the end.

  -- Steve

----------------


Interface Types
    3.2.1

    Add
        type_definition ::= ... | interface_type_definition
    --------
    3.4

    Add after paragraph 23:
        A type which implements an interface type (see 3.9.4) inherits
        subprograms from the interface type, in the same way as for a type
        derived from the interface type.
    --------
    3.4.1(9)

    Add after the first sentence:
        If a type T1 implements an interface type T2 (see 3.9.4),
        then T2'Class also covers all types covered by T1'Class.

    --------

    3.9.1

    In paragraph 2, replace
        record_extension_part ::= WITH record_definition
    with
        record_extension_part ::= WITH [interface_list AND] record_definition
    .

    Add between sentences 3 and 4 of paragraph 3:
         The accessibilty level (see 3.10.2) of a type which implements an
         interface type (see 3.9.4) shall not be statically deeper than
         that of the interface type.

    Add after sentence 1 of paragraph 4:
         An interface type shall not be
         implemented in a generic body if the interface type is declared
         outside that body.
    --------

    3.9.3

     Add after paragraph 6:

         Corresponding rules apply for a type which inherits an abstract
         subprogram by implementing an interface type (see 3.9.4, 3.4).

    --------
    3.9.4 Interface Types
        An interface type is an abstract tagged type intended for use
        in providing a restricted form of multiple inheritance.
        A tagged type may "implement" multiple interfaces, thereby allowing
        multiple views of objects of the type.
                                 Syntax
        interface_type_definition ::= [LIMITED] INTERFACE [WITH interface_list]
        interface_list ::= interface_subtype_mark {AND interface_subtype_mark}
                                 Legality Rules
        An interface type (also called an "interface") is a specific abstract
        tagged type that is defined by an interface_type_definition.

        An interface type shall have no components.

        All primitive operations 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.

        A descendant of a type which names an interface type in an
        interface_list, or which is derived from an interface type,
        is said to "implement" the interface and any other interfaces
        implemented by that interface.
        A class-wide type implements the interfaces implemented by the
        corresponding specific type. The corresponding full view of a
        partial view of a type implements all interfaces implemented by
        the partial view.

        A type which implements an interface shall be limited if and only
        if the interface type is limited.

        If a partial view defines an interface type, then the corresponding
        full view shall define an interface type.

        If a full view implements an interface, then the corresponding
        partial view (if any) shall implement the interface.

        For an interface type declared in a visible part, a primitive
        subprogram shall not be declared in the private part.

        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.

   --------

    4.6

    Replace paragraphs 21-23 with:

      If the target type is tagged, then either:
        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.

      If the target type is not included in any of the above five cases,
      there shall be a type that is an ancestor of both the target type and the
      operand type.

    --------

    7.3

    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
          WITH [interface_list AND] PRIVATE;

    --------
    8.3

    Add after paragraph 12:
        An implicit declaration of an inherited subprogram which is neither
        abstract nor a null procedure overrides that of a subprogram
        inherited by implementing an interface which is implicitly
        declared at the same point.

        If one subprogram overrides another and a type inherits subprograms
        corresponding to both which are implicitly declared at the same point,
        then the inherited subprogram corresponding to the overriding
        subprogram overrides the other inherited subprogram.

        If an inherited subprogram corresponds to another subprogram and a
        type inherits subprograms corresponding to both which are implicitly
        declared at the same point, then the subprogram corresponding to the
        inherited subprogram overrides the other inherited subprogram.

        If a type which implements an interface inherits two or more
        fully conformant abstract subprograms, or two or more fully
        conformant null subprograms, which are implicitly declared
        at the same point, and if the preceding rules would not specify
        any overriding relationships among these subprograms, then
        the implementation shall select one of these subprograms and it
        shall override the others.

    --------

    12.5

    Add
        formal_type_declaration ::= ... | formal_interface_type_definition
    --------

    12.5.1

    Replace
        formal_derived_type_definition ::= [ABSTRACT] NEW subtype_mark
                                           [WITH PRIVATE]
    with
        formal_derived_type_definition ::= [ABSTRACT] NEW subtype_mark
                                           [WITH [interface_list AND] PRIVATE]


    Add after paragraph 18:

        The actual type shall implement any interfaces that are implemented by
        the formal type.

    --------
    12.5.5 Formal Interface Types

    The class determined for a formal interface type is the class of all
    interface types.


                                 Syntax
        formal_interface_type_definition ::= interface_type_definition

                                 Legality Rules
    The actual type shall implement any interfaces that are implemented by
    the formal type.
    The actual type shall be limited if and only if the formal type is limited.

    --------

    13.14

    Add after paragraph 7:

        The implementation of an interface causes freezing of the
        interface type.

    --------

Discussion:
   1) A derived type is never an interface type. One can derive from an
      interface type, but the result is not an interface. Instead, define
      a new interface type which implements the "parent" interface.
      Similarly, a classwide type is never an interface type.

   2) We try to avoid having one section of the manual override another.
      Is the definition
          An interface type (also called an "interface") is a specific abstract
          tagged type that is defined by an interface_type_definition.
      "bad" in that sense? See 3.9.2(2) (the definition of a tagged type)
      and 3.9.3(2) (the definition of an abstract type).

   3) It is intended that in the case where the implementation
      arbitrarily chooses one overrider from among group of inherited
      subprograms, users should be unable to determine which
      member was chosen.

      In particular, users should be unable to do this by means of the
      Access (or Unchecked_Access) attribute.

      In order to accomplish this, and for other reasons, 4.5.2(13)'s rules
      about access-to-subprogram equality need to be loosened further to allow
      implementation-dependent results in the case of comparing access values
      which denote distinct null subprograms.

      That change is outside of the scope of this AI; it belongs in
      the AI in which null procedures are defined.

      Note that, formally speaking, the Address attribute poses no such
      problems. Although the requirements of the marketplace may be much
      more restrictive, the language itself allows an implementation
      tremendous freedom with respect to Address attribute values
      (see AI-173, 13.7.2(5)).

   4) 12.5.5 refers to "the class of all interface types". Is there a
      problem with having a class which is not closed with respect to
      derivation? As noted above, a derived type is never an interface type.

   5) There is no need to explicitly state that an interface type
      shall not implement itself (just as circular derivations are
      disallowed). This is a consequence of the rule that a partial
      view of an interface must implement any interfaces which are
      implemented by the full view.

   6) Recommended level of support for representation items for
      interface types = "confirmation only" ? Or perhaps no
      representation items at all for interface types - only
      operational items; would this be a legality rule or just the
      recommended level of support?


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

From: Robert A. Duff
Sent: Thursday, September 26, 2002  6:28 PM

> This is my attempt at incorporating the comments of the Vienna meeting
> into AI-251.
...
> I'm not at all sure that these are all improvements, but I believe/hope that
> they reflect the consensus of the group.

Well, it might be useful to hear your objections...

Nitpick: You use "which" where I would use "that" in most cases.
(All except after ",".)

You use "point" where the RM uses "place".

> Discussion:
>
>    1) A derived type is never an interface type. One can derive from an
>       interface type, but the result is not an interface. Instead, define
>       a new interface type which implements the "parent" interface.
>       Similarly, a classwide type is never an interface type.

But interface'Class is legal, right?

>    2) We try to avoid having one section of the manual override another.
>       Is the definition
>           An interface type (also called an "interface") is a specific
>           abstract tagged type that is defined by an
>           interface_type_definition.
>       "bad" in that sense? See 3.9.2(2) (the definition of a tagged type)
>       and 3.9.3(2) (the definition of an abstract type).

There was a definite attempt in RM95 to put *all* the rules for abstract
things together in one section.  It wouldn't hurt to preserve that.

>    3) It is intended that in the case where the implementation
>       arbitrarily chooses one overrider from among group of inherited
>       subprograms, users should be unable to determine which
>       member was chosen.
>
>       In particular, users should be unable to do this by means of the
>       Access (or Unchecked_Access) attribute.
>
>       In order to accomplish this, and for other reasons, 4.5.2(13)'s rules
>       about access-to-subprogram equality need to be loosened further to allow
>       implementation-dependent results in the case of comparing access values
>       which denote distinct null subprograms.

I never liked that rule, but I guess you're right.

>       That change is outside of the scope of this AI; it belongs in
>       the AI in which null procedures are defined.
>
>       Note that, formally speaking, the Address attribute poses no such
>       problems. Although the requirements of the marketplace may be much
>       more restrictive, the language itself allows an implementation
>       tremendous freedom with respect to Address attribute values
>       (see AI-173, 13.7.2(5)).

Right.

>    4) 12.5.5 refers to "the class of all interface types". Is there a
>       problem with having a class which is not closed with respect to
>       derivation? As noted above, a derived type is never an interface type.

Why is it not closed under derivation?  Oh, I see, a type derived from
an interface is not an interface.  Well, yeah, it's bothersome to call
that a "class".  I don't see an easy way around it.

>    5) There is no need to explicitly state that an interface type
>       shall not implement itself (just as circular derivations are
>       disallowed). This is a consequence of the rule that a partial
>       view of an interface must implement any interfaces which are
>       implemented by the full view.

Could use a NOTE.

>    6) Recommended level of support for representation items for
>       interface types = "confirmation only" ? Or perhaps no
>       representation items at all for interface types - only
>       operational items; would this be a legality rule or just the
>       recommended level of support?

I'm not sure what you're getting at here.  Interfaces have no
components, so what's the "representation" issue.  Sure, I guess
operational items should be allowed.... Is it necessary to say anything?

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

From: Tucker Taft
Sent: Friday, September 27, 2002  4:12 PM

> This is my attempt at incorporating the comments of the Vienna meeting
> into AI-251.
> ...
>     --------
>     3.9.4 Interface Types
>         An interface type is an abstract tagged type intended for use
>         in providing a restricted form of multiple inheritance.
>         A tagged type may "implement" multiple interfaces, thereby allowing
>         multiple views of objects of the type.
>                                  Syntax
>         interface_type_definition ::= [LIMITED] INTERFACE [WITH interface_list]
>         interface_list ::= interface_subtype_mark {AND interface_subtype_mark}
>                                  Legality Rules
>         An interface type (also called an "interface") is a specific abstract
>         tagged type that is defined by an interface_type_definition.

This is not a legality rule.  It is static semantics (or simply a definition).

>
>         An interface type shall have no components.

This is not a legality rule, so "shall have" => "has".
This is the static semantics (since there is no way to specify
components, it is silly to make this a legality/"shall" rule).

>         All primitive operations 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.
>
>         A descendant of a type which names an interface type in an
>         interface_list, or which is derived from an interface type,
>         is said to "implement" the interface and any other interfaces
>         implemented by that interface.
>         A class-wide type implements the interfaces implemented by the
>         corresponding specific type. The corresponding full view of a
>         partial view of a type implements all interfaces implemented by
>         the partial view.

More static semantics (no "shalls" in these).

> ...

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

From: Randy Brukardt
Sent: Friday, September 27, 2002  8:21 PM

Steve Baird wrote:

> This is my attempt at incorporating the comments of the Vienna meeting
> into AI-251.

...

Humm, I don't see any syntax for declaring that an original tagged type
implements an interface. There is only syntax for declaring that on an
extension. That doesn't seem right, why should it be necessary to declare an
extra type in order to say that a new tagged type implements some interface?
It also means that a private type which implements an interface cannot be
completed by a tagged type that is not an extension.

    type Root is tagged Window and record ...;

I would expect a syntax like:
    record_type_definition ::= [[abstract] tagged] [limited] [interface_list
and] record_definition

...

>   In order to accomplish this, and for other reasons, 4.5.2(13)'s rules
>   about access-to-subprogram equality need to be loosened further to allow
>   implementation-dependent results in the case of comparing access values
>   which denote distinct null subprograms.
>
>   That change is outside of the scope of this AI; it belongs in
>   the AI in which null procedures are defined.

What are you talking about? THIS is the AI where null procedures are defined!
The ARG did not decide to split those out of this AI. We would do that only
if there was a substantial chance that this AI would not be adopted while
null procedures would be.

---

It would also be valuable if the !proposal summary and !discussion sections were
updated to reflect the current proposal. They are getting way out of date,
and I'm not sure if the implementation models, etc. still apply to this
current wording.

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

From: Tucker Taft
Sent: Sunday, September 29, 2002  5:29 PM

You just use the type extension syntax,
but make the parent type be the interface.
This is desirable so that a "normal" abstract tagged
type may be changed into an interface
type without changing any other code.

As with "abstract", a type is not an
interface unless the word "interface" appears
in its definition.  Deriving from an interface
does not make the derived type an interface.
In fact, derived types are never interfaces.
Interfaces are always considered "root" types
that happen to implement other interfaces.

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

From: Randy Brukardt
Sent: Monday, September 29, 2002  6:44 PM

Humm, that would work, but it would be completely inconsistent and unexpected.
Moreover, what you would expect to do has no sensible way to indicate the
correct thing to do. Indeed, the RM would be completely devoid on any
indication of how to do this (as the derivation rules completely follow from
existing language rules, and aren't being modified).

My "mental image" of these things is that they are a new kind of item. If you
want a type to "implement" an interface, you put the interface into an
appropriate interface_list. That is not true in the case of derivation.

Moreover, deriving from an interface gives a incorrect view of the
(single-inheritance) type hierarchy. Such an derivation makes it appear that
the new type is a member of the interface type. That's simply wrong, if the
intent is to create a new hierarchy of types that happens to implement some
interface.

The fact that deriving these give a non-interface type is OK by me, but I view
that is something that exists at all simply to avoid contract model problems
with interfaces. Essentially, deriving one of these has to do SOMETHING, but
its not something that you would ever do in new code. Yes, it's helpful in
converting abstract types to interfaces, but I don't see that as something that
will happen often (for two reasons: "if it ain't broke, don't fix it", and
because most abstract types have concrete primitive operations, simply because
they could).

We'll have to dicuss this at the meeting.

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

From: Tucker Taft
Sent: Monday, September 30, 2002  8:34 PM

I consider it essential that interface types and
abstract tagged types are very nearly interchangeable,
and that you can go back and forth relatively
easily, so long as the interface type is the first
one listed after "new".

The meaning of 'Class, "covers", implements, etc., should
all be such that it makes very little difference in
"type NT is new T with ..."
whether T is an interface, or an abstract tagged type.

At least I see that as an important goal.

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

From: Randy Brukardt
Sent: Monday, September 29, 2002  10:03 PM

> I consider it essential that interface types and
> abstract tagged types are very nearly interchangeable,
> and that you can go back and forth relatively
> easily, so long as the interface type is the first
> one listed after "new".

I don't think this will be possible most of the time, because interfaces allow
multiple inheritance, and abstract types allow concrete operations and
components. Usually one or the other will be a requirement.

> The meaning of 'Class, "covers", implements, etc., should
> all be such that it makes very little difference in
> "type NT is new T with ..."
> whether T is an interface, or an abstract tagged type.

There certainly is value in keeping the proposal simple by reusing the abstract
rules in the usage of these things. And, as I said, I really don't care what
derivation means. What I object to is being required to make some random
interface "more important" than the others by being required to derive from it
when declaring the root of a type hierarchy. Just because some type happens to
implement an interface doesn't make the interface a parent of the type
hierarchy.

Indeed, one could argue that is the problem with Controlled types. (However,
they can't be an interface because they do have components - at least they do
in Janus/Ada. Illustrating that point.)

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

From: Michael F. Yoder
Sent: Thursday, October 24, 2002  9:05 AM

We can get the benefit of Eiffel's renamings, but without awful syntax,
if we allow interface renamings to tweak things in a manner similar to
procedure renamings changing parameter names.  Something like:

   interface T2 renames T1 is
      procedure Name_Collision_On_P (args) renames P;
      function Oops_F_Has_Homonyms (args) return blah renames F;
   end interface;

Then, change the rule so that homonyms from different interfaces are
always illegal. If the user wants to complete two or more homonyms the
same way, she can use renamings-as-bodies to complete those she's forced
to rename.

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

From: Michael F. Yoder
Sent: Thursday, October 24, 2002  9:31 AM

Sorry, I didn't include enough context for this to make sense to those
who didn't happen to be at the last meeting. The context is that a
tagged type is inheriting from two or more interfaces that have
homonyms, so the effect of blending them together isn't clear. There are
two cases:

(1) The homonyms aren't all mode-conformant to one another: one body
can't complete all of them.
(2) The homonyms are all mode-conformant, so completing them all is at
least possible.

There's probably uses for this ability other than avoiding name
collisions, e.g.:

   interface Sunviews_Style_Interface renames X_Style_Interface is...

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

From: Pascal Leroy
Sent: Thursday, October 24, 2002  9:33 AM

I see that you believe that the proposal is not complex enough already ;-)

I mean, this AI is already extremely complicated, both from the language
design standpoint and from the implementation standpoint.  So complex in
fact that I doubt if it will make it into the final amendment.  And even if
it does, speaking with my implementer hat, I have a hard time believing that
we will make the vast investment needed to support it in our products (do I
sound like Robert?).

So I think we should strive to simplify the proposal, not add bells and
whistles that solve cornercase issues.

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

From: Pascal Leroy
Sent: Thursday, October 24, 2002  10:03 AM

I agree that this proposal is way past the complexit threshhold. I don't
know if it will make it into the amendment, but I can be pretty sure
that folks at ACT are not going to give this one any attention regardless.

The danger of course is that if we get piles of proposals of this kind,
then a strong argument can and will be made to leave the whole thing
alone rather than rock the boat with over-complex stuff.

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

From: Michael F. Yoder
Sent: Thursday, October 24, 2002  1:21 PM

>>I see that you believe that the proposal is not complex enough already

I thought the proposal was horridly complex also until I sorted through
what it actually meant. Also, much apparent complexity came from keeping
multiple variations in mind at the same time, only one of which can
actually be used for a final proposal.

I'm not a big OOP fan, but having a halfway-house technology gets the
worst of both worlds: OOP advocates will sniff at it, but you still have
the maintenance costs. If that sounds like a "keeping up with Java"
argument, well, it probably *is* that sort of an argument. I think
exception types fall into the same category.

The one instance where I might disagree with Tucker is that I'd prefer
that the line between the classes having treeish inclusion and the
others (the interfaces) be kept visible, and I would cheerfully buy
simplicity by making it more visible.

Oh, and finally, I don't expect the issue to be a cornercase issue in
practice unless interfaces just aren't used. If it were a cornercase
issue, why would the awful syntax of Eiffel's renaming conventions (for
solving this problem) ever occur? But perhaps Bob Duff can elaborate
about the circumstances in which he saw these things.

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

From: Tucker Taft
Sent: Thursday, October 24, 2002  1:33 PM

I think this is much more important for Eiffel
because they use inheritance for *everything*
(including with/use clauses).  Note that Java
has no renaming capability, and they are succeeding
hansomely.  Also, Eiffel has multiple *implementation*
inheritance, whereas our proposal is a better fit for
Java, which has multiple *interface* inheritance.
In Eiffel, you have *code* that you are inheriting
potentially from two places, and perhaps that means you
more frequently will need to resolve conflicts.  With the proposed
interfaces, no code is being inherited, which might
simplify doing "renaming by editing" ;-).

My bottom line is I really don't think renaming
is important to making this useful, and Java
is my best evidence.  In Ada, we get to use
result type and defaulted parameters to distinguish
as well, whereas in Java, they don't have that
flexibility.  (Using defaulted but ignored parameters
to distinguish things might be a clever hack around
name conflicts, for those who really find them serious.)

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

From: Robert A. Duff
Sent: Thursday, October 24, 2002  3:57 PM

> But perhaps Bob Duff can elaborate
> about the circumstances in which he saw these things.

I now believe that the name-clash problem should not be solved.
(I argued the opposite at the meeting, but Tucker's speech on the
subject convinced me to change my mind.)

I think this interfaces AI and the other AI about type stubs are the two
most important AI's on the table.  People really do choose other
languages over Ada because of these two major issues.  My evidence is of
course anecdotal, as is nearly all evidence in the so-called "science"
of Computer Science.  I have not done market surveys and the like.
Sigh.

I don't much care whether these two features make it into the Amendment,
but I think it's important to the long-term success of Ada that they get
implemented by the major compiler vendors, and in a uniform fashion (the
"uniform" part is what standards are all about!).

It worries me when I see Robert and Pascal threatening to not implement
these.  (I do understand the reasons.  SofCheck doesn't have tons of
funding to do this sort of thing, either, unfortunately.)

The "problem" we're discussing is that if you import two interfaces, and
get a name clash, you can get "stuck".  But the *usual* way to fix the
problem is to go edit the source code of one or both interfaces --
change the names so they don't clash.  The only time that won't work is
when you're importing two libraries, and you can't (or don't want to)
change their source.

The "problem" can only occur if the interfaces proposal is a great
success (widely implemented, and used by many vendors).  So I say:
let's solve that problem when it happens.  I.e. consider any "renaming"
bells and whistles as a totally separate AI from the basic interfaces
proposal.  There is no technical reason to solve the problem *now*.

I say, let's agree on a standard for the interfaces thing, and let's
implement it in the various compilers.  If and when Ada becomes wildly
successful, we can worry about the naming-clash problem.

Other points:

    - The naming clash problem really is a corner case: You have to have
      two independent developers choose the same name, the same number
      and types of parameters, the same result type, AND DIFFERENT
      MODES.

    - Any solution to the "problem" that I can think of is totally
      independent from the basic interfaces proposal -- so there's no
      reason to include it now.  As they say, let's burn that bridge
      when we come to it.

    - The interfaces proposal should be kept as simple as possible, so
      that vendors may find it feasible to implement on limited
      budgets.

    - Java doesn't address the "problem", and does just fine.

    - I don't agree with Mike that OOP advocates will "sniff at" the
      interfaces stuff without a solution to the naming clash problem.

For all these reasons, I am opposed to solving the naming clash problem
as part of this AI.  There's no harm in creating another AI to address
the problem, but *that* AI should be voted in 2015 or so.

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

From: Robert A. Duff
Sent: Thursday, October 24, 2002  4:15 PM

> I thought the proposal was horridly complex also until I sorted through
> what it actually meant. Also, much apparent complexity came from keeping
> multiple variations in mind at the same time, only one of which can
> actually be used for a final proposal.

I agree with that.  The proposal represents non-trivial implementation
effort, but it's not *that* bad.  It always seems worse when you're
weighing 17 possible solutions to the same problem.

But I still say: let's keep it as simple as possible.
(But no simpler.  ;-))

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

From: Michael F. Yoder
Sent: Thursday, October 24, 2002  6:11 PM

>- I don't agree with Mike that OOP advocates will "sniff at" the
>      interfaces stuff without a solution to the naming clash problem.

Just to set the record straight, I didn't say that; this is combining a
phrase into a different context. What I believe is that OOP advocates
will sniff at Ada if it has no multiple-inheritance mechanism that they
choose to regard as real. (There doesn't seem to be much enthusiasm for
the generic mixin mechanism et al., and I don't entirely blame them.)
Java seems to have established interfaces as real, and they don't break
the good properties (e.g. time-boundedness) of the single-inheritance
model, at least not for operations on the treeish classes.

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

From: Robert Dewar
Sent: Thursday, October 24, 2002  7:20 PM

Hmmm, this seems dubious to me. Why do you believe this?

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

From: Robert A. Duff
Sent: Thursday, October 24, 2002  4:15 PM

> Robert A Duff wrote:
>
> >- I don't agree with Mike that OOP advocates will "sniff at" the
> >      interfaces stuff without a solution to the naming clash problem.
> >
> Just to set the record straight, I didn't say that; this is combining a
> phrase into a different context.

Sorry if I misquoted you.

>... What I believe is that OOP advocates
> will sniff at Ada if it has no multiple-inheritance mechanism that they
> choose to regard as real. (There doesn't seem to be much enthusiasm for
> the generic mixin mechanism et al., and I don't entirely blame them.)
> Java seems to have established interfaces as real, and they don't break
> the good properties (e.g. time-boundedness) of the single-inheritance
> model, at least not for operations on the treeish classes.

I agree with that.  So the question is, will the lack of a solution to
the naming clash problem cause folks to scorn our multiple-inheritance
of interfaces solution?  I think not.

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

From: Robert I. Eachus
Sent: Thursday, October 24, 2002  7:50 PM

>I now believe that the name-clash problem should not be solved.
>(I argued the opposite at the meeting, but Tucker's speech on the
>subject convinced me to change my mind.)

I think the problem is actually a bit larger than you think, but I don't
think that it is anything that the ARG, or WG9 can or should solve.
There are many cases where existing languages will see a clash and Ada
won't.  Great.  There is one case where Ada in my opinion should see a
problem and alert the user at compile time.  For example, let's say you
are inheriting from two different container types.  One has an Append
with an in out parameter, the parameter of the other is mode in.  Forget
for a moment all the philosophical and design issues which could result
in choosing one or the other.  There is a design problem here, and the
right thing for the compiler to do is to tell the user, not paper it
over.  If the user concludes that the two interfaces are not compatible?
He is right.  There are ugly workarounds, but they should be ugly.

The other case that hasn't been discussed as much, but has been
mentioned.  It is where you have say two Append procedures, one with a
single parameter and one that adds a position parameter with a default.
 It is possible to inherit from both interfaces, but the only way to
actually call the one parameter version is to rename it.  This is
currently one of the more unexpected ;-) features of Ada when it pops
up. But again there is no reason the ARG should try to "fix" anything.
If through generic instantiation and/or use clauses a call becomes
ambiguous for this reason, the programmer has to do real work to decide
between calls which should go to the one parameter version, and calls
which should use the default.  Shrug.   The compiler can only alert the
programmer to potential problems, it can't read his mind.

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

From: Steve Baird
Sent: Tuesday, January 28, 2003 11:58 AM

This is my attempt at incorporating the comments of the Bedford meeting
into AI-251.

Changes include:
  1) New type declaration syntax.

  2) New derivation model (a type no longer "implements" an interface; it
     really is "derived" from the interface type, introducing a
     restricted notion of multiple parents for a derived type).

  3) New membership test, conversion rules.

  4) New homograph overriding requirements.

See also the discussion section at the end.

The last time I sent mail to the ARG list, it somehow ended up in
HTML format. I hope that problem has been straightened out,
but my apologies in advance if this message ends up similarly mangled.

  -- Steve

----------------



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 a parent type and zero or more interface parent types.



Replace paragraph 8 with:

    Each class of types that includes the parent type or an interface parent
    type also includes the derived type.

Add after paragraph 25:

    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.

    Note: this includes some declarations of non-derived types.


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 parent types, if any.

Replace the last sentence of paragraph 10 with:

    The ultimate ancestor of a type is the ancestor of that type, if any, that
    is not a descendant of of any other type and that is not an interface
    type (see 3.9.4).


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 intended for use
        in providing a restricted form of multiple inheritance.
        A tagged type may be derived from multiple interface types, thereby
        allowing multiple views of objects of the type.

                                 Syntax

        interface_type_definition ::= [LIMITED] INTERFACE [WITH interface_list]

                                 Legality Rules

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

        An interface type shall have no components.

        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.

        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.

        For an interface type declared in a visible part, a primitive
        subprogram shall not be declared in the private part.

        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.

4.5.2  Relational Operators and Membership Tests

                                Name Resolution

        Two types are said to "potentially share descendants" either if one
        covers the other or if both are class-wide and the corresponding
        specific type associated with at least one is an interface type.

        The simple_expression shall resolve to be of a type that potentially
        shares descendants with the tested type.

4.6 Type Conversions

        If the target type is tagged then either:

            - 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 potentially share descendants.

        If the target type is not included in any of the above five cases,
        there shall be a type that is an ancestor of both the target type
        and the operand type.


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
          WITH [interface_list AND] PRIVATE;


8.3 Visibility

    Add after paragraph 12:

        An implicit declaration of an inherited subprogram which is neither
        abstract nor a null procedure overrides that of a subprogram
        inherited from an interface type which is implicitly
        declared at the same point.

        If one subprogram overrides another and a type inherits subprograms
        corresponding to both which are implicitly declared at the same point,
        then the inherited subprogram corresponding to the overriding
        subprogram overrides the other inherited subprogram.

        If an inherited subprogram corresponds to another subprogram and a
        type inherits subprograms corresponding to both which are implicitly
        declared at the same point, then the subprogram corresponding to the
        inherited subprogram overrides the other inherited subprogram.


    Add after paragraph 26:

        If a descendant of an interface type inherits two homographs
        which are implicitly declared at the same point, then both shall
        be overridden.

        Note: this requirement may be impossible to satisfy if the homographs
        are not mode-conformant.

12.5 Formal Types

    Add to syntax

        formal_type_declaration ::= ... | formal_interface_type_definition



12.5.1 Formal Private Types

    Replace
        formal_derived_type_definition ::= [ABSTRACT] NEW subtype_mark
                                           [WITH PRIVATE]
    with
        formal_derived_type_definition ::= [ABSTRACT] NEW subtype_mark
                                           [WITH [interface_list AND] PRIVATE]


    Add after paragraph 15:

        The actual 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
        interface types.

                                 Syntax

        formal_interface_type_definition ::= interface_type_definition


                                 Legality Rules

        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.14 Freezing Rules

    Add after paragraph 7:

        The declaration of a specific descendant of an interface type freezes
        the interface type.

--------

Discussion:

1) The compatibility rules for membership testing could be tightened to
   filter out more cases where the result of a membership test could not
   possibly be True. The two types could be required to agree with respect to
   limitedness, accessibility level, and/or set of enclosing generic bodies
   (with a recheck generic spec rule as well). Do we want all this in a
   name resolution rule? I'd say no.

2) "Potentially share descendants" seems like an awkward term.
    How about "weakly compatible"? "Potentially intersecting"?
    "Potentially convertible"? The idea here is that we want to allow any
    membership tests where the answer is not known statically. This includes
    a case like

       type T1 is tagged null record;
       type T2 is interface;

       X1 : T1'Class := ... ;
       X2 : T2'Class := ... ;

       Flag : Boolean := (X1 in T2'Class) and (X2 in T1'Class);

   because of the possibility of a type which is descended from both
   T1 and T2, e.g.
       type T3 is new T1 and T2 with null record;
   .

3) It might be possible to express the legality rules for type conversions more
   concisely. Would it be equivalent to replace the 3 listed conditions with
            - The operand type descended from the target type; or
            - The operand and target types shall potentially share descendants.
   ?

4) What representation attributes does an interface type have?
   Can values for these attributes be specified?
   What do T'Size and T'Alignment mean for any abstract type, not
   just for interface types?

5) Although an interface type which has an interface parent "is derived from"
   that type, it is not "a derived type".

6) Inherited homographs must be overridden. If the homographs are
   not mode-conformant, then this may be impossible. Life's hard.

7) Nothing here (in particular, nothing in the 8.3 stuff) is intended to
   change the semantics of a program which declares no interface types.

8) An example to illustrate some of the 8.3 stuff:

           package P1 is
               type T1 is interface;
               procedure P (X : T1) is abstract; -- P'1
           end P1;

           package P2 is
               type T2 is interface with P1.T1;  -- P'2 (implicit)
               procedure P (X : T2) is abstract; -- P'3
           end P2;

           type D is new Some_Tagged_Type and P1.T1 and P2.T2 with null record;

       D inherits procedures P'4, P'5, and P'6, corresponding to
       P'1, P'2, and P'3, respectively.

       Since P'3 overides P'2, P'6 overrides P'5 by the "overriding is
       preserved by inheritance" rule.

       Since P'2 corresponds to P'1,  P'5 overrides P'4 by the "inherited
       copies of your parent's ops hide inherited copies of your grandparent's
       ops" rule.

9) Yes, it really is illegal if the parent type of a "with private"
   type's completion happens to be descended from some interface that the
   private view was not descended from. This may turn out to be a pain.

   Relaxing the rule that a partial view must be derived from the same
   interfaces as the completion is derived from is not out of the question, but
   it could open the door for some very peculiar situations. If this example,

        package P is
          packge 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 P2_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 P2;

   , were legal (it is illegal because the completion of T1 is descended
   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.


--------

In defining interface types, a couple of design decisions seem to be
of interest.

First, there is the question of whether interface types should
be a whole new class of type or a special kind of abstract tagged
type. Since interface types have to abide by all of the restrictions
of an abstract tagged type, the former approach would have involved
a lot of repetition. The latter approach was chosen.

Second, there is the question of derivation and operator inheritance.
If a type "implements" an interface, then it must inherit operators
from the interface type in order to override them. In an earlier version
of this AI, the "implementing" type was not considered to be derived from the
parent interface type and operator inheritance was accomplished via
a separate mechanism. Now, a type which implements an interface type
is defined to be derived from the interface type, thereby simplifying the
operator inheritance rules and the definitions of terms such as
"ancestor", "descendant", and "cover". This means that a given type may
be immediately derived from more than one type, but at most one of them
can be a non-interface type. It is no longer the case that every type
has an "ultimate ancestor", but the term remains well-defined in all the
cases where it is used in the reference manual.

The rules for type conversion and membership testing need revision to
cope with the possibility that an interface type and a tagged type
might have a common descendant even though statically available information
suggests no relationship between the two types.

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 than accomplished
by mapping the 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 with a
reference to this dispatch table permutation map.

Classwide streaming operations for interface types require
some care. Consider the case of a call to Some_Interface_Type'Class'Input
occuring as the controlling operand of a dispatching call. The
implementation must be able to determine where to dispatch to in this case.
Membership testing may also require maintaining additional information
at runtime.

It is intended that a type which is 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.

It is intended that there should be no real difference between the two
forms of derivation from interface type parents. 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.

---------

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 tagged
       with P1.Ifc1 and P2.Ifc2 and
       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;

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

From: Steve Baird
Sent: Tuesday, May 13, 2003  3:59 PM

AI-251 introduces null procedures. Their dynamic semantics are
specified as follows:

    The execution of a null procedure is invoked by a subprogram call.
    This execution has no effect.

This seems wrong. This wording should be replaced with

    The execution of a null procedure is invoked by a subprogram call.
    The execution of the subprogram_body of a null procedure has no
effect.
.

RM 6.3.2(10) breaks the execution of a subprogram call down into several
steps. Calling a null procedure is different than calling a non-null
procedure
only with respect to the step "The subprogram body is then executed.". The
other steps (evaluation of the name or prefix of the call, parameter
evaluation,
and parameter copy-back) are performed in the same way as for a non-null
procedure.

Elaboration checking is not an issue here because a null procedure cannot
have a forward declaration.

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

From: Robert I. Eachus
Sent: Friday, June 13, 2003  3:31 AM

There is nothing all that exciting here--I hope--but it does seem worth
capturing.  First, this shows one way of "fixing" the naming clash
problem.  And it really is a good programming in the large approach.  An
underlying type may be bound to a dozen or more interfaces, but changes
to one binding won't affect the others, adversely or otherwise.

Second, it brings up an interesting issue.  In this example, List may be
an interface type, but the Iterator type is in reality part of the
interface.  There is no problem implementing that part of the interface,
but it would be nice to have a way to specify that type Iterator, and
two operations on the type that don't have parameters of type List are
still part of the List interface, and need to be provided by any type
which implements the interface.

This is actually a much more general problem.  I'd say that as many as
50% of the types that would make reasonable interface types have some
sort of "helper" type declarations in the package that defines the
interface.   Arbitrarily deciding that all the other types declared in a
package that declares an interface type are part of the interface in
some way might work, but then there would be the problem of binding
existing types to one of these helper types.

And this particular way of creating iterators shows why this can be painful.

=========================================================================
Alexander Kopilovitch wrote:

It seems to be an unfortunate collision that multiple new packages
should be proposed when Abstract Interfaces still aren't established
firmly. It is highly likely that some packages (for example, Data
structures components) may look significantly better if they can use
Abstract Interfaces.

I personally don't see any conflict.  The interface AI will allow easier
bindings to C++ and Java.  But in Ada, mix-ins are a better abstraction
IMHO for containers.  The advantage is that you can easily put objects
in a container even if the original declarer of the type/class had no
idea that they would be put in a container.  For example:
...

Yes, but what about similar containers, such as various flavors of List?
And it is not an easy task to align properly the above your words with
another your opinion (with which I fully agree), posted here about 3
weeks ago - I mean the following:

...  In Ada you tend not to have one sort
package in your toolbox, or one list type implementation, you have
several.  Now the programmer solving some problem sees a part of his
decomposition that can be solved by a list package or a sort package,
and does a generic instantiation.  The problem is that there is no easy
way, in Ada, to express a binding to a member of the class of sort
generics, but to delay the choice of the actual mapping.

This is why one of the features I expect interfaces to add to Ada is a
better way of organizing collections of sort algorithms and the like.

Real subtle point.  Interfaces as currently planned will ALLOW multiple
interface inheritance.  They can also be used as I described in the
first paragraph to provide multiple instances of a single abstract
interface.  I expect the multiple (interface) inheritance use to be
common when interfacing to code written in other OO languages.  The
usage I described is almost not a programming convention but a way to
describe the relationship between a number of otherwise unrelated
abstractions.  They all implement the same interface, so they can be
passed to generics as instances of that interface, but otherwise there
is no necessary relation between the abstractions.

Can you use multiple interface inheritance to describe abstractions that
match more than one interface?  Sure, and it will happen.  For example
imagine an AVL_Tree package which clearly allows random (indexed)
access, and also has an ability to do an inorder walk.  It can also
implement the list interface.  The question is whether it will be
common.  The problem becomes a programming in the large issue.  It is
going to be much easier to create a list 'view' of the AVL_Tree package
and keep it consistant with the list interface, than to maintain a
package that directly implements multiple interfaces.

This is probably a very good example, so let me follow it through a bit.

    generic
      type Element is private;
    package Lists is
      type List is abstract interface;
      function Head(L: in List) return Element;
      function Is_Empty(L: in List) return Boolean;
      procedure Append(L: in out List; E: in Element);

      type Iterator is private with null;

      function Create(L: in List) return Iterator;
      function Next(I: in Iterator) return Element;
      function At_End(I: in Iterator) return Boolean;

    private
      ...
    end Lists;

I did the iterator this way because it brings up an interesting question
about interface types.  Obviously an instance of the Lists package also
creates a new Iterator type, and that type is closely related to type
List.  But are functions like Next and At_End part of the List
interface?  I think it is an issue AI-251 needs to address.

Now I want to create a binding between this interface and a tree type.
The package spec is easy:

    with Lists;
    generic
      with package Trees is new AVL_Trees;
      -- Could make the element type explicit, but no reason to, it is
      -- already specifed by the element type in the AVL_Trees instance.
      -- (And by Elements.Element.  They had better match.)
    package List_View is

      type List is private with null;

      function Head(L: in List) return Element;
      function Is_Empty(L: in List) return Boolean;
      procedure Append(L: in out List; E: in Element);

      type Iterator is private with null;

      function Create(L: in List) return Iterator;
      function Next(I: in Iterator) return Element;
      function At_End(I: in Iterator) return Boolean;

    private
      type List is Trees.AVL_Tree;
      type Iterator is Trees.Inorder_Walk with null;
    end List_View;

Now it gets tough.  Not because it is hard to write, but their is one
tough decision.  Head is fairly easy to write, start at the root of the
tree and follow left links until you find one that is null.  The
contents of that node are what you want to return.  Is_Empty and the
Iterator operations shouldn't be too hard, since I specified that
AVL_Trees provides an inorder walk interator.  But what to do about
Append?  I see three options:

1) Always raise Program_Error.  In other words, this is just a view, and
not a full list implementation.

2) Raise an error if the new Element does go at the end of the list.
Err, tree.  This allows the Append operation to be used to insert a
sorted set of Elements one at a time.

3) Insert the Element at the proper position in the tree, and invalidate
any existing Iterators, that are past the position the new Element will
be inserted in, either in the implementation or by fiat.  (In other
words, document it as an error to insert with an open Iterator.)

Whichever solution you choose, all done.  Notice that we have created a
binding between one abstraction Lists, and another, AVL_Trees, that
never explicitly uses the new interface feature to implement multiple
inheritance.  We could have done it, but some of the List operations
only loosely make sense for an AVL_Tree viewed as a tree (the Head
operation) while others will be hidden by the interface entirely.  It
may be that some of the operations for the List view may perfectly match
the available operations for the AVL_Tree (Is_Empty?), but many will
have different names.  So what?  The "overhead" of making the list view
explicit makes maintenance much easier.

=======================================================================

Added for ARG:  Notice that if I make List_View.List an explicit
interface derived from Lists.List or more properly from an instance of
Lists.Lists, let's call in My_List, there will be a problem.  I get a
function:

function Create(L: in List_View.List) return My_Lists.Iterator;

That function is of no use to anyone.  But the operations Next and
At_End have no parameters (or return values) of type List.  So they are
not part of the (proposed Ada) interface.  However, an Interface type,
and Create, Next, and At_End operations are a part of the interface from
a design point of view.  Do I have a solution?  Not yet.  I can actually
do what I want in current Ada--but without the multiple interface
inheritance.  It is trying to map an existing abstraction to a different
interface that brings these issues to light.

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

From: Pascal Leroy
Sent: Monday, June 16, 2003  2:10 AM

> Added for ARG:  Notice that if I make List_View.List an explicit
> interface derived from Lists.List or more properly from an instance of

> Lists.Lists, let's call in My_List, there will be a problem.  I get a
> function:
>
> function Create(L: in List_View.List) return My_Lists.Iterator;
>
> That function is of no use to anyone.  But the operations Next and
> At_End have no parameters (or return values) of type List.  So they are
> not part of the (proposed Ada) interface.

I don't see why you claim that the above function is "of no use to
anyone".  It creates a perfectly good iterator.  The annoyance is that,
to play with this iterator, you have to use operations from both
List_View and My_Lists.  This happens often and I agree that it can be
pretty confusing.  However, I have a feeling that the Object.Operation
notation would help tremendously here:

	Iter : My_Lists.Iterator;
	L : List_View.List;
	...
	Iter := L.Create;
	while not Iter.At_End loop
		...
		Iter.Next;
	end loop;

In fact, I was not too excited about the Object.Operation notation, but
I find this case quite compelling, as the programmer is relieved from
having to find the "right" package.

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

From: Stephen W Baird
Sent: Tuesday, September 2, 2003  4:02 PM

This version of AI-251 reflects the feedback of the Toulouse meeting.

Changes include:
    1) Null procedures are no longer defined in this AI; they have their
       own AI now.
    2) Subprogram overriding rules are revised.
    3) Membership test legality rules are defined in terms of
       convertibility.
    4) The term "interface ancestor" replaces the term "interface
       parent".
    5) The discussion section and the examples are updated.

[This is version /08 of the AI - ED]

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

From: Robert I Eachus
Sent: Tuesday, September 2, 2003  8:45 PM

Stephen W Baird wrote:

>        A descendant of an interface type shall be limited if and only
>        if the interface type is limited.

I want to be sure I understand this rule and how it interacts with
generic instantiation.  It is currently common to define a generic with
a limited type parameter, if the generic does not depend on assignment
or equality for the type.  I'm worried about the "only if:" part of the
rule here.  Can a decendant of an interface type be passed as a generic
formal parameter of a limited type?

The problem case of course is if the generic actually derives a type
from the generic parameter:

generic
   type Foo is limited private;
   ..
package Bar is

   type Foobar is new Foo...;
   ...
   function Foob return Foobar;

end Bar;

package My_Bar is new Bar(Foo:
Some_Non_Limited_Type_Descended_From_An_Interface);

But according to your explanation:

8) Someone asked why a limited interface cannot be implemented by a
   non-limited type. 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 seems
   like an unnecessary complication. 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.

There should be no problem, because in the instance, type Foobar will be
non-limited if Some_Non_Limited_Type_Descended_From_An_Interface is also
non-limited.  So even if an object of type Foobar is passed to some classwide
operation for Some_Non_Limited_Type..., or one of its parents, there should be
no problem.

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

From: Tucker Taft
Sent: Sunday, September 28, 2003  9:51 AM

I have had real trouble keeping the syntax
for interface types in my head.  Every time
I write an example, I get something wrong.
I just took a look at AI-251, and I can see
why ;-).  We aren't being consistent, and
I suspect I am at least partly to blame.

Here is the inconsistency I find most mystifying:

     type T is new Q and R and S with record ... end record;

     type T is new Q with R and S and private;

I certainly think of "with record ...;" and "with private;"
as similar constructs.  I feel strongly these two need
to be reconciled somehow.

And here is the form for one interface inheriting from others:

     type P is interface with Q and R;

(no "new" even though this is a "derived" interface).

I am now trying to figure out what to propose
for the syntax for inheriting from protected and
task interfaces.  Here are two possibilities:

     protected type T is new <interface_list> with ...
   or:
     protected type T with <interface_list> is ...

and then there is the "derived" interface case:

     protected interface PI is new <interface_list> with ...
    or:
     protected interface PI with <interface_list> is ...


Any comments/suggestions?

I *think* I would prefer always putting interfaces
after the word "with", or never doing so.  The half-and-half
approach is too confusing and hard to remember.

I also feel some preference for having "new" in any
type that is derived from something else.  As it is
now, derived interfaces don't have "new" anywhere.

A radical suggestion would be to put the word "interface"
out front:

    interface Q;

    interface Q is new R and S;

In any case, I found the current proposal a bit of a
confusing mish-mosh, and the protected/task interface
proposal seems likely to make it worse...

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

From: Tucker Taft
Sent: Sunday, September 28, 2003  12:04 PM

> A radical suggestion would be to put the word "interface"
> out front:
>
>    interface Q;
>
>    interface Q is new R and S;
> ...

This would be treating "interface" as a
substitution for the term "type" instead of
as a kind of type.  This works better for protected
and task interfaces, since the syntax clearly
looks better with the word "type" *replaced*
with the word "interface" rather than writing something like:

    protected type interface PI is ...
   or
    protected interface type PI is ...

So maybe this radical suggestion has something
going for it, especially if we are considering
generalizing it to cover protected/task interfaces.

And it allows the use of "new" for derived
interfaces, which would also be more consistent
in my view.

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

From: Vincent Celier
Sent: Sunday, September 28, 2003  12:13 PM

Ar you suggesting that these should be replaced with

     protected interface PI is ...

?

So,

     protected Interface is ...

would be a single_protected_declaration, and

     protected interface PI is ...

would be an interface protected type declaration.

I am not sure I would like that.

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

From: Tucker Taft
Sent: Sunday, September 28, 2003  12:31 PM

> Ar you suggesting that these should be replaced with
>
>      protected interface PI is ...
>
> ?

Yes.

>
> So,
>
>      protected Interface is ...
>
> would be a single_protected_declaration, and
>
>      protected interface PI is ...
>
> would be an interface protected type declaration.
>
> I am not sure I would like that.

Good point.  I think here we would disallow the
use of "Interface" as the name of a singleton protected
(or task) object, even if interface is a non-reserved keyword.
That seems like an acceptable incompatibility.

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

From: Pascal Leroy
Sent: Monday, September 29, 2003  2:02 AM

>Here is the inconsistency I find most mystifying:
>
>     type T is new Q and R and S with record ... end record;
>
>     type T is new Q with R and S and private;
>
>I certainly think of "with record ...;" and "with private;"
>as similar constructs.  I feel strongly these two need
>to be reconciled somehow.

I agree that this needs fixing.  As a matter of fact, I see this discrepancy as
a bug in the AI.  It appears that the syntax for private extension comes from
the Vienna meeting, but at the subsequent Bedford meeting we changed the order
of things, and came up with the syntax for record extension, above.  Evidently
the AI was not updated consistently.

>And here is the form for one interface inheriting from others:
>
>     type P is interface with Q and R;
>
>(no "new" even though this is a "derived" interface).

Again this was discussed several times, and as a matter of fact I find it
useful that there is no "new" here, as the syntax makes it clear that you are
not declaring a derived type, but only composing interfaces.

>I am now trying to figure out what to propose
>for the syntax for inheriting from protected and
>task interfaces.  Here are two possibilities:
>
>     protected type T is new <interface_list> with ...
>   or:
>     protected type T with <interface_list> is ...

The first syntax is the one that is consistent with the Bedford decision.

>and then there is the "derived" interface case:
>
>     protected interface PI is new <interface_list> with ...
>    or:
>     protected interface PI with <interface_list> is ...

I am concerned by the possible lookahead problems for parsers: when you reach
the word "interface", you don't know if it's a keyword or an identifier.
Furthermore, this syntax is inconsistent with the interface composition syntax
above.

>Good point.  I think here we would disallow the
>use of "Interface" as the name of a singleton protected
>(or task) object, even if interface is a non-reserved keyword.
>That seems like an acceptable incompatibility.

This doesn't make sense at all.  The sole purpose of unreserved keywords is to
avoid incompatibilities.  As you know, unreserved keywords are already a
contentious issue.  Unreserved keywords with incompatibilities are sure to be
shot down at the WG9 level.  For my part, I am going to oppose any AI that has
such a blatant incompatibility anyway.

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

From: Tucker Taft
Sent: Monday, September 29, 2003  5:38 AM

P> I agree that this needs fixing.  As a matter of fact, I see this
P> discrepancy as a bug in the AI.  It appears that the syntax for private
P> extension comes from the Vienna meeting, but at the subsequent Bedford
P> meeting we changed the order of things, and came up with the syntax for
P> record extension, above.  Evidently the AI was not updated consistently.

OK, that makes me feel better.
Though I frankly couldn't remember which we settled
on.  I take it that it was the first of the two above.

>     I am now trying to figure out what to propose
>     for the syntax for inheriting from protected and
>     task interfaces.  Here are two possibilities:
>
>          protected type T is new <interface_list> with ...
>        or:
>          protected type T with <interface_list> is ...
>
P> The first syntax is the one that is consistent with the Bedford decision.

OK.

>     and then there is the "derived" interface case:
>
>          protected interface PI is new <interface_list> with ...
>         or:
>          protected interface PI with <interface_list> is ...
>
P> I am concerned by the possible lookahead problems for parsers: when you
P> reach the word "interface", you don't know if it's a keyword or an
P> identifier.  Furthermore, this syntax is inconsistent with the interface
P> composition syntax above.

Well, there really isn't any ambiguity, provided you are
willing to look ahead a bit.  I believe Vincent was more worried
about conceptual ambiguity.  However, I now believe that
if we do decide to have non-reserved keywords, then we must
not worry about conceptual ambiguity, but rather rely on
programmers to have good sense.  In all of our proposals it
is possible to make things look funny by using the non-reserved
keyword as an identifier in the same construct where the
non-reserved keyword is usable as a keyword.

>     Good point.  I think here we would disallow the
>     use of "Interface" as the name of a singleton protected
>     (or task) object, even if interface is a non-reserved keyword.
>     That seems like an acceptable incompatibility.
>
P> This doesn't make sense at all. ...

I agree with you.  We need to accept that the non-reserved
keyword proposal is subject to the possibility of (intentional?)
confusion, but so be it.  As you point out, it is solely
a compatibility technique, so introducing incompatibilities
*and* non-reserved keywords makes little or no sense.

I also realized after I replied that "protected type interface Blah is ..."
has exactly the same problem.  That is, "protected type Interface is ..."
would be a type, whereas "protected type interface Blah is ..."
would be an interface.

There also would be nothing preventing someone from writing
"type Interface is interface;" or, perhaps worse,
"type Interface is tagged;" and other weird-o combinations.

P> The sole purpose of unreserved keywords
P> is to avoid incompatibilities.  As you know, unreserved keywords are
P> already a contentious issue.  Unreserved keywords with incompatibilities
P> are sure to be shot down at the WG9 level.  For my part, I am going to
P> oppose any AI that has such a blatant incompatibility anyway.

I hear you.  The proposal does *not* have these incompatibilities.
They appeared only in my quick, and ill-conceived I realize now,
response to Vincent.

You made no comment on the idea of using "interface" as a
*substitute* for the word "type" in the syntax.  Any
comments on that?

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

From: Robert A. Duff
Sent: Monday, September 29, 2003  5:43 AM

> This doesn't make sense at all.  The sole purpose of unreserved keywords
> is to avoid incompatibilities.  As you know, unreserved keywords are
> already a contentious issue.  Unreserved keywords with incompatibilities
> are sure to be shot down at the WG9 level.  For my part, I am going to
> oppose any AI that has such a blatant incompatibility anyway.

The potential confusion between "Interface is" and "interface Mumble is"
is no big deal, in my opinion.  If you get it wrong, you get a
compile-time error message, which is relatively harmless.

Useful compiler options would be to warn about uses of unreserved
keywords as identifiers, or to treat them as reserved words.
Let's leave that sort of thing to compiler options.

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

From: Pascal Leroy
Sent: Monday, September 29, 2003  7:49 AM

> OK, that makes me feel better.
> Though I frankly couldn't remember which we settled
> on.

That's why we have meeting minutes.  They contain really useful
information, and are make very pleasurable reading, so I think you
should look at them more often. ;-)

> >          protected interface PI is new <interface_list> with ...
>
> Well, there really isn't any ambiguity, provided you are
> willing to look ahead a bit.

You're right of course, and from that perspective it is quite different
from the ambiguity that Vincent pointed out (and which we must avoid at
all costs).  However, I am still concerned that this could wreak havoc
in our LALR(1) parser.

> There also would be nothing preventing someone from writing
> "type Interface is interface;" or, perhaps worse, "type
> Interface is tagged;" and other weird-o combinations.

And we should ignore such oddities.  Yes, you can write really bizarre
Ada code if you try hard enough, but programmers who write code that way
get what they deserve.  We should be concerned about helping readability
for folks who write reasonable code.

> You made no comment on the idea of using "interface" as a
> *substitute* for the word "type" in the syntax.  Any
> comments on that?

I don't like the idea of using "interface" as a _substitute_ for "type",
because I would like to see the word "type" whenever we declare a type.
That seems like a useful readability invariant.  However, if you look at
the Bedford minutes, you'll see:

"Pascal suggests: interface type I2 is new I1 and I2 and I0;"

So I am not opposed to moving "interface" at the beginning of the
declaration.

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

From: Robert Dewar
Sent: Monday, September 29, 2003  8:42 AM

> I am concerned by the possible lookahead problems for parsers: when you
> reach the word "interface", you don't know if it's a keyword or an
> identifier.  Furthermore, this syntax is inconsistent with the interface
> composition syntax above.=20

A little lookahead limited to a few tokens is no big deal, if necessary
the lexer can do this if you are stuck with a parser with no lookahead
capability. I would not distort designs too much at this level of concern.

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

From: Robert A. Duff
Sent: Monday, September 29, 2003  2:40 PM

> That's why we have meeting minutes.  They contain really useful
> information, and are make very pleasurable reading, so I think you
> should look at them more often. ;-)

Yes, they're at least as amusing as the comics in The Boston Globe.
Perhaps I should start reading these minutes to my 10-year-old son at
bed time, instead of the Isaac Asimov robot stories he demands these
days.

> > >          protected interface PI is new <interface_list> with ...
> >
> > Well, there really isn't any ambiguity, provided you are
> > willing to look ahead a bit.
>
> You're right of course, and from that perspective it is quite different
> from the ambiguity that Vincent pointed out (and which we must avoid at
> all costs).  However, I am still concerned that this could wreak havoc
> in our LALR(1) parser.

Seems like you could make it work.  I mean, at least the *lexer* can
look ahead?

> > There also would be nothing preventing someone from writing
> > "type Interface is interface;" or, perhaps worse, "type
> > Interface is tagged;" and other weird-o combinations.

..or "type Untagged_Nonlimited is tagged limited..."

> And we should ignore such oddities.  Yes, you can write really bizarre
> Ada code if you try hard enough, but programmers who write code that way
> get what they deserve.  We should be concerned about helping readability
> for folks who write reasonable code.

I'll get up on my high horse now (basically agreeing with Pascal).  My
language design principle is *not* "Punish those who make bad programs."
My language design principle is: "Make it easy to make good programs!"
The former is futile.  The latter is hard work, but it's our job.

[Editor's note: Most of the following discussion is on the concept of
unreserved keywords, and is filed there (in AI-284).]

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

From: Tucker Taft
Sent: Sunday, October 5, 2003  12:14 PM

Wording about homograph hiding for AI-251:

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:

   - At most one shall be a non-null non-abstract subprogram, and it
     overrides all the others;

   - If all are null procedures or abstract subprograms, then any null procedure
     overrides all abstract subprograms; if more than one homograph remains that
     is not thus overridden, then their profiles shall be fully conformant with
     one another, and one is chosen arbitrarily to override the others.

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

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

From: Stephen W. Baird
Sent: Wednesday, October 15, 2003  12:44 PM

This version of AI-251 [Editor's note, this is version /09] incorporates the
feedback from the Sydney ARG meeting.

Significant changes include:

    * Tuck's suggested wording changes for 8.3

    * Syntax changes
      - type T2 is new T1 with Ifc1 and null record;
        becomes
        type T2 is new T1 and Ifc1 with null record;

      - type T2 is new T1 with Ifc1 and private;
        becomes
        type T2 is new T1 and Ifc1 with private;

      - type IFc is interface with A and B;
        becomes
        type Ifc is interface A and B;

    * An observation that this AI effectively implies that tag components
      for all tagged types must reside at the same offset; user-specified
      tag placement becomes much more difficult to support.

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

From: Gary Dismukes
Sent: Wednesday, October 15, 2003  3:58 PM

Steve,

I have a couple of comments on the 8.3 wording:

> 8.3 Visibility
>
> Replace paragraphs 9/1 - 13 with
>
> ...
>       * If two or more homographs are implicitly declared at the same place:
>
>         - At most one shall be a non-null non-abstract subprogram, and it
>           overrides all the others;

Remove the hyphens for consistency with RM style (nonnull, nonabstract).

>         - If all are null procedures or abstract subprograms, then any null
>           procedure overrides all abstract subprograms; if more than one
>           homograph remains that is not thus overridden, then their profiles
>           shall be fully conformant with one another, and one is chosen
>           arbitrarily to override the others.

Both of the above bulleted paragraphs contain legality wording
(the uses of "shall"), but until now this set of paragraphs has
been classified strictly as static semantics.  That seems like
a problematic departure from the current structure and needs
to be resolved somehow.  In the present version of 8.3, legality
rules only appear in 8.3(26/1) (plus the Name Resolution rules
in 24-25).

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

From: Stephen W. Baird
Sent: Thursday, November 4, 2003  5:27 PM

Gary wrote (on 10/15/03):
> Both of the above bulleted paragraphs contain legality wording
> (the uses of "shall"), but until now this set of paragraphs has
> been classified strictly as static semantics.  That seems like
> a problematic departure from the current structure and needs
> to be resolved somehow.  In the present version of 8.3, legality
> rules only appear in 8.3(26/1) (plus the Name Resolution rules
> in 24-25).

Good point. In response, here is another attempt at section 8.3.  I moved the
problematic legality rules out of the static semantics section and into the
middle of 26/1, which is then split into 3 paragraphs.

Gary also wrote -
> Remove the hyphens for consistency with RM style

You are right, but bear in mind that this is only an ASCII approximation
to what will end up in the RM.

[Editor's note: This wording is in version /10.]

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

From: Tucker Taft
Sent: Saturday, January 10, 2004  12:50 PM

The wording in AI-251 for 8.3(26/1, part 2) goes as follows:

     If two or more homographs are implicitly
     declared at the same place then
     at most one shall be a non-null non-abstract
     subprogram. If all are null or
     abstract, then all of the null subprograms
     shall be fully conformant with
     one another. If all are abstract,
     then all of the subprograms shall be fully
     conformant with one another.

I think we need to change this to:

    ... declared at the same place {and not overridden
    by a non-overridable declaration} then at most one...

And it is important that this added condition is understood
to apply to the whole paragraph (which might imply some further
restructuring in the wording of the paragraph).

I think it is important that you can inherit from interfaces
with non-fully-conforming primitives, so long as you override
the offending ones.

On a separate topic, I remember we debated the definition
of "ultimate ancestor type."  The latest AI-251 wording
(2003-11-04, version 10, web-cvs rev 1.18) has ultimate
ancestor not including any interface types.  Is that
still what we want?  It seems to violate the general desire
to allow an existing "normal" abstract type to be converted to
an interface type without significant change to existing
semantics.

Perhaps we checked the RM and found it didn't matter, but
I will admit it still makes me a bit uncomfortable.

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

From: Stephen W. Baird
Sent: Saturday, January 10, 2004  1:58 PM

Tuck says:
...
> I think we need to change this to:

>     ... declared at the same place {and not overridden
>     by a non-overridable declaration} then at most one...

Good point. I agree.

> And it is important that this added condition is understood
> to apply to the whole paragraph (which might imply some further
> restructuring in the wording of the paragraph).

I think your wording is fine as is.

> I think it is important that you can inherit from interfaces
> with non-fully-conforming primitives, so long as you override
> the offending ones.

Right.

> On a separate topic, I remember we debated the definition
> of "ultimate ancestor type."  The latest AI-251 wording
> (2003-11-04, version 10, web-cvs rev 1.18) has ultimate
> ancestor not including any interface types.  Is that
> still what we want?

I think that we do not want a type to have more than one ultimate ancestor.
If you want to allow interface types as ultimate ancestors, that is the problem
you have to deal with.

> It seems to violate the general desire
> to allow an existing "normal" abstract type to be converted to
> an interface type without significant change to existing
> semantics.

Could you be more specific?

> Perhaps we checked the RM and found it didn't matter, but
> will admit it still makes me a bit uncomfortable.

The (soon to be distributed) version of AI-251 that I'm currently working on
includes the following definition of "ultimate ancestor":

    3.4.1 Derivation Classes

    ...

    Replace the last sentence of paragraph 10 with:

        An *ultimate ancestor* of a type is an ancestor of that type that
        is not a descendant of any other type and that is not an interface
        type (see 3.9.4). Each untagged type has a unique ultimate ancestor.

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

From: Tucker Taft
Sent: Saturday, January 10, 2004  10:44 PM

We could define the ultimate ancestor by following the "parent"
relationship, where the parent is the first type
named in the derived type or interface type definition, whether
or not it is an interface type.

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

From: Robert A. Duff
Sent: Sunday, January 11, 2004  9:03 AM

I think the notion of "ultimate ancestor" is used only for access and
array types.  So why should we care if tagged types can have just one,
or more than one, or if the concept is even defined for them?

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

From: Pascal Leroy
Sent: Monday, January 12, 2004  4:38 AM

Tuck proposed:

> We could define the ultimate ancestor by following the
> "parent" relationship, where the parent is the first type
> named in the derived type or interface type definition, whether
> or not it is an interface type.

I would hate to give the impression that the first type named in the
derivation is special (that's in the case where it is an interface; of
course it is special when it is a normal tagged type).  I realize that
your proposed definition would not really make the first type different
from all the others, but it would be confusing.

Bob commented:

> I think the notion of "ultimate ancestor" is used only for
> access and array types.  So why should we care if tagged
> types can have just one, or more than one, or if the concept
> is even defined for them?

Assuming that this is true (and Bob is infallible in matters of Ada
doctrine) then the sensible thing to do is to define ultimate ancestor
only for untagged types.

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

From: Randy Brukardt
Sent: Monday, January 12, 2004  3:41 PM

Pascal replied to Bob:
>
> > I think the notion of "ultimate ancestor" is used only for
> > access and array types.  So why should we care if tagged
> > types can have just one, or more than one, or if the concept
> > is even defined for them?
>
> Assuming that this is true (and Bob is infallible in matters of Ada
> doctrine) then the sensible thing to do is to define ultimate ancestor
> only for untagged types.

"Ultimate ancestor" is used in 3.4.1(10) [definition]; 7.6.1(11) [access];
4.5.3(6-7) [array]; 3.10.2 [access]; 4.3.2(4.a) [tagged, but not normative -
it's describing what was NOT done in defining the language]; and 9.3(2)
[access].

Still, I'd prefer to define it as Steve's latest description: interfaces are
ignored for this purpose. One can consider the "ultimate ancestor" as the
first type that can have data components and concrete primitive operations.

Tucker's worry:
"It seems to violate the general desire to allow an existing "normal"
abstract type to be converted to an interface type without significant
change to existing semantics."
isn't significant here, because (1) the definition of ultimate ancestor
doesn't affect the semantics of an abstract type; and (2) it isn't going to
be possible to change to an interface very often anyway. Certainly, every
abstract type I've ever used has had some concrete routines - they provide a
convinient place to put shared operations which will rarely, if ever, need
to be overridden. That's especially true if you prefer to keep your
derivation trees shallow (branching directly above the root). Therefore,
most of the time, you'll need to add another level to use an interface --
direct conversion is impossible.

The reason to keep the semantics similar for all tagged types (including
interfaces) is just to make them easier to use and understand for Ada users.
But if there are necessary differences, we shouldn't get overly concerned
about them.

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

From: Tucker Taft
Sent: Monday, January 12, 2004  3:34 PM

The alternative we discussed was to define the ultimate ancestor(s)
of a type.  We could then claim that for an untagged record
or array type, there is exactly one, for what that's worth.

It just seems odd to talk about the "ultimate ancestor"
being a type that clearly has ancestors.

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

From: Randy Brukardt
Sent: Tuesday, January 13, 2004  3:07 PM

Bob said:
> I think the notion of "ultimate ancestor" is used only for
> access and array types.  So why should we care if tagged
> types can have just one, or more than one, or if the concept
> is even defined for them?

On that line, here's what my meeting notes say about this topic (I didn't
remember discussing it, I just found these as I was processing the minutes...)

"Ultimate ancestor" is used rarely, and only access types seem to care (and
they can't have multiple ancestors.) So we should allow multiple ultimate
ancestors; add a note saying that "the ultimate ancestor" is well-defined for
untagged types. 3.4.1(10) would change "The ancestor" to  "An ancestor". Then
add "For an untagged type, there is only one ultimate ancestor, and it is
designated as *the ultimate ancestor* of the type."

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

From: Robert A. Duff
Sent: Tuesday, January 13, 2004  4:02 PM

> Then add "For an untagged type, there is only one ultimate ancestor, and it
> is designated as *the ultimate ancestor* of the type."

Do we really need the ", and it is designated..." part?  I mean, doesn't
that follow from the normal rules of English?

Just trying to cut down on useless verbiage...

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

From: Stephen W. Baird
Sent: Wednesday, January 14, 2004  6:07 PM

This version of AI-251 reflects the discussion of the San Diego meeting.

Changes include:
  1)  An interface type which has an interface ancestor (e.g. "type T is
      interface Some_Other_Interface_Type") is a derived type. Formerly it was
      defined to not be a derived type and Erhard asked why. The only reason I
      could come up with was that it seemed odd to declare a derived type
      without using the keyword "new". This did not seem to justify introducing
      the confusing notion of a type which "is derived from" another type but
      which is not "a derived type".

  2)  Changes to the definition of "ultimate ancestor" which have already been
      discussed in recent ARG mail messages.

  3)  Tuck's recently suggested change to the overriding rules.

  4)  Some minor presentation changes:
         a definition which was formerly in a "legality rules" section
	    was moved into a "static semantics" section
         "Random notes" => "Technical notes";
         the addition of technical note #9 .

[Editor's note: This is version /11.]

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

From: Javier Miranda
Sent: Thursday, July  8, 2004  5:41 AM

!standard 03.04    (02)                            04-05-27  AI95-00251/13

At lines 149-151 it is said:

   type I1_Ref is access all T1'Class;  --  typo here???
   DT1_Var : aliased DT1;
   Ref: I1_Ref := DT1_Var'Access;

... and I should say:

   type I1_Ref is access all I1'Class;  --  'I1' (not 'T1')
   DT1_Var : aliased DT1;
   Ref: I1_Ref := DT1_Var'Access;

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

From: John Barnes
Sent: Friday, July  9, 2004  1:52 AM

My mistake.  I added some introductory stuff as part of the editorial review
and this was part of it.

Indeed it should be I1'Class as in the para just above.

Blame the English again!

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

From: Stephen W. Baird
Sent: Tuesday, August 31, 2004  3:25 PM

As an action item from the Palma meeting, I was asked to examine
the interactions between AI-251 (interface types) and user-specified
tag placement.

Given an object of type My_Interface_Type'Class as the controlling operand
in a dispatching call, the implementation must be able to locate the
tag component in order to access the dispatch table. All implementation models
that have been seriously discussed require that an interface type and a type
which implements that interface agree with respect to (statically known) tag
placement, typically at the very beginning of the record.

13.5.1 states
    An implementation may allow such implementation-defined names to be used
    in record_representation_clauses. An implementation can restrict such
    component_clauses in any manner it sees fit.

The problem is that this does not permit imposing other restrictions once
a component_clause has been accepted. For example, if two tagged types
are successfully compiled with different user-specified tag placements, then
it is not ok to reject some subsequent compilation_unit when an attempt is
made to have descendants of these two types both implement some interface type.

An implementation certainly has the option of simply requiring that the
tag component must always reside, for example, at the start of the record.
One way to accomplish this would be to provide no mechanism to allow the
user to specify otherwise.

This incompatible restriction is likely to displease an existing Ada95 user
who is placing tags at some non-standard location (assuming her vendor
provides some mechanism to do this), wants to migrate to Ada05,
and has no intention of using Interface types.

A second approach would be to allow implementations to require some sort
of implementation-defined "representational compatibility" between an
interface type and a type which implements the interface. This would
probably require an inordinately large amount of very murky RM verbiage.

This approach would have the (arguably insignificant) advantage of allowing
the user to define a gaggle of tagged types (both interface and not)
that all have their tags at the same non-standard offset and those types
would all be compatible with each other with respect to this requirement.

As a third approach, I suggest some sort of pragma, probably a restriction
pragma, which promises that a given non-derived tagged type and all of its
descendants will never be used to implement any interface.
All violations would be rejected at compile-time.

Vendors could then require this pragma as a prerequisite for nonconfirming
user-specified tag placement.

It would be even more convenient if user-specified tag placement could be
viewed as also implicitly specifying this restriction, but I don't think
this is currently permitted by the language.

If only a small number of vendors support user-specified tag placement to
begin with, and if this is the only known use for this restriction, then
perhaps this pragma should be implementation-defined, not language-defined.

IBM/Rational supports user-specified tag placement. Does anyone else?

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

From: Randy Brukardt
Sent: Tuesday, August 31, 2004  5:41 PM

Steve wrote:

> As a third approach, I suggest some sort of pragma, probably a restriction
> pragma, which promises that a given non-derived tagged type and all of its
> descendants will never be used to implement any interface.
> All violations would be rejected at compile-time.

> Vendors could then require this pragma as a prerequisite for nonconfirming
> user-specified tag placement.

> It would be even more convenient if user-specified tag placement could be
> viewed as also implicitly specifying this restriction, but I don't think
> this is currently permitted by the language.

This option would be preferable, I think. There wouldn't be any problem
defining such a rule for the Standard.

However, one implementer (lets call them "Rat" for short) has consistently
opposed having legality rules depend on representation items. "Rat" killed
AI-319 (Object_Size attribute) primarily for this reason. I'm sure that
"Rat" would strongly oppose such a solution. (Removing tongue from cheek.)

I think it would be a good idea to make a concrete proposal as to the form
of the required pragma, so we can see what is being proposed. It's not clear
to me what you have in mind; I would have thought that the pragma would
apply only to the base type of a hierarchy, and I don't see how to make a
restriction pragma that only applies to a single type. (A type name is not
an expression.)

>If only a small number of vendors support user-specified tag placement to
>begin with, and if this is the only known use for this restriction, then
>perhaps this pragma should be implementation-defined, not language-defined.

>IBM/Rational supports user-specified tag placement. Does anyone else?

Sigh. How soon they forget. :-)

Janus/Ada does not have a way to name the tag component, but there is no
restriction on its placement. It just goes into the first available DWord
not used by a component placed by a representation clause.

I would welcome a solution in the standard for this purpose; otherwise, we
are taking a step backwards (as noted in Steve's note).

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

From: Stephen W. Baird
Sent: Friday, September 3, 2004  7:07 PM

> I think it would be a good idea to make a concrete proposal as to the form
> of the required pragma, so we can see what is being proposed. It's not clear
> to me what you have in mind; I would have thought that the pragma would
> apply only to the base type of a hierarchy,

... I did say "non-derived" ...

> and I don't see how to make a
> restriction pragma that only applies to a single type. (A type name is not
> an expression.)
>

That's a good point. I'd forgotten that a Restrictions pragma can't apply
to a type. I suppose an entirely new representation pragma would be needed.
Yuck.

I haven't been able to come up with a reasonable name for this beast. This
is a bad sign.

I'm starting to think that the right approach might be to append something
to 13.5.1(15) along the lines of
     "Such representation clauses may affect the values of
      implementation-defined aspects of representation."

and then add an implementation permission section to 3.9.4 (see AI-251)
something like
     "An implementation may require that a descendant of an Interface type
      and the Interface type shall be compatible with respect to
      implementation-defined aspects of representation."

and perhaps an AARM note explaining what this is really all about. It would
need to be clearly stated that this should not be interpreted to mean that
implementations have the freedom to arbitrarily reject derivations from
interface types (e.g. because of an incompatibility in the "initial character
of identifier" aspect of representation).

Still, this seems preferable to adding a whole new section to the manual
along the lines of C.5 (Discard_Names) or 13.2 (Pack).

Comments?

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

From: Tucker Taft
Sent: Saturday, January 22, 2005  1:14 PM

I have been reviewing parts of John's forthcoming and
much-anticipated rationale, chapter by chapter.  It is
an extremely useful "grounding" since you suddenly see
proposed rules or syntaxes that are either great, or
simply awful.

Today's installment focused on generics, and it reminded
me of a general question I have had -- what are formal
interfaces good for?  I certainly am not suggesting
we should drop them, but currently the rules for matching
formals and actuals seem very strict, and might very
well interfere with creating "generic" generics.  But
without some idea of how formal interfaces might be used,
it is pretty tough to know.  So my question is,
does anyone have any good examples of use?

-Tuck

P.S. Here are my own thoughts on the subject:

-------

I have been having *some* thoughts on how a formal interface might
be useful.  First, I think it makes sense to focus on
why you might write a generic package that mentions an
interface type at all in its formal part.  It might be:

    package Linked_Lists is
       -- Define an interface useful for singly-linked lists
       type Linkable is interface;
       function Next(L : access constant Linkable)
         return access Linkable'Class is abstract;
       procedure Set_Next(L : access Linkable;
         Next : access Linkable'Class) is abstract;

       generic
         type Element(<>) is new Linkable with private;
       package Homogeneous_Lists is
         -- Create a homogeneous list, given an element
         -- type that implements Linkable
         type List is private;
         procedure Append(L : in out List; E : not null access Element);
         procedure Prepend(L : in out List; E : not null access Element);
         function First(L : List) return access Element;
           -- returns null if L is empty
         function Remove_First(L : in out List) return access Element;
         function Last(L : List) return access Element;
         function Remove_Last(L : in out List) return access Element;
       end Homogeneous_Lists;
    end Linked_Lists;

    package Linked_Lists.Heterogeneous_Lists is new
      Homogeneous_Lists(Linkable'Class);
        -- Provide a heterogeneous "List" type which allows
        -- elements of any linkable type.
        -- (I think this is very cool, though I know it gives
        --  Randy heartaches ;-)

------

Ok, so that is an example of a generic that uses an interface
in the specification of a formal type.  But why would the
interface itself want to be a formal type?  Perhaps one
of the reasons is that there are already a dozen "Linkable"
interfaces out there being used for this and that, and the
last thing we need is yet *another* one.  So instead of
reinventing a Linkable interface, we change the above
abstraction as a whole to be generic:

    generic

       type Linkable is interface;
         -- Import an interface useful for singly-linked lists
       with function Next(L : access constant Linkable)
         return access Linkable'Class is abstract;
       with procedure Set_Next(L : access Linkable;
         Next : access Linkable'Class) is abstract;

    package Generic_Linked_Lists is

       generic
         type Element(<>) is new Linkable with private;
       package Homogeneous_Lists is
         -- Create a homogeneous list, given an element

       ... etc.

Does the above make sense?  Does it even work?  In particular,
I wonder whether the Element type ends up with its own
Next and Set_Next (formal) dispatching operations, thanks to the
formal abstract subprograms declared in the outer generic.
Did we ever talk about that?  I suppose we don't really
need for formal Element type to inherit the dispatching
operations Next and Set_Next, since we can accomplish
the same thing by making a dispatching call using the
one's associated with Linkable, but it makes some sense
that a formal derived type would inherit the formal dispatching
operations from an ancestor formal type.  Perhaps it is
unnecessary complexity, given the ability to reach them
via a dispatching call, and the fact you can't override
them anyway.  But it is interesting.

In any case, I have wandered off the track.  The main
question is this an example of how formal interfaces
would be used?  I suppose roughly the same thing could
be accomplished by combining the nested generics into
a single generic, e.g.:

    generic

       type Linkable is interface;
         -- Import an interface useful for singly-linked lists
       with function Next(L : access constant Linkable)
         return access Linkable'Class is abstract;
       with procedure Set_Next(L : access Linkable;
         Next : access Linkable'Class) is abstract;
       type Element(<>) is new Linkable with private;
     package Linked_Lists is
       type List is private;
       ...

though then, for some reason, it seems a bit harder to
understand.

I see a somewhat similar situation might arise with a
generalized scheduling package which wants to take a task
type:

    package Scheduling is
       type Root_Task is task interface;
       type Scheduling_Params is record
           Cycle : Milliseconds;
           Etc ...
       end record;


       generic
           type Some_Task(<>) is new Root_Task with private;
       package Controller is
           function Create_And_Start_Task(Params: Scheduling_Params)
             return access Some_Task;
           procedure Change_Cycle(New_Cycle : Milliseconds;
             T : not null access Some_Task);
           ...
       end Controller;
    end Scheduling;

Here, again, there might be many Root_Task interfaces
(given that the language doesn't define one ;-),
and it might be better to import one rather than
have each abstraction with its own root task.
Hence:

     generic
         type Root_Task is task interface;
     package Scheduling is
         type Scheduling_Params is record ...

----

None of the above are what I would call "compelling"
examples.  Does anyone have any better ones?

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

From: Pascal Leroy
Sent: Monday, January 24, 2005  5:38 AM

Tuck wrote:

>     generic
>
>        type Linkable is interface;
>          -- Import an interface useful for singly-linked lists
>        with function Next(L : access constant Linkable)
>          return access Linkable'Class is abstract;
>        with procedure Set_Next(L : access Linkable;
>          Next : access Linkable'Class) is abstract;
>
>     package Generic_Linked_Lists is
>
>        generic
>          type Element(<>) is new Linkable with private;
>        package Homogeneous_Lists is
>          -- Create a homogeneous list, given an element
>
>        ... etc.
>
> Does the above make sense?  Does it even work?  In particular,
> I wonder whether the Element type ends up with its own
> Next and Set_Next (formal) dispatching operations, thanks to the
> formal abstract subprograms declared in the outer generic.
> Did we ever talk about that?  I suppose we don't really
> need for formal Element type to inherit the dispatching
> operations Next and Set_Next, since we can accomplish
> the same thing by making a dispatching call using the
> one's associated with Linkable, but it makes some sense
> that a formal derived type would inherit the formal dispatching
> operations from an ancestor formal type.  Perhaps it is
> unnecessary complexity, given the ability to reach them
> via a dispatching call, and the fact you can't override
> them anyway.

The wording for derived operations is currently based on primitive-ness,
and Next and Set_Next are not primitive operations of Linkable (although
they are dispatching), so they are not inherited.  As you point out, it's
OK because you can call them in a dispatching manner.  I wouldn't change
the rules here, because having to inherit the formal abstract subprograms
would add more complexity both in the language and in compilers, for
little benefits.

> The main
> question is this an example of how formal interfaces
> would be used?

I suppose that "naked" interfaces would seldom be used, but interfaces
derived from other interfaces make a lot of sense.

In one of his papers, John had an example where he had geometric objects
(triangles, circles, etc.), and he was mixing them with an interface
Printable to be able to print them out.
He had a generic like:

	generic
	   type T is abstract tagged private;
	package Make_Printable is
	   type Printable_T is abstract new T and Printable with private;
	   ...
	end;

But I suspect that in real life you might have "specializations" of the
interface Printable, say for different output devices.  So instead of
directly deriving from Printable, you might want to derive from any
interface that implements Printable:

	generic
	   type T is abstract tagged private;
	   type Any_Printable is interface and Printable;
	package Make_Printable is
	   type Printable_T is abstract new T and Any_Printable with
private;
	   ...
	end;


If you assume that people will have rather fancy hierarchies of interfaces
(that's what they do in Java and C#) then the second generic is
substantially more flexible than the first one.

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

From: Gary Dismukes
Sent: Thursday, February 17, 2005  3:09 PM

Javier Miranda noticed that the syntax for interface_type_definition
is inconsistent between AI-251 and AI-345.  The syntax given in AI-345
specifies an "AND" preceding the interface list whereas AI-251 doesn't
have the "AND", and the AARM syntax is what's in AI-345.  Shouldn't
AI-251 be revised to be consistent with the syntax of AI-345?

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

From: Randy Brukardt
Sent: Thursday, February 17, 2005  6:13 PM

I don't know the right answer here. If we insist on updating all of the AIs
for the results of later AIs, we'll have a huge (and relatively useless) job
on our hands. So, we've usually left AIs as linear changes: first 251, which
is modified by 345, which is modified by 401, etc. Similarly, we don't
update AIs when they conflict with other AIs; only the AARM and Amendment
show the net overall change. And, since we have updated the AARM and added
appropriate notes, references back to the AIs should be necessary only in
cases of in-depth research.

Of course, if you're volunteering to update the 40 or so AIs, I'll be happy
to tell you what needs to be done. :-) :-)

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

From: Gary Dismukes
Sent: Thursday, February 17, 2005  7:40 PM

OK, that's fine.  For some reason I was under the impression that you
were trying to keep the corrigendum sections of the AIs in synch with
the RM changes, so thought I should ask about this inconsistency.

> Of course, if you're volunteering to update the 40 or so AIs, I'll be happy
> to tell you what needs to be done. :-) :-)

Nope, not volunteering.  I think I can live with the status quo. ;-)

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

From: Randy Brukardt
Sent: Thursday, February 17, 2005  8:05 PM

> OK, that's fine.  For some reason I was under the impression that you
> were trying to keep the corrigendum sections of the AIs in synch with
> the RM changes, so thought I should ask about this inconsistency.

With *editorial* changes, yes, but not changes that are big enough to have
required another AI. Smaller editorial changes have to be reflected
somewhere, and that would either mean presentation AIs or updates-in-place
(and clearly the latter are easier, and better for everyone). Big changes
are handled by new AIs, and it wouldn't make much sense to modify the old
ones in that case.

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

From: Adam Beneschan
Sent: Saturday, February 19, 2005  1:22 PM

!topic Ada 05 allows many interesting new type conversions
!reference RM05 4.6
!from Adam Beneschan 05-02-18
!discussion

It appears that, on reading 4.6 carefully (yes, I know Randy would
tell me "Don't do that"), you can now convert an integer to a record
type.

The Ada 95 manual's intent was (loosely) to allow type conversions
between types that had a common ancestor, and to allow certain other
explicitly defined type conversions in four cases.  The organization
was:

- If case 1 then ...
- If case 2 then ...
- If case 3 then ...
- If case 4 then ...
- If none of the above four cases apply, then there must be a common
  ancestor, and furthermore blah-blah-blah.

The Ada 05 manual reorganized this, and the organization is now
something like:

- If there is a common ancestor, then blah-blah-blah.
- If there is no common ancestor, then:
--  If the target type is numeric then ...
--  If the target type is an array type then ...
--  If the target type is universal_access then ...
--  If the target type is a general access-to-object type then ...
--  If the target type is an access-to-subprogram type then ...

BUT it doesn't say what happens if there's no common ancestor and the
target type doesn't fall into one of these five cases, such as the
case where the target type is a record type.  Since there's no "If"
that applies to this case, it would appear that there is no Legality
Rule that makes this illegal, regardless of what the operand is.

What's missing is something after the last "If" saying:

--  If the target type does not fall into one of the above five cases,
    then the type conversion is illegal.

I realize this is a nitpick, but I'm sure it was an oversight not to
include something like this, and I think it probably ought to be
added.

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

From: Christoph Grein
Sent: Tuesday, February 22, 2005  4:35 AM

Ada 2005 Amendment 1 (Draft 10)

3.9.4 Interface Types

Insert new clause: [AI95-00251-01; AI95-00345-01]
An interface type is an abstract tagged type that provides a restricted
form of multiple inheritance. A tagged, task, or protected type
{can}have one or more interface types as ancestors.

Probably "can" missing.

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

From: Adam Beneschan
Sent: Thursday, April 21, 2005  7:47 PM

!topic Can interface types have discriminants?
!reference RM2006 3.9.4, 3.2, 3.7, AI-251
!from Adam Beneschan 05-04-21
!discussion


There seems to be a small hole that could lead to confusion.

The chart in 3.2(12) implies that interface types are composite types.
Yes, I realize that this isn't really a language rule since it's in a
Note.  3.2(3-4) lists which types are elementary types and which types
are composite types.  Interface types aren't listed in either one;
3.2(4) says "The composite types are the record types, record
extensions, array types, task types, and protected types", and I don't
see anything that says that an interface type is a record type.
(3.9.4 says an interface type is a tagged type, but nothing in 3.9
says that all tagged types are record types.)

3.7(1) says, "A composite type (other than an array type) can have
discriminants".  Since 3.2(12) seems to imply that an interface type
can be a composite type, 3.7(1) would seem to allow interface types to
have discriminants.

3.9.4(6) says, "An interface type has no components".  In the AARM,
this sentence is in square brackets, and there's a note that says
"This follows from the syntax".  But it doesn't, because "the syntax"
of 3.2.1(3) allows type declarations to have known discriminant parts,
and if we assume an interface type is a composite type, 3.7(1) also
allows interface types to have discriminants.  A discriminant is a
component.  (3.8(9) is explicit in saying that the discriminants of a
record type are components of the record; but for non-record and
non-array types, the only place that seems to say that a discriminant
is a component is 3.2(5).)  So without 3.9.4(6), it appears that it
would be legal for an interface type to have a discriminant, but
3.9.4(6) makes this illegal, meaning it shouldn't be in square
brackets in the AARM.

AI-251 (including the appendix) makes no mention of the issue of
whether interface types can have discriminants---nobody seems to have
thought about this possibility, even to point out that it should be
illegal.  I don't see any reason for interface types to have
discriminants, and I think they would cause a headache, so I'm
assuming they're disallowed.  But trying to find the answer to the
question "Can interface types have discriminants?" in RM2006 led me
down what seems to be a tortuous path.

My suggestions, to clear up any possible confusion: (1) Fix 3.2(4) to
include "interface type" in the list of composite types; (2) fix
3.7(1) to say "A composite type (other than an array type or an
interface type) can have discriminants."

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

From: Tucker Taft
Sent: Thursday, April 21, 2005  8:18 PM

> AI-251 (including the appendix) makes no mention of the issue of
> whether interface types can have discriminants---nobody seems to have
> thought about this possibility, even to point out that it should be
> illegal...

Discriminants were discussed and it was clear
they were inappropriate, as they create all the
same headaches as multiple inheritance of components,
as well as several other headaches unique to discriminants.
As you suggest, this analysis should have been captured
in AI-251.

> ...
> My suggestions, to clear up any possible confusion: (1) Fix 3.2(4) to
> include "interface type" in the list of composite types; (2) fix
> 3.7(1) to say "A composite type (other than an array type or an
> interface type) can have discriminants."

Sounds like a reasonable suggestion...
And thanks for the thoughtful analysis.

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

From: Bob Duff
Sent: Friday, April 22, 2005  7:16 AM

Adam Beneschan wrote:

> AI-251 (including the appendix) makes no mention of the issue of
> whether interface types can have discriminants---nobody seems to have
> thought about this possibility, even to point out that it should be
> illegal.

Well, a couple of months ago, I thought about the possibility,
and I asked Tucker, "Can interfaces have discriminants?".  ;-)
He said no.  That makes sense; allowing them would cause
implementation headaches similar to normal components.
One could no longer assume that record layout is the same
for all types in a class.

I'm not sure if there are any semantic problems as well.
Since discriminants are constant, the classic "diamond pattern"
of multiple inheritance doesn't cause as much trouble,
because it doesn't really matter whether constants are
duplicated.

Anyway, the intent was definitely to disallow discrims.

Nice exegesis by the way.  ;-)

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

From: Robert I. Eachus
Sent: Tuesday, May 10, 2005  10:54 PM

>Anyway, go read the availability rules in 13.13.2(39-49/2) in the draft 11
>AARM. You should be able to figure out any answer about stream attributes
>there (although they might make *your* head hurt); if you can't, then tell
>us what you think the hole is.

First:  13.1 (15.1):
      {/8652/0040
<http://www.adaic.com/standards/rm-amend/html/defect1.html#8652/0040>/}
{/AI95-00108-01
<http://www.ada-auth.org/cgi-bin/cvsweb.cgi/AIs/AI-00108.TXT>/} A
derived type inherits each type-related aspect of representation of its
parent type that was directly specified before the declaration of the
derived type, or (in the case where the parent is derived) that was
inherited by the parent type from the grandparent type. A derived
subtype inherits each subtype-specific aspect of representation of its
parent subtype that was directly specified before the declaration of the
derived type, or (in the case where the parent is derived) that was
inherited by the parent subtype from the grandparent subtype, but only
if the parent subtype statically matches the first subtype of the parent
type. An inherited aspect of representation is overridden by a
subsequent representation item that specifies the same aspect of the
type or subtype.

There are two things missing here.  First, I assume that allowing
representation clauses for interface types is not intended.  But it
seems to me that, at least in the AARM, a note to this paragraph should
spell out that  representation attributes are not inherited from
interface types.

3.9.4(7/2):
     {/AI95-00251-01
<http://www.ada-auth.org/cgi-bin/cvsweb.cgi/AIs/AI-00251.TXT>/} All
user-defined primitive subprograms of an interface type shall be
abstract subprograms or null procedures.

So predefined operators like "="  (as opposed to user defined "=") are
allowed, as are the attribute subprograms like 'Read and 'Write.  That's
how I remembered the new RM wording, and I think the user-defined is
required.

But 13.13.2(2..6) says:

   For every subtype S of a specific type /T/, the following attributes
are defined.

   S'Write

   S'Read...

Seems pretty clear, and I can't see how interface types are not specific
types.

13.13.2(10..14): For every subtype S'Class of a class-wide type /T/'Class:

          S'Class'Write
         ...
         S'Class'Read

Looks to me like class-wide Read and Write are there too.

So say you have the following declarations:

  package P is
    type I is interface;
  end P;

  with P; with Ada.Finalization;
  package Q is
    type D is new Ada.Finalization.Controlled and P.I;
  end Q;

    with P, Q;
   procedure Fubar is
   begin
      ..
      P.I'Write(...,P.I(D));
   end Fubar;

Head hurts....

Seriously, P.I'Class'Write(...,D);  seems well defined and even possibly
useful, but allowing the 'Read and 'Write of abstract types be called
seems mischievous.

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

From: Gary Dismukes
Sent: Wednesday, May 11, 2005  1:17 AM

> Seriously, P.I'Class'Write(...,D);  seems well defined and even possibly
> useful, but allowing the 'Read and 'Write of abstract types be called
> seems mischievous.

But that's nothing new.  It's just that those attribute have to be invoked
by a dispatching call in the case of an abstract type, and in fact the
'Class versions are defined to make dispatching calls to 'Read and 'Write.
The dispatchingness of the non-'Class versions is clarified in AI-335.

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

From: Randy Brukardt
Sent: Wednesday, May 11, 2005  3:07 PM

> There are two things missing here.  First, I assume that allowing
> representation clauses for interface types is not intended.  But it
> seems to me that, at least in the AARM, a note to this paragraph should
> spell out that  representation attributes are not inherited from
> interface types.

Interfaces could have representation clauses if the implementation allows
it. A clause that specified the location of the tag might be very useful in
some models. But we don't *require* any.

In any case, there is no problem here, because this says that the
representation is inherited from the parent type. If the parent type is an
interface, it will inherit whatever values there are there - no problem. But
interfaces that are not parents (that is, are in an interface_list) don't
participate. So representation is inherited from exactly one type, which is
good.

> 3.9.4(7/2):
>      {/AI95-00251-01
> <http://www.ada-auth.org/cgi-bin/cvsweb.cgi/AIs/AI-00251.TXT>/} All
> user-defined primitive subprograms of an interface type shall be
> abstract subprograms or null procedures.
>
> So predefined operators like "="  (as opposed to user defined "=") are
> allowed, as are the attribute subprograms like 'Read and 'Write.  That's
> how I remembered the new RM wording, and I think the user-defined is
> required.

Right, the predefined operations are allowed. But there are no requirements
on those operations; they're predefined after all!

...
> So say you have the following declarations:
>
>   package P is
>     type I is interface;
>   end P;
>
>   with P; with Ada.Finalization;
>   package Q is
>     type D is new Ada.Finalization.Controlled and P.I;
>   end Q;
>
>     with P, Q;
>    procedure Fubar is
>    begin
>       ..
>       P.I'Write(...,P.I(D));
>    end Fubar;
>
> Head hurts....

You didn't look at the availability rules. T'Read etc. are *always* defined
for *all* types; but there are types that it can't be called for.

> Seriously, P.I'Class'Write(...,D);  seems well defined and even possibly
> useful, but allowing the 'Read and 'Write of abstract types be called
> seems mischievous.

But if you had, you would have found that P.I'Read is in fact allowed. Of
course, it does nothing, because there are no components. And it is good
that it is defined, so that if an interface is the parent type, the
attributes compose properly.

That is, for
    type R is new P.I with ...
we want R'Read to be well-defined by default; but that requires its parent's
'Read to be well-defined by default.

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

From: Bob Duff
Sent: Monday, June  6, 2005  12:36 PM

I'm using draft 11.8 of the [A]ARM.

3.9.4(7/2):

7/2   {AI95-00251-01} [An interface type has no components.]

    7.a/2 Proof: This follows from the syntax.

That doesn't seem right to me.  It follows from the syntax, plus the rule saying
interfaces can't have discriminants, plus the rule saying interfaces can't have
non-interface ancestors.  Maybe we should just say "An interface type shall not
have components.", and move it to "Legality Rules".

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

From: Tucker Taft
Sent: Monday, June  6, 2005  7:12 PM

It seems weird to me to make this into a legality rule.
I would rather it remain a consequence of other rules.
I would suggest we beef up the "Proof" rather than
making this into a legality rule.

I suppose I could be convinced otherwise, but it
feels to me like it should be syntactically "obvious"
that an interface doesn't have any components.

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

From: Randy Brukardt
Sent: Monday, June  6, 2005  9:25 PM

Tucker writes:

> It seems weird to me to make this into a legality rule.
> I would rather it remain a consequence of other rules.
> I would suggest we beef up the "Proof" rather than
> making this into a legality rule.

That's OK, but we don't actually have a rule preventing discriminants on
interfaces. It seems that we assumed that this one would take care of it in
3.7, and assumed that this one was taken care of by 3.9.4. So which one
should change? (Bob seems to want both, which is silly.)

> I suppose I could be convinced otherwise, but it
> feels to me like it should be syntactically "obvious"
> that an interface doesn't have any components.

Right, but Bob is also thinking about discriminants and inherited
components. I think that fixing 3.7(8) to not allow discriminant parts on
interfaces and beefing up the Reason would be enough.

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

From: Pascal Leroy
Sent: Tuesday, June  7, 2005  3:33 AM

I agree with Tucker that we should beef up the proof, but surely there is
no reason to move this rule to the "Legality Rules" section, as it is just
a consequence of other rules.

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

From: Pascal Leroy
Sent: Tuesday, June  7, 2005  4:01 AM

> That's OK, but we don't actually have a rule preventing
> discriminants on interfaces.

Oh, I see, I missed that.

> It seems that we assumed that
> this one would take care of it in 3.7, and assumed that this
> one was taken care of by 3.9.4. So which one should change?

I'd say do the same as for array, i.e. 3.7(1/2) and 3.7(8/1).

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

From: Erhard Ploedereder
Sent: Wednesday, June  8, 2005  7:04 AM

> I suppose I could be convinced otherwise, but it
> feels to me like it should be syntactically "obvious"
> that an interface doesn't have any components.

The problem are the discriminants, which are definitely not
excluded syntactically. And I had a real hard time finding a
rule that excluded them for interface types. And, if I recall
correctly, D.11 did not have such a rule.

I hope that D 11.8 has one; if anyone appeals to 3.9.4(7/2),
7/2   {AI95-00251-01} [An interface type has no components.]
I then side with Bob that this should be a legality rule.

(I do agree that the syntax is obvious wrt. the "other components".)

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

From: Tucker Taft
Sent: Wednesday, June  8, 2005  12:22 PM

I believe we could disallow them syntactically,
in the sense that we could have a "syntax" rule that
an interface type shall not have a discriminant part.
Don't ask for the LALR grammar for that one... ;-)

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

From: Randy Brukardt
Sent: Wednesday, June  8, 2005  2:16 PM

> I hope that D 11.8 has one; if anyone appeals to 3.9.4(7/2),
> 7/2   {AI95-00251-01} [An interface type has no components.]
> I then side with Bob that this should be a legality rule.

Nope, I think we agree that 3.7(1) and 3.7(8) should put "interfaces" next
to "arrays". We didn't try to disallow them syntactically for arrays, it
doesn't make much sense to do so for interfaces. (And I suspect we'd have
contract problems for generics.)

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

From: Bob Duff
Sent: Monday, June  6, 2005 12:33 PM

Randy asked me to review the new RM, through section 7.5, which I did.
I have 12 questions that I will e-mail to the group.
The rest of the comments are minor issues, which I will send to Randy.

----------------

Here's the first question.

I'm using draft 11.8 of the [A]ARM.

Are interface types considered to be composite types?  Should they?

3.2(4/2) says no:

4/2   {AI95-00326-01} The composite types are the record types, record
extensions, array types, task types, and protected types.

3.2(2) says:

2     {class (of types)} Types are grouped into classes of types, reflecting
the similarity of their values and primitive operations.
{language-defined class (of types)} There exist several language-defined
classes of types (see NOTES below). {elementary type} Elementary types are
those whose values are logically indivisible; {composite type} {component}
composite types are those whose values are composed of component values.
{aggregate: See also composite type}

Interface types have no components.  But neither to null record types,
and those are composite.  It seems to me that every type should be either
elementary or composite.  And interface types are (sort of) like private types,
which are considered composite even when the full type is not.  So it seems to
me interface types should be composite.

3.7(1/2):

1/2   {AI95-00326-01} [{discriminant} {type parameter: See discriminant}
{parameter: See also discriminant} A composite type (other than an array type)
can have discriminants, which parameterize the type.

Should "other than an array type" be "other than an array or interface type"?

Same issue in 3.7(8/1).

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

From: Tucker Taft
Sent: Monday, June  6, 2005  1:33 PM

> Are interface types considered to be composite types?  Should they?

They should be.  Clearly int_type'Class contains lots of composite
types, so it would be weird if int_type itself wasn't considered
composite.

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

From: Randy Brukardt
Sent: Monday, June  6, 2005  2:42 PM

> I'm using draft 11.8 of the [A]ARM.

Just so everyone understands, this was a special version created
specifically so that Bob could start his review. It wasn't published any
other way.

> Are interface types considered to be composite types?  Should they?

Certainly Tucker's revision of the type classification chart includes
interfaces under composite types.

If says something else in the Standard, it probably should be fixed.

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

From: Stephen W. Baird
Sent: Monday, June  6, 2005  3:02 PM

> Are interface types considered to be composite types?  Should they?
>

3.9.4(1/2) states that "An interface type is an abstract tagged type ...".
Does that answer the question, or am I confused?

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

From: Bob Duff
Sent: Monday, June  6, 2005  4:08 PM

Steve said:

> Bob Duff <bobduff@sofcheck.com> wrote on 06/06/2005 10:32:58 AM:
> >
> > Are interface types considered to be composite types?  Should they?
> >
>
> 3.9.4(1/2) states that "An interface type is an abstract tagged type ...".
> Does that answer the question, or am I confused?

No, I don't think that works.  I don't see any place that says "all
tagged types are composite".

3.2(4/2) says:

4/2   {AI95-00326-01} The composite types are the record types, record
extensions, array types, task types, and protected types.

and then 3.2(4.1/2) contradicts that by saying so are incomplete and
private types.  I'll let the contradiction slide, although "The
composite types include..." might be more correct than "The composite
types are" -- I didn't say so, because it seemed insufficiently broken.

But I do think we need to say somewhere that interfaces are composite.

By the way, para 31 of the Forward:

The main classes of types are
elementary types (comprising enumeration, numeric, and access types) and
composite types (including array and record types).

makes it clear that the intent is that every type is either elementary
or composite, and not both.  And, as Tucker pointed out, there's the
"closed under derivation" property of classes to uphold.

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

From: Randy Brukardt
Sent: Monday, June  6, 2005  4:19 PM

> > Are interface types considered to be composite types?  Should they?
>
> 3.9.4(1/2) states that "An interface type is an abstract tagged type ...".
> Does that answer the question, or am I confused?

You're confused, because the paragraph that Bob referenced which defines
composite types mentioned neither tagged types nor abstract types. I think
it is pretty clear that all tagged types should be composite (in the current
language), but it appears that the Ada 9X wanted to leave the door open for
tagged elementary types in the future, so they avoided ever saying that.

We need to fix the paragraph that defines composite types. It's odd that
Tucker noticed that the type chart (3.2(12-13)) was confusing, but didn't
notice that the normative wording didn't match the chart. Of course, I
shouldn't talk; I never checked, either.

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

From: Pascal Leroy
Sent: Tuesday, June  7, 2005  3:23 AM

> Are interface types considered to be composite types?  Should they?

I agree with others that they should be.

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

From: Randy Brukardt
Sent: Monday, June 13, 2005 11:53 PM

> 3.2(4/2) says:
>
> 4/2   {AI95-00326-01} The composite types are the record types, record
> extensions, array types, task types, and protected types.
>
> and then 3.2(4.1/2) contradicts that by saying so are incomplete and
> private types.  I'll let the contradiction slide, although "The
> composite types include..." might be more correct than "The composite
> types are" -- I didn't say so, because it seemed insufficiently broken.

No, that's not quite correct. We're talking about incomplete *views* and
partial *views* (incomplete and private types are misnamed; there really isn't
such a thing). So, while a *type* can be composite, it doesn't make a lot of
sense to talk about a *view* being a *composite type*. The wording was changed
from the Ada 95 wording for this reason (in part because Tucker complained a
lot). It says that "an incomplete or partial view is considered a composite
type", not that it *is* a composite type. What this means is that for the
purposes of legality rules, such a view is treated as a composite type. But
that doesn't make the view composite; that's a property of the type (and thus
depends on the full type). These views don't appear in the chart of
language-defined classes for this reason.

To summarize: a view can be *considered* composite; but only a full type *is*
composite.

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

!topic Multiple overriding
!reference RM06 8.3, 3.9.2, 3.9.3
!from Adam Beneschan 05-06-17
!discussion


Interfaces now make it possible that two (or more) inherited
homographs can be declared at the same place.  8.3(26.1/2) makes it
clear that one of those, P1, will override all the others---P2, P3,
etc.  (Of course, I don't intend for "P1", "P2", etc., to reflect the
subprogram name---all those subprograms have the same identifier.)

Suppose that a later homograph P100 is explicitly declared that
overrides P1.  Does it also override P2, P3, etc.?

The rules seem to imply that it does.  8.3(9/1-10/1) seems to indicate
that whenever two homographs occur in the same declarative region, and
one is overridable and one is non-overridable, the non-overridable one
overrides the overridable one.  Whether the overridable one was
already overridden previously seems not be taken into account by the
rules.

The answer to the above question (Does P100 override P2, etc.) affects
the legality of a couple situations, and I'm not clear that a "yes"
answer leads to the desired result.

Case I:

    package Pak1 is
        type I1 is interface;
        procedure Foo (X : in I1) is abstract;
    end Pak1;

    package Pak2 is
        type Rec is tagged null record;
        procedure Foo (X : in Rec);
    end Pak2;

    package Pak3 is
        type Child is abstract new Pak2.Rec and Pak1.I1 with record
            ...
        end record;
        -- (A)
        -- procedure Foo (X : in Child) is abstract;    --Foo1
        -- procedure Foo (X : in Child);                --Foo2
    private
        procedure Foo (X : in Child) is abstract;       --Foo3
    end Pak3;

At point (A), two implicit homographs are declared as shown.  The
rules say that Foo2 overrides Foo1 at that point.  Later, though,
there is an abstract overriding procedure in the private part.

3.9.3(10) says:

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

If there were no Foo declared for the type I1, Foo3 would be illegal.
However, since Foo *is* declared for I1, there is now an abstract
subprogram (Foo1) implicitly declared in the visible part.  If the
interpretation is that Foo3 overrides both Foo1 and Foo2, then the
"unless" condition of 3.9.3(10) is satisfied, and the declaration of
Foo3 is legal.  I suspect this is not what is desired.


Case II:

    package Pak1 is
        type I1 is interface;
        procedure Foo (X : in out I1) is null;
    end Pak1;

    package Pak2 is
        type Rec is tagged null record;
        procedure Foo (X : in Rec);
    end Pak2;

    package Pak3 is
        type Child is new Pak2.Rec and Pak1.I1 with record
            ...
        end record;
        -- (A)
        -- procedure Foo (X : in out Child) is null;    --Foo1
        -- procedure Foo (X : in Child);                --Foo2

        procedure Foo (X : in Child);                   --Foo3
    end Pak3;

At point (A), two implicit homographs are declared as shown.  The
rules say that Foo2 overrides Foo1 at that point.  Furthermore, since
the rules about "full conformance" only apply when we select a null
(resp. abstract) procedure from multiple null procedures to be the
overriding one, and that isn't the case here (8.3(26.1/2)), the
derived type declaration is legal.

Later, we declare an overriding procedure.  3.9.2(10/2) says, in part,

    If the dispatching operation overrides an inherited subprogram, it
    shall be subtype conformant with the inherited subprogram.

Whether the declaration of Foo3 is legal depends on whether Foo3 is
considered to override both Foo1 and Foo2.  If it does, then Foo3 is
illegal (it's not mode conformant with Foo1).  If Foo3 overrides only
Foo2 [because Foo1 was previously overridden], then the declaration of
Foo3 is legal.  I'm guessing that having Foo3 be illegal would be
surprising, if nothing else, because the rules about homographs when
multiple inheritance occurs is that a "real" subprogram is supposed to
override a null one, and it would seem a bit surprising that the null
one, which supposedly got superseded by the real subprogram, still has
an effect on the legality of a later declaration.

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

From: Adam Beneschan
Sent: Friday, June 17, 2005  7:43 PM

Second thoughts re Case II:

After I sent this, it occurred to me that 3.9.2(20/2) has an impact on
all this: "For the execution of a call on a dispatching operation, the
body executed ... for an implicitly declared dispatching operation
that is overridden is the body for the overriding subprogram".

I'm assuming that in the above case, we *do* want Foo3 to override
Foo1 and Foo2, because then Foo3 becomes the overriding subprogram for
a dispatching operation for both Pak2.Rec and Pak1.I1.  Thus, if we
say Foo(X) where X has type Pak1.I1'Class, and the actual object's tag
is for the type Child, I'm assuming that we'd want Foo3 to be
executed.  In that case, it makes sense that Foo3 would be illegal,
since the parameter mode is different than a dispatching operation on
Pak1.I1'Class would expect.  So I think I goofed when I wrote my
previous post.

Of course, there's also the minor problem that, according to the
rules, Foo1 is overridden by *both* Foo2 and Foo3, so that when
3.9.2(20/2) talks about "*the* overriding subprogram" for Foo1, it's
sort of lying because there's more than one overriding subprogram.
But I think we can all assume Foo3 is what is meant.  I don't know
whether it's worthwhile to try to fix the rules in this case.

However, I think the case I previously mentioned, involving 3.9.3(10),
is still a problem.  (Perhaps the solution is to add a bit of language
to 3.9.3(10).)

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

From: Pascal Leroy
Sent: Tuesday, August 23, 2005  5:35 AM

An issue that has been discussed several times by the ARG, but never
properly resolved, is that of the interaction between tag placement and
interfaces.

The problem is that it is possible to combine interfaces in ways that
would result in conflicting placement for the tag.  Consider for instance:

	type I1 is interface; -- Tag at offset 0 by default.

	type T1 is tagged
	   record
	      Component : Natural;
	   end record;

	for T use
	   record
	      Component at 0 range 0..31; -- Tag somewhere else,
                                          -- probably at offset 4.
	   end record;

	type R is new T and I1 with ...; -- Where is the tag?

Also, some compilers allow the user to specify the location of the tag in
a tagged record type, and it would seem sensible to be able to specify the
location of the tag for an interface, in particular if it is used for
interfacing (pun?) with another language.  We  don't want to *require*
support for this kind of representation item, but we certainly want  to
*allow* it.  Say that an implementation wishes to support tag placement
with the implementation-defined attribute Tag_Offset, and consider:

	type I1 is interface;
	for I1'Tag_Offset use 0; -- Just an example syntax, could be a
                                 -- pragma, etc.

	type I2 is interface;
	for I2'Tag_Offset use 4;

	type R is new I1 and I2 with null record; -- Where is the tag?

What should an implementation do with these examples?  It doesn't make
sense to reject the representation clauses because that would defeat the
purpose (remember that the three types could live in three separately
compiled units).  But obviously there is not consistent layout for type R.

There is currently no rule in the RM that would allow an implementation to
reject the declaration of type R.  The paragraph that allows an
implementation to reject a representation item that it doesn't like is
13.1(13/1) which reads:

"A representation or operational item that is not supported by the
implementation is illegal, or raises an exception at run time."

This rule assumes that whether a representation item is supported or not
can be determined in isolation, by looking only at this representation
item (and possibly at other representation items for the same type).  It's
not the situation we have here: each representation item is perfectly
fine, it's their interaction that causes trouble.

In order to deal with this issue, I am proposing to add a post-compilation
rule to allow an implementation to refuse to produce an executable if it
encounters representation items that it thinks are somehow inconsistent:

Add after 13.1(13/1)

Post-Compilation Rules

"A set of representation or operational items that, taken together, are
not supported by the implementation, is rejected prior to running the
partition."

In practice it's unlikely that implementations would defer the check until
link-time for the fun of it, so the customary "optimization" of
post-compilation rule would probably apply, and the declaration of type R
would be rejected at compilation time.

This solution represents no additional work for implementation that don't
support nondefault tag placement, and is the more flexible in terms of
when the actual check is performed for those that do.

Feedback welcome.

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

From: Arnaud Charlet
Sent: Tuesday, August 23, 2005  6:54 AM

> 	type I1 is interface;
> 	for I1'Tag_Offset use 0; -- Just an example syntax, could be a
> pragma, etc.

> What should an implementation do with these examples?

Given that the code is using non standard attributes or pragmas, it
is up to the implementation to define the semantics in this case I would
say.

> There is currently no rule in the RM that would allow an implementation to
> reject the declaration of type R.

And that does not seem to be needed within the language as defined in
the RM.

> In order to deal with this issue, I am proposing to add a post-compilation
> rule to allow an implementation to refuse to produce an executable if it
> encounters representation items that it thinks are somehow inconsistent:

That seems fine to add this rule as part of your implementation, but
I do not see it as needed as part of the Ada 2005 standard.
Could you explain why would this be needed or what this would bring ?

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

From: Erhard Ploedereder
Sent: Tuesday, August 23, 2005  8:04 AM

note that:

13.1(13/1) which reads:
"A representation or operational item that is not supported by the
implementation is illegal, or raises an exception at run time."

and

Add after 13.1(13/1)
Post-Compilation Rules
"A set of representation or operational items that, taken together, are
not supported by the implementation, is rejected prior to running the
partition."

are in direct contradiction for a set of one. The latter disallows the
raising of an exception perrmitted by the first one.

Apart from the lawyerly observation...the first one must have, for some
reason, allowed the raising of an exception because there was no static
way of deciding. If so, this would likely afflict the second rule as well,
if written in this generality. (Hence, archeaology required on the 1. rule.)

If not so, the second one could simply replace (for good reason) the first
one. Lest someone shout: it is NOT an incompatibility to render a program
illegal that cannot have worked in the first place.

Secondary comment:
For the problem at hand, a static legality rule would work just fine.
(I hate "optimized post-compilation rules".)

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

From: Pascal Leroy
Sent: Tuesday, August 23, 2005  8:37 AM

> 13.1(13/1) which reads:
> "A representation or operational item that is not supported
> by the implementation is illegal, or raises an exception at run time."
>
> and
>
> Add after 13.1(13/1)
> Post-Compilation Rules
> "A set of representation or operational items that, taken
> together, are not supported by the implementation, is
> rejected prior to running the partition."
>
> are in direct contradiction for a set of one. The latter
> disallows the raising of an exception perrmitted by the first one.

Good point.  The second should say "A set of two or more...".

> Apart from the lawyerly observation...the first one must
> have, for some reason, allowed the raising of an exception
> because there was no static way of deciding. If so, this
> would likely afflict the second rule as well, if written in
> this generality. (Hence, archeaology required on the 1. rule.)

That crossed my mind, and the reason why I didn't include an exception in
the second rule is that, in the case of a set of two or more
representation items, it's not at all clear where the exception should be
raised, and therefore where it could be handled (not that handling these
exceptions is a great idea, but we want well-defined semantics).  These
are questions that are best left unanswered.

> Secondary comment:
> For the problem at hand, a static legality rule would work
> just fine. (I hate "optimized post-compilation rules".)

It's hard to write a static legality rule that would cover
implementation-defined attributes/pragmas/whatever.  Furthermore, I was
concerned that a static legality rule could possibly lead to contract
model issues.

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

From: Bob Duff
Sent: Tuesday, August 23, 2005  8:38 AM

Erhard said:

> note that:
>
> 13.1(13/1) which reads:
> "A representation or operational item that is not supported by the
> implementation is illegal, or raises an exception at run time."
>
> and
>
> Add after 13.1(13/1)
> Post-Compilation Rules
> "A set of representation or operational items that, taken together, are
> not supported by the implementation, is rejected prior to running the
> partition."
>
> are in direct contradiction for a set of one. The latter disallows the
> raising of an exception perrmitted by the first one.

Good point.

I believe the part about raising an exception is there because some rep
clauses or pragmas cannot be checked at compile time, because they can
contain non-static expressions.  Storage_Size comes to mind.
If you write:

    for T'Storage_Size use Read_From_Keyboard(...);

you can't expect the compiler to detect problems (like request for too
much memory) before running the program!

Anyway, I think it's important that we understand that this rule has
nothing to do with implementation-defined pragmas and attributes.
Implementation-defined pragmas and attributes can violate this rule, or
in fact *any* language rule.  It just doesn't make any sense to give
permission to invent pragmas and attributes, without also giving
permission to invent the rules that apply to them.

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

From: Bob Duff
Sent: Tuesday, August 23, 2005  7:49 AM

> 	type R is new T and I1 with ...; -- Where is the tag?

I agree that an implementation should be allowed to reject this.

I think 13.5.1(21) could reasonably be interpreted to allow such
rejection:

21    An implementation may reserve a storage place for the tag field of a
      tagged type, and disallow other components from overlapping that place.

This rule was intended to disallow the rep clause on T.
(Well, to *allow* that disallowance.  ;-))
But in Ada 200X, the implementation could allow that clause on T,
and then say "I reserve offset 0 for the tag of R,"
and complain because Component overlaps that.

I don't think we need to worry about whether the implementation is
rejecting T or R, here -- illegal is illegal.  (Even though they might
be separately compiled.)

We could add an AARM annotation after the above paragraph,
showing your example.

> Also, some compilers allow the user to specify the location of the tag in
> a tagged record type, and it would seem sensible to be able to specify the
> location of the tag for an interface, in particular if it is used for
> interfacing (pun?) with another language.  We  don't want to *require*
> support for this kind of representation item, but we certainly want  to
> *allow* it.  Say that an implementation wishes to support tag placement
> with the implementation-defined attribute Tag_Offset, and consider:
...

I think if an implementation invents attributes, it gets to make up the
rules for those attributes.  So in this case, the implementation can
*certainly* say R is illegal.  It can choose to say so at compile time
or link time.  Or even at run time, thought that would be unwise.

We don't need to give special permissions related to implementation
defined attributes or pragmas.

...
> This rule assumes that whether a representation item is supported or not
> can be determined in isolation, by looking only at this representation
> item (and possibly at other representation items for the same type).  It's
> not the situation we have here: each representation item is perfectly
> fine, it's their interaction that causes trouble.

That's true from a compiler-writer's perspective, but I don't think the
language needs to care whether it's this thing or that thing that's
illegal, versus the "interaction" between them that's illegal.

> In order to deal with this issue, I am proposing to add a post-compilation
> rule to allow an implementation to refuse to produce an executable if it
> encounters representation items that it thinks are somehow inconsistent:
>
> Add after 13.1(13/1)
>
> Post-Compilation Rules
>
> "A set of representation or operational items that, taken together, are
> not supported by the implementation, is rejected prior to running the
> partition."

This seems too broad, to me.  If you think we need some permission, I
think we should make it apply to the very specific case shown by your
first example (assuming you don't buy my interpretation of 13.5.1(21)).
The above wording could be taken as permission to disallow all kinds of
rep clauses, even in the presence of the SP Annex.

I'm quite sure we don't need any wording related to your second example.

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

From: Pascal Leroy
Sent: Tuesday, August 23, 2005  8:30 AM

...
> This rule was intended to disallow the rep clause on T.
> (Well, to *allow* that disallowance.  ;-))
> But in Ada 200X, the implementation could allow that clause
> on T, and then say "I reserve offset 0 for the tag of R," and
> complain because Component overlaps that.
>
> I don't think we need to worry about whether the
> implementation is rejecting T or R, here -- illegal is
> illegal.  (Even though they might be separately compiled.)

You are missing the point.

Say that you have an (Ada 95) implementation that currently supports T.
Not required, but surely allowed, right?  Now this implementation (and its
users) want to move to Ada 2005.  Nobody wants to start rejecting T. They
want to reject R, which is were the logical impossibility arises. I claim
that there is no rule in the manual that currently allows this.

> I think if an implementation invents attributes, it gets to
> make up the rules for those attributes.

I disagree. If an implementation claims that an attribute (or a pragma)
is a representation item, it has to comply with the rules regarding
representation items, and again there is no rule that says that you can
reject a declaration because you didn't like some distantly related
representation item.

Of course, an implementation could say that the attribute is not a
representation item, but a thingamabob, but that would be
counter-productive because it would force it to spell out the static and
dynamic rules for thingamabobs, and it would force the users to learn
these rules.

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

From: Bob Duff
Sent: Tuesday, August 23, 2005  9:03 AM

> You are missing the point.
>
> Say that you have an (Ada 95) implementation that currently supports T.
> Not required, but surely allowed, right?

Yes.

>...Now this implementation (and its
> users) want to move to Ada 2005.  Nobody wants to start rejecting T.

An implementation is allowed to start rejecting T.
But I agree: the implementer probably does not *want* to do so.
And there is no requirement to do so.

>...They
> want to reject R, which is were the logical impossibility arises.  I claim
> that there is no rule in the manual that currently allows this.

What's wrong with my argument that 13.5.1(21) allows the implementation
to reject programs containing R, but not programs containing T but not R?

The RM shouldn't care whether the error message points to the line of
code with the rep clause for T, or the line of code declaring R.  In
fact, a friendly implementation would point to *both* lines.

> > I think if an implementation invents attributes, it gets to
> > make up the rules for those attributes.
>
> I disagree.  If an implementation claims that an attribute (or a pragma)
> is a representation item, it has to comply with the rules regarding
> representation items, and again there is no rule that says that you can
> reject a declaration because you didn't like some distantly related
> representation item.
>
> Of course, an implementation could say that the attribute is not a
> representation item, but a thingamabob, but that would be
> counter-productive because it would force it to spell out the static and
> dynamic rules for thingamabobs, and it would force the users to learn
> these rules.

An implementation can define an attribute called Tag_Offset,
and say in its documentation, "This is a representation item,
except that whenever you use it, you have to obey the following
rules: (1) every identifier in the program must have at least three
characters.  (2) No type name can start with the letter 'R'.
(3) <Some other silly rule.>  (4) You can't inherit from
an interface that has a different Tag_Offset."

Our job as language designers/maintainers it to promote uniformity for
the standard features, not to force implementers to design their
implementation-defined features in a sensible way.

From a practical point of view: How could you write an ACATS test that
fails implementations that define Tag_Offset in a "wrong" way?
If you can't test it, then it's not a meaningful rule.
You can't fail the ACATS because of what's in your implementation's
documentation!

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

From: Pascal Leroy
Sent: Tuesday, August 23, 2005  10:01 AM

> From a practical point of view: How could you write an ACATS
> test that fails implementations that define Tag_Offset in a
> "wrong" way? If you can't test it, then it's not a meaningful
> rule.

I could certainly write a test based on my first example (the one without
Tag_Offset).  It would have an OPTIONAL ERROR on the declaration of T (in
case the implementation doesn't allow the record rep spec) and an OK on
the declaration of R (because if you allowed T I still don't see on what
ground you are going to reject R).  And you would have to petition the
test claiming that you don't know how to support R.  And I would argue
that there is a perfectly good implementation strategy to deal with this
(put the tag at offset -4 by default) and that you should change your
compiler.

> You can't fail the ACATS because of what's in your
> implementation's documentation!

Ever heard of the Documentation Requirements?

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

From: Bob Duff
Sent: Tuesday, August 23, 2005  10:52 AM

Pascal said:

> > From a practical point of view: How could you write an ACATS
> > test that fails implementations that define Tag_Offset in a
> > "wrong" way? If you can't test it, then it's not a meaningful
> > rule.
>
> I could certainly write a test based on my first example (the one without
> Tag_Offset).

You're mixing up the two examples.  My claim is that you can't test
alleged rules that apply to *implementation-defined* features: pragmas,
attributes, non-standard modes.  This has to do with your *second*
example.

I agree that your *first* example is a testable issue.

>...It would have an OPTIONAL ERROR on the declaration of T (in
> case the implementation doesn't allow the record rep spec) and an OK on
> the declaration of R (because if you allowed T I still don't see on what
> ground you are going to reject R).  And you would have to petition the
> test claiming that you don't know how to support R.

No, I would base my petition on 13.5.1(21):

21    An implementation may reserve a storage place for the tag field of a
      tagged type, and disallow other components from overlapping that place.

I would say, "My implementation reserves offset 0 for the tag field of
R, and your test tries to make a component of R (namely Component)
overlap that place.  Therefore, your test is wrong."

I would not have to appeal to any difficulty of implementation.

This is the third time I've mentioned 13.5.1(21)!  Please tell
me specifically what you think of my interpretation of that paragraph!
I admit, it's a *little* bit of a stretch.  ;-)

(Oh, by the way, my hypothetical implementation would not choose to
reserve offset 0 for the tag field of T -- just for R.)

>...  And I would argue
> that there is a perfectly good implementation strategy to deal with this
> (put the tag at offset -4 by default) and that you should change your
> compiler.

Hmm, I hadn't thought of *that* implementation.  I was thinking I'd have
to play the C++ mutiple inheritance game.

> > You can't fail the ACATS because of what's in your
> > implementation's documentation!
>
> Ever heard of the Documentation Requirements?

I've heard of them.  ;-)  They are completely meaningless in any formal
sense, they are untestable, and they serve to *damage* the quality of
most implementation's documentation.  To my knowledge, nobody has ever
failed validation due to what's in their documentation, despite the fact
that in every implementation I've looked at, the documentation contains
serious falsehoods, and fails to properly address some of the
Documentation Requirements.

I claim that if I added this sentence:

    In this implementation, identifiers are limited to 6 characters.

to our documentation, we would not fail validation because of it.
(So long as the compiler does not limit identifiers to 6 characters,
of course.)

To summarize my suggestions:

    Your first example may or may not be a real issue.
    I think an AARM annotation is sufficient, but I wouldn't
    object to a narrow rule that addresses that specific issue.

    Your second example is not an issue, and we shouldn't waste ink
    talking about it in the RM.

Note that we agree on the outcome: compilers should be allowed to reject
both of your examples.  And compilers that choose to reject them can
also *accept* the examples without the "R".  We're merely arguing about
whether the RM needs to be changed to make that true, or whether it's
already true.

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

From: Pascal Leroy
Sent: Wednesday, August 24, 2005  8:01 AM

> I would say, "My implementation reserves offset 0 for the tag
> field of R, and your test tries to make a component of R
> (namely Component) overlap that place.  Therefore, your test
> is wrong."

And if I was running the circus I would say "surely your implementation
doesn't reserve offset 0 since it didn't reject the record representation
clause on type T, so you are lying to me; give yourself a slap on the
hand, and go fix your compiler".

> This is the third time I've mentioned 13.5.1(21)!  Please
> tell me specifically what you think of my interpretation of
> that paragraph! I admit, it's a *little* bit of a stretch.  ;-)

13.5.1(21) is an implementation advice regarding the support of record
representation clauses.  It tells you that you may reject a record
representation clause because some component overlaps with the storage you
have reserved for the tag.  In my first example, that mean that you are
perfectly entitled to reject the representation item for T.

However, I don't see how you could interpret this paragraph as giving you
a permission to reject a distantly related *type declaration*.  Remember,
there is no representation item for R, so you have to reject the type
declaration.  I guess I fail to see how 13.5.1(21) support the logical
connection between "I put the tag at offset 0" (fine) and "therefore I
reject R" (huh?).  In terms of logic, this doesn't seem to have more
justification than, say, "I put the tag at offset 0, therefore I reject
any tagged record that is not a null record".

I always find it unpleasant when the rules in section 13 cause constructs
that have nothing to do with representation items to be illegal.  There
are cases where that has to happen, and this is obviously one of these.
But I hate the notion that you have to dig an obscure IA to justify
rejecting a type declaration that looks perfectly good.  Imagine the
user's puzzlement when they get an error on type R saying "see
13.5.1(21)".

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

From: Bob Duff
Sent: Wednesday, August 24, 2005  3:17 PM

Pascal said:

> > I would say, "My implementation reserves offset 0 for the tag
> > field of R, and your test tries to make a component of R
> > (namely Component) overlap that place.  Therefore, your test
> > is wrong."
>
> And if I was running the circus...

I love that phrase, "if I was running the circus".  I first heard it
from Steve Baird.  Did he make it up?  ;-)

>... I would say "surely your implementation
> doesn't reserve offset 0 since it didn't reject the record representation
> clause on type T, so you are lying to me; give yourself a slap on the
> hand, and go fix your compiler".

I'm not lying!  My hypothetical implementation reserves offset 0 in R,
but does not reserve offset 0 in T.  What's wrong with that?

The actual rule my implementation uses is "If a tagged type inherits
from an interface, offset 0 is reserved for the tag."  13.5.1(21) seems
to me to allow that -- it doesn't say that I always have to reserve the
same storage place, or that if I reserve a place for the tag in one type
I must do so for all types.

I'm astonished that you think this is a lie.  The implementation wants
to reserve offset 0 for tags of interfaces.  Therefore of course it must
also reserve offset 0 for anything derived from an interface.  This is
really the truth -- it's really what's going on in the machine code!

> > This is the third time I've mentioned 13.5.1(21)!  Please
> > tell me specifically what you think of my interpretation of
> > that paragraph! I admit, it's a *little* bit of a stretch.  ;-)
>
> 13.5.1(21) is an implementation advice regarding the support of record
> representation clauses.

It's a "recommended level of support", which mutates from impl advice
into hard rules in the SP annex.  All the rules about which rep clauses
must be supported, and which need not be supported, are "recommended
level of support".  If you insist on a new rule forbidding (or allowing
implementations to forbid) your first example, then I would presume this
new rule should also be "recommended level of support".

>...  It tells you that you may reject a record
> representation clause because some component overlaps with the storage you
> have reserved for the tag.

It doesn't say anything about rejecting rep clauses.  It just says the
impl can disallow the overlapping.  Your example has overlapping (in R,
but not in T).

>...  In my first example, that mean that you are
> perfectly entitled to reject the representation item for T.
>
> However, I don't see how you could interpret this paragraph as giving you
> a permission to reject a distantly related *type declaration*.  Remember,
> there is no representation item for R, so you have to reject the type
> declaration.

"Reject" is Ada-83-think.  See 1.1.3(4.a).

There is no requirement that my compiler point to a particular line of
code, and say that's the one I'm rejecting.

>  I guess I fail to see how 13.5.1(21) support the logical
> connection between "I put the tag at offset 0" (fine) and "therefore I
> reject R" (huh?).  In terms of logic, this doesn't seem to have more
> justification than, say, "I put the tag at offset 0, therefore I reject
> any tagged record that is not a null record".

21    An implementation may reserve a storage place for the tag field of a
      tagged type, and disallow other components from overlapping that place.

R is a tagged type.  This rule allows me to reserve offset 0 for the tag
of R.  You have requested (in your first example) to have a component
overlapping that place.  (Your request is somewhat indirect -- it's
inherited from T.)  My hypothetical implementation disallows that
overlapping -- as we both agree it should.

> I always find it unpleasant when the rules in section 13 cause constructs
> that have nothing to do with representation items to be illegal.

R is derived from a type whose representation has been specified.
Why do you think R has "nothing to do with representation items"?

>...There
> are cases where that has to happen, and this is obviously one of these.
> But I hate the notion that you have to dig an obscure IA to justify
> rejecting a type declaration that looks perfectly good.  Imagine the
> user's puzzlement when they get an error on type R saying "see
> 13.5.1(21)".

I'm perfectly capable of designing a clear error message for this case,
and so are you.  But that's got nothing to do with the Ada RM.


P.S. I'm trying to think of wording to suggest that will allow
implementations to disallow your first example.  That's what you want,
right?  The best I can come up with is:

    If a type is derived from one or more interfaces, then the
    implementation may reserve a storage place for the tag field.
    It may disallow other components from overlapping that place,
    even if the relevant record_representation_clause came from an
    ancestor type.

But that's just a special case of what 13.5.1(21) already says!

Maybe just adding a NOTE, saying that 13.5.1(21) applies even in cases
where the rep clause came from an ancestor type?

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

From: Stephen W. Baird
Sent: Wednesday, August 24, 2005  4:22 PM

> I love that phrase, "if I was running the circus".  I first heard it
> from Steve Baird.  Did he make it up?  ;-)

"If I Ran the Circus" is a book by Dr. Seuss.

See also his discussion of extended character sets, "On Beyond Zebra!".

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

From: Pascal Leroy
Sent: Thursday, August 25, 2005  3:33 AM

> It's a "recommended level of support", which mutates from
> impl advice into hard rules in the SP annex.  All the rules
> about which rep clauses must be supported, and which need not
> be supported, are "recommended level of support".

There is no such thing as "hard rules".  The "recommended level of
support" IA become implementation requirements in annex C.  That's
reasonably clear from C.2(2).

This is another reason why I don't like 13.5.1(21): what does it mean for
an IR to say "an implementation may reserve..."?  This doesn't sound like
a requirement to me.  In fact 13.5.1 has an implementation permissions
section, and 13.5.1(21) should really go in that section.

> If you
> insist on a new rule forbidding (or allowing implementations
> to forbid) your first example, then I would presume this new
> rule should also be "recommended level of support".

No, it should be an implementation permission.  And it should go in 13.1,
because it really doesn't have much to do with record representation
clauses in the first place.

> "Reject" is Ada-83-think.  See 1.1.3(4.a).
>
> There is no requirement that my compiler point to a
> particular line of code, and say that's the one I'm rejecting.

Yeah, give me a break.  1.1.3(4.a) is largely disconnected from reality.
Try to pass the ACATS with a compiler that prints out '?' when it finds an
error anywhere in a unit.

>     If a type is derived from one or more interfaces, then the
>     implementation may reserve a storage place for the tag field.
>     It may disallow other components from overlapping that place,
>     even if the relevant record_representation_clause came from an
>     ancestor type.

I would not want to mention record_representation_clauses explicitly,
because I believe that other representation items can have the same
effect.  For instance, say that in my example I remove the
record_representation_clause and put a pragma Pack (T) instead.  In these
circumstances, I might choose to allocate only 16 bits for the tag
(instead of the default 32) because I know that my dispatch tables are all
gathered together in a small segment of my address space.

This causes the same problem because the interface assumes that the tag
has the default 32-bit representation, so there is no consistent layout
for R.

Of course, I am not saying that any of this is required, but we don't want
the RM to forbid it.

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

From: Bob Duff
Sent: Friday, August 26, 2005  8:26 AM

> > It's a "recommended level of support", which mutates from
> > impl advice into hard rules in the SP annex.  All the rules
> > about which rep clauses must be supported, and which need not
> > be supported, are "recommended level of support".
>
> There is no such thing as "hard rules".

By "hard rules", I just mean rules that a conforming implementation has
to obey, as opposed to mere "impl advice", which is "soft".

>...  The "recommended level of
> support" IA become implementation requirements in annex C.  That's
> reasonably clear from C.2(2).

Right.

> This is another reason why I don't like 13.5.1(21): what does it mean for
> an IR to say "an implementation may reserve..."?  This doesn't sound like
> a requirement to me.  In fact 13.5.1 has an implementation permissions
> section, and 13.5.1(21) should really go in that section.

There are many cases like this, and people do seem to find them
confusing.  It's sort of a double negative or something.

Consider, for example, 13.3(72), which is another example of the same
thing:

72    An implementation need not support specified Component_Sizes that are
      less than the Size of the component subtype.

73    An implementation should support specified Component_Sizes that are
      factors and multiples of the word size. For such Component_Sizes, the
      array should contain no gaps between components. For other
      Component_Sizes (if supported), the array should contain no gaps between
      components when packing is also specified; the implementation should
      forbid this combination in cases where it cannot support a no-gaps
      representation.

The reason it's written that way is so that when the SP Annex includes
these rules by reference, the "need not" part will be included.  If para
72 were not part of the "recommended levels", then implementations
conforming to the SP Annex would be required to obey para 73, but would
have no permission as in para 72, which would be impossible to implement.

I agree this is confusing, but unless we change it everywhere,
I suggest we stick with this style.

Consider the implementation permission at 13.1(20):

An implementation may place implementation-defined restrictions on
representation items.

If you conform to the SP annex, you no longer have this permission,
because it is *not* part of the "recommended levels".

> > If you
> > insist on a new rule forbidding (or allowing implementations
> > to forbid) your first example, then I would presume this new
> > rule should also be "recommended level of support".
>
> No, it should be an implementation permission.  And it should go in 13.1,
> because it really doesn't have much to do with record representation
> clauses in the first place.
>
> > "Reject" is Ada-83-think.  See 1.1.3(4.a).
> >
> > There is no requirement that my compiler point to a
> > particular line of code, and say that's the one I'm rejecting.
>
> Yeah, give me a break.

That's not a very language-lawyerly style of debate.  ;-)

>...  1.1.3(4.a) is largely disconnected from reality.
> Try to pass the ACATS with a compiler that prints out '?' when it finds an
> error anywhere in a unit.

You'd have to do a lot of splitting of B tests.  People don't do that
because it's easier to implement good error recovery, and because people
don't want to make useless compilers.  But I believe such splitting is
within the validation rules, and certainly conforms to the RM.

> >     If a type is derived from one or more interfaces, then the
> >     implementation may reserve a storage place for the tag field.
> >     It may disallow other components from overlapping that place,
> >     even if the relevant record_representation_clause came from an
> >     ancestor type.
>
> I would not want to mention record_representation_clauses explicitly,
> because I believe that other representation items can have the same
> effect.  For instance, say that in my example I remove the
> record_representation_clause and put a pragma Pack (T) instead.  In these
> circumstances, I might choose to allocate only 16 bits for the tag
> (instead of the default 32) because I know that my dispatch tables are all
> gathered together in a small segment of my address space.

Good point.  You just convinced that the rule should go in 13.1, and
that it should cover all rep items.  What about pragma Convention?
I suppose that could cause similar trouble?

But I still think the new rule should be "recommended level", so that it
gets included by reference in the SP Annex.

Your original suggestion was:

    Add after 13.1(13/1)

    Post-Compilation Rules

    "A set of representation or operational items that, taken together, are
    not supported by the implementation, is rejected prior to running the
    partition."

I still don't think that's quite what we want.  Erhard pointed out one
reason.

Another reason is that the above talks about what *is* supported.  What
we need to do is *define* what needs to be supported, and what need not
be supported (if the SP Annex is supported).  Something like this:

    An implementation may disallow a type declaration if it inherits
    from one or more interfaces, and the representation or operational
    items that apply to the interfaces, ancestor types, and the type
    itself, conflict with one another.

    [...AARM example using pragma Pack or pragma Convention...]

I see no reason to make this a post-compilation rule.

> This causes the same problem because the interface assumes that the tag
> has the default 32-bit representation, so there is no consistent layout
> for R.
>
> Of course, I am not saying that any of this is required, but we don't want
> the RM to forbid it.

Agreed.

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

From: Pascal Leroy
Sent: Wednesday, August 31, 2005  2:28 AM

> Good point.  You just convinced that the rule should go in
> 13.1, and that it should cover all rep items.  What about
> pragma Convention? I suppose that could cause similar trouble?

Yes, I suppose that Convention could have a similar effect.

> Another reason is that the above talks about what *is*
> supported.  What we need to do is *define* what needs to be
> supported, and what need not be supported (if the SP Annex is
> supported).  Something like this:
>
>     An implementation may disallow a type declaration if it inherits
>     from one or more interfaces, and the representation or operational
>     items that apply to the interfaces, ancestor types, and the type
>     itself, conflict with one another.
>
>     [...AARM example using pragma Pack or pragma Convention...]
>
> I see no reason to make this a post-compilation rule.

I think it's only progenitors that can cause trouble.  Furthermore, I
would not mention the type itself, as I would expect the representation
items for the type to be rejected, not the type declaration.  So my
preference would go to something like:

"An implementation may disallow a type_declaration if it has one or more
progenitors, and the representation or operational items that apply to the
progenitors and the ancestor types conflict with one another."

Is "may disallow" OK?  Or should it be "need not support"?

Do we have to mention "operational items"?  I don't think so because they
are not subject to the inheritance rule that apply to representation
items.

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

From: Tucker Taft
Sent: Wednesday, August 31, 2005  2:37 PM

...
> I think it's only progenitors that can cause trouble.  Furthermore, I
> would not mention the type itself, as I would expect the representation
> items for the type to be rejected, not the type declaration.  So my
> preference would go to something like:
>
> "An implementation may disallow a type_declaration if it has one or more
> progenitors, and the representation or operational items that apply to the
> progenitors and the ancestor types conflict with one another."
>
> Is "may disallow" OK?  Or should it be "need not support"?
>
> Do we have to mention "operational items"?  I don't think so because they
> are not subject to the inheritance rule that apply to representation
> items.

This sounds funny to me.  How could an implementation possibly
support conflicting aspect items?  Don't we want to *require* that
such conflicting aspect items be treated as an error, unless the
conflict can be (and is) resolved by an overriding aspect item?
To me "conflicting" implies a problem that must be reported.
I would include operational items for completeness.

I also don't see how this should be something that depends on
whether the implementation supports the Systems Programming Annex.

It seems this could be described either as a normal legality rule
(always the best if possible), or as an implementation requirement.
This rule seems inappropriate for an implementation permission
or as advice that doesn't become a requirement until you get
to the SP Annex.

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

From: Bob Duff
Sent: Wednesday, August 31, 2005  5:25 PM

I think you're getting confused by the double-negative nature of
recommended levels of support that give permission.

I think we need a permission, here, because we don't want to define
"conflicting".  We want to leave that up to the implementation.  That
is, we want to allow an implementation to declare certain type_decls
illegal, based on the *implementation*'s definition of conflicting
representation items.

We could then add a *requirement* that conflicting rep items cause some
illegality, but that seems unnecessary.

The point about SP annex is not to make something required, but to allow
implementations to reject these things.  Implementations that do not
support the SP annex already have permission to reject anything they
like.  We just need to make sure the wording does not negate that
permission for implementations that support the SP annex.

I suppose we could have a legality rule: Conflicting blah blah is
illegal.  "Conflicting blah blah" is implementation defined.

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

From: Tucker Taft
Sent: Wednesday, August 31, 2005  11:07 PM

I would prefer something like these latter two sentences.  I believe we
have gotten ourselves into trouble more than once with the double-negative
style of implementation permission, and if they confuse us,
the must be totally impenetrable to the typical user.

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

From: Pascal Leroy
Sent: Thursday, September 1, 2005  2:37 AM

> This sounds funny to me.  How could an implementation
> possibly support conflicting aspect items?  Don't we want to
> *require* that such conflicting aspect items be treated as an
> error, unless the conflict can be (and is) resolved by an
> overriding aspect item? To me "conflicting" implies a problem
> that must be reported. I would include operational items for
> completeness.

The only predefined operational items are, as far as I can tell,
External_Tag and the stream attributes.  Neither of those are inherited:
External_Tag always gets a default value, and the stream attributes are
built anew, possibly incorporating the corresponding attribute of the
parent.  In both cases, there cannot be any conflict.  So if we go for a
legality rule with a limited scope, we should not permit rejection of
operational items: we don't want an implementation to argue that there is
some mysterious conflict that prevents it from supporting some perfectly
good streaming attributes.

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

From: Pascal Leroy
Sent: Thursday, September 1, 2005  2:29 AM

Why don't you try to put some wording together?  It would be nice to have
something for Randy when he comes back from vacation, after Labor Day.

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

From: Bob Duff
Sent: Monday, September 5, 2005  3:56 PM

How about:

If a type_declaration has one or more progenitors, then any
representation items that apply to the progenitors and the ancestor
types shall not conflict with one another.  It is implementation defined
whether a set of representation items conflict.

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

From: Stephen W. Baird
Sent: Monday, September 5, 2005  4:44 PM

Is it sufficiently clear from this wording that a conflict is possible
even if no representation items apply to the progenitors?

In this example,

     package Pkg1 is
       type Ifc is interface;

       type T is tagged record Fld : Integer; end record;
       for T use record Fld at 0 range 0 .. Integer'Size - 1; end record;
       -- forces non-default tag location, at least for most implementations
     end Pkg1;

     with Pkg1;
     package Pkg2 is
       type D is new Pkg1.T and Pkg1.Ifc;
     end Pkg2;

we certainly want an implementation to have the option of rejecting Pkg2,
even if Pkg1 is accepted.

On the other hand, it would seem very odd to allow the rejection of Pkg2
if the representation clause in Pkg1 were commented out.
Do you think that we need to make the distinction between these two cases
clearer, or that the wording you've suggested already accomplishes this,
or that the distinction is unimportant?

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

From: Bob Duff
Sent: Monday, September 5, 2005  6:31 PM

> Is it sufficiently clear from this wording that a conflict is possible
> even if no representation items apply to the progenitors?

No.  :-(

> In this example,
>
>      package Pkg1 is
>        type Ifc is interface;
>
>        type T is tagged record Fld : Integer; end record;
>        for T use record Fld at 0 range 0 .. Integer'Size - 1; end record;
>        -- forces non-default tag location, at least for most implementations
>      end Pkg1;
>
>      with Pkg1;
>      package Pkg2 is
>        type D is new Pkg1.T and Pkg1.Ifc;
>      end Pkg2;
>
> we certainly want an implementation to have the option of rejecting Pkg2,
> even if Pkg1 is accepted.
>
> On the other hand, it would seem very odd to allow the rejection of Pkg2
> if the representation clause in Pkg1 were commented out.

I agree with all of the above.  Certainly, if there are no rep items in
sight, the implementation should be required to choose some
representation that works.  So I guess the point is: a rep item could
conflict with another rep item, or with a default-chosen rep.  But
default-chosen reps can't conflict with each other.

With that in mind, let me try again:

A type_declaration is illegal if it has one or more progenitors, and a
representation item applies to a progenitor or ancestor, and this
representation item conflicts with the representation of some other
progenitor or ancestor.  The cases that cause conflicts are
implementation defined.

AARM examples can make it clear what in the world the above actually
means in real life.

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

From: Pascal Leroy
Sent: Tuesday, September 6, 2005  2:50 AM

Sounds good to me.

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

From: Robert Dewar
Sent: Monday, September 5, 2005  7:21 PM

> If a type_declaration has one or more progenitors, then any
> representation items that apply to the progenitors and the ancestor
> types shall not conflict with one another.  It is implementation defined
> whether a set of representation items conflict.

can you see a case where the conflicting rep items are all ones that
are required to be accepted. Pascal's example is crtainly a case
where the rep clause in question does not have to be accepted.

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

From: Bob Duff
Sent: Monday, September 5, 2005  8:10 PM

Well, I suppose pragma Pack is required to be accepted.
But it's not required to affect the tag representation.
As far as I can tell, the only issue is the representation
of tags -- it has to agree for all progenitors and ancestors
of a given type.

>... Pascal's example is crtainly a case
> where the rep clause in question does not have to be accepted.

Right.

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

From: Robert Dewar
Sent: Monday, September 5, 2005  8:31 PM

Right

Can't we just be specific then and say something like

It is implementation defined whether component clauses for
tagged types are accepted, and if so, what such clauses
are accepted. In some cases, the acceptance of such a rep
clause may depend on the presence of other type declarations
either in the same package or in some different unit.
An implementation may reject the rep clause, or reject
some type declaration affected by this rep clause, or
reject the progran at bind/link time.

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

From: Pascal Leroy
Sent: Tuesday, September 6, 2005  2:57 AM

It's not only component_clauses that cause trouble.  Pragma Pack and
pragma Convention could conceivably alter the layout of the tag.
Furthermore, Bob convinced me that there is no point in deferring the
check to link time when it can always be done at compile time.

I think that Bob's wording is just fine.  It addresses the problem at
hand, and is specific enough that implementations cannot use it to reject
any random type_declaration.

We should remember that implementers are not in the business of sabotaging
their own implementation, so they are unlikely to use this rule to reject
types that do not cause serious trouble.  At any rate, we should trust the
hidden hand of the market.

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

From: Robert Dewar
Sent: Tuesday, September 6, 2005  5:39 AM

I think pragma Convention is not a problem (i.e. ok to reject), but to
me rejecting pragma Pack (or decls depending on it in someway) would
be non-conforming and should not be allowed.

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

From: Robert Dewar
Sent: Tuesday, September 6, 2005  5:41 AM

> It's not only component_clauses that cause trouble.  Pragma Pack and
> pragma Convention could conceivably alter the layout of the tag.
> Furthermore, Bob convinced me that there is no point in deferring the
> check to link time when it can always be done at compile time.

Actually, I think the permission should extend quite generally to
any rep clause that is not required to be accepted.

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

From: Robert I. Eachus
Sent: Tuesday, September 6, 2005  9:33 PM

Bob Duff wrote:

>  How about:
>
>If a type_declaration has one or more progenitors, then any
>representation items that apply to the progenitors and the ancestor
>types shall not conflict with one another.  It is implementation defined
>whether a set of representation items conflict.

I prefer a wording that starts out closer to the expected problem:

A type declaration is illegal if it inherits one or more conflicting
representation items from progenitors or ancestor types.  It is
implementation defined whether a set of representation items conflict.

So far so good. But as I understand the discussion there is another
sentence needed:

A type-related representation item is not allowed if it conflicts with a
user-specified representation item inherited from a progenitor or ancestor type.


Obviously if this sentence is added to the paragraph, I would put it in
the middle.  Ancient history now, but anyone else remember that derived
types were allowed to have different representation specifications to
support a Steelman requirement?  That requirement is mostly satisfied in
practice by conversions between unrelated numeric types, and was never
very useful for enumeration types anyway.

Two further questions:  Are there any operational items that we have to
worry about here?  The only ones that I can think of that might be an
issue are 'Read and 'Write, and as I read 13.13.2(8.1/1) those are not
inherited for tagged types.

Second, do we need to define a recommend level of support here for the
purposes of Annex C?  If so what should it be?  I think that 13.5.1(22)
covers the tag location issue.  But do we need to recommend that
indentical specifications for things like bit-order don't conflict?
Should we go further and recommend that clauses that place the tag at
the beginning of a record be considered non-conflicting?  Or just that
clauses that place the tag in the same place be allowed?  I don't like
that since it probably takes some work by implementors, when what we
really want is clauses that place the tag in its normal place for that
implementation be considered non-conflicting.

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

[This thread occurred privately, and then was forwarded later to the ARG list;
thus it's out of order here. - ED]

From: Javier Miranda
Sent: Thursday, September 1, 2005  2:47 AM

Pascal,

This is a combined example (based in the examples used in this thread):

>	type I1 is interface;
>	for I1'Tag_Offset use 0; -- Just an example syntax, could be a pragma, etc.
>
>	type I2 is interface;
>	for I2'Tag_Offset use 4;
>
>       type T1 is tagged record
>          Component : Natural;
>       end record;
>       for T1 use record
>          Component at 0 range 0 .. 31; -- Tag somewhere else, probably at 4
>       end record;
>
>	type R is new T1 and I1 and I2 with null record; -- Where is the tag?

Being interfaces "abstract" types with no data it seems to me wrong to specify
the position of their tag; they have no data and I only see appropriate the use
of representation clauses in types containing data.

> ... But obviously there is not consistent layout for type R.

Following this argument there is no conflict; the layout of R should be
specified after its declaration (which is compatible with existing Ada95 code)

       type R is new I1 and I2 with null record;
       for  R'Tag_Offset use ...;
       for I1'Tag_Offset use ...;
       for I2'Tag_Offset use ...;

> "An implementation may disallow a type_declaration if it has one or more
> progenitors, and the representation or operational items that apply to
> the progenitors and the ancestor types conflict with one another."

Following the proposed approach this rule is no longer needed.

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

From: Javier Miranda
Sent: Thursday, September 1, 2005  3:23 AM

I have just detected one bug in my example. Replace:

>        type R is new I1 and I2 with null record;
>        for  R'Tag_Offset use ...;
>        for I1'Tag_Offset use ...;
>        for I2'Tag_Offset use ...;

by
        type R is new I1 and I2 with null record;
        for  R'Tag_Offset use ...;
        for  R.I1'Tag_Offset use ...;
        for  R.I2'Tag_Offset use ...;

or even better:

        type R is new I1 and I2 with null record;
        for  R use
           Tag    at ... range ...;
           I1'Tag at ... range ...;
           I2'Tag at ... range ...;
           ...;
        end record;

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

From: Robert Dewar
Sent: Wednesday, September 14, 2005  3:04 AM

> Being interfaces "abstract" types with no data it seems to me wrong to specify
> the position of their tag; they have no data and I only see appropriate the use
> of representation clauses in types containing data.

These are implementation dependent clauses, so you cannot say "wrong"
about them!

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

From: Pascal Leroy
Sent: Friday, September 2, 2005  3:00 AM

> Being interfaces "abstract" types with no data it seems to me
> wrong to specify the position of their tag; they have no data
> and I only see appropriate the use of representation clauses
> in types containing data.

You are confused by your implementation model, for which presumably the
Tag_Offset attribute would not make much sense.  In order to ensure C++
compatibility, you have several tags for each object.  That's fine, but
this is certainly *not* an implementation model that we want to *require*
(although of course we want to allow it).  Speaking for IBM Rational, we
are going to have a single tag for each object (and a number of auxiliary
data structures for dealing with interfaces).  A single tag is actually
the model that the ARG had in mind when designing interfaces.  The premise
was that interfaces should be substantially easier/cheaper to implement
than full multiple inheritance, and therefore would not need the
associated runtime baggage.  I am not saying that your implementation is a
bad idea, but it is rather atypical I think, in the sense that you favored
compatibility with C++ over other criteria.

So this discussion regarding tag placement is probably only relevant to
implementations that have a single tag per object.  For such an
implementation, one reason for wanting the tag at a non-zero offset might
be to interface with another language/library that has such a requirement.
Another reason might be for interfacing with a system that doesn't care
about the location of the tag, but needs it to get "out of the way" (we
have had that request once).

Note incidentally that, ignoring the tag for a moment, it makes perfect
sense to specify some representation items for interfaces.  Even though
there are no values of interface types, there are pieces of code that are
compiled using only the minimal properties of interfaces.  For instance:

	type Int is interface;
	for Int'Alignment use 8;

	procedure P (X : Int'Class) is
	begin
	   ... -- Take advantage of the alignment of X to generate better
               -- code here.
	end;

Evidently, all objects of types descended from Int would have to be 8-byte
aligned.  Generally speaking interfaces represent a "contract", and that
contract may include physical properties like the alignment.

> Following this argument there is no conflict; the layout of R
> should be specified after its declaration (which is
> compatible with existing Ada95 code). Thus your example would
> be written as follows:
>
>         type R is new I1 and I2 with null record;
>         for  R'Tag_Offset use ...;
>         for  R.I1'Tag_Offset use ...;
>         for  R.I2'Tag_Offset use ...;

This is inventing new syntax (R.I1 is not a valid name in any dialect of
Ada).  Implementations are not allowed to do that even for
implementation-defined attributes, and we (the ARG) are not going to do
that at such a late date, even if this was a good idea, which it isn't.

> or even better:
>
>         type R is new I1 and I2 with null record;
>         for  R use
>            Tag    at ... range ...;
>            I1'Tag at ... range ...;
>            I2'Tag at ... range ...;
>            ...;
>         end record;

This is just fine, but again it only makes sense in your implementation
model.  The rest of us will have a single tag per object.

> Following the proposed approach this rule is no longer needed.

I disagree for the reasons exposed above.  The point is that the RM should
not disallow specifying the tag location for an implementation that uses a
single tag per object.  At the moment it does.  This needs fixing.

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

From: Robert Dewar
Sent: Friday, September 2, 2005  5:52 AM

I find this entire discussion confused and muddled by
implementation models. After all there is no requirement
that objects have tag fields at all, and there are
perfectly viable implementation models where there are
no such fields.

I would completely remove from the RM any attempt to
talk about details of rep clauses for implementation
dependent fields. Let an implementation decide what
it does or does not want to allow here, and don't make
any attempt to promote portability.

If an implementation accepts a combination of rep clauses
that results in a blow up, that's a bug!

Javier has started an untracked thread here which seems
very unfortunate,

Javier, please take care of properly forwarding these
messages to the arg list.

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

From: Robert Dewar
Sent: Friday, September 2, 2005  6:02 AM

Pascal Leroy wrote:
>   I am not saying that your implementation is a
> bad idea, but it is rather atypical I think, in the sense that you favored
> compatibility with C++ over other criteria.

atypical is a curious word to use for the implementation used
by far the most Ada programmers, and so far the only available
implementation of Ada 2005 :-)

And yes indeed, since one of the major purposes of interfaces
for us is interfacing to other languages, conforming to the C++
ABI is indeed a requirement for us. We don't think we have users
for whom better performance than C++ in this respect is a
requirement, whereas interfacing to C++ is clearly a customer
requirement.

If our competitors choose to ignore what we think is a very
important requirement, that's fine with us, but the RM should
not in any way prefer or suggest one implementation over another.
Let the marketplace decide, and let implementations do what
they like here without constraints. Perhaps someone will surprise
us wit a completely different model that we have not anticipated
and hich is better than either of the models on the table.

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

From: Pascal Leroy
Sent: Saturday, September 3, 2005  2:44 AM

> I find this entire discussion confused and muddled by
> implementation models. After all there is no requirement that
> objects have tag fields at all, and there are perfectly
> viable implementation models where there are no such fields.

This being a chapter 13 issue, implementation models are perfectly
relevant.  Yes, it is possible to implement tagged types without tag
fields, but that's not the point.  The point is that an implementation
should be *allowed* to use an implementation model that has one tag field
per object.  The RM as written precludes such a model.

> I would completely remove from the RM any attempt to
> talk about details of rep clauses for implementation
> dependent fields. Let an implementation decide what
> it does or does not want to allow here, and don't make
> any attempt to promote portability.
>
> If an implementation accepts a combination of rep clauses
> that results in a blow up, that's a bug!

I fear that you are missing the point.  The construct that is problematic
in the case under discussion is not a rep clause, it a type declaration.
Implementations are allowed to reject rep clauses that they don't like,
but they don't get to reject type declarations that they don't like.  A
permission is needed in this particular case.

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

From: Pascal Leroy
Sent: Saturday, September 3, 2005  2:52 AM

> atypical is a curious word to use for the implementation used
> by far the most Ada programmers, and so far the only
> available implementation of Ada 2005 :-)

The word "atypical" was not intended to imply a value judgment.  However,
I believe that most the compilers which support interfaces, in whatever
language, don't use a full-fledged MI model.  In that sense your solution
is at one extreme of the design space.  That's just a fact, and it's true
regardless of how many people use your technology, so please don't give me
the marketing pep talk.

> If our competitors choose to ignore what we think is a very
> important requirement, that's fine with us, but the RM should
> not in any way prefer or suggest one implementation over
> another. Let the marketplace decide, and let implementations
> do what they like here without constraints.

Agreed.  The point I am arguing is that the RM is currently preventing a
single-tag-per-object implementation model, and that's just not acceptable
to me.  If this situation persisted, I would have no qualms about voting
against the entire Amendment.

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

From: Robert Dewar
Sent: Saturday, September 3, 2005  6:58 AM

Pascal Leroy wrote:

> This being a chapter 13 issue, implementation models are perfectly
> relevant.  Yes, it is possible to implement tagged types without tag
> fields, but that's not the point.  The point is that an implementation
> should be *allowed* to use an implementation model that has one tag field
> per object.  The RM as written precludes such a model.

I guess I would agree if this can be done without too much
violence to the current semantic model.

> I fear that you are missing the point.  The construct that is problematic
> in the case under discussion is not a rep clause, it a type declaration.
> Implementations are allowed to reject rep clauses that they don't like,
> but they don't get to reject type declarations that they don't like.  A
> permission is needed in this particular case.

It's pretty uncomfortsable to have legality in the absence
of rep clauses be implementation dependent.

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

From: Robert Dewar
Sent: Saturday, September 3, 2005  7:11 AM

...
> The word "atypical" was not intended to imply a value judgment.  However,
> I believe that most the compilers which support interfaces, in whatever
> language, don't use a full-fledged MI model.  In that sense your solution
> is at one extreme of the design space.  That's just a fact, and it's true
> regardless of how many people use your technology, so please don't give me
> the marketing pep talk.

Well, certainly one important Java compiler, gcj, uses
the c++ abi, precisely for easy interfacing to C++.

> Agreed.  The point I am arguing is that the RM is currently preventing a
> single-tag-per-object implementation model, and that's just not acceptable
> to me.  If this situation persisted, I would have no qualms about voting
> against the entire Amendment.

I am confused, I though that this amendment had
already been approved by WG 9. Isn't that what the
line:

!status WG9 Approved 04-06-18

in AI-251 is about?

That being said, I think it reasonable to make an effort to
accomodate the requirements of one of the few compilers
attempting to implement the language, but I still do not like
legality in the absence of rep clauses being impl defined.

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

From: Robert Dewar
Sent: Saturday, September 3, 2005  7:18 AM

> The word "atypical" was not intended to imply a value judgment.  However,
> I believe that most the compilers which support interfaces, in whatever
> language, don't use a full-fledged MI model.  In that sense your solution
> is at one extreme of the design space.  That's just a fact, and it's true
> regardless of how many people use your technology, so please don't give me
> the marketing pep talk.

Actually, it seems to me to be a fairly natural choice for
implementing the interface model as defined in Ada 2005. I
don't see it as extreme.

(and by the way don't you find it a bit over the top to be
using terms like atypical and extreme .. I do, these are
certainly value-laden words better kept out of technical
discussions :-)

I do see it as reasonable to accomodate the one tag model
if it can be done without too much difficulty. I must say I
found the discussion of rep clauses and Tag_Offset confusing,
since as I think you agree the discussion of impl defined
attributes and rep clauses cannot by definition be relevant
to the discussion of what type declarations are legal.

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

From: Pascal Leroy
Sent: Monday, September 5, 2005 12:42 AM

> It's pretty uncomfortsable to have legality in the absence
> of rep clauses be implementation dependent.

Well, there has to be a rep clause somewhere for a problem to arise.  But
it can be on a distantly-related type.

Anyway I share your discomfort, and that's why I like Bob Duff's notion of
making the permission very narrow.

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

From: Pascal Leroy
Sent: Monday, September 5, 2005 12:46 AM

> I am confused, I though that this amendment had
> already been approved by WG 9. Isn't that what the
> line:
>
> !status WG9 Approved 04-06-18
>
> in AI-251 is about?

Individual AIs have been approved by WG9, but that happened at a time when
we had not considered the (numerous) interactions between the AIs.  There
won't be a formal WG9 vote on the totality of the Amendment (including all
the interactions) before there is a final RM, which is expected to happen
this fall.  Of course, corrections at this point are of a rather minor
nature, but they can still have a quite significant impact on
implementations (that's the case with this representation business).

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

From: Pascal Leroy
Sent: Monday, September 5, 2005 12:59 AM

> (and by the way don't you find it a bit over the top to be
> using terms like atypical and extreme .. I do, these are
> certainly value-laden words better kept out of technical
> discussions :-)

You're right.  Apologies for that.

> I do see it as reasonable to accomodate the one tag model
> if it can be done without too much difficulty. I must say I
> found the discussion of rep clauses and Tag_Offset confusing,
> since as I think you agree the discussion of impl defined
> attributes and rep clauses cannot by definition be relevant
> to the discussion of what type declarations are legal.

Javier mentioned the Tag_Offset attribute, which is not the interesting
example, because you can do pretty much what you like with implementation
defined attributes.  More interesting is the following example, shown in
my original message.  First, declare an interface without any
representation item:

	type Int is interface;

The compiler generates code for Int or Int'Class (e.g. dispatching calls)
assuming that the tag is at offset 0.  Then declare a root tagged type T1
with a record rep clause that forces a "normal" component to land at
offset 0:

	type T1 is tagged
	   record
	      C : Integer;
	   end record;

	for T1 use
	   record
	      C at 0 range 0..31;
	   end record;

With this representation clause, presumably the tag is at offset 4.  Of
course there is no requirement for an implementation to support this rep
clause, but if it does in Ada 95 (ours does) it would be distinctly
obnoxious to have to make it illegal in Ada 2005 (that would be an
unacceptable incompatibility for us).  Now combine Int and T1 as follows:

	type T2 is new T1 and Int with null record;

Int wants the tag at offset 0, but T1 has a user component there, so there
is no consistent layout for T2.  I claim that an implementation should be
allowed to reject T2 on the grounds that it pulls together inconsistent
representation items.  Forcing implementations to either reject the rep
clause for T1, or to use a full multiple inheritance model, are not
acceptable options in my view.

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

From: Robewrt Dewar
Sent: Monday, September 5, 2005  3:02 AM

>>It's pretty uncomfortsable to have legality in the absence
>>of rep clauses be implementation dependent.
>
> Well, there has to be a rep clause somewhere for a problem to arise.  But
> it can be on a distantly-related type.

But isn't this a impl defined rep clause?
in which case you are allowed to reject the program

it is fine for this rejection to be in the form of rejecting a type
declaration (the issue of what is rejected is irrelevant from an
RM point of view, and irrelevant from an ACATS point of view for
an impl-defined rep clause). If you are saying that you want
permission to reject a type clause just in case there might be
such a rep clause, I don't like it. Better just not to allow
the rep clause, the rejection can be at bind time if necessary.

> Anyway I share your discomfort, and that's why I like Bob Duff's notion of
> making the permission very narrow.

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

From: Robert Dewar
Sent: Monday, September 5, 2005  3:22 AM

Pascal Leroy wrote:

> Int wants the tag at offset 0, but T1 has a user component there, so there
> is no consistent layout for T2.  I claim that an implementation should be
> allowed to reject T2 on the grounds that it pulls together inconsistent
> representation items.  Forcing implementations to either reject the rep
> clause for T1, or to use a full multiple inheritance model, are not
> acceptable options in my view.

There is no requirement that the example program you gave be allowed.
It can be rejected because of the combined presence of the rep clause
and the type declaration. To me it is fine if this rejection comes
in the form of rejecting the type declaration.

Certainly we don't require that implementations accept the rep clause
you suggest.

I would suggest a rather general permission of the form:

Programs containing rep clauses not required by the RM may be rejected.
This rejection may be in the form of rejecting the rep clause itself,
or in the form of rejecting other constructs depending in some way on
the rep clause in question or in the form of rejection at bind time.

To me this is already implicit in the language definition. I don't
think we should ever be in the business of saying that you don't need
to accept some impl defined extension, but if you do then the consequence.

But I have no objection to making some gweneral statement to that
effect, and in fact making it more general.

For example, I think iot is fine for an implementation to introduce
a 128-bit integer type that cannot be used for indexing arrays.

Bottom line. A compiler may reject the program you provide.
How this rejection occurs should not be micromanaged by the RM.
is that you must accept all programs containing this extension.

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


Questions? Ask the ACAA Technical Agent