www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Bug 65] New: Strange results overriding interface return with class return

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/bugzilla/show_bug.cgi?id=65

           Summary: Strange results overriding interface return with class
                    return
           Product: D
           Version: 0.150
          Platform: PC
               URL: http://www.digitalmars.com/drn-
                    bin/wwwnews?digitalmars.D.bugs/3287
        OS/Version: Windows
            Status: NEW
          Keywords: wrong-code
          Severity: critical
          Priority: P2
         Component: DMD
        AssignedTo: bugzilla digitalmars.com
        ReportedBy: smjg iname.com


The compiler allows a method with an interface return type to be overridden
with a class return type.  However, when this is done, strange things happen,
from AVs to doing things that seem to have no relation to the method that was
called.

Two similar testcases:

----- covariant_int2.d -----
import std.stdio;

interface Father {}

class Mother {
     Father test() {
         writefln("Called Mother.test!");
         return new Child(42);
     }
}

class Child : Mother, Father {
     int data;

     this(int d) { data = d; }

     override Child test() {
         writefln("Called Child.test!");
         return new Child(69);
     }
}

void main() {
     Child aChild = new Child(105);
     Mother childsMum = aChild;
     Child childsChild = aChild.test();
     Child mumsChild = cast(Child) childsMum.test();
}

----- covariant_int4.d -----
import std.stdio;

interface Father {
     void showData();
}

class Mother {
     Father test() {
         writefln("Called Mother.test!");
         return new Child(42);
     }
}

class Child : Mother, Father {
     int data;

     this(int d) { data = d; }

     override Child test() {
         writefln("Called Child.test!");
         return new Child(69);
     }

     void showData() {
         writefln(data);
     }
}

void main() {
     Child aChild = new Child(105);
     Mother childsMum = aChild;

     aChild.test();
     Father mumTest = childsMum.test();
     aChild.showData();
     mumTest.showData();
}

----------
D:\My Documents\Programming\D\Tests\bugs>covariant_int2
Called Child.test!
Called Child.test!
Error: Access Violation

D:\My Documents\Programming\D\Tests\bugs>covariant_int4
Called Child.test!
Called Child.test!
105
Child
----------

I'm guessing that the underlying cause of both is the same - as speculated
before

http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D.bugs/2070

interface references aren't compatible with class references.  This means that
when a method is covariantly overridden from interface to class, and it is then
called through the base class, a class reference is returned, which is no good
as the base class method, and hence the caller through it, needs an interface
reference.

The spec doesn't explicitly forbid this, but if it isn't supposed to work then
the compiler should be giving an error (and the spec updated accordingly). 
Otherwise, it could be fixed to work like this:

- The compiler would detect that a method is being overridden from Father
(interface) to Child (class), and compile Child.test to return an interface
reference for compatibility.

- When the method is called through a Child reference, the caller would need to
implicitly convert the returned Father reference to a Child reference.  Of
course, this conversion can be optimised away if the context dictates that a
Father reference is required.

- It would be necessary to throw in a restriction or two.  A class cannot
derive from both a class and an interface, or multiple interfaces, if they
define methods with the same name and parameter types but one has a class
return and the other has an interface return.  Assuming that it would be
impossible to compile the method to be compatible with both simultaneously.

I haven't experimented with interface-to-interface covariant overrides, so
don't know if these work.  But I can imagine there being complications when
multiple interface inheritance is involved.

The question: Is it worth making this work?  Or do these complications mean
that we ought to disallow interface-to-class overrides altogether?


-- 
Mar 22 2006
next sibling parent Thomas Kuehne <thomas-dloop kuehne.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

d-bugmail puremagic.com schrieb am 2006-03-22:
 The compiler allows a method with an interface return type to be overridden
 with a class return type.  However, when this is done, strange things happen,
 from AVs to doing things that seem to have no relation to the method that was
 called.
[snip] Added to DStress as http://dstress.kuehne.cn/run/i/interface_23_A.d http://dstress.kuehne.cn/run/i/interface_23_B.d http://dstress.kuehne.cn/run/i/interface_23_C.d http://dstress.kuehne.cn/run/i/interface_23_D.d Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFEJAux3w+/yD4P9tIRAt5wAJ0cS3D9WA8t9kcyIh9R45PCXePsiwCfZDUy j416iT8Nk/EiGgs9tufXEwc= =SSd2 -----END PGP SIGNATURE-----
Mar 24 2006
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/bugzilla/show_bug.cgi?id=65


deewiant gmail.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |deewiant gmail.com





I presume this is related, though not quite the same - the return type is not
overridden, only the type of what is returned. If that made any sense. Testcase
follows:

------------------
interface I {
        I[] foo();
        uint x();
}

class Class : I {
        I[] foo() {
                // changing this to I[] f = new Class[1] fixes the bug
                Class[] f = new Class[1];
                f[0] = new Class;
                return f;
        }

        uint x() {
                return 0;
        }
}

void main() {
        Class c = new Class();
        assert (c.x == 0);
        assert (c.foo[0].x == 0);
}
------------------

Note also the array of length 1 - if "new Class" is returned directly, the code
generated is fine.


-- 
Apr 03 2006
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/bugzilla/show_bug.cgi?id=65






That's about converting between array of class and array of interface, and not
to do with return type covariance.  So it warrants a separate bug report.


-- 
Apr 04 2006
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/bugzilla/show_bug.cgi?id=65







 That's about converting between array of class and array of interface, and not
 to do with return type covariance.  So it warrants a separate bug report.
 
Bug 85 is now that bug report. --
Apr 04 2006
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/bugzilla/show_bug.cgi?id=65


larsivar igesund.net changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |larsivar igesund.net





This bug is listed as fixed in DMD 0.151, could someone verify it?


-- 
May 09 2006
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/bugzilla/show_bug.cgi?id=65






Testcase interface_23_D still fails.


-- 
May 09 2006
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/bugzilla/show_bug.cgi?id=65


bugzilla digitalmars.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|                            |FIXED





Fixed 0.158


-- 
May 25 2006