Thread overview
Bug? NVI functions can't call normal interface functions?
Mar 17, 2013
Johannes Pfau
Mar 17, 2013
Maxim Fomin
Mar 17, 2013
Maxim Fomin
Mar 20, 2013
Johannes Pfau
March 17, 2013
import core.stdc.stdio;
interface Timer
{
    final int run() { printf("Timer.run()\n"); fun(); return 1; };
    int fun();
}
interface Application
{
    final int run() { printf("Application.run()\n"); fun(); return 2; };
    int fun();
}

class TimedApp : Timer, Application
{
    int fun()
    {
        printf("TimedApp.fun()\n");
        return 0;
    }
}

void main()
{
    auto app = new TimedApp;
    (cast(Application)app).run();
    (cast(Timer)app).run();
    app.Application.run();
    app.Timer.run();
}

Output:
----
Application.run()
TimedApp.fun()
Timer.run()
TimedApp.fun()
Application.run()
Timer.run()
----

Note how "TimedApp.fun()" is called if cast was used, but not if app.*Interface*. was called. I guess this is a bug?
March 17, 2013
Yes, this is a bug.

The funny thing here is that instead TimedApp.fun(), object.Object.toString() is called due to shift error in virtual table offset jumping. I guess due to interfaces' ABI a class instance isn't passed properly. Using

    override string toString()
    {
        printf("bzzzz\n");
        return "";
    }

reveals the bug. Valgrind also doesn't detect error like this. Vtable storage layout may explain where bug comes from:

_D4main8TimedApp6__vtblZ:
	dd	offset FLAT:_D4main8TimedApp7__ClassZ@64
	db	000h,000h,000h,000h	;....
	dd	offset FLAT:_D6object6Object8toStringMFZAya@64
	db	000h,000h,000h,000h	;....
	dd	offset FLAT:_D6object6Object6toHashMFNbNeZm@64
	db	000h,000h,000h,000h	;....
	dd	offset FLAT:_D6object6Object5opCmpMFC6ObjectZi@64
	db	000h,000h,000h,000h	;....
	dd	offset FLAT:_D6object6Object8opEqualsMFC6ObjectZb@64
	db	000h,000h,000h,000h	;....
	dd	offset FLAT:_D6object6Object8opEqualsMFC6ObjectC6ObjectZb@64
	db	000h,000h,000h,000h	;....
	dd	offset FLAT:_D4main8TimedApp3funMFZi@64
	db	000h,000h,000h,000h	;....


I remember Don was posting similar issue but do not remember the #.
March 17, 2013
Actually it was http://d.puremagic.com/issues/show_bug.cgi?id=4589
March 19, 2013
On Sun, 17 Mar 2013 09:59:03 -0400, Maxim Fomin <maxim@maxim-fomin.ru> wrote:

> Actually it was http://d.puremagic.com/issues/show_bug.cgi?id=4589

I don't think this is the same problem.

I modified the program a bit:

import core.stdc.stdio;
interface Timer
{
    final int run() { printf("Timer.run(), this=%x\n", cast(void*)this); fun(); return 1;}
    int fun();
}
interface Application
{
    final int run() { printf("Application.run(), this=%x\n", cast(void *)this); fun(); return 2; };
    int fun();
}

class TimedApp : Timer, Application
{
    int fun()
    {
        printf("TimedApp.fun(), this=%x\n", cast(void*)this);
        return 0;
    }
}

void main()
{
    auto app = new TimedApp;
    (cast(Application)app).run();
    (cast(Timer)app).run();
    app.Application.run();
    app.Timer.run();
}

New output:

Application.run(), this=10007ff8
TimedApp.fun(), this=10007fe0
Timer.run(), this=10007ff0
TimedApp.fun(), this=10007fe0
Application.run(), this=10007fe0
Timer.run(), this=10007fe0

Note that Application.run() is given the interface pointer 10007ff8, whereas the *true* object pointer is 10007fe0.  This is normal and expected.

However, in the *last* call to Application.run (and Timer.run), it's given the pointer 10007fe0, the true object pointer.  This gives it the completely WRONG vtable to use for calling fun.  I think this is different than the bug I posted.  It may be related, I don't know.

If this bug is not already reported, it should be.

-Steve
March 20, 2013
Am Tue, 19 Mar 2013 17:51:52 -0400
schrieb "Steven Schveighoffer" <schveiguy@yahoo.com>:

> On Sun, 17 Mar 2013 09:59:03 -0400, Maxim Fomin <maxim@maxim-fomin.ru> wrote:
> 
> > Actually it was http://d.puremagic.com/issues/show_bug.cgi?id=4589
> 
> I don't think this is the same problem.
> 
> I modified the program a bit:
> 
> import core.stdc.stdio;
> interface Timer
> {
>      final int run() { printf("Timer.run(), this=%x\n",
> cast(void*)this); fun(); return 1;}
>      int fun();
> }
> interface Application
> {
>      final int run() { printf("Application.run(), this=%x\n",
> cast(void *)this); fun(); return 2; };
>      int fun();
> }
> 
> class TimedApp : Timer, Application
> {
>      int fun()
>      {
>          printf("TimedApp.fun(), this=%x\n", cast(void*)this);
>          return 0;
>      }
> }
> 
> void main()
> {
>      auto app = new TimedApp;
>      (cast(Application)app).run();
>      (cast(Timer)app).run();
>      app.Application.run();
>      app.Timer.run();
> }
> 
> New output:
> 
> Application.run(), this=10007ff8
> TimedApp.fun(), this=10007fe0
> Timer.run(), this=10007ff0
> TimedApp.fun(), this=10007fe0
> Application.run(), this=10007fe0
> Timer.run(), this=10007fe0
> 
> Note that Application.run() is given the interface pointer 10007ff8, whereas the *true* object pointer is 10007fe0.  This is normal and expected.
> 
> However, in the *last* call to Application.run (and Timer.run), it's given the pointer 10007fe0, the true object pointer.  This gives it the completely WRONG vtable to use for calling fun.  I think this is different than the bug I posted.  It may be related, I don't know.
> 
> If this bug is not already reported, it should be.
> 
> -Steve

I'll report it soon and I already have a bug fix. (The compiler indeed always passed the object pointer. This is valid for base classes, but not for interfaces. Adding an isInterface check + CastExpression solves this issue)