Skip to content

Commit

Permalink
Merge pull request #5361 from WalterBright/fix15579
Browse files Browse the repository at this point in the history
fix Issue 15579 - extern(C++) interfaces/multiple-inheritance
  • Loading branch information
andralex committed Jan 22, 2016
2 parents 5ce9dcc + 316f2e9 commit c6ac10a
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 72 deletions.
5 changes: 3 additions & 2 deletions src/dclass.d
Expand Up @@ -1156,6 +1156,9 @@ public:
structsize = Target.ptrsize * 2; // allow room for __vptr and __monitor
}

// Add vptr's for any interfaces implemented by this class
structsize += setBaseInterfaceOffsets(structsize);

uint offset = structsize;
for (size_t i = 0; i < members.dim; i++)
{
Expand All @@ -1165,8 +1168,6 @@ public:
if (sizeok == SIZEOKfwd)
return;

// Add vptr's for any interfaces implemented by this class
structsize += setBaseInterfaceOffsets(structsize);
sizeok = SIZEOKdone;

// Calculate fields[i].overlapped
Expand Down
43 changes: 21 additions & 22 deletions src/e2ir.c
Expand Up @@ -3970,28 +3970,6 @@ elem *toElem(Expression *e, IRState *irs)
ClassDeclaration *cdfrom = tfrom->isClassHandle();
ClassDeclaration *cdto = t->isClassHandle();

if (cdfrom->cpp)
{
if (cdto->cpp)
{
/* Casting from a C++ interface to a C++ interface
* is always a 'paint' operation
*/
goto Lret; // no-op
}

/* Casting from a C++ interface to a class
* always results in null because there is no runtime
* information available to do it.
*
* Casting from a C++ interface to a non-C++ interface
* always results in null because there's no way one
* can be derived from the other.
*/
e = el_bin(OPcomma, TYnptr, e, el_long(TYnptr, 0));
goto Lret;
}

int offset;
if (cdto->isBaseOf(cdfrom, &offset) && offset != OFFSET_RUNTIME)
{
Expand Down Expand Up @@ -4024,6 +4002,27 @@ elem *toElem(Expression *e, IRState *irs)
// Casting from derived class to base class is a no-op
}
}
else if (cdfrom->cpp)
{
if (cdto->cpp)
{
/* Casting from a C++ interface to a C++ interface
* is always a 'paint' operation
*/
goto Lret; // no-op
}

/* Casting from a C++ interface to a class
* always results in null because there is no runtime
* information available to do it.
*
* Casting from a C++ interface to a non-C++ interface
* always results in null because there's no way one
* can be derived from the other.
*/
e = el_bin(OPcomma, TYnptr, e, el_long(TYnptr, 0));
goto Lret;
}
else
{
/* The offset from cdfrom => cdto can only be determined at runtime.
Expand Down
108 changes: 60 additions & 48 deletions src/todt.c
Expand Up @@ -640,13 +640,25 @@ dt_t **cpp_type_info_ptr_toDt(ClassDeclaration *cd, dt_t **pdt)
* Put out initializers of ad->fields[].
* Although this is consistent with the elements[] version, we
* have to use this optimized version to reduce memory footprint.
* Params:
* ad = aggregate with members
* pdt = tail of initializer list
* concreteType = structs: null, classes: top level class
* Returns:
* updated tail of dt_t list
*/
dt_t **membersToDt(AggregateDeclaration *ad, dt_t **pdt,
ClassDeclaration *concreteType)
{
//printf("membersToDt(ad = '%s')\n", ad->toChars());
ClassDeclaration *cd = ad->isClassDeclaration();

/* Order:
* { base class } or { __vptr, __monitor)
* interfaces
* fields
*/

unsigned offset;
if (cd)
{
Expand All @@ -666,6 +678,30 @@ dt_t **membersToDt(AggregateDeclaration *ad, dt_t **pdt,
else
offset = 0;

if (cd)
{
// Interface vptr initializations
toSymbol(cd); // define csym

for (size_t i = 0; i < cd->vtblInterfaces->dim; i++)
{
BaseClass *b = (*cd->vtblInterfaces)[i];
for (ClassDeclaration *cd2 = concreteType; 1; cd2 = cd2->baseClass)
{
assert(cd2);
unsigned csymoffset = baseVtblOffset(cd2, b);
if (csymoffset != ~0)
{
if (offset < b->offset)
pdt = dtnzeros(pdt, b->offset - offset);
pdt = dtxoff(pdt, toSymbol(cd2), csymoffset);
break;
}
}
offset = b->offset + Target::ptrsize;
}
}

for (size_t i = 0; i < ad->fields.dim; i++)
{
if (ad->fields[i]->_init && ad->fields[i]->_init->isVoidInitializer())
Expand Down Expand Up @@ -730,30 +766,6 @@ dt_t **membersToDt(AggregateDeclaration *ad, dt_t **pdt,
offset = vd->offset + vd->type->size();
}

if (cd)
{
// Interface vptr initializations
toSymbol(cd); // define csym

for (size_t i = 0; i < cd->vtblInterfaces->dim; i++)
{
BaseClass *b = (*cd->vtblInterfaces)[i];
for (ClassDeclaration *cd2 = concreteType; 1; cd2 = cd2->baseClass)
{
assert(cd2);
unsigned csymoffset = baseVtblOffset(cd2, b);
if (csymoffset != ~0)
{
if (offset < b->offset)
pdt = dtnzeros(pdt, b->offset - offset);
pdt = dtxoff(pdt, toSymbol(cd2), csymoffset);
break;
}
}
offset = b->offset + Target::ptrsize;
}
}

if (offset < ad->structsize)
pdt = dtnzeros(pdt, ad->structsize - offset);

Expand Down Expand Up @@ -792,6 +804,30 @@ dt_t **membersToDt(AggregateDeclaration *ad, dt_t **pdt,
else
offset = 0;

if (cd)
{
// Interface vptr initializations
toSymbol(cd); // define csym

for (size_t i = 0; i < cd->vtblInterfaces->dim; i++)
{
BaseClass *b = (*cd->vtblInterfaces)[i];
for (ClassDeclaration *cd2 = concreteType; 1; cd2 = cd2->baseClass)
{
assert(cd2);
unsigned csymoffset = baseVtblOffset(cd2, b);
if (csymoffset != ~0)
{
if (offset < b->offset)
pdt = dtnzeros(pdt, b->offset - offset);
pdt = dtxoff(pdt, toSymbol(cd2), csymoffset);
break;
}
}
offset = b->offset + Target::ptrsize;
}
}

assert(firstFieldIndex <= elements->dim &&
firstFieldIndex + ad->fields.dim <= elements->dim);
for (size_t i = 0; i < ad->fields.dim; i++)
Expand Down Expand Up @@ -835,30 +871,6 @@ dt_t **membersToDt(AggregateDeclaration *ad, dt_t **pdt,
offset = vd->offset + vd->type->size();
}

if (cd)
{
// Interface vptr initializations
toSymbol(cd); // define csym

for (size_t i = 0; i < cd->vtblInterfaces->dim; i++)
{
BaseClass *b = (*cd->vtblInterfaces)[i];
for (ClassDeclaration *cd2 = concreteType; 1; cd2 = cd2->baseClass)
{
assert(cd2);
unsigned csymoffset = baseVtblOffset(cd2, b);
if (csymoffset != ~0)
{
if (offset < b->offset)
pdt = dtnzeros(pdt, b->offset - offset);
pdt = dtxoff(pdt, toSymbol(cd2), csymoffset);
break;
}
}
offset = b->offset + Target::ptrsize;
}
}

if (offset < ad->structsize)
pdt = dtnzeros(pdt, ad->structsize - offset);

Expand Down
56 changes: 56 additions & 0 deletions test/runnable/cppa.d
Expand Up @@ -982,6 +982,61 @@ void testeh3()
}
}

/****************************************/
// 15579

extern (C++)
{
class Base
{
//~this() {}
void based() { }
ubyte x = 4;
}

interface Interface
{
int MethodCPP();
int MethodD();
}

class Derived : Base, Interface
{
short y = 5;
int MethodCPP();
int MethodD() { return 3; }
}

Derived cppfoo(Derived);
Interface cppfooi(Interface);
}

void test15579()
{
Derived d = new Derived();
assert(d.x == 4);
assert(d.y == 5);
assert(d.MethodD() == 3);
assert(d.MethodCPP() == 30);

d = cppfoo(d);
assert(d.x == 7);
assert(d.y == 8);
version (Win64)
{
// needs more work
}
else
{
assert(d.MethodD() == 3);
assert(d.MethodCPP() == 30);
}
printf("d = %p, i = %p\n", d, cast(Interface)d);
Interface i = cppfooi(d);
assert(i.MethodD() == 3);
assert(i.MethodCPP() == 30);
}

/****************************************/

void main()
Expand Down Expand Up @@ -1018,6 +1073,7 @@ void main()
testeh();
testeh2();
testeh3();
test15579();

printf("Success\n");
}
62 changes: 62 additions & 0 deletions test/runnable/extra-files/cppb.cpp
Expand Up @@ -578,4 +578,66 @@ void throwle()
#endif

/******************************************/
// 15579

class Base
{
public:
//virtual ~Base() {}
virtual void base();
unsigned char x;
};

class Interface
{
public:
virtual int MethodCPP() = 0;
virtual int MethodD() = 0;
};

class Derived : public Base, public Interface
{
public:
Derived();
short y;
int MethodCPP();
#if _WIN32 || _WIN64
int MethodD();
#else
int MethodD() { return 3; } // need def or vtbl[] is not generated
#endif
};

void Base::base() { }
int Derived::MethodCPP() { return 30; }
Derived::Derived() { }


Derived *cppfoo(Derived *d)
{
assert(d->x == 4);
assert(d->y == 5);
assert(d->MethodD() == 3);
assert(d->MethodCPP() == 30);

d = new Derived();
d->x = 7;
d->y = 8;
assert(d->MethodD() == 3);
assert(d->MethodCPP() == 30);
return d;
}

Interface *cppfooi(Interface *i)
{
printf("i = %p\n", i);
assert(i->MethodD() == 3);
assert(i->MethodCPP() == 30);

Derived *d = new Derived();
printf("d = %p, i = %p\n", d, (Interface *)d);
return d;
}

/******************************************/

0 comments on commit c6ac10a

Please sign in to comment.