Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions gen/classes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ bool DtoIsObjcLinkage(Type *_to) {
DtoResolveClass(to->sym);
return to->sym->classKind == ClassKind::objc;
}

return false;
}

Expand Down Expand Up @@ -391,7 +391,7 @@ DValue *DtoDynamicCastInterface(Loc loc, DValue *val, Type *_to) {
// In this case we want to call the Objective-C runtime to first
// get a Class object from the `id`.
// Then check if class_conformsToProtocol returns true,
// if it does, then we can cast and return the casted value,
// if it does, then we can cast and return the casted value,
// otherwise return null.
if (DtoIsObjcLinkage(_to)) {
llvm::Function *getClassFunc =
Expand All @@ -400,10 +400,10 @@ DValue *DtoDynamicCastInterface(Loc loc, DValue *val, Type *_to) {
llvm::Function *kindOfProtocolFunc =
getRuntimeFunction(loc, gIR->module, "class_conformsToProtocol");

// id -> Class
// id -> Class
LLValue *obj = DtoRVal(val);
LLValue *objClass = gIR->CreateCallOrInvoke(getClassFunc, obj);

// Get prototype_t handle
LLValue *protoTy = getNullPtr();
if (auto ifhndl = _to->isClassHandle()->isInterfaceDeclaration()) {
Expand All @@ -413,7 +413,7 @@ DValue *DtoDynamicCastInterface(Loc loc, DValue *val, Type *_to) {
// Class && kindOfProtocolFunc(Class) ? id : null
LLValue *ret = gIR->ir->CreateSelect(
gIR->CreateCallOrInvoke(kindOfProtocolFunc, objClass, protoTy),
obj,
obj,
getNullPtr()
);
return new DImValue(_to, ret);
Expand Down
43 changes: 21 additions & 22 deletions gen/objcgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ std::string objcGetTypeEncoding(Type *t) {
tmp.append(isUnion ? "(" : "{");
tmp.append(t->toChars());
tmp.append("=");

for(unsigned int i = 0; i < sym->numArgTypes(); i++) {
tmp.append(objcGetTypeEncoding(sym->argType(i)));
}
Expand Down Expand Up @@ -139,7 +139,7 @@ std::string objcGetClassMethodListSymbol(const char *className, bool meta) {
return objcGetSymbolName(meta ? "_OBJC_$_CLASS_METHODS_" : "_OBJC_$_INSTANCE_METHODS_", className);
}

std::string objcGetProtoMethodListSymbol(const char *className, bool meta, bool optional) {
std::string objcGetProtoMethodListSymbol(const char *className, bool meta, bool optional) {
return optional ?
objcGetSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_OPT_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_", className) :
objcGetSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_", className);
Expand Down Expand Up @@ -315,7 +315,7 @@ LLStructType *objcGetProtocolType(const llvm::Module& module) {

LLConstant *objcEmitList(llvm::Module &module, LLConstantList objects, bool alignSizeT, bool countOnly) {
LLConstantList members;

// Emit nullptr for empty lists.
if (objects.empty())
return nullptr;
Expand Down Expand Up @@ -368,7 +368,7 @@ size_t objcGetClassFlags(ClassDeclaration *decl) {
size_t flags = 0;
if (!decl->baseClass)
flags |= RO_ROOT;

if (decl->objc.isMeta)
flags |= RO_META;

Expand All @@ -390,7 +390,7 @@ ClassDeclaration *objcGetMetaClass(ClassDeclaration *decl) {

ClassDeclaration *objcGetSuper(ClassDeclaration *decl) {
return (decl->objc.isRootClass() || !decl->baseClass) ?
decl :
decl :
decl->baseClass;
}

Expand All @@ -399,7 +399,7 @@ ClassDeclaration *objcGetSuper(ClassDeclaration *decl) {
//

ptrdiff_t objcGetInstanceStart(llvm::Module &module, ClassDeclaration *decl, bool meta) {
ptrdiff_t start = meta ?
ptrdiff_t start = meta ?
getTypeAllocSize(objcGetClassType(module)) :
getPointerSize();

Expand All @@ -418,13 +418,13 @@ ptrdiff_t objcGetInstanceStart(llvm::Module &module, ClassDeclaration *decl, boo
}

size_t objcGetInstanceSize(llvm::Module &module, ClassDeclaration *decl, bool meta) {
size_t start = meta ?
size_t start = meta ?
getTypeAllocSize(objcGetClassType(module)) :
getPointerSize();

if (meta)
return start;

return start + dmd::size(decl, decl->loc);
}

Expand All @@ -441,12 +441,12 @@ LLConstant *ObjCState::getClassRoTable(ClassDeclaration *decl) {
if (auto it = classRoTables.find(decl); it != classRoTables.end()) {
return it->second;
}

// No need to generate RO tables for externs.
// nor for null declarations.
if (!decl || decl->objc.isExtern)
return getNullPtr();

// Base Methods
auto meta = decl->objc.isMeta;
auto name = objcResolveName(decl);
Expand All @@ -469,7 +469,7 @@ LLConstant *ObjCState::getClassRoTable(ClassDeclaration *decl) {
}

if (!meta) {

// Instance variables
if (auto baseIvars = createIvarList(decl)) {
ivarList = getOrCreate(objcGetIvarListSymbol(name), baseIvars->getType(), OBJC_SECNAME_CONST);
Expand Down Expand Up @@ -501,7 +501,7 @@ LLConstant *ObjCState::getClassTable(ClassDeclaration *decl) {
if (auto it = classTables.find(decl); it != classTables.end()) {
return it->second;
}

// If decl is null, just return a null pointer.
if (!decl)
return getNullPtr();
Expand All @@ -512,7 +512,7 @@ LLConstant *ObjCState::getClassTable(ClassDeclaration *decl) {
auto table = getOrCreate(sym, objcGetClassType(module), OBJC_SECNAME_DATA, decl->objc.isExtern);
classTables[decl] = table;
this->retain(table);

// Extern tables don't need a body.
if (decl->objc.isExtern)
return table;
Expand Down Expand Up @@ -547,7 +547,7 @@ ObjcClassInfo *ObjCState::getClass(ClassDeclaration *decl) {

if (!decl->objc.isMeta)
classInfo->ref->setInitializer(classInfo->table);

return classInfo;
}

Expand Down Expand Up @@ -638,7 +638,7 @@ LLConstant *ObjCState::createProtocolTable(InterfaceDeclaration *decl) {
optClassMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage);
optClassMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility);
}

auto protoType = objcGetProtocolType(module);
auto allocSize = getTypeAllocSize(protoType);

Expand All @@ -652,7 +652,7 @@ LLConstant *ObjCState::createProtocolTable(InterfaceDeclaration *decl) {
members.push_back(getNullPtr()); // instanceProperties (TODO)
members.push_back(DtoConstUint(allocSize)); // size
members.push_back(DtoConstUint(0)); // flags

return LLConstantStruct::getAnon(
members,
true
Expand Down Expand Up @@ -734,7 +734,7 @@ ObjcMethodInfo *ObjCState::getMethod(FuncDeclaration *decl) {
methodInfo->llfunction = decl->fbody ?
DtoBitCast(DtoCallee(decl), getOpaquePtrType()) :
getNullPtr();

this->retain(methodInfo->name);
this->retain(methodInfo->type);
this->retain(methodInfo->selector);
Expand All @@ -754,7 +754,7 @@ LLConstant *ObjCState::createMethodList(ClassDeclaration *decl, bool optional) {
LLConstantList methodList;

if (decl) {

auto methodDeclList = getMethodsForType(decl, optional);
for(auto func : methodDeclList) {
methodList.push_back(createMethodInfo(func));
Expand Down Expand Up @@ -829,7 +829,7 @@ LLConstant *ObjCState::createIvarInfo(VarDeclaration *decl) {

LLConstant *ObjCState::createIvarList(ClassDeclaration *decl) {
LLConstantList ivarList;

for(auto field : decl->fields) {
ivarList.push_back(createIvarInfo(field));
}
Expand All @@ -840,13 +840,12 @@ LLConstant *ObjCState::createIvarList(ClassDeclaration *decl) {
// HELPERS
//
LLValue *ObjCState::deref(ClassDeclaration *decl, LLType *as) {

// Protocols can also have static functions
// as such we need to also be able to dereference them.
if (auto proto = decl->isInterfaceDeclaration()) {
return DtoLoad(as, getProtocol(proto)->ref);
}

// Classes may be class stubs.
// in that case, we need to call objc_loadClassRef instead of just
// loading from the classref.
Expand All @@ -869,7 +868,7 @@ ObjcList<FuncDeclaration *> ObjCState::getMethodsForType(ClassDeclaration *decl,
if (decl) {
for(size_t i = 0; i < decl->objc.methodList.length; i++) {
auto method = decl->objc.methodList.ptr[i];

if (isProtocol) {
if (method->objc.isOptional == optional)
funcs.push_back(method);
Expand Down
21 changes: 18 additions & 3 deletions gen/tocall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -727,14 +727,29 @@ class ImplicitArgumentsBuilder {
// class pointer
Type *thistype = gIR->func()->decl->vthis->type;
if (thistype != iface->type) {
DImValue *dthis = new DImValue(thistype, DtoLoad(DtoType(thistype),thisptrLval));
thisptrLval = DtoAllocaDump(DtoCastClass(loc, dthis, iface->type));
auto thisVal = DtoLoad(DtoType(thistype), thisptrLval);
auto thisTypeClass = thistype->toBasetype()->isTypeClass();
auto ifaceTypeClass = iface->type->toBasetype()->isTypeClass();
const bool sameInterfaceSymbol =
thisTypeClass && ifaceTypeClass &&
thisTypeClass->sym->isInterfaceDeclaration() &&
thisTypeClass->sym == ifaceTypeClass->sym;

if (sameInterfaceSymbol) {
// Qualifier-only interface casts don't require dynamic cast routing.
thisptrLval = DtoAllocaDump(
new DImValue(iface->type,
DtoBitCast(thisVal, DtoType(iface->type))));
} else {
DImValue dthis(thistype, thisVal);
thisptrLval = DtoAllocaDump(DtoCastClass(loc, &dthis, iface->type));
Comment thread
gulugulubing marked this conversation as resolved.
Outdated
}
}
}
}
args.push_back(thisptrLval);
} else if (thiscall && dfnval && dfnval->vthis) {

if (objccall && directcall) {

// ... or a Objective-c direct call argument
Expand Down
79 changes: 79 additions & 0 deletions tests/codegen/gh5114.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
module tests.codegen.gh5114;

// During interface contract context setup, a qualifier-only cast for the same
// interface symbol (e.g., `const(I)` -> `I`) must be handled as a repaint/
// bitcast and must not route through dynamic interface cast lowering.
//
// True dynamic interface casts (different interface symbols) are still valid
// and are covered below.
//
// RUN: %ldc -c %s
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: line 11 already tests compilation, so you can remove line 10

// RUN: %ldc -unittest -main -run %s

extern (D):

interface I {
void fn() const
out {
// Positive case: contract body performs a true interface -> interface cast.
auto b = cast(const(B)) this;
assert(b !is null);
assert(b.b() == 22);
};
}

interface A {
int a();
}

interface B {
int b() const;
}

interface J : I {
}

class C : I, A, B {
override void fn() const
out (; true)
{
}

override int a() {
return 11;
}

override int b() const {
return 22;
}
}

class D : J, B {
override void fn() const
out (; true)
{
}

override int b() const {
return 22;
}
}

unittest {
A a = new C();

// True dynamic cast: interface -> interface, resolved via druntime cast hook.
B b = cast(B) a;

assert(b !is null);
assert(b.b() == 22);

// Ensure the interface contract executes in extern(D) call flow.
I i = cast(I) a;
i.fn();

// Derived-interface call flow for I's contract; this may route through a
// non-same-interface contract context conversion.
J j = new D();
j.fn();
}
Loading