When a class implements an interface that has re-implemented an ancestor interface, it is possible for a dynamic cast to produce the wrong result.
I have this tagged as "wrong-code", but I believe the problem is due to both InterfaceDeclaration RTTI and the implementation of phobos/internal/cast.d:_d_isbaseof2.
Comment #1 by dvdfrdmn — 2008-04-19T12:19:00Z
Created attachment 248
Test Cast
Comment #2 by k.hara.pg — 2015-07-18T12:16:31Z
Still happens in D2.
(In reply to David Friedman from comment #1)
> Created attachment 248 [details]
> Test Cast
Simplified the test case:
interface IA { int mA(); }
interface IB : IA { int mB(); }
interface IC : IB { }
interface ID : IA, IC { int mD(); }
class C : ID
{
int mA() { return 1; }
int mB() { return 2; }
int mD() { return 3; }
}
void main()
{
C c = new C;
IA ia = c;
IB ib = cast(IB)ia;
assert(ib.mB() == 2);
}
Comment #3 by k.hara.pg — 2015-07-18T13:02:30Z
The problem is in both dmd and druntime code.
For the class/interface hierarchy:
interface IA { ... }
interface IB : IA { ... }
interface IC : IB { }
interface ID : IA, IC { ... }
class C : ID { ... }
The layout of pointer to vtbls in the class C instance object is:
(IA)
IB
IA, IC
C : ID
----------------------
0 n n+ptrsize
// Note: n is the size of __vtbl + __monitor + sum of C field sizes.
// It's 8 with -m32.
Currently dmd generates following tree of `object.Interface` structs to represent the hierarchy:
+-> CI(IA).interfaces[] == []
| A
| +-------------------------+
| |
| CI(IB).interfaces[] == [CI(IA)/0]
| A
| +-------------------------+
| |
| CI(IC).interfaces[] == [CI(IB)/0]
| A
| +---------------------------+----------+
| | |
+----------------------+ | |
| | |
CI(ID).interfaces[] == [CI(IA)/0, CI(IC)/0] |
A |
+----------------------+ +----------+
| |
CI(C).interfaces[] == [CI(ID)/n, CI(IC)/n+ptrsize]
// Note: CI(X) == ClassInfo(X) == typeid(X).info
// CI(X)/num --> num == object.Interface.offset
For the cast from IA to IB, druntime once downcast IA to Object, then calculate offset from Object(== C) to IB.
It's handled by _d_interface_cast(), _d_dynamic_cast(), and _d_isbaseof2() functions in druntime/src/rt/_cast.d.
The problem is in _d_isbaseof2(). It should return n+ptrsize for the cast from Object to IB, but while the iteration of above thee by breadth-first, it incorrectly returns n.
-------
The way to fix the issue:
1. Fix compiler-generated Interfaces[] tree
2. Fix _d_isbaseof2() function to calculate correct offset.