I'm seeing a case where 'this' being passed to a C++ function is wrong.
C++ multiple-inheritance calls expect that 'this' pointers are adjusted by the offset of the base class.
Something like this:
extern(C++) class Base { ~this() {} size_t i; } // Base is 16 bytes
extern(C++) interface I { void f(); }
extern(C++) class C : Base, I
{
final override void f();
}
C is:
{
_vtbl,
i,
_I_vtbl (this+16h)
}
void code(C c)
{
// crash:
c.f(); passes this = 'c' to f(), but C++ expects 'c + C._I_vtbl.offsetof'
}
In this example, I have a class instance 'c', and I'm calling 'f' (introduced by the interface), which is marked final, so D is able to perform a static call to the C++ function with no vtable lookup.
I have managed to get this to link, and D does call the correct C++ function, but D doesn't seem to adjust the 'this' pointer correctly for the call.
The C++ function 'f' expects that 'this' is an I pointer (not a C pointer), which is offset by sizeof(Base) from the start of C, but D just passes 'this' unaltered. The result is that within C::f(), the C++ code expects that 'C' is at 'this-16h', and everything is 16 bytes out of phase leading to a prompt crash.
TL;DR: Calling of multiple-inherited C++ interfaces requires adjustment of the 'this' pointer before making the call.
Comment #1 by bugzilla — 2016-01-27T23:53:34Z
This was addressed by:
https://github.com/D-Programming-Language/dmd/pull/5364
Since the code snippet you post here looks like the test case in 5364, I don't see what the different is.
> Something like this:
I cannot do anything with this, since it already looks like the test case in 5364. Clearly, something DIFFERENT is happening if it fails for you, meaning it is DIFFERENT code in some way that you have not posted.
PLEASE submit test cases with reproducible code snippets. "Something like this" is not going to work. So many times I have tried to fill in what the missing pieces might be, only to fail and eventually discover that the submitter had omitted critical information, not realizing that it was critical.
Comment #2 by bugzilla — 2016-01-28T00:05:28Z
I did some guesswork, and figured out that if you remove the 'final', it will work successfully, so I suggest that as a workaround for the moment.
Comment #3 by turkeyman — 2016-01-28T00:19:59Z
Okay, I'll give that a shot.
Incidentally, 99% of my functions are final, and about 80% of my virtuals are also final.
It'd be good to make finalised virtuals work properly a priority.
Final also has a mangling problem.
Reduced a test case.
You'll see a crash as it tries to call the non vtable member 'BAADFOOD'.
Bonus points:
void test() is extern(C) because extern(C++) doesn't mangle the type properly; doesn't link.
Comment #7 by turkeyman — 2016-01-28T12:20:42Z
Also, I don't think this particular crash looks the same as the situation of my other crash; neither rax or rcx are messed up in quite the same way, but they are both messed up, so it might be related.
The other case is that the pointers are dereferenced one level too deep before the call. This case appears that that the wrong offset is used as the vtable pointer.
Comment #8 by github-bugzilla — 2016-01-31T01:34:57Z