mirror of https://github.com/microsoft/clang.git
[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
This commit is contained in:
parent
0a54ccf3d6
commit
cf7bc8edf8
|
@ -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">;
|
||||
|
|
|
@ -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 = [{
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue