www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Multiple inheritance and covariant return types.

reply Frank Fischer <frank.fischer s2001.tu-chemnitz.de> writes:
Hi,

I have another question on multiple inheritance. The following code does not
work (tested with DMD 1.020 and 2.003)

---
interface A {}

interface B {}

interface AB: A, B {}

abstract class X {
    abstract A func();
}

interface Y {
    B func();
}

class XY: X, Y {
    AB func() { return null; }
}
---
The compiler complains about the line 'AB func() { return null; }':

t.d(38): function t.XY.func incompatible covariant types A() and B()

but I think it should work, because AB is both, an A and a B, so the return
type is covariant with both overridden methods. In fact, the example works
fine, if X is an interface instead of an abstract class.

Is this a bug or is it my mistake?

Thanks,
Frank
Aug 09 2007
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Frank,

 Hi,
 
 I have another question on multiple inheritance. The following code
 does not work (tested with DMD 1.020 and 2.003)
 
 ---
 interface A {}
 interface B {}
 
 interface AB: A, B {}
 
 abstract class X {
 abstract A func();
 }
 interface Y {
 B func();
 }
 class XY: X, Y {
 AB func() { return null; }
 }
 ---
 The compiler complains about the line 'AB func() { return null; }':
 t.d(38): function t.XY.func incompatible covariant types A() and B()
 
 but I think it should work, because AB is both, an A and a B, so the
 return type is covariant with both overridden methods. In fact, the
 example works fine, if X is an interface instead of an abstract class.
 
 Is this a bug or is it my mistake?
 
 Thanks,
 Frank

The issues is that casting from a class that implements A, B and AB to each of these results in a slightly different result. IIRC when a class is cast to an interface, what you get is a pointer to a v-table inside of the class (not the normal one). However because AB inherits from A and B, you end up getting two different v-tables. So returning an AB interface instance would give you something that is an AB and might be an A or B but not both. (This is all at the binary level of course)
Aug 09 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
BCS wrote:
 Reply to Frank,
 
 Hi,

 I have another question on multiple inheritance. The following code
 does not work (tested with DMD 1.020 and 2.003)

 ---
 interface A {}
 interface B {}

 interface AB: A, B {}

 abstract class X {
 abstract A func();
 }
 interface Y {
 B func();
 }
 class XY: X, Y {
 AB func() { return null; }
 }
 ---
 The compiler complains about the line 'AB func() { return null; }':
 t.d(38): function t.XY.func incompatible covariant types A() and B()

 but I think it should work, because AB is both, an A and a B, so the
 return type is covariant with both overridden methods. In fact, the
 example works fine, if X is an interface instead of an abstract class.

 Is this a bug or is it my mistake?

 Thanks,
 Frank

The issues is that casting from a class that implements A, B and AB to each of these results in a slightly different result. IIRC when a class is cast to an interface, what you get is a pointer to a v-table inside of the class (not the normal one). However because AB inherits from A and B, you end up getting two different v-tables. So returning an AB interface instance would give you something that is an AB and might be an A or B but not both. (This is all at the binary level of course)

So, AB isn't "A or B" but "A and B" and the OP really wants some way to say "A or B". Specifically 'func' needs to return either A or B based on usage, eg. A a = func(); //returns A B b = func(); //returns B Unfortunately you can't overload based on return type or this might have worked: interface A {} interface B {} interface AB: A, B {} abstract class X { abstract A func(); } interface Y { B func(); } template TFunc(T) { T func() { return null; } } class XY: X, Y { mixin TFunc!(A); mixin TFunc!(B); } void main() { XY xy = new XY; A a = xy.func(); B b = xy.func(); } Regan
Aug 09 2007
parent Regan Heath <regan netmail.co.nz> writes:
Regan Heath wrote:
 BCS wrote:
 Reply to Frank,

 Hi,

 I have another question on multiple inheritance. The following code
 does not work (tested with DMD 1.020 and 2.003)

 ---
 interface A {}
 interface B {}

 interface AB: A, B {}

 abstract class X {
 abstract A func();
 }
 interface Y {
 B func();
 }
 class XY: X, Y {
 AB func() { return null; }
 }
 ---
 The compiler complains about the line 'AB func() { return null; }':
 t.d(38): function t.XY.func incompatible covariant types A() and B()

 but I think it should work, because AB is both, an A and a B, so the
 return type is covariant with both overridden methods. In fact, the
 example works fine, if X is an interface instead of an abstract class.

 Is this a bug or is it my mistake?

 Thanks,
 Frank

The issues is that casting from a class that implements A, B and AB to each of these results in a slightly different result. IIRC when a class is cast to an interface, what you get is a pointer to a v-table inside of the class (not the normal one). However because AB inherits from A and B, you end up getting two different v-tables. So returning an AB interface instance would give you something that is an AB and might be an A or B but not both. (This is all at the binary level of course)

So, AB isn't "A or B" but "A and B" and the OP really wants some way to say "A or B". Specifically 'func' needs to return either A or B based on usage, eg. A a = func(); //returns A B b = func(); //returns B Unfortunately you can't overload based on return type or this might have worked: interface A {} interface B {} interface AB: A, B {} abstract class X { abstract A func(); } interface Y { B func(); } template TFunc(T) { T func() { return null; } } class XY: X, Y { mixin TFunc!(A); mixin TFunc!(B); } void main() { XY xy = new XY; A a = xy.func(); B b = xy.func(); }

Actually, ignore me I think I must have the wrong end of the stick. Regan
Aug 09 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Frank Fischer wrote:
 Hi,
 
 I have another question on multiple inheritance. The following code does not
 work (tested with DMD 1.020 and 2.003)
 
 ---
 interface A {}
 
 interface B {}
 
 interface AB: A, B {}
 
 abstract class X {
     abstract A func();
 }
 
 interface Y {
     B func();
 }
 
 class XY: X, Y {
     AB func() { return null; }
 }
 ---
 The compiler complains about the line 'AB func() { return null; }':
 
 t.d(38): function t.XY.func incompatible covariant types A() and B()
 
 but I think it should work, because AB is both, an A and a B, so the return
 type is covariant with both overridden methods. In fact, the example works
 fine, if X is an interface instead of an abstract class.
 
 Is this a bug or is it my mistake?
 
 Thanks,
 Frank
 

It's not a bug. Classes are *convertible* to interfaces that it implements but they are not *covariant* with said interfaces. The same thing happens for an interface and it's super interfaces. The interface is not covariant with is super interfaces, only convertible to them. (exception maybe the first interface). Thus in the example above AB is not both an A and B. This behavior is different than Java and other OO languages, and is so for performance reasons. There are some tricks the compiler does to allows override as if the types were covariant, but it only works in some situations. You can see it for yourself with the following code: interface A {} interface B {} interface AB: A, B {} class ABImpl : AB { } void test() { writefln("%X", cast(void *) ab); writefln("%X", cast(void *) cast(AB) ab); writefln("%X", cast(void *) cast(A) ab); writefln("%X", cast(void *) cast(B) ab); } -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Aug 09 2007
parent Frank Fischer <frank.fischer s2001.tu-chemnitz.de> writes:
Bruno Medeiros wrote:

 Frank Fischer wrote:

 I have another question on multiple inheritance. The following code does
 not work (tested with DMD 1.020 and 2.003)
 
 ---
 interface A {}
 
 interface B {}
 
 interface AB: A, B {}
 
 abstract class X {
     abstract A func();
 }
 
 interface Y {
     B func();
 }
 
 class XY: X, Y {
     AB func() { return null; }
 }
 ---
 The compiler complains about the line 'AB func() { return null; }':
 
 t.d(38): function t.XY.func incompatible covariant types A() and B()
 

It's not a bug. Classes are *convertible* to interfaces that it implements but they are not *covariant* with said interfaces. The same thing happens for an interface and it's super interfaces. The interface is not covariant with is super interfaces, only convertible to them. (exception maybe the first interface). Thus in the example above AB is not both an A and B. This behavior is different than Java and other OO languages, and is so for performance reasons. There are some tricks the compiler does to allows override as if the types were covariant, but it only works in some situations.

Thanks for the explanations. So I have to live with that behaviour and handle the problem in a different way. Frank
Aug 09 2007