From cf7bc8edf8cccb1b5de656c403cb55ad44132e98 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 1 Apr 2016 23:23:52 +0000 Subject: [PATCH] [Objective-C] Introduce objc_runtime_visible attribute. The objc_runtime_visible attribute deals with an odd corner case where a particular Objective-C class is known to the Objective-C runtime (and, therefore, accessible by name) but its symbol has been hidden for some reason. For such classes, teach CodeGen to use objc_lookUpClass to retrieve the Class object, rather than referencing the class symbol directly. Classes annotated with objc_runtime_visible have two major limitations that fall out from places where Objective-C metadata needs to refer to the class (or metaclass) symbol directly: * One cannot implement a subclass of an objc_runtime_visible class. * One cannot implement a category on an objc_runtime_visible class. Implements rdar://problem/25494092. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265201 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/Attr.td | 6 +++ include/clang/Basic/AttrDocs.td | 7 +++ include/clang/Basic/DiagnosticSemaKinds.td | 6 +++ lib/CodeGen/CGObjCMac.cpp | 49 ++++++++++++++++++++ lib/Sema/SemaDeclAttr.cpp | 3 ++ lib/Sema/SemaDeclObjC.cpp | 17 +++++++ test/CodeGenObjC/attr-objc-runtime-visible.m | 19 ++++++++ test/SemaObjC/attr-objc-runtime-visible.m | 19 ++++++++ 8 files changed, 126 insertions(+) create mode 100644 test/CodeGenObjC/attr-objc-runtime-visible.m create mode 100644 test/SemaObjC/attr-objc-runtime-visible.m diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 65e2e06aaf..332796ea18 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1241,6 +1241,12 @@ def ObjCRuntimeName : Attr { let Documentation = [ObjCRuntimeNameDocs]; } +def ObjCRuntimeVisible : Attr { + let Spellings = [GNU<"objc_runtime_visible">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [ObjCRuntimeVisibleDocs]; +} + def ObjCBoxable : Attr { let Spellings = [GNU<"objc_boxable">]; let Subjects = SubjectList<[Record], ErrorDiag, "ExpectedStructOrUnion">; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 97fca5512e..426c019dd9 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -612,6 +612,13 @@ can only be placed before an @protocol or @interface declaration: }]; } +def ObjCRuntimeVisibleDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +This attribute specifies that the Objective-C class to which it applies is visible to the Objective-C runtime but not to the linker. Classes annotated with this attribute cannot be subclassed and cannot have categories defined for them. + }]; +} + def ObjCBoxableDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index d305fae106..39212e31b1 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -715,6 +715,12 @@ def err_objc_root_class_subclass : Error< def warn_objc_root_class_missing : Warning< "class %0 defined without specifying a base class">, InGroup; +def err_objc_runtime_visible_category : Error< + "cannot implement a category for class %0 that is only visible via the " + "Objective-C runtime">; +def err_objc_runtime_visible_subclass : Error< + "cannot implement subclass %0 of a superclass %1 that is only visible via the " + "Objective-C runtime">; def note_objc_needs_superclass : Note< "add a super class to fix this problem">; def warn_dup_category_def : Warning< diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index a8206bc89a..3985201cc5 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -349,6 +349,20 @@ public: return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation"); } + llvm::Constant *getLookUpClassFn() { + CodeGen::CodeGenTypes &Types = CGM.getTypes(); + ASTContext &Ctx = CGM.getContext(); + // Class objc_lookUpClass (const char *) + SmallVector Params; + Params.push_back( + Ctx.getCanonicalType(Ctx.getPointerType(Ctx.CharTy.withConst()))); + llvm::FunctionType *FTy = + Types.GetFunctionType(Types.arrangeBuiltinFunctionDeclaration( + Ctx.getCanonicalType(Ctx.getObjCClassType()), + Params)); + return CGM.CreateRuntimeFunction(FTy, "objc_lookUpClass"); + } + /// GcReadWeakFn -- LLVM objc_read_weak (id *src) function. llvm::Constant *getGcReadWeakFn() { // id objc_read_weak (id *) @@ -981,6 +995,12 @@ protected: /// defined. The return value has type ProtocolPtrTy. llvm::Constant *GetProtocolRef(const ObjCProtocolDecl *PD); + /// Return a reference to the given Class using runtime calls rather than + /// by a symbol reference. + llvm::Value *EmitClassRefViaRuntime(CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + ObjCCommonTypesHelper &ObjCTypes); + public: /// CreateMetadataVar - Create a global variable with internal /// linkage for use by the Objective-C runtime. @@ -2673,6 +2693,25 @@ llvm::Constant *CGObjCCommonMac::GetProtocolRef(const ObjCProtocolDecl *PD) { return GetOrEmitProtocolRef(PD); } +llvm::Value *CGObjCCommonMac::EmitClassRefViaRuntime( + CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + ObjCCommonTypesHelper &ObjCTypes) { + llvm::Constant *lookUpClassFn = ObjCTypes.getLookUpClassFn(); + + llvm::Value *className = + CGF.CGM.GetAddrOfConstantCString(ID->getObjCRuntimeNameAsString()) + .getPointer(); + ASTContext &ctx = CGF.CGM.getContext(); + className = + CGF.Builder.CreateBitCast(className, + CGF.ConvertType( + ctx.getPointerType(ctx.CharTy.withConst()))); + llvm::CallInst *call = CGF.Builder.CreateCall(lookUpClassFn, className); + call->setDoesNotThrow(); + return call; +} + /* // Objective-C 1.0 extensions struct _objc_protocol { @@ -4633,6 +4672,11 @@ llvm::Value *CGObjCMac::EmitClassRefFromId(CodeGenFunction &CGF, llvm::Value *CGObjCMac::EmitClassRef(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { + // If the class has the objc_runtime_visible attribute, we need to + // use the Objective-C runtime to get the class. + if (ID->hasAttr()) + return EmitClassRefViaRuntime(CGF, ID, ObjCTypes); + return EmitClassRefFromId(CGF, ID->getIdentifier()); } @@ -6874,6 +6918,11 @@ llvm::Value *CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF, llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { + // If the class has the objc_runtime_visible attribute, we need to + // use the Objective-C runtime to get the class. + if (ID->hasAttr()) + return EmitClassRefViaRuntime(CGF, ID, ObjCTypes); + return EmitClassRefFromId(CGF, ID->getIdentifier(), ID->isWeakImported(), ID); } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 89c7eafa03..9066e761ad 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -5548,6 +5548,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ObjCRuntimeName: handleObjCRuntimeName(S, D, Attr); break; + case AttributeList::AT_ObjCRuntimeVisible: + handleSimpleAttribute(S, D, Attr); + break; case AttributeList::AT_ObjCBoxable: handleObjCBoxable(S, D, Attr); break; diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 6121973eb1..b62f9130bd 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -1831,6 +1831,13 @@ Decl *Sema::ActOnStartCategoryImplementation( if (IDecl) DiagnoseUseOfDecl(IDecl, ClassLoc); + // If the interface has the objc_runtime_visible attribute, we + // cannot implement a category for it. + if (IDecl && IDecl->hasAttr()) { + Diag(ClassLoc, diag::err_objc_runtime_visible_category) + << IDecl->getDeclName(); + } + /// Check that CatName, category name, is not used in another implementation. if (CatIDecl) { if (CatIDecl->getImplementation()) { @@ -1968,6 +1975,16 @@ Decl *Sema::ActOnStartClassImplementation( dyn_cast(IDecl), IMPDecl->getLocation(), 1); } + + // If the superclass has the objc_runtime_visible attribute, we + // cannot implement a subclass of it. + if (IDecl->getSuperClass() && + IDecl->getSuperClass()->hasAttr()) { + Diag(ClassLoc, diag::err_objc_runtime_visible_subclass) + << IDecl->getDeclName() + << IDecl->getSuperClass()->getDeclName(); + } + return ActOnObjCContainerStartDefinition(IMPDecl); } diff --git a/test/CodeGenObjC/attr-objc-runtime-visible.m b/test/CodeGenObjC/attr-objc-runtime-visible.m new file mode 100644 index 0000000000..6e224e7189 --- /dev/null +++ b/test/CodeGenObjC/attr-objc-runtime-visible.m @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fobjc-runtime=macosx-10.9.0 -emit-llvm %s -o - | FileCheck %s + +// RUN: %clang_cc1 -triple i386-apple-darwin -fobjc-runtime=macosx-fragile-10.9.0 -emit-llvm %s -o - | FileCheck %s + +@interface Root ++(Class)class; +@end + +__attribute__((objc_runtime_visible)) +__attribute__((objc_runtime_name("MyRuntimeVisibleClass"))) +@interface A : Root +@end + +// CHECK: [[CLASSNAME:@.*]] = private unnamed_addr constant [22 x i8] c"MyRuntimeVisibleClass +// CHECK: define i8* @getClass() #0 { +Class getClass(void) { + // CHECK: call i8* @objc_lookUpClass(i8* getelementptr inbounds ([22 x i8], [22 x i8]* [[CLASSNAME]], i32 0, i32 0)) #2 + return [A class]; +} diff --git a/test/SemaObjC/attr-objc-runtime-visible.m b/test/SemaObjC/attr-objc-runtime-visible.m new file mode 100644 index 0000000000..b5ec809ff2 --- /dev/null +++ b/test/SemaObjC/attr-objc-runtime-visible.m @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s + +__attribute__((objc_runtime_visible)) +@interface A +@end + +@interface A(X) +@end + +@implementation A(X) // expected-error{{cannot implement a category for class 'A' that is only visible via the Objective-C runtime}} +@end + +@interface B : A +@end + +@implementation B // expected-error{{cannot implement subclass 'B' of a superclass 'A' that is only visible via the Objective-C runtime}} +@end + + -- 2.40.0