Version 1.4 of ai05s/ai05-0197-1.txt

Unformatted version of ai05s/ai05-0197-1.txt version 1.4
Other versions for file ai05s/ai05-0197-1.txt

!standard 3.9.2(20.1/2)          11-03-17 AI05-0197-1/02
!standard 3.9.2(20.2/2)
!class binding interpretation 10-02-08
!status work item 10-02-08
!status received 09-06-09
!priority Low
!difficulty Medium
!qualifier Error
!subject Dispatching when there are multiple inherited operations
!summary
Dispatching calls the inherited concrete subprogram when multiple operations are inherited.
!question
AI05-0126-1 changes the wording of 3.9.2(20.2/2) to talk about "the corresponding operation of the parent type or progenitor type from which the operation was inherited" handles the cases that motivated that question.
But what happens here?
package Pack1 is type Int1 is interface; procedure Op1 (X : Int1) is null; -- (1) end Pack1;
package Pack2 is type T2 is tagged null record; private procedure Op1 (X : T2); -- (2) end Pack2;
with Pack1, Pack2; package Pack2.Pack3 is type T3 is new Pack2.T2 and Pack1.Int1 with null record; -- procedure Op1 (X : T3) is null; [inherited (1)] --private -- procedure Op1 (X : T3); [inherited (2)] end Pack2.Pack3;
with Pack2.Pack3; procedure Proc4 is X : Pack3.T3; begin Pack2.Pack3.Op1 (X); end Proc4;
Now there are two implicitly declared operations that could be considered as "the operation" in the new 3.9.2(20.2). At any point, only one or the other will be visible, but which one depends on whether the private part of Pack2.Pack3 is visible at that point. Applying 3.9.2(20.2) [as modified by AI05-126] means we have to determine which one we're talking about, so that we can determine "the parent type or progenitor type from which the operation was inherited" -- we can't call both. If it's the first one, then the call to Op1 is the same as a call on (1), which is null; if it's the second one, then it's the same as a call on (2). Which is intended? (A call on (2).)
!recommendation
(See Summary.)
!wording
Insert a new bulleted item after 3.9.2(20.1/3):
* If the corresponding operation is a predefined operator then the action comprises an invocation of that operator;
[Editor's note: This handles predefined "=", which is not inherited but rather constructed anew for each time.]
Modify 3.9.2(20.2/2):
* otherwise, the action is the same as the action for the corresponding operation
of the parent type or progenitor type from which the operation was inherited. {If there is more than one such corresponding operation, the action is that for the operation that is not a null procedure, if any; otherwise, the action is that of an arbitrary one of the operations.}
Add to the AARM note 3.9.2(20.a):
For the last bullet, if there are multiple corresponding operations for the parent and progenitors, all but one of them have to be a null procedure. (If the progenitors declared abstract routines, there would have to be an explicit overriding of the operation, and then the first bullet would apply.) We call the non-null routine if one exists.
[Editor's note: An alternative formulation for this wording would be to talk about the parent: "If there is more than one such corresponding operation, the action is that for the operation of the parent type, if any; otherwise, the action is that of an arbitrary one of the operations." This works because the parent operation is the only one that can be concrete. Not sure if this is better; rewording of the AARM Note would be required if this option is chosen.]
!discussion
Wording using "overriding" seems better than the proposed wording: "If there is more than one such corresponding operation, the action is that for the operation that overrides the others."
But this does not work properly, as "overriding" depends on the order of implicit declarations. If the declarations don't occur at the same place, the later one overrides the earlier one (by 8.3(12)), even though the later one might be null. For example:
package Pack1 is type Int1 is interface; private procedure Op1 (X : Int1) is null; -- (1) end Pack1;
package Pack2 is type T2 is tagged null record; procedure Op1 (Y : Int1); -- (2) end Pack2;
with Pack1, Pack2; package Pack1.Pack3 is type T3 is new Pack2.T2 and Pack1.Int1 with null record; -- procedure Op1 (Y : T3); [inherited (2)] -- (3) --private -- procedure Op1 (X : T3) is null; -- [inherited (1), overrides (3) by 8.3(12)] -- (4) end Pack1.Pack3;
package body Pack1.Pack3 is Obj : T3; begin Op1 (X => Obj); -- (5) end Pack1.Pack3;
For the call (5), (4) is the declaration that is called. That can be verified by using the formal parameter name X in named notation in the call; a call using parameter name Y is illegal as declaration (3) is hidden from all visibility.
(4) is a null procedure, which we don't want to call in this case. Since (5) is a dispatching call, we call the body determined by the definition of the execution of a dispatching call. The wording using overriding would indeed call (4), which is not what we want.
However, so long as we take care that the wording of the definition of the execution of a dispatching call is correct, we are OK (even for statically bound calls like this one). With proper wording, we still call the body of the operation declared at (2). That makes it important that we get this wording right.
[Editor's note: An alternative solution would be to ban hidden primitive routines for interfaces. They're already illegal for abstract subprograms, so only null subprograms would be affected, and this case seems to be messier than it is worth. Then we could use the overriding wording.]
--!corrigendum 3.9.2(20.2/2)
!ACATS Test
An ACATS C-Test similar to the example in the question should be constructed.
!appendix

!topic AI05-126
!reference AI05-126, 3.9.2(20-20.2)
!from Adam Beneschan 09-06-09
!discussion


I have a feeling AI05-126 still isn't quite right.

I notice that the phrase in 3.9.2(20.2) was changed from

  "otherwise, the action is the same as the action for the
  corresponding operation of the parent type"

to

  "otherwise, the action is the same as the action for the
  corresponding operation of the parent type or progenitor type from
  which the operation was inherited".

I think that that change was necessary, or else this simple case couldn't be
handled:

  package Pack1 is
      type Int1 is interface;
      procedure Op1 (X : Int1) is null;
  end Pack1;

  package Pack2 is
      type T2 is tagged null record;
  end Pack2;

  with Pack1, Pack2;
  package Pack3 is
      type T3 is new Pack2.T2 and Pack1.Int1 with null record;
  end Pack3;

  with Pack3;
  procedure Proc4 is
      X : Pack3.T3;
  begin
      Pack3.Op1 (X);
  end Proc4;

Using the old wording, we'd run into problems when determining what happens
during the call on the dispatching operation on an object of type T3.  3.9.2(20)
wouldn't apply, since the Op1 operation is not "explicitly declared" for the
type T3; 3.9.2(20.1) doesn't apply since no tasks or protected types are
involved; so we have to apply 3.9.2(20.2), which referred to the "corresponding
operation of the parent type".  But the parent type is T2 and there is no
corresponding operation.

The new wording, which talks about "the corresponding operation of the parent
type or progenitor type from which the operation was inherited" handles this
case.  But what happens here?

  package Pack1 is
      type Int1 is interface;
      procedure Op1 (X : Int1) is null;
  end Pack1;

  package Pack2 is
      type T2 is tagged null record;
  private
      procedure Op1 (X : Int1);
  end Pack2;

  with Pack1, Pack2;
  package Pack2.Pack3 is
      type T3 is new Pack2.T2 and Pack1.Int1 with null record;
      -- procedure Op1 (X : T3) is null;  [inherited from Int1]
  --private
      -- procedure Op1 (X : T3);          [inherited from T2]
  end Pack2.Pack3;

  with Pack2.Pack3;
  procedure Proc4 is
      X : Pack3.T3;
  begin
      Pack2.Pack3.Op1 (X);
  end Proc4;

Now there are two implicitly declared operations that could be considered as
"the operation" in the new 3.9.2(20.2).  At any point, only one or the other
will be visible, but which one depends on whether the private part of
Pack2.Pack3 is visible at that point. Applying 3.9.2(20.2) [as modified by
AI05-126] means we have to determine *which* *one* we're talking about, so that
we can determine "the parent type or progenitor type from which the operation
was inherited".  If it's the first one, then the call to Op1 is the same as the
call to the corresponding operation on Int1, which is null; if it's the second
one, then it's the same as the call to the corresponding operation on T2, the
one defined in Pack2.

In this (statically tagged) case, it might make sense to use the one that's
visible at the point of the call.  From Proc4, we can "see" the null Op1
inherited from Int1, and we can't see the Op1 inherited from T2, so it *might*
make sense that we can't directly call the latter one---although I thought that
in cases like this the intent was that the one with the body should be called.
(That's what would happen if an overriding Op1 were explicitly declared in the
private part of Pack2.Pack3.)  So even in the statically tagged case, I don't
think the wording in AI05-126 leads to the desired result.

But the dynamically tagged case is more confusing:

  with Pack1;
  procedure Proc5 (X : Int1'Class) is
  begin
      Pack1.Op1 (X);
  end Proc5;

Assume Proc5 is called on an object of type T3.  3.9.2(20-20.2) is still
supposed to apply, using the type identified by the controlling tag, and under
the new wording we need to determine what "the operation" in the phrase "parent
type or progenitor type from which the operation was inherited" means, and I'm
not at all clear on what "the operation" is.  The whole section deals with "a
call on a dispatching operation", and I think "the dispatching operation" is the
one declared in Pack1.  Clearly it would make no sense to say that this is "the
operation" for applying 3.9.2(20.2).

So what is "the operation"?  If we mean the one that's implicitly declared for
the type identified by the controlling tag, i.e. T3---there are two of them,
which one?  (And note that it may not work to decree that it's the one that
overrides the other, because I could mess it up with this:)

  package Pack1 is
      type Int1 is interface;
  private
      procedure Op1 (X : Int1) is null;
  end Pack1;

  package Pack2 is
      type T2 is tagged null record;
      procedure Op1 (X : Int1);
  end Pack2;

  with Pack1, Pack2;
  package Pack1.Pack3 is
      type T3 is new Pack2.T2 and Pack1.Int1 with null record;
      -- procedure Op1 (X : T3);          [inherited from T2]
  --private
      -- procedure Op1 (X : T3) is null;
      --     [inherited from Int1, overrides the one inherited from T2
      --      by 8.3(12)]
  end Pack1.Pack3;

So I don't think the proposed wording in AI05-126 is adequate.

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

From: Steve Baird
Sent: Thursday, March 3, 2011  3:28 PM

> AI05-0197-1: Improve this wording (with help from Tucker). [Sorry, I
> don't have any more detail in my notes than that; they say "Tucker
> does not like this wording which" and little else.]

Neither Tuck nor I could reconstruct the problem that we thought we had identified at the Tampa meeting, but I did find a couple of new issues while trying.

Again, thanks to Randy and Tuck for their review and suggestions.

Problem #1:

Applying 3.9.2(20-20.3) to a dispatching call to the predefined "=" operator of
a tagged type, it says

   Is it explicitly declared? No. Move to next bullet.

   Is it implemented via an entry/protected subp? No. Move to next
   bullet.

   This one begins with "otherwise", so it must be right!
   We use the parent's equality op and extension components
   do not participate in the result. Not good.

Suggested fix:

!wording (in addition to the existing wording for this AI)

   Insert a new bulleted item after 3.9.2(20.1/3):

   * If the corresponding operation is a predefined operator
     then the action comprises an invocation of that operator;

This should not require any change to any compiler; this is just changing the
wording to reflect what was obviously intended to begin with.


Problem #2:

Making an arbitrary choice among null procedures assumes that they are
interchangeable, and leads to problems if they are not.

Consider the following example:

    declare
       package Pkg1 is
           type Ifc is interface;
           procedure Op (X : in out Ifc) is null;
       end Pkg1;

       package Pkg2 is
          type T is tagged null record
            with Type_Invariant => Is_Valid (T);
          procedure Op (X : in out T) is null;
          function Is_Valid (X : T) return Boolean;
       end Pkg2;
       package body Pkg2 is ... end Pkg2;

       package Pkg3 is
          type D is new Pkg1.T and Pkg2.Ifc with null record;
       end Pkg3;
    begin ...; end;

Does a dispatching call to Pkg3.Op where the tag of the controlling operand is
Pkg3.D'Tag result in a call to Is_Valid?

It seems like it depends on the "arbitrary" choice mentioned in this AI's new
wording for 3.9.2(20.2/2), which defines the dynamic semantics of such a
dispatching call:

   * otherwise, the action is the same as the action for the
     corresponding operation of the parent type or progenitor
     type from which the operation was inherited. {If there is
     more than one such corresponding operation, the action is
     that for the operation that is not a null procedure, if any;
     otherwise, the action is that of an arbitrary one of the
     operations.}

If we flip a coin to decide which from among two candidates is the
"corresponding operation ... from which the operation was inherited", and if
exactly one of these two candidates includes a call to Is_Valid in its dynamic
semantics, then it seems like we have a problem.

Both here and when 8.3(12.3/2) says "one is chosen arbitrarily", we are relying
on the premise that syntactically null procedures with appropriately conformant
profiles are interchangeable with respect to dynamic semantics.

One approach to this problem (which Randy can explain his views on in a separate
message) would involve two steps:

    1) In these two arbitrary-choice situations (3.9.2 and 8.3),
       we add a preference rule preferring operations inherited
       from a non-interface type over operations inherited from
       an interface type.

    2) We take whatever steps are needed (possibly none?)
       in order to ensure that null procedures which are primitive ops
       of interface types really are interchangeable (e.g., we
       already disallow pre and post conditions for null procedures).

This issue needs more work.

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

From: Randy Brukardt
Sent: Friday, March 4, 2011  1:15 AM

...
> Both here and when 8.3(12.3/2) says "one is chosen arbitrarily", we
> are relying on the premise that syntactically null procedures with
> appropriately conformant profiles are interchangeable with respect to
> dynamic semantics.
>
> One approach to this problem (which Randy can explain his views on in
> a separate message) would involve two steps:
>
>     1) In these two arbitrary-choice situations (3.9.2 and 8.3),
>        we add a preference rule preferring operations inherited
>        from a non-interface type over operations inherited from
>        an interface type.
>
>     2) We take whatever steps are needed (possibly none?)
>        in order to ensure that null procedures which are primitive ops
>        of interface types really are interchangeable (e.g., we
>        already disallow pre and post conditions for null procedures).
>
> This issue needs more work.

