diff --git a/gen/dvalue.cpp b/gen/dvalue.cpp index bdad72263b..9650c4514e 100644 --- a/gen/dvalue.cpp +++ b/gen/dvalue.cpp @@ -64,13 +64,16 @@ DRValue::DRValue(Type *t, LLValue *v) : DValue(t, v) { //////////////////////////////////////////////////////////////////////////////// DImValue::DImValue(Type *t, llvm::Value *v) : DRValue(t, v) { - // TODO: get rid of Tfunction exception +#ifndef NDEBUG + const auto tb = t->toBasetype(); +#endif + assert(tb->ty != TY::Tarray && "use DSliceValue for dynamic arrays"); + assert(!tb->isFunction_Delegate_PtrToFunction() && + "use DFuncValue for function pointers and delegates"); + // v may be an addrspace qualified pointer so strip it before doing a pointer // equality check. - assert(t->toBasetype()->ty == TY::Tfunction || - stripAddrSpaces(v->getType()) == DtoType(t)); - assert(t->toBasetype()->ty != TY::Tarray && - "use DSliceValue for dynamic arrays"); + assert(stripAddrSpaces(v->getType()) == DtoType(t)); } //////////////////////////////////////////////////////////////////////////////// @@ -105,19 +108,41 @@ LLValue *DSliceValue::getPtr() { //////////////////////////////////////////////////////////////////////////////// -DFuncValue::DFuncValue(Type *t, FuncDeclaration *fd, LLValue *v, LLValue *vt, - LLValue *vtable) - : DRValue(t, v), func(fd), vthis(vt), vtable(vtable) {} +namespace { +LLValue *createFuncRValue(Type *t, LLValue *funcPtr, LLValue *vthis) { + if (t->toBasetype()->ty == TY::Tdelegate) { + assert(vthis); + return DtoAggrPair(vthis, funcPtr); + } + return funcPtr; +} +} + +DFuncValue::DFuncValue(Type *t, FuncDeclaration *fd, LLValue *funcPtr, + LLValue *vt, LLValue *vtable) + : DRValue(t, createFuncRValue(t, funcPtr, vt)), func(fd), funcPtr(funcPtr), + vthis(vt), vtable(vtable) { + assert(t->toBasetype()->isFunction_Delegate_PtrToFunction()); +} -DFuncValue::DFuncValue(FuncDeclaration *fd, LLValue *v, LLValue *vt, +DFuncValue::DFuncValue(FuncDeclaration *fd, LLValue *funcPtr, LLValue *vt, LLValue *vtable) - : DFuncValue(fd->type, fd, v, vt, vtable) {} + : DFuncValue(fd->type, fd, funcPtr, vt, vtable) {} bool DFuncValue::definedInFuncEntryBB() { return isDefinedInFuncEntryBB(val) && (!vthis || isDefinedInFuncEntryBB(vthis)); } +DFuncValue *DFuncValue::paintAs(Type *t) { + if (t == type) + return this; + + return new DFuncValue( + t, func, funcPtr, + vthis && t->toBasetype()->ty == TY::Tdelegate ? vthis : nullptr, vtable); +} + //////////////////////////////////////////////////////////////////////////////// DLValue::DLValue(Type *t, LLValue *v) : DValue(t, v) { @@ -132,8 +157,8 @@ DRValue *DLValue::getRVal() { LLValue *rval = DtoLoad(DtoMemType(type), val); - const auto ty = type->toBasetype()->ty; - if (ty == TY::Tbool) { + const auto tb = type->toBasetype(); + if (tb->ty == TY::Tbool) { assert(rval->getType() == llvm::Type::getInt8Ty(gIR->context())); if (isOptimizationEnabled()) { @@ -146,8 +171,14 @@ DRValue *DLValue::getRVal() { // truncate to i1 rval = gIR->ir->CreateTrunc(rval, llvm::Type::getInt1Ty(gIR->context())); - } else if (ty == TY::Tarray) { + } else if (tb->ty == TY::Tarray) { return new DSliceValue(type, rval); + } else if (tb->isPtrToFunction()) { + return new DFuncValue(type, nullptr, rval); + } else if (tb->ty == TY::Tdelegate) { + const auto contextPtr = DtoExtractValue(rval, 0, ".ptr"); + const auto funcPtr = DtoExtractValue(rval, 1, ".funcptr"); + return new DFuncValue(type, nullptr, funcPtr, contextPtr); } return new DImValue(type, rval); diff --git a/gen/dvalue.h b/gen/dvalue.h index 3a1b779d93..a0698227b6 100644 --- a/gen/dvalue.h +++ b/gen/dvalue.h @@ -141,18 +141,21 @@ class DSliceValue : public DRValue { /// optional vtable pointer. class DFuncValue : public DRValue { public: - FuncDeclaration *func; - llvm::Value *vthis; + FuncDeclaration *const func; + llvm::Value *const funcPtr; + llvm::Value *const vthis; llvm::Value *vtable; - DFuncValue(Type *t, FuncDeclaration *fd, llvm::Value *v, + DFuncValue(Type *t, FuncDeclaration *fd, llvm::Value *funcPtr, + llvm::Value *vt = nullptr, llvm::Value *vtable = nullptr); + DFuncValue(FuncDeclaration *fd, llvm::Value *funcPtr, llvm::Value *vt = nullptr, llvm::Value *vtable = nullptr); - DFuncValue(FuncDeclaration *fd, llvm::Value *v, llvm::Value *vt = nullptr, - llvm::Value *vtable = nullptr); bool definedInFuncEntryBB() override; DFuncValue *isFunc() override { return this; } + + DFuncValue *paintAs(Type *t); }; /// Represents a D value in memory via a low-level lvalue (pointer). diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 0cc63e70b8..aa7c82b4c0 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -527,6 +527,10 @@ DValue *DtoCastInt(const Loc &loc, DValue *val, Type *_to) { } else { rval = new llvm::SIToFPInst(rval, tolltype, "", gIR->scopebb()); } + } else if (to->isPtrToFunction()) { + IF_LOG Logger::cout() << "cast function pointer: " << *tolltype << '\n'; + rval = gIR->ir->CreateIntToPtr(rval, tolltype); + return new DFuncValue(_to, nullptr, rval); } else if (to->ty == TY::Tpointer) { IF_LOG Logger::cout() << "cast pointer: " << *tolltype << '\n'; rval = gIR->ir->CreateIntToPtr(rval, tolltype); @@ -569,7 +573,9 @@ DValue *DtoCastPtr(const Loc &loc, DValue *val, Type *to) { fatal(); } - return new DImValue(to, rval); + return totype->isPtrToFunction() + ? static_cast(new DFuncValue(to, nullptr, rval)) + : static_cast(new DImValue(to, rval)); } DValue *DtoCastFloat(const Loc &loc, DValue *val, Type *to) { @@ -766,9 +772,7 @@ DValue *DtoPaintType(const Loc &loc, DValue *val, Type *to) { return new DSliceValue(to, slice->getLength(), slice->getPtr()); } } else if (auto func = val->isFunc()) { - if (tb->ty == TY::Tdelegate) { - return new DFuncValue(to, func->func, DtoRVal(func), func->vthis); - } + return func->paintAs(to); } else { // generic rvalue LLValue *rval = DtoRVal(val); LLType *tll = DtoType(tb); @@ -1538,7 +1542,7 @@ DValue *DtoSymbolAddress(const Loc &loc, Type *type, Declaration *decl) { // to the module member list. DtoDefineFunction(flitdecl); - return new DFuncValue(flitdecl, DtoCallee(flitdecl, false)); + return new DFuncValue(type, flitdecl, DtoCallee(flitdecl, false)); } if (FuncDeclaration *fdecl = decl->isFuncDeclaration()) { @@ -1555,7 +1559,7 @@ DValue *DtoSymbolAddress(const Loc &loc, Type *type, Declaration *decl) { } DtoResolveFunction(fdecl); assert(!DtoIsMagicIntrinsic(fdecl)); - return new DFuncValue(fdecl, DtoCallee(fdecl)); + return new DFuncValue(type, fdecl, DtoCallee(fdecl)); } if (SymbolDeclaration *sdecl = decl->isSymbolDeclaration()) { diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 97f5ca9f0c..681245957c 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -712,8 +712,9 @@ class ImplicitArgumentsBuilder { size_t index = args.size(); - if (dfnval && (dfnval->func->ident == Id::ensure || - dfnval->func->ident == Id::require)) { + if (dfnval && dfnval->func && + (dfnval->func->ident == Id::ensure || + dfnval->func->ident == Id::require)) { // can be the this "context" argument for a contract invocation // (pass a pointer to the aggregate `this` pointer, which can naturally be // used as the contract's parent context in case the contract features @@ -747,7 +748,7 @@ class ImplicitArgumentsBuilder { args.push_back(ctxarg); } else if (nestedcall) { // ... or a nested function context arg - if (dfnval) { + if (dfnval && dfnval->func) { LLValue *contextptr = DtoNestedContext(loc, dfnval->func); args.push_back(contextptr); } else { @@ -767,6 +768,7 @@ class ImplicitArgumentsBuilder { if (irFty.arg_objcSelector) { assert(dfnval); + assert(dfnval->func); const auto selector = dfnval->func->objc.selector; assert(selector); LLGlobalVariable *selptr = gIR->objc.getMethVarRef(*selector); @@ -836,7 +838,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, // get callee llvm value LLValue *callable = DtoCallableValue(fnval); LLFunctionType *callableTy = irFty.funcType; - if (dfnval && dfnval->func->isCsymbol()) { + if (dfnval && dfnval->func && dfnval->func->isCsymbol()) { // See note in DtoDeclareFunction about K&R foward declared (void) functions // later defined as (...) functions. We want to use the non-variadic one. if (irFty.funcType->getNumParams() == 0 && irFty.funcType->isVarArg()) @@ -1017,7 +1019,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, attrlist = llvm::Intrinsic::getAttributes(gIR->context(), cf->getIntrinsicID()); } - } else if (dfnval) { + } else if (dfnval && dfnval->func) { call->setCallingConv(getCallingConvention(dfnval->func)); } else { call->setCallingConv(gABI->callingConv(tf, iab.hasContext)); @@ -1044,6 +1046,12 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, if (rbase->ty == TY::Tarray) { return new DSliceValue(resulttype, retllval); + } else if (rbase->isPtrToFunction()) { + return new DFuncValue(resulttype, nullptr, retllval); + } else if (rbase->ty == TY::Tdelegate) { + const auto contextPtr = DtoExtractValue(retllval, 0, ".ptr"); + const auto funcPtr = DtoExtractValue(retllval, 1, ".funcptr"); + return new DFuncValue(resulttype, nullptr, funcPtr, contextPtr); } return new DImValue(resulttype, retllval); diff --git a/gen/toir.cpp b/gen/toir.cpp index be09d2a682..25960d04f8 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -880,8 +880,18 @@ class ToElemVisitor : public Visitor { llvm::Value *offsetValue = nullptr; + const auto tb = e->type->toBasetype(); + assert(tb->ty != TY::Tdelegate); + assert(tb->ty != TY::Tfunction); + if (e->offset == 0) { offsetValue = baseValue; + + if (tb->isPtrToFunction()) + if (auto dfn = base->isFunc()) { + result = dfn->paintAs(e->type); + return; + } } else { LLType *elemType = DtoType(base->type); if (elemType->isSized()) { @@ -912,9 +922,14 @@ class ToElemVisitor : public Visitor { } } + if (tb->isPtrToFunction()) { + result = new DFuncValue(e->type, nullptr, offsetValue); + return; + } + // Casts are also "optimized into" SymOffExp by the frontend. - LLValue *llVal = (e->type->toBasetype()->isintegral() - ? p->ir->CreatePtrToInt(offsetValue, DtoType(e->type)) + LLValue *llVal = + (tb->isintegral() ? p->ir->CreatePtrToInt(offsetValue, DtoType(e->type)) : DtoBitCast(offsetValue, DtoType(e->type))); result = new DImValue(e->type, llVal); } @@ -945,9 +960,8 @@ class ToElemVisitor : public Visitor { if (DFuncValue *fv = v->isFunc()) { Logger::println("is func"); // Logger::println("FuncDeclaration"); - FuncDeclaration *fd = fv->func; - assert(fd); - result = new DFuncValue(fd, DtoCallee(fd)); + assert(fv->func); + result = fv->paintAs(e->type); return; } if (v->isIm()) { @@ -984,11 +998,10 @@ class ToElemVisitor : public Visitor { // function pointers are special if (e->type->toBasetype()->ty == TY::Tfunction) { DValue *dv = toElem(e->e1); - LLValue *llVal = DtoRVal(dv); if (DFuncValue *dfv = dv->isFunc()) { - result = new DFuncValue(e->type, dfv->func, llVal); + result = dfv->paintAs(e->type); } else { - result = new DImValue(e->type, llVal); + result = new DFuncValue(e->type, nullptr, DtoRVal(dv)); } return; } @@ -1072,18 +1085,18 @@ class ToElemVisitor : public Visitor { fdecl->visibility.kind != Visibility::package_; // Get the actual function value to call. - LLValue *funcval = nullptr; + LLValue *funcPtr = nullptr; LLValue *vtable = nullptr; if (nonFinal) { DtoResolveFunction(fdecl); - std::tie(funcval, vtable) = DtoVirtualFunctionPointer(l, fdecl); + std::tie(funcPtr, vtable) = DtoVirtualFunctionPointer(l, fdecl); } else { - funcval = DtoCallee(fdecl); + funcPtr = DtoCallee(fdecl); } - assert(funcval); + assert(funcPtr); LLValue *vthis = (DtoIsInMemoryOnly(l->type) ? DtoLVal(l) : DtoRVal(l)); - result = new DFuncValue(fdecl, funcval, vthis, vtable); + result = new DFuncValue(e->type, fdecl, funcPtr, vthis, vtable); } else { llvm_unreachable("Unknown target for VarDeclaration."); } @@ -1952,8 +1965,11 @@ class ToElemVisitor : public Visitor { DValue *u = toElem(e->e1); LLValue *contextptr; if (DFuncValue *f = u->isFunc()) { - assert(f->func); - contextptr = DtoNestedContext(e->loc, f->func); + contextptr = f->vthis; + if (!contextptr) { + assert(f->func); + contextptr = DtoNestedContext(e->loc, f->func); + } } else { contextptr = (DtoIsInMemoryOnly(u->type) ? DtoLVal(u) : DtoRVal(u)); } @@ -1989,8 +2005,7 @@ class ToElemVisitor : public Visitor { fptr = DtoCallee(e->func); } - result = new DImValue( - e->type, DtoAggrPair(DtoType(e->type), contextptr, fptr, ".dg")); + result = new DFuncValue(e->type, e->func, fptr, contextptr); } ////////////////////////////////////////////////////////////////////////////// @@ -2281,12 +2296,14 @@ class ToElemVisitor : public Visitor { genFuncLiteral(fd, e); LLFunction *callee = DtoCallee(fd, false); - if (fd->isNested()) { - LLValue *cval = DtoNestedContext(e->loc, fd); - result = new DImValue(e->type, DtoAggrPair(cval, callee, ".func")); + LLValue *contextPointer = nullptr; + if (e->type->toBasetype()->ty == TY::Tdelegate) { + contextPointer = DtoNestedContext(e->loc, fd); } else { - result = new DFuncValue(e->type, fd, callee); + assert(!fd->isNested()); } + + result = new DFuncValue(e->type, fd, callee, contextPointer); } ////////////////////////////////////////////////////////////////////////////// @@ -2920,8 +2937,8 @@ bool toInPlaceConstruction(DLValue *lhs, Expression *rhs) { Logger::println("is a constructor call, checking lhs of DotVarExp"); if (toInPlaceConstruction(lhs, dve->e1)) { Logger::println("success, calling ctor on in-place constructed lhs"); - auto fnval = new DFuncValue(fd, DtoCallee(fd), DtoLVal(lhs)); - DtoCallFunction(ce->loc, ce->type, fnval, ce->arguments); + auto fnval = DFuncValue(fd, DtoCallee(fd), DtoLVal(lhs)); + DtoCallFunction(ce->loc, ce->type, &fnval, ce->arguments); return true; } } diff --git a/tests/compilable/gh4759.d b/tests/compilable/gh4759.d new file mode 100644 index 0000000000..443ce4dcc0 --- /dev/null +++ b/tests/compilable/gh4759.d @@ -0,0 +1,7 @@ +// RUN: %ldc -c %s + +void func(void delegate()) +{ + enum ident(alias F) = __traits(identifier, __traits(parent, F)); + func({ enum i = ident!({}); }); +}