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
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">;
}];
}
+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 = [{
def warn_objc_root_class_missing : Warning<
"class %0 defined without specifying a base class">,
InGroup<ObjCRootClass>;
+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<
return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation");
}
+ llvm::Constant *getLookUpClassFn() {
+ CodeGen::CodeGenTypes &Types = CGM.getTypes();
+ ASTContext &Ctx = CGM.getContext();
+ // Class objc_lookUpClass (const char *)
+ SmallVector<CanQualType,1> 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 *)
/// 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.
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 {
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<ObjCRuntimeVisibleAttr>())
+ return EmitClassRefViaRuntime(CGF, ID, ObjCTypes);
+
return EmitClassRefFromId(CGF, ID->getIdentifier());
}
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<ObjCRuntimeVisibleAttr>())
+ return EmitClassRefViaRuntime(CGF, ID, ObjCTypes);
+
return EmitClassRefFromId(CGF, ID->getIdentifier(), ID->isWeakImported(), ID);
}
case AttributeList::AT_ObjCRuntimeName:
handleObjCRuntimeName(S, D, Attr);
break;
+ case AttributeList::AT_ObjCRuntimeVisible:
+ handleSimpleAttribute<ObjCRuntimeVisibleAttr>(S, D, Attr);
+ break;
case AttributeList::AT_ObjCBoxable:
handleObjCBoxable(S, D, Attr);
break;
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<ObjCRuntimeVisibleAttr>()) {
+ 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()) {
dyn_cast<NamedDecl>(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<ObjCRuntimeVisibleAttr>()) {
+ Diag(ClassLoc, diag::err_objc_runtime_visible_subclass)
+ << IDecl->getDeclName()
+ << IDecl->getSuperClass()->getDeclName();
+ }
+
return ActOnObjCContainerStartDefinition(IMPDecl);
}
--- /dev/null
+// 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];
+}
--- /dev/null
+// 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
+
+