diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -1955,6 +1955,13 @@ const ObjCIvarDecl *getNextIvar() const { return NextIvar; } void setNextIvar(ObjCIvarDecl *ivar) { NextIvar = ivar; } + ObjCIvarDecl *getCanonicalDecl() override { + return cast(FieldDecl::getCanonicalDecl()); + } + const ObjCIvarDecl *getCanonicalDecl() const { + return const_cast(this)->getCanonicalDecl(); + } + void setAccessControl(AccessControl ac) { DeclAccess = ac; } AccessControl getAccessControl() const { return AccessControl(DeclAccess); } diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -3404,6 +3404,7 @@ uint64_t ASTContext::lookupFieldBitOffset(const ObjCInterfaceDecl *OID, const ObjCImplementationDecl *ID, const ObjCIvarDecl *Ivar) const { + Ivar = Ivar->getCanonicalDecl(); const ObjCInterfaceDecl *Container = Ivar->getContainingInterface(); // FIXME: We should eliminate the need to have ObjCImplementationDecl passed diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -3350,6 +3350,9 @@ return ED->getASTContext().getLangOpts().CPlusPlus? ED->getDefinition() : nullptr; + if (auto *OID = dyn_cast(DC)) + return OID->getDefinition(); + // We can see the TU here only if we have no Sema object. In that case, // there's no TU scope to look in, so using the DC alone is sufficient. if (auto *TU = dyn_cast(DC)) diff --git a/clang/test/Modules/merge-objc-interface.m b/clang/test/Modules/merge-objc-interface.m new file mode 100644 --- /dev/null +++ b/clang/test/Modules/merge-objc-interface.m @@ -0,0 +1,105 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -emit-llvm -o %t/test.bc -F%t/Frameworks %t/test.m \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache +// RUN: %clang_cc1 -emit-llvm -o %t/test.bc -F%t/Frameworks %t/test-functions.m \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules.cache + +// Test a case when Objective-C interface ivars are present in two different modules. + +//--- Frameworks/Foundation.framework/Headers/Foundation.h +@interface NSObject +@end + +//--- Frameworks/Foundation.framework/Modules/module.modulemap +framework module Foundation { + header "Foundation.h" + export * +} + +//--- Frameworks/ObjCInterface.framework/Headers/ObjCInterface.h +#import +@interface ObjCInterface : NSObject { +@public + id _item; +} +@end + +@interface WithBitFields : NSObject { +@public + int x: 3; + int y: 4; +} +@end + +//--- Frameworks/ObjCInterface.framework/Modules/module.modulemap +framework module ObjCInterface { + header "ObjCInterface.h" + export * +} + +//--- Frameworks/ObjCInterfaceCopy.framework/Headers/ObjCInterfaceCopy.h +#import +@interface ObjCInterface : NSObject { +@public + id _item; +} +@end + +@interface WithBitFields : NSObject { +@public + int x: 3; + int y: 4; +} +@end + +// Inlined function present only in Copy.framework to make sure it uses decls from Copy module. +__attribute__((always_inline)) void inlinedIVarAccessor(ObjCInterface *obj, WithBitFields *bitFields) { + obj->_item = 0; + bitFields->x = 0; +} + +//--- Frameworks/ObjCInterfaceCopy.framework/Modules/module.modulemap +framework module ObjCInterfaceCopy { + header "ObjCInterfaceCopy.h" + export * +} + +//--- test.m +#import +#import + +@implementation ObjCInterface +- (void)test:(id)item { + _item = item; +} +@end + +@implementation WithBitFields +- (void)reset { + x = 0; + y = 0; +} +@end + +//--- test-functions.m +#import + +void testAccessIVar(ObjCInterface *obj, id item) { + obj->_item = item; +} +void testAccessBitField(WithBitFields *obj) { + obj->x = 0; +} + +#import + +void testAccessIVarLater(ObjCInterface *obj, id item) { + obj->_item = item; +} +void testAccessBitFieldLater(WithBitFields *obj) { + obj->y = 0; +} +void testInlinedFunction(ObjCInterface *obj, WithBitFields *bitFields) { + inlinedIVarAccessor(obj, bitFields); +}