diff --git a/compiler/src/dmd/backend/drtlsym.d b/compiler/src/dmd/backend/drtlsym.d index c89d25ece34e..2aec454f1e43 100644 --- a/compiler/src/dmd/backend/drtlsym.d +++ b/compiler/src/dmd/backend/drtlsym.d @@ -109,7 +109,6 @@ Symbol* getRtlsym(RTLSYM i) @trusted case RTLSYM.CALLFINALIZER: symbolz(ps,FL.func,FREGSAVED,"_d_callfinalizer", 0, t); break; case RTLSYM.CALLINTERFACEFINALIZER: symbolz(ps,FL.func,FREGSAVED,"_d_callinterfacefinalizer", 0, t); break; case RTLSYM.ALLOCMEMORY: symbolz(ps,FL.func,FREGSAVED,"_d_allocmemory", 0, t); break; - case RTLSYM.INTERFACE_CAST: symbolz(ps,FL.func,FREGSAVED,"_d_interface_cast", 0, t); break; case RTLSYM.ARRAYCATT: symbolz(ps,FL.func,FREGSAVED,"_d_arraycatT", 0, t); break; case RTLSYM.ARRAYAPPENDCD: symbolz(ps,FL.func,FREGSAVED,"_d_arrayappendcd", 0, t); break; case RTLSYM.ARRAYAPPENDWD: symbolz(ps,FL.func,FREGSAVED,"_d_arrayappendwd", 0, t); break; diff --git a/compiler/src/dmd/backend/rtlsym.d b/compiler/src/dmd/backend/rtlsym.d index 584ac2de0d34..79b2569efc41 100644 --- a/compiler/src/dmd/backend/rtlsym.d +++ b/compiler/src/dmd/backend/rtlsym.d @@ -54,7 +54,6 @@ enum RTLSYM CALLFINALIZER, CALLINTERFACEFINALIZER, ALLOCMEMORY, - INTERFACE_CAST, ARRAYCATT, ARRAYAPPENDCD, ARRAYAPPENDWD, diff --git a/compiler/src/dmd/e2ir.d b/compiler/src/dmd/e2ir.d index 8f15c1a7972d..c0850af326f5 100644 --- a/compiler/src/dmd/e2ir.d +++ b/compiler/src/dmd/e2ir.d @@ -4781,15 +4781,7 @@ elem* toElemCast(CastExp ce, elem* e, bool isLvalue, ref IRState irs) } else { - if (cdfrom.isInterfaceDeclaration()) - { - elem* ep = el_param(el_ptr(toExtSymbol(cdto)), e); - e = el_bin(OPcall, TYnptr, el_var(getRtlsym(RTLSYM.INTERFACE_CAST)), ep); - } - else - { - assert(ce.lowering, "This case should have been rewritten to `_d_cast` in the semantic phase"); - } + assert(ce.lowering, "This case should have been rewritten to `_d_cast` in the semantic phase"); } return Lret(ce, e); } diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index c8fadf852d4f..6f9ac8674622 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -3807,6 +3807,58 @@ Package resolveIsPackage(Dsymbol sym) return pkg; } +/** + * Performs the lowering of a CastExp to a call to `.object._d_cast`, + * populating the `lowering` field of the CastExp. + * This is only done for casts between classes/interfaces. + * + * Params: + * cex = the CastExp to lower + * sc = the current scope + */ +private void lowerCastExp(CastExp cex, Scope* sc) +{ + Type t1b = cex.e1.type.toBasetype(); + Type tob = cex.to.toBasetype(); + + if (t1b.ty != Tclass || tob.ty != Tclass) + return; + + ClassDeclaration cdfrom = t1b.isClassHandle(); + ClassDeclaration cdto = tob.isClassHandle(); + + int offset; + if ((cdto.isBaseOf(cdfrom, &offset) && offset != ClassDeclaration.OFFSET_RUNTIME) + || cdfrom.classKind == ClassKind.cpp) + return; + + Identifier hook = Id._d_cast; + if (!verifyHookExist(cex.loc, *sc, hook, "d_cast", Id.object)) + return; + + // Lower to .object._d_cast!(To)(exp.e1) + Expression lowering = new IdentifierExp(cex.loc, Id.empty); + lowering = new DotIdExp(cex.loc, lowering, Id.object); + + auto tiargs = new Objects(); + // Unqualify the type being casted to, avoiding multiple instantiations + auto unqual_tob = tob.unqualify(MODFlags.wild | MODFlags.const_ | + MODFlags.immutable_ | MODFlags.shared_); + tiargs.push(unqual_tob); + lowering = new DotTemplateInstanceExp(cex.loc, lowering, hook, tiargs); + + auto arguments = new Expressions(); + // Unqualify the type being casted from to avoid multiple instantiations + auto unqual_t1b = t1b.unqualify(MODFlags.wild | MODFlags.const_ | + MODFlags.immutable_ | MODFlags.shared_); + Expression e1c = cex.e1.copy(); + e1c.type = unqual_t1b; + arguments.push(e1c); + + lowering = new CallExp(cex.loc, lowering, arguments); + + cex.lowering = lowering.expressionSemantic(sc); +} private extern (C++) final class ExpressionSemanticVisitor : Visitor { @@ -6948,7 +7000,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (tf.next.isBaseOf(t, &offset) && offset) { exp.type = tf.next; - result = Expression.combine(argprefix, exp.castTo(sc, t)); + auto casted_exp = exp.castTo(sc, t); + if (auto cex = casted_exp.isCastExp()) + { + lowerCastExp(cex, sc); + } + result = Expression.combine(argprefix, casted_exp); return; } } @@ -9366,63 +9423,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - // If the cast is from an alias this, we need to unalias it - if (t1b.ty == Tstruct) + if (auto cex = ex.isCastExp()) { - if (auto t1b_unalias = t1b.aliasthisOf()) - { - t1b = t1b_unalias; - } - } - - if (t1b.ty == Tclass && tob.ty == Tclass) - { - CastExp cex = ex.isCastExp(); - - if (cex is null) - goto LskipCastLowering; - - ClassDeclaration cdfrom = t1b.isClassHandle(); - ClassDeclaration cdto = tob.isClassHandle(); - - int offset; - if (!(cdto.isBaseOf(cdfrom, &offset) && offset != ClassDeclaration.OFFSET_RUNTIME) - && cdfrom.classKind != ClassKind.cpp) - { - if (!cdfrom.isInterfaceDeclaration()) - { - - Identifier hook = Id._d_cast; - if (!verifyHookExist(cex.loc, *sc, hook, "d_cast", Id.object)) - goto LskipCastLowering; - - // Lower to .object._d_cast!(To)(exp.e1) - Expression lowering = new IdentifierExp(cex.loc, Id.empty); - lowering = new DotIdExp(cex.loc, lowering, Id.object); - - auto tiargs = new Objects(); - // Unqualify the type being casted to, avoiding multiple instantiations - auto unqual_tob = tob.unqualify(MODFlags.wild | MODFlags.const_ | - MODFlags.immutable_ | MODFlags.shared_); - tiargs.push(unqual_tob); - lowering = new DotTemplateInstanceExp(cex.loc, lowering, hook, tiargs); - - auto arguments = new Expressions(); - // Unqualify the type being casted from to avoid multiple instantiations - auto unqual_t1b = t1b.unqualify(MODFlags.wild | MODFlags.const_ | - MODFlags.immutable_ | MODFlags.shared_); - cex.e1.type = unqual_t1b; - arguments.push(cex.e1); - - lowering = new CallExp(cex.loc, lowering, arguments); - - cex.lowering = lowering.expressionSemantic(sc); - } - } + lowerCastExp(cex, sc); } - LskipCastLowering: - result = ex; } diff --git a/druntime/mak/DOCS b/druntime/mak/DOCS index 574a18b19c82..d61d53323b12 100644 --- a/druntime/mak/DOCS +++ b/druntime/mak/DOCS @@ -542,7 +542,6 @@ DOCS=\ $(DOCDIR)\rt_adi.html \ $(DOCDIR)\rt_alloca.html \ $(DOCDIR)\rt_arraycat.html \ - $(DOCDIR)\rt_cast_.html \ $(DOCDIR)\rt_config.html \ $(DOCDIR)\rt_deh.html \ $(DOCDIR)\rt_deh_win32.html \ diff --git a/druntime/mak/SRCS b/druntime/mak/SRCS index 065c6d720799..b9d3bdc738cc 100644 --- a/druntime/mak/SRCS +++ b/druntime/mak/SRCS @@ -565,7 +565,6 @@ SRCS=\ src\rt\adi.d \ src\rt\alloca.d \ src\rt\arraycat.d \ - src\rt\cast_.d \ src\rt\cmath2.d \ src\rt\config.d \ src\rt\cover.d \ diff --git a/druntime/src/core/internal/cast_.d b/druntime/src/core/internal/cast_.d index da928344e531..c4de76ea6266 100644 --- a/druntime/src/core/internal/cast_.d +++ b/druntime/src/core/internal/cast_.d @@ -101,6 +101,27 @@ private void* _d_class_cast(To)(const return scope Object o) return _d_class_cast_impl(o, typeid(To)); } +/************************************* + * Attempts to cast interface Object o to class type `To`. + * Returns o if successful, null if not. + */ +private void* _d_interface_cast(To)(void* p) @trusted +{ + if (!p) + return null; + + Interface* pi = **cast(Interface***) p; + + Object o2 = cast(Object)(p - pi.offset); + void* res = null; + size_t offset = 0; + if (o2 && _d_isbaseof2!To(typeid(o2), offset)) + { + res = cast(void*) o2 + offset; + } + return res; +} + /** * Hook that detects the type of cast performed and calls the appropriate function. * Params: @@ -149,7 +170,21 @@ void* _d_cast(To, From)(From o) @trusted return null; } - return null; + static if (is(From == interface)) + { + static if (is(From == To)) + { + return cast(void*)o; + } + else + { + return _d_interface_cast!To(cast(void*)o); + } + } + else + { + return null; + } } private bool _d_isbaseof2(To)(scope ClassInfo oc, scope ref size_t offset) @@ -241,3 +276,36 @@ private bool _d_isbaseof2(To)(scope ClassInfo oc, scope ref size_t offset) assert(_d_cast!D(a) is null); // A(a) to D assert(_d_class_cast!D(a) is null); } + +@safe pure unittest +{ + interface I1 {} + interface I2 {} + interface I3 {} + class A {} + class B : A, I1, I2 {} + class C : B, I3 {} + + I1 bi = new B(); + assert(_d_cast!I2(bi) !is null); // I1(b) to I2 + assert(_d_interface_cast!I2(cast(void*)bi) !is null); + + assert(_d_cast!A(bi) !is null); // I1(b) to A + assert(_d_interface_cast!A(cast(void*)bi) !is null); + + assert(_d_cast!B(bi) !is null); // I1(b) to B + assert(_d_interface_cast!B(cast(void*)bi) !is null); + + assert(_d_cast!I3(bi) is null); // I1(b) to I3 + assert(_d_interface_cast!I3(cast(void*)bi) is null); + + assert(_d_cast!C(bi) is null); // I1(b) to C + assert(_d_interface_cast!C(cast(void*)bi) is null); + + assert(_d_cast!I1(bi) !is null); // I1(b) to I1 + assert(_d_interface_cast!I1(cast(void*)bi) !is null); + + I3 ci = new C(); + assert(_d_cast!I1(ci) !is null); // I3(c) to I1 + assert(_d_interface_cast!I1(cast(void*)ci) !is null); +} diff --git a/druntime/src/object.d b/druntime/src/object.d index fb3fe86b56b5..b4dc2f720495 100644 --- a/druntime/src/object.d +++ b/druntime/src/object.d @@ -1572,8 +1572,57 @@ class TypeInfo_Delegate : TypeInfo } private extern (C) Object _d_newclass(const TypeInfo_Class ci); -private extern (C) int _d_isbaseof(scope TypeInfo_Class child, - scope const TypeInfo_Class parent) @nogc nothrow pure @safe; // rt.cast_ + +extern(C) int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @nogc nothrow pure @safe +{ + import core.internal.cast_ : areClassInfosEqual; + + if (areClassInfosEqual(oc, c)) + return true; + + do + { + if (oc.base && areClassInfosEqual(oc.base, c)) + return true; + + // Bugzilla 2013: Use depth-first search to calculate offset + // from the derived (oc) to the base (c). + foreach (iface; oc.interfaces) + { + if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c)) + return true; + } + + oc = oc.base; + } while (oc); + + return false; +} + +/****************************************** + * Given a pointer: + * If it is an Object, return that Object. + * If it is an interface, return the Object implementing the interface. + * If it is null, return null. + * Else, undefined crash + */ +extern(C) Object _d_toObject(return scope void* p) @nogc nothrow pure @trusted +{ + if (!p) + return null; + + Object o = cast(Object) p; + Interface* pi = **cast(Interface***) p; + + /* Interface.offset lines up with ClassInfo.name.ptr, + * so we rely on pointers never being less than 64K, + * and Objects never being greater. + */ + if (pi.offset < 0x10000) + return cast(Object)(p - pi.offset); + + return o; +} /** * Runtime type information about a class. diff --git a/druntime/src/rt/cast_.d b/druntime/src/rt/cast_.d deleted file mode 100644 index dc2b78c7f846..000000000000 --- a/druntime/src/rt/cast_.d +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Implementation of array assignment support routines. - * - * Copyright: Copyright Digital Mars 2004 - 2010. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Walter Bright, Sean Kelly - * Source: $(DRUNTIMESRC rt/_cast_.d) - */ - -/* Copyright Digital Mars 2004 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.cast_; - -debug(cast_) import core.stdc.stdio : printf; -import core.internal.cast_ : areClassInfosEqual; - -extern (C): -@nogc: -nothrow: -pure: - -/****************************************** - * Given a pointer: - * If it is an Object, return that Object. - * If it is an interface, return the Object implementing the interface. - * If it is null, return null. - * Else, undefined crash - */ -Object _d_toObject(return scope void* p) -{ - if (!p) - return null; - - Object o = cast(Object) p; - ClassInfo oc = typeid(o); - Interface* pi = **cast(Interface***) p; - - /* Interface.offset lines up with ClassInfo.name.ptr, - * so we rely on pointers never being less than 64K, - * and Objects never being greater. - */ - if (pi.offset < 0x10000) - { - debug(cast_) printf("\tpi.offset = %zd\n", pi.offset); - return cast(Object)(p - pi.offset); - } - return o; -} - -/************************************* - * Attempts to cast interface Object o to class c. - * Returns o if successful, null if not. - */ -void* _d_interface_cast(void* p, ClassInfo c) -{ - debug(cast_) printf("_d_interface_cast(p = %p, c = '%.*s')\n", p, cast(int) c.name.length, c.name.ptr); - if (!p) - return null; - - Interface* pi = **cast(Interface***) p; - - debug(cast_) printf("\tpi.offset = %zd\n", pi.offset); - Object o2 = cast(Object)(p - pi.offset); - void* res = null; - size_t offset = 0; - if (o2 && _d_isbaseof2(typeid(o2), c, offset)) - { - debug(cast_) printf("\toffset = %zd\n", offset); - res = cast(void*) o2 + offset; - } - debug(cast_) printf("\tresult = %p\n", res); - return res; -} - -int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe -{ - if (areClassInfosEqual(oc, c)) - return true; - - do - { - if (oc.base && areClassInfosEqual(oc.base, c)) - return true; - - // Bugzilla 2013: Use depth-first search to calculate offset - // from the derived (oc) to the base (c). - foreach (iface; oc.interfaces) - { - if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2(iface.classinfo, c, offset)) - { - offset += iface.offset; - return true; - } - } - - oc = oc.base; - } while (oc); - - return false; -} - -int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe -{ - size_t offset = 0; - return _d_isbaseof2(oc, c, offset); -}