Skip to content

Commit

Permalink
fix Issue 1747 - class to base interface static cast is incorrect in …
Browse files Browse the repository at this point in the history
…some cases

A class to base interface conversion is always static cast (offset is idetermined at compile time).

Also refactor e2ir.c code and add comment to describe what happens there.
  • Loading branch information
9rnsr committed Jul 19, 2015
1 parent 1378b1c commit 5cefffd
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 29 deletions.
16 changes: 8 additions & 8 deletions src/class.c
Expand Up @@ -1634,7 +1634,7 @@ bool InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset)
{
BaseClass *b = cd->interfaces[j];

//printf("\tbase %s\n", b->sym->toChars());
//printf("\tX base %s\n", b->sym->toChars());
if (this == b->sym)
{
//printf("\tfound at offset %d\n", b->offset);
Expand All @@ -1648,8 +1648,11 @@ bool InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset)
}
if (isBaseOf(b, poffset))
{
if (j && poffset && cd->isInterfaceDeclaration())
*poffset = OFFSET_RUNTIME;
if (poffset)
{
if (j && cd->isInterfaceDeclaration())
*poffset = OFFSET_RUNTIME;
}
return true;
}
}
Expand All @@ -1662,28 +1665,25 @@ bool InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset)
return false;
}


bool InterfaceDeclaration::isBaseOf(BaseClass *bc, int *poffset)
{
//printf("%s.InterfaceDeclaration::isBaseOf(bc = '%s')\n", toChars(), bc->sym->toChars());
for (size_t j = 0; j < bc->baseInterfaces_dim; j++)
{
BaseClass *b = &bc->baseInterfaces[j];

//printf("\tY base %s\n", b->sym->toChars());
if (this == b->sym)
{
//printf("\tfound at offset %d\n", b->offset);
if (poffset)
{
*poffset = b->offset;
if (j && bc->sym->isInterfaceDeclaration())
*poffset = OFFSET_RUNTIME;
}
return true;
}
if (isBaseOf(b, poffset))
{
if (j && poffset && bc->sym->isInterfaceDeclaration())
*poffset = OFFSET_RUNTIME;
return true;
}
}
Expand Down
43 changes: 27 additions & 16 deletions src/e2ir.c
Expand Up @@ -3958,15 +3958,12 @@ elem *toElem(Expression *e, IRState *irs)
goto Lret;
}

// Casting from base class to derived class requires a runtime check
// Casting between class/interface may require a runtime check
if (fty == Tclass && tty == Tclass)
{
// Casting from derived class to base class is a no-op
int offset;
int rtl = RTLSYM_DYNAMIC_CAST;

ClassDeclaration *cdfrom = tfrom->isClassHandle();
ClassDeclaration *cdto = t->isClassHandle();

if (cdfrom->cpp)
{
if (cdto->cpp)
Expand All @@ -3988,13 +3985,14 @@ elem *toElem(Expression *e, IRState *irs)
e = el_bin(OPcomma, TYnptr, e, el_long(TYnptr, 0));
goto Lret;
}
if (cdfrom->isInterfaceDeclaration())
{
rtl = RTLSYM_INTERFACE_CAST;
}

int offset;
if (cdto->isBaseOf(cdfrom, &offset) && offset != OFFSET_RUNTIME)
{
/* The offset from cdfrom=>cdto is known at compile time.
/* The offset from cdfrom => cdto is known at compile time.
* Cases:
* - class => base class (upcast)
* - class => base interface (upcast)
*/

//printf("offset = %d\n", offset);
Expand All @@ -4015,13 +4013,26 @@ elem *toElem(Expression *e, IRState *irs)
e = el_bin(OPcond, TYnptr, e, ex);
}
}
goto Lret; // no-op
else
{
// Casting from derived class to base class is a no-op
}
}
else
{
/* The offset from cdfrom => cdto can only be determined at runtime.
* Cases:
* - class => derived class (downcast)
* - interface => derived class (downcast)
* - class => foreign interface (cross cast)
* - interface => base or foreign interface (cross cast)
*/
int rtl = cdfrom->isInterfaceDeclaration()
? RTLSYM_INTERFACE_CAST
: RTLSYM_DYNAMIC_CAST;
elem *ep = el_param(el_ptr(toSymbol(cdto)), e);
e = el_bin(OPcall, TYnptr, el_var(rtlsym[rtl]), ep);
}

/* The offset from cdfrom=>cdto can only be determined at runtime.
*/
elem *ep = el_param(el_ptr(toSymbol(cdto)), e);
e = el_bin(OPcall, TYnptr, el_var(rtlsym[rtl]), ep);
goto Lret;
}

Expand Down
65 changes: 60 additions & 5 deletions test/runnable/interface2.d
Expand Up @@ -114,7 +114,8 @@ void test4()
{
Z z = new Z();
if (cast(K) z)
{ printf("not ok\n");
{
printf("not ok\n");
assert(0);
}
}
Expand Down Expand Up @@ -231,7 +232,8 @@ class C9 : IC9
}

void f9(IA9 i1, IB9 i2)
{ int i;
{
int i;

printf("f9\n");
i = i1.i1();
Expand Down Expand Up @@ -682,12 +684,12 @@ interface Iface2

class C1_20 : Iface1
{
C2_20 func1(){ return null; }
C2_20 func1(){ return null; }
}

class C2_20 : Iface2
{
C1_20 func2(){ return null; }
C1_20 func2(){ return null; }
}

void test20()
Expand Down Expand Up @@ -911,14 +913,66 @@ class C27 : I27
}

void test27()
{ C27 c = new C27();
{
C27 c = new C27();
c.x = 8;
I27 i = c;
assert(i.foo() == 3);
assert(I27.foo() == 3);
assert(i.bar() == 87);
}

/*******************************************************/
// 1747

void test1747()
{
interface IA { int mA(); }
interface IB : IA { int mB(); }
interface IC : IB { }
interface ID : IA, IC { int mD(); }

// offset: 0 +n +n + ptrsize
// (IA)
// IB
// IA, IC
static class C : ID
{
int mA() { return 1; }
int mB() { return 2; }
int mD() { return 3; }
}

C c = new C; void* pc = *cast(void**)&c;
ID id = c; void* pid = *cast(void**)&id;
IC ic = c; void* pic = *cast(void**)&ic;
IB ib = c; void* pib = *cast(void**)&ib;
IA ia = c; void* pia = *cast(void**)&ia;

//printf(" c = %p\n", pc);
//printf("id = %p\n", pid);
//printf("ic = %p\n", pic);
//printf("ib = %p\n", pib);
//printf("ia = %p\n", pia);

size_t n = pid - pc;
assert(pic == pc + n + (void*).sizeof);
assert(pib == pc + n + (void*).sizeof); // OK <- NG
assert(pia == pc + n);

assert(id.mA() == 1);
//assert(id.mB() == 2); // NG (returns 1, bugzilla 2013 case)
assert(id.mD() == 3);

assert(ic.mA() == 1);
//assert(ic.mB() == 2); // NG (returns 1, bugzilla 2013 case)

assert(ib.mA() == 1);
assert(ib.mB() == 2); // OK <- NG

assert(ia.mA() == 1);
}

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

private interface IFoo
Expand Down Expand Up @@ -1097,6 +1151,7 @@ int main()
test25();
test26();
test27();
test1747();
test2553();
test11034();
testTypeid();
Expand Down

0 comments on commit 5cefffd

Please sign in to comment.