From 386477a54137833a4ecd713b950346e3fb605315 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Fri, 28 Dec 2018 17:44:54 +0000 Subject: [PATCH] [objc-gnustep2] Fix a bug in category generation. We were not emitting a protocol definition while generating the category method list. This was fine in most cases, because something else in the library typically referenced any given protocol, but it caused linker failures if the category was the only reference to a given protocol. llvm-svn: 350130 --- clang/lib/CodeGen/CGObjCGNU.cpp | 26 +++++++++++++++++----- .../test/CodeGenObjC/gnustep2-category-protocol.m | 25 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 clang/test/CodeGenObjC/gnustep2-category-protocol.m diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index 534650b..548bd6b 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -277,6 +277,8 @@ protected: Fields.addInt(Int8Ty, 0); } + virtual llvm::Constant *GenerateCategoryProtocolList(const + ObjCCategoryDecl *OCD); virtual ConstantArrayBuilder PushPropertyListHeader(ConstantStructBuilder &Fields, int count) { // int count; @@ -1164,6 +1166,15 @@ class CGObjCGNUstep2 : public CGObjCGNUstep { return MethodList.finishAndCreateGlobal(".objc_protocol_method_list", CGM.getPointerAlign()); } + llvm::Constant *GenerateCategoryProtocolList(const ObjCCategoryDecl *OCD) + override { + SmallVector Protocols; + for (const auto *PI : OCD->getReferencedProtocols()) + Protocols.push_back( + llvm::ConstantExpr::getBitCast(GenerateProtocolRef(PI), + ProtocolPtrTy)); + return GenerateProtocolList(Protocols); + } llvm::Value *LookupIMPSuper(CodeGenFunction &CGF, Address ObjCSuper, llvm::Value *cmd, MessageSendInfo &MSI) override { @@ -3099,18 +3110,21 @@ llvm::Constant *CGObjCGNU::MakeBitField(ArrayRef bits) { return ptr; } +llvm::Constant *CGObjCGNU::GenerateCategoryProtocolList(const + ObjCCategoryDecl *OCD) { + SmallVector Protocols; + for (const auto *PD : OCD->getReferencedProtocols()) + Protocols.push_back(PD->getNameAsString()); + return GenerateProtocolList(Protocols); +} + void CGObjCGNU::GenerateCategory(const ObjCCategoryImplDecl *OCD) { const ObjCInterfaceDecl *Class = OCD->getClassInterface(); std::string ClassName = Class->getNameAsString(); std::string CategoryName = OCD->getNameAsString(); // Collect the names of referenced protocols - SmallVector Protocols; const ObjCCategoryDecl *CatDecl = OCD->getCategoryDecl(); - const ObjCList &Protos = CatDecl->getReferencedProtocols(); - for (ObjCList::iterator I = Protos.begin(), - E = Protos.end(); I != E; ++I) - Protocols.push_back((*I)->getNameAsString()); ConstantInitBuilder Builder(CGM); auto Elements = Builder.beginStruct(); @@ -3132,7 +3146,7 @@ void CGObjCGNU::GenerateCategory(const ObjCCategoryImplDecl *OCD) { GenerateMethodList(ClassName, CategoryName, ClassMethods, true), PtrTy); // Protocol list - Elements.addBitCast(GenerateProtocolList(Protocols), PtrTy); + Elements.addBitCast(GenerateCategoryProtocolList(CatDecl), PtrTy); if (isRuntime(ObjCRuntime::GNUstep, 2)) { const ObjCCategoryDecl *Category = Class->FindCategoryDeclaration(OCD->getIdentifier()); diff --git a/clang/test/CodeGenObjC/gnustep2-category-protocol.m b/clang/test/CodeGenObjC/gnustep2-category-protocol.m new file mode 100644 index 0000000..6463474 --- /dev/null +++ b/clang/test/CodeGenObjC/gnustep2-category-protocol.m @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-freebsd -S -emit-llvm -fobjc-runtime=gnustep-2.0 -o - %s | FileCheck %s + +// Regression test. We weren't emitting definitions for protocols used in +// categories, causing linker errors when the category was the only reference +// to a protocol in a binary. + +// CHECK: @._OBJC_PROTOCOL_Y = global +// CHEKC-SAME: section "__objc_protocols", comdat, align 8 + + +@interface X +{ +id isa; +} +@end +@implementation X +@end + +@protocol Y @end + +@interface X (y) +@end +@implementation X (y) @end + + -- 2.7.4