I don't really have any views specifically on this problem, but the discussion
of it has gotten me very concerned that there is something fundamentally wrong
with the way we handle Pre/Post/Type_Invariant aspects on dispatching calls.
I'll call these "contract aspects" in the discussion below; most of the points
apply to all three of them.

Tucker has explained that the contract aspects that apply to a particular
subprogram body are always determined when that subprogram is declared. In
particular, inheritance never changes the contract aspects of a subprogram body.

That clearly works well on the body side of the contract; if the contracts
changed with inheritance, it would be much harder to figure out what properties
can be depended upon (or have to be preserved in the results).

OTOH, it doesn't seem to work as well on the call side of the contract. The
current rules say that the contracts depend on the actual body executed; the
implementation is likely to be a wrapper around the "regular" body (if the
contracts are normally enforced at the call site).

This has some unfortunate effects when interfaces are "added" into an existing
type hierarchy. For example, consider:

    package P1 is
       type T1 is tagged private;
       function Is_Valid (Obj : T1) return Boolean;
       procedure P1 (Obj : in out T1);
    private
       ...
    end P1;

    package P2 is
       type I2 is interface;
       function Is_Wobbly (Obj : I2) return Boolean is abstract;
       procedure P1 (Obj : in out I2) is null
           with Post'Class => Is_Wobbly (I2);
       procedure P2 (Obj : in I2) is null
           when Pre'Class => Is_Wobbly (I2);
    end P2;

    with P1, P2;
    package P3 is
       type T3 is new P1.T1 and P2.I2 with private;
       overriding
       function Is_Wobbly (Obj : T3) return Boolean;
       overriding
       procedure P2 (Obj : in T3);
    private
       ...
    end P3;

    with P1, P3;
    procedure Main is
        procedure Do_It (Obj : in P3.T3'Class) is
        begin
           Obj.P1; -- (1)
           Obj.P2; -- (2)
        end Do_It;
        O3 : P3.T3;
    begin
        Do_It (O3);
    end Main;

The dispatching call to P1 at (1) will call P1.P1, inherited from type T1.
This P1 has no postcondition, so Is_Wobbly will not be called on exit from P1.
However, the following call to P2 at (2) will call P3.P2; it will inherit the
precondition from the interface. Thus Is_Wobbly will be called on entrance to
P2. If P1 returned a value for which Is_Wobbly is False (perhaps because the
programmer forgot to override P1 for type T3), the *precondition* on the call to
P2 would fail. But that is bizarre; given the postcondition matches the
precondition for the type of the calls, it will be totally mysterious to the
programmer as to why the failure is there. The only way to reason about these
calls is to know the details of the contracts for all of the various possible
routines involved -- that way seems to lead to madness! (And of course the code
will most likely not be this easy to analyze).

It is of course possible to create similar examples with the Type_Invariant
aspect.

These examples bother me so much simply because here we have a contract that we
appear to be promising to enforce, yet we aren't enforcing it. Lying to the
client (whether or not there is some logical reason for it) does not seem like a
good policy.

This is just one effect of these rules. More generally, these rules prevent any
analysis of the contracts of a dispatching call -- by the programmer, by the
compiler, or by any tool that doesn't have access to the complete source code of
the program. That's because even if the entire program obeys a programming style
rule to avoid the "funny" cases, a subprogram that hasn't even been written yet
can add additional contracts (via any of the class-wide or specific contract
aspects) -- and those would render any analysis wrong.

One side effect of this that generating the contract aspects at the point of the
call is not possible in general. That of course means that the compiler cannot
eliminate checks of those aspects when they are not needed, nor point out when
they are guaranteed to fail. It also means that wrappers are needed for the
dispatching versions of routines if the compiler generates such aspects at the
call site for statically bound calls.

The rules for combining contracts (especially preconditions) also seem confusing
at best. Adding a precondition to an overriding routine causes a *weaker*
precondition; the original class-wide precondition no longer needs to be true.
That does not seem helpful and surely makes analysis harder.

All of these rules seem to vary from the usual model of dispatching calls. For
instance, constraints can be thought of a weaker form of contract aspects (as
preconditions involving only a single parameter, for instance). Ada 95 required
constraints to match exactly (3.9.2(10/2) requires subtype conformance) for
routines that override inherited routines (that is, possible dispatching
targets). (Ada 2012 will of course extend this to subtype predicates.) Ada also
has similar rules for other properties (including Convention, also in
3.9.2(10/2), and the No_Return aspect (6.5.1(6/2))). It would have had similar
rules for global in/global out annotations had those been defined, and most
likely would have similar rules for exception contracts as well.

So why should contract aspects (Pre/Post/Type_Invariant) be different? If these
are considered part of the profile, with suitable rules all of the bizarre cases
go away.

The easiest rule would be to require (full) conformance of contract aspects that
apply to a subprogram. Effectively, only one (class-wide) precondition could
apply to a dispatching call - the same precondition would apply to all of the
subprograms that could be dispatched to. This sounds very limiting, but as
Tucker has pointed out, such a precondition could be made up of other
dispatching calls. (Sorry Tucker for using your argument against you... :-)

If we were to make such aspects part of the profile, then it would seem that we
should have similar requirements on access-to-subprogram types. That would not
be strictly necessary, but note that the same sorts of issues apply to calls
through access-to-subprogram types. These bother me far less (especially as
wrappers might be required to deal with access-before-elaboration checks), but
consistency seems valuable.

We could use other somewhat weaker rules. One thing that bothers me about the
"absolute" rule is that a lot of potentially dispatching routines are never
called that way in practice. Such routines could have conventional specific
contract aspects without problems.

One could even imagine using Tucker's original rules (although that leaves
Steve's problem unsolved) so long as there is the possibility of compile-time
enforcement of a stricter rule so that at least "well-structured" dispatching
calls could have their properties known at the call site.

For me, the most important part of contract aspects is that they are known at
the call site. This opens up the possibility of the compiler eliminating the
checks (and even more importantly, warning when the contracts are known to fail)
without having to know anything about the implementation of the subprogram.
Denying this possibility to dispatching calls makes such calls a second-class
citizen in the Ada universe, and reduces the contract aspects to little more
than fancy Assert pragmas (only Type_Invariant does much automatically).

Thus I think we need to reconsider this area. (As a side effect, such
reconsideration may very well eliminate the problem that Steve was trying to
fix.)

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

From: Tucker Taft
Sent: Friday, March 4, 2011  9:43 AM

After thinking more about this, I now agree with Randy that we have a problem.
It arises whenever an operation inherited from an interface is overridden with
an operation inherited from some other type.  My conclusion is that it may be
necessary for a new version of the inherited routine to be generated, so that
the compiler can insert the additional checks implied by Pre'Class, Post'Class,
etc. aspects inherited from the interface.

This breaks the principle that you can always just reuse the code when you
inherit an operation, but I believe in the presence of multiple inheritance of
class-wide aspects, we don't really have a choice.

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

From: Randy Brukardt
Sent: Friday, March 4, 2011  9:46 PM

I'm happy to hear that; I'd hate to think that I was making less sense than
Charlie Sheen...

However, I don't think this quite works, because of the "weakening" rules for
preconditions. The new precondition inherited from the interface could
"counterfeit" the precondition on the original body, leading to a scenario where
the body is called without problem with parameters that violate the precondition
that it knows about. That seems pretty nasty.

To give a specific example:

    package P1 is
       type T1 is tagged private;
       function Is_Valid (Obj : T1) return Boolean;
       procedure P (Obj : in out T1)
          with Pre'Class => Is_Valid (Obj);
    private
       ...
    end P1;

    package body P1 is
       procedure P (Obj : in out T1) is
       begin
          -- Code here that assume Is_Valid is True for Obj.
       end P;
    end P1;


    package P2 is
       type I2 is interface;
       function Is_Wobbly (Obj : I2) return Boolean is abstract;
       procedure P (Obj : in out I2) is null
           with Pre'Class => Is_Wobbly (I2);
       function Something_Wobbly return I2
           when Post'Class => Is_Wobbly (Something_Wobbly'Result);
    end P2;

    with P1, P2;
    package P3 is
       type T3 is new P1.T1 and P2.I2 with private;
       overriding
       function Is_Wobbly (Obj : T3) return Boolean;
       overriding
       function Something_Wobbly return T3;
    private
       ...
    end P3;

    with P1, P3;
    procedure Main is
        procedure Do_It (Obj : in P3.T3'Class) is
        begin
           Obj.P; -- (1)
        end Do_It;
    begin
        Do_It (P3.Something_Wobbly);
    end Main;

Using the new semantics Tucker suggested, the call at (1) has to pass its
precondition, as Is_Wobbly(Obj) has to be true (based on the postcondition of
Something_Wobbly). However, since preconditions are effectively combined with
"or", Is_Valid(Obj) might in fact be False. And if it is, the body of P is going
to be mighty surprised to get an object that violates its precondition!

(I don't think this problem happens for Post or Type_Invariant, as they are
"anded", nor would it happen if the precondition was described as a
Dynamic_Predicate.)

Also note that this "weakening" means that even Pre'Class that necessarily must
apply to all calls cannot be generated at the call-site (because of the possible
need to "or" it with some other precondition) -- which eliminates the ability of
the compiler to do much in the way of checking elimination. (Again, this is not
a problem with the other aspects.)

It seems that "weakening" doesn't apply to multiple inheritance as much as it
does to the "primary" inheritance. But that doesn't seem to lead to a rule that
makes much sense, as it would seem to treat progenitors different than interface
parents (something we've avoided in the past).

The easy fix would be to combine the preconditions with "and". But I realize
there are logical issues with that on the call side of the equation. It strikes
me that there are logical issues on one side or the other whenever contract
aspects are combined; they only make sense if there is only one.

Thus a radical solution would be to require that exactly one precondition apply
to each subprogram (either Pre or Pre'Class, possibly inherited). To support
combining and "weakening", we'd need a way to refer to the aspects of a parent
routine, so that they can be used in a new Pre aspect. An attribute would work,
something like: P'Parent_Pre.

That would mean that you couldn't change Pre'Class precondition; if you need to
do that, you'd have to use a Pre on each subprogram in the inheritance. Not sure
if that is too complicated. And you couldn't assume much about the calls if you
did that (rather than using a single Pre'Class), but as that is the current
state, I can hardly imagine that it is harmful.

Anyway, more thought is needed.

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

From: Jean-Pierre Rosen
Sent: Saturday, March 5, 2011  1:26 AM

> Using the new semantics Tucker suggested, the call at (1) has to pass
> its precondition, as Is_Wobbly(Obj) has to be true (based on the
> postcondition of Something_Wobbly). However, since preconditions are
> effectively combined with "or", Is_Valid(Obj) might in fact be False.
> And if it is, the body of P is going to be mighty surprised to get an
> object that violates its precondition!

Doesn't Eiffel have the same problem? How is it handled? (Just trying to avoid
reinventing the wheel).

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

From: Randy Brukardt
Sent: Saturday, March 5, 2011  2:14 AM

I had wondered the same thing, but am not sure if Eiffel has interfaces or some
other form of multiple inheritance. (Without that, this particular problem
cannot come up.)

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

From: Bob Duff
Sent: Saturday, March 5, 2011  7:32 AM

Eiffel has multiple inheritance.  Not just interfaces -- you can inherit two
non-abstract methods that conflict, and there's some syntax for resolving the
conflict (combine them into one, rename one or both, ....).

I haven't had time to understand the issue you guys are talking about, so I
don't know how it relates to Eiffel.

One possibly-related thing is that in Eiffel preconditions are checked by the
caller ("as if", of course).

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

From: Tucker Taft
Sent: Saturday, March 5, 2011  10:28 AM

Eiffel has full multiple inheritance.

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

From: Jean-Pierre Rosen
Sent: Saturday, March 5, 2011  11:26 AM

> I had wondered the same thing, but am not sure if Eiffel has
> interfaces or some other form of multiple inheritance. (Without that,
> this particular problem cannot come up.)

Eiffel has full multiple inheritance (Bertrand Meyer has claimed that no
language is usable without full multiple inheritance - but that was before
interfaces)

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

Questions? Ask the ACAA Technical Agent