diff --git a/compiler/src/dmd/dclass.d b/compiler/src/dmd/dclass.d index 950f7db11370..ffb8572793da 100644 --- a/compiler/src/dmd/dclass.d +++ b/compiler/src/dmd/dclass.d @@ -277,7 +277,6 @@ extern (C++) class ClassDeclaration : AggregateDeclaration return false; } - enum OFFSET_RUNTIME = 0x76543210; enum OFFSET_FWDREF = 0x76543211; /******************************************* @@ -426,7 +425,6 @@ extern (C++) final class InterfaceDeclaration : ClassDeclaration * (Actually, if it is an interface supported by cd) * Output: * *poffset offset to start of class - * OFFSET_RUNTIME must determine offset at runtime * Returns: * false not a base * true is a base diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index 0ccd016b9c2d..2d31d91f6ff4 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -4993,10 +4993,22 @@ private void lowerCastExp(CastExp cex, Scope* sc) ClassDeclaration cdto = tob.isClassHandle(); int offset; - if ((cdto.isBaseOf(cdfrom, &offset) && offset != ClassDeclaration.OFFSET_RUNTIME) - || cdfrom.classKind == ClassKind.cpp || cdto.classKind == ClassKind.cpp) + if (cdto.isBaseOf(cdfrom, &offset)) + return; // codegen has to deal with pointer adjustment + + if (cdfrom.classKind != ClassKind.d || + (cdto.classKind != ClassKind.d && !cdto.isInterfaceDeclaration())) + { + if (cdfrom.classKind == cdto.classKind) + return; // for non-D classes, let the backend take care + + // conversions across different linkage result in null + Expression lowerNull = new NullExp(cex.loc, cex.to); + cex.lowering = lowerNull .expressionSemantic(sc); return; + } + // cast D -> unrelated D class / any interface calls _d_cast Identifier hook = Id._d_cast; if (!verifyHookExist(cex.loc, *sc, hook, "d_cast", Id.object)) return; diff --git a/compiler/src/dmd/glue/e2ir.d b/compiler/src/dmd/glue/e2ir.d index eacb980858b8..caa9ba27564c 100644 --- a/compiler/src/dmd/glue/e2ir.d +++ b/compiler/src/dmd/glue/e2ir.d @@ -4644,7 +4644,7 @@ elem* toElemCast(CastExp ce, elem* e, bool isLvalue, ref IRState irs) ClassDeclaration cdto = t.isClassHandle(); int offset; - if (cdto.isBaseOf(cdfrom, &offset) && offset != ClassDeclaration.OFFSET_RUNTIME) + if (cdto.isBaseOf(cdfrom, &offset)) { /* The offset from cdfrom => cdto is known at compile time. * Cases: @@ -4679,26 +4679,13 @@ elem* toElemCast(CastExp ce, elem* e, bool isLvalue, ref IRState irs) // Casting from derived class to base class is a no-op } } - else if (cdfrom.classKind == ClassKind.cpp) + else if (cdfrom.classKind == cdto.classKind) { - if (cdto.classKind == ClassKind.cpp) - { - /* Casting from a C++ interface to a C++ interface - * is always a 'paint' operation - */ - return Lret(ce, e); // 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 is no way one - * can be derived from the other. + /* Casting from a non-D linkage class/interface to a unrelated class/interface + * is always a 'paint' operation (for dmd, other backends might use RTTI + * of other languages) */ - e = el_bin(OPcomma, TYnptr, e, el_long(TYnptr, 0)); - return Lret(ce, e); + return Lret(ce, e); // no-op } else { diff --git a/compiler/src/dmd/glue/toir.d b/compiler/src/dmd/glue/toir.d index 89768fc471a7..2f597a7888cd 100644 --- a/compiler/src/dmd/glue/toir.d +++ b/compiler/src/dmd/glue/toir.d @@ -273,7 +273,6 @@ elem* getEthis(Loc loc, ref IRState irs, Dsymbol fd, Dsymbol fdp = null, Dsymbol int offset; cdp.isBaseOf(cd, &offset); - assert(offset != ClassDeclaration.OFFSET_RUNTIME); //printf("%s to %s, offset = %d\n", cd.toChars(), cdp.toChars(), offset); if (offset) { diff --git a/compiler/test/runnable/casting.d b/compiler/test/runnable/casting.d index 1eb262c5c682..82f4aa48d6b1 100644 --- a/compiler/test/runnable/casting.d +++ b/compiler/test/runnable/casting.d @@ -210,6 +210,82 @@ void test14218() } } +/***************************************************/ +// https://github.com/dlang/dmd/issues/23262 + +extern (C++) interface iface23262 +{ + int funCpp(); +} + +extern (C++) class cpp23262 +{ + int funCpp() { return 1; } +} + +class class23262 : Object, iface23262 +{ + extern (C++) int funCpp() { return 42; } +} + +void test23262() +{ + Object obj = new class23262; + auto cpp = cast(iface23262) obj; // ok for C++ interface + assert(cpp); + assert(cpp.funCpp() == 42); + + auto cpp2 = cast(cpp23262) obj; + assert(cpp2 is null); // impossible for C++ class + + auto d = cast(class23262) cpp; + assert(d is null); // no way back + + auto i = cast(iface23262) cpp2; + assert(cast(void*)i is cast(void*)cpp2); // reinterpret cast + + auto cppobj = new cpp23262; + auto d2 = cast(class23262) cppobj; + assert(d2 is null); // classes of different linkage never mix + + version(OSX) + { + } +} + +version(OSX) +{ +// Objective-C never mixes with other linkages, so cannot add Object +// with ClassInfo into the hierarchy +extern (Objective-C) +extern interface NSString +{ + import core.attribute : selector; + const(char)* UTF8String() @selector("UTF8String"); +} +extern (Objective-C) class baseObjC {} + +class classObjC : baseObjC, NSString +{ + extern (Objective-C) override const(char)* UTF8String() { return null; } +} + +void test23262_osx() +{ + auto objc = new classObjC; + baseObjC base = objc; + auto objc2 = cast(classObjC) base; // reinterpret cast for dmd + version (DigitalMars) + assert(objc is objc2); + + assert(cast(Object) objc is null); + assert(cast(cpp23262) objc is null); + assert(cast(iface23262) objc is null); + + assert(cast(NSString) base !is null); // reinterpret cast or better +} +} // OSX + /***************************************************/ int main() @@ -223,6 +299,9 @@ int main() test10842(); test11722(); test14218(); + test23262(); + version(OSX) + test23262_osx(); printf("Success\n"); return 0;