[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.

llvm-svn: 265201
This commit is contained in:
Douglas Gregor 2016-04-01 23:23:52 +00:00
parent 9d8a97ebd1
commit 24ae22c047
8 changed files with 126 additions and 0 deletions

View file

@ -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">;

View file

@ -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 = [{

View file

@ -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<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<

View file

@ -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<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 *)
@ -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<ObjCRuntimeVisibleAttr>())
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<ObjCRuntimeVisibleAttr>())
return EmitClassRefViaRuntime(CGF, ID, ObjCTypes);
return EmitClassRefFromId(CGF, ID->getIdentifier(), ID->isWeakImported(), ID);
}

View file

@ -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<ObjCRuntimeVisibleAttr>(S, D, Attr);
break;
case AttributeList::AT_ObjCBoxable:
handleObjCBoxable(S, D, Attr);
break;

View file

@ -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<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()) {
@ -1968,6 +1975,16 @@ Decl *Sema::ActOnStartClassImplementation(
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);
}

View file

@ -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];
}

View file

@ -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