diff --git a/gen/classes.cpp b/gen/classes.cpp index f5d164ac746..3ecc92f03b8 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -513,8 +513,9 @@ LLValue* DtoVirtualFunctionPointer(DValue* inst, FuncDeclaration* fdecl, char* n // sanity checks assert(fdecl->isVirtual()); assert(!fdecl->isFinal()); - assert(fdecl->vtblIndex > 0); // 0 is always ClassInfo/Interface* assert(inst->getType()->toBasetype()->ty == Tclass); + // 0 is always ClassInfo/Interface* unless it is a CPP interface + assert(fdecl->vtblIndex > 0 || (fdecl->vtblIndex == 0 && fdecl->linkage == LINKcpp)); // get instance LLValue* vthis = inst->getRVal(); diff --git a/gen/toir.cpp b/gen/toir.cpp index d1ee19fe181..b48f7ccad6c 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -1496,11 +1496,13 @@ DValue* DotVarExp::toElem(IRState* p) // If we are calling a non-final interface function, we need to get // the pointer to the underlying object instead of passing the // interface pointer directly. + // Unless it is a cpp interface, in that case, we have to match + // C++ behavior and pass the interface pointer. LLValue* passedThis = 0; if (e1type->ty == Tclass) { TypeClass* tc = static_cast(e1type); - if (tc->sym->isInterfaceDeclaration() && nonFinal) + if (tc->sym->isInterfaceDeclaration() && nonFinal && !tc->sym->isCPPinterface()) passedThis = DtoCastInterfaceToObject(l, NULL)->getRVal(); } LLValue* vthis = l->getRVal(); diff --git a/ir/irclass.cpp b/ir/irclass.cpp index 8f0094a1ae6..441f960acce 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -283,23 +283,25 @@ llvm::GlobalVariable * IrAggr::getInterfaceVtbl(BaseClass * b, bool new_instance std::vector constants; constants.reserve(vtbl_array.dim); - // start with the interface info - VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3); + if (!b->base->isCPPinterface()) { // skip interface info for CPP interfaces + // start with the interface info + VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3); - // index into the interfaces array - llvm::Constant* idxs[2] = { - DtoConstSize_t(0), - DtoConstSize_t(interfaces_index) - }; + // index into the interfaces array + llvm::Constant* idxs[2] = { + DtoConstSize_t(0), + DtoConstSize_t(interfaces_index) + }; - llvm::Constant* c = llvm::ConstantExpr::getGetElementPtr( - getInterfaceArraySymbol(), idxs, true); + llvm::Constant* c = llvm::ConstantExpr::getGetElementPtr( + getInterfaceArraySymbol(), idxs, true); - constants.push_back(c); + constants.push_back(c); + } // add virtual function pointers size_t n = vtbl_array.dim; - for (size_t i = 1; i < n; i++) + for (size_t i = b->base->vtblOffset(); i < n; i++) { Dsymbol* dsym = static_cast(vtbl_array.data[i]); if (dsym == NULL) @@ -320,7 +322,59 @@ llvm::GlobalVariable * IrAggr::getInterfaceVtbl(BaseClass * b, bool new_instance fd->codegen(Type::sir); assert(fd->ir.irFunc && "invalid vtbl function"); - constants.push_back(fd->ir.irFunc->func); + LLFunction *fn = fd->ir.irFunc->func; + + // If the base is a cpp interface, 'this' parameter is a pointer to + // the interface not the underlying object as expected. Instead of + // the function, we place into the vtable a small wrapper, called thunk, + // that casts 'this' to the object and then pass it to the real function. + if (b->base->isCPPinterface()) { + TypeFunction *f = (TypeFunction*)fd->type->toBasetype(); + assert(f->fty.arg_this); + + // create the thunk function + OutBuffer name; + name.writestring("Th"); + name.printf("%i", b->offset); + name.writestring(fd->mangle()); + LLFunction *thunk = LLFunction::Create(isaFunction(fn->getType()->getContainedType(0)), + DtoLinkage(fd), name.toChars(), gIR->module); + + // create entry and end blocks + llvm::BasicBlock* beginbb = llvm::BasicBlock::Create(gIR->context(), "entry", thunk); + llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endentry", thunk); + gIR->scopes.push_back(IRScope(beginbb, endbb)); + + // copy the function parameters, so later we can pass them to the real function + std::vector args; + llvm::Function::arg_iterator iarg = thunk->arg_begin(); + for (; iarg != thunk->arg_end(); ++iarg) + args.push_back(iarg); + + // cast 'this' to Object + LLValue* &thisArg = args[(f->fty.arg_sret == 0) ? 0 : 1]; + LLType* thisType = thisArg->getType(); + thisArg = DtoBitCast(thisArg, getVoidPtrType()); + thisArg = DtoGEP1(thisArg, DtoConstInt(-b->offset)); + thisArg = DtoBitCast(thisArg, thisType); + + // call the real vtbl function. + LLValue *retVal = gIR->ir->CreateCall(fn, args); + + // return from the thunk + if (thunk->getReturnType() == LLType::getVoidTy(gIR->context())) + llvm::ReturnInst::Create(gIR->context(), beginbb); + else + llvm::ReturnInst::Create(gIR->context(), retVal, beginbb); + + // clean up + gIR->scopes.pop_back(); + thunk->getBasicBlockList().pop_back(); + + fn = thunk; + } + + constants.push_back(fn); } // build the vtbl constant diff --git a/ir/irtypeclass.cpp b/ir/irtypeclass.cpp index eff67141429..9d4be09daeb 100644 --- a/ir/irtypeclass.cpp +++ b/ir/irtypeclass.cpp @@ -188,7 +188,7 @@ void IrTypeClass::addBaseClassData( ArrayIter it2(*base->vtblInterfaces); VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3); - Type* first = interfaces_idx->type->nextOf()->pointerTo(); + Type* first = interfaces_idx->type->nextOf()->pointerTo(); // align offset offset = (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1);