www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 15579] New: extern(C++) interfaces/multiple-inheritence

https://issues.dlang.org/show_bug.cgi?id=15579

          Issue ID: 15579
           Summary: extern(C++) interfaces/multiple-inheritence
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: dmd
          Assignee: nobody puremagic.com
          Reporter: turkeyman gmail.com

I'm noticing some differences in struct layout between C++ and D.
I'm doing tests by making some classes, putting members in between the
locations I expect vtable pointers to be placed, and then comparing
the offsets of the members.
This is what I'm seeing:

In C++:

class Base
{
  virtual ~Base() {}
  size_t x;
};

class Interface
{
  virtual Method() = 0;
};

class Derived : public Base, public Interface
{
  size_t y;
};


In C++, Derived is:
{
  void *__Base_vtable;
  size_t x;
  void *__Interface_vtable;
  size_t y;
}

This is as I expect.

-------------------
D:

extern(C++) class Base
{
  ~this() {}
  size_t x;
}

extern(C++) interface Interface
{
  abstract Method();
}

extern(C++) class Derived : Base, Interface
{
  size_t y;
}


Derived appears to be:
{
  void *__Base_vtable;
  size_t x;
  size_t y;
}

So, D seems to think 'y' is where Interface_vtable should be...
I'm not sure where D thinks the Interface_vtable pointer actually is.
What's interesting though, is that I can call 'Method' introduced by
Interface, D does try and call something... which means it must get a
vtable pointer somewhere, but it crashes on a bad function pointer, so
something goes wrong.

Digging into the bad virtual call, I get this:
(Note, my test case is not exactly what's above; in this test,
Interface has a few functions and I'm calling the 4th one in the
vtable)

00007FF72DB7381B  mov         rbx,qword ptr [this]      // rbx is 'this'
00007FF72DB7381F  mov         rcx,qword ptr [rbx+10h]   // rcx is [this+10h],
which is actually the proper location for Interface_vtable, contrary to what I
found above

Surprisingly, this looks good. rcx is Interface_vtable, and it found it at the
location I expect, which means my test above was lying to me about the location
of 'y'.
But here it gets a little weird...

00007FF72DB73823  mov         rcx,qword ptr [rcx]       // what's this? ie, rcx
= rcx[0]

rcx is now the first function pointer in the vtable; NOT the function I'm
calling, which is actually [rcx+18h]!

00007FF72DB73826  sub         rsp,20h                   // nothing of interest
00007FF72DB7382A  mov         rax,qword ptr [rcx]       // dereferencing the
function pointer!

rax is now the first 8 bytes of program code of the first function in the
vtable

00007FF72DB7382D  call        qword ptr [rax+18h]       // calling rax+18h

If rax were rcx from the second operation above, 18h is the proper
vtable offset of the function I'm trying to call, and this would be
correct, but there are 2 bonus dereferences there that shouldn't be.
I don't know how interfaces are implemented in D, is this showing some
relationship to normal D interface calls?
This is certainly not proper extern(C++) behaviour. The offset of y
seems confused, and the extra 2 dereferences shouldn't be there.


Tests are with DMD-Win64 compared against MSC2015-Win64.

--
Jan 18 2016