Skip to content

Commit 2c1f19a

Browse files
committed
Implement support for Clang's objc_runtime_visible attribute.
When a Clang-defined Objective-C class has the objc_runtime_visible attribute, use objc_lookUpClass to get the Objective-C class object rather than referencing the symbol directly. Also, ban subclassing of Objective-C-runtime-visible classes as well as @objc on members of extensions of such classes. As a drive-by needed for this test, make ClassDecl::getObjCRuntimeName() respect the Clang objc_runtime_name attribute. Fixes rdar://problem/25494454.
1 parent f9ef148 commit 2c1f19a

File tree

11 files changed

+111
-9
lines changed

11 files changed

+111
-9
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3278,6 +3278,10 @@ class ClassDecl : public NominalTypeDecl {
32783278
/// the Objective-C runtime.
32793279
StringRef getObjCRuntimeName(llvm::SmallVectorImpl<char> &buffer) const;
32803280

3281+
/// Determine whether the class is only visible to the Objective-C runtime
3282+
/// and not to the linker.
3283+
bool isOnlyObjCRuntimeVisible() const;
3284+
32813285
/// Returns the appropriate kind of entry point to generate for this class,
32823286
/// based on its attributes.
32833287
///

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,9 +1456,11 @@ ERROR(circular_class_inheritance,none,
14561456
NOTE(class_here,none,
14571457
"class %0 declared here", (Identifier))
14581458
ERROR(inheritance_from_final_class,none,
1459-
"inheritance from a final class %0", (Identifier))
1459+
"inheritance from a final class %0", (Identifier))
14601460
ERROR(inheritance_from_objc_generic_class,none,
1461-
"inheritance from a generic Objective-C class %0", (Identifier))
1461+
"inheritance from a generic Objective-C class %0", (Identifier))
1462+
ERROR(inheritance_from_objc_runtime_visible_class,none,
1463+
"inheritance from an Objective-C class %0 only visible via the runtime", (Identifier))
14621464

14631465

14641466
// Enums
@@ -2486,6 +2488,9 @@ ERROR(objc_in_extension_context,none,
24862488
"members of constrained extensions cannot be declared @objc", ())
24872489
ERROR(objc_in_generic_extension,none,
24882490
"@objc is not supported within extensions of generic classes", ())
2491+
ERROR(objc_in_objc_runtime_visible,none,
2492+
"@objc is not supported within extensions of classes only visible via the Objective-C runtime", ())
2493+
24892494
ERROR(objc_for_generic_class,none,
24902495
"generic subclasses of '@objc' classes cannot have an explicit '@objc' "
24912496
"attribute because they are not directly visible from Objective-C", ())

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,12 @@ FUNCTION(GetObjectClass, object_getClass, C_CC,
841841
ARGS(ObjCPtrTy),
842842
ATTRS(NoUnwind, ReadOnly))
843843

844+
// Class objc_lookUpClass(const char *name);
845+
FUNCTION(LookUpClass, objc_lookUpClass, C_CC,
846+
RETURNS(ObjCClassPtrTy),
847+
ARGS(Int8PtrTy),
848+
ATTRS(NoUnwind, ReadNone))
849+
844850
// Metadata *swift_getObjectType(id object);
845851
//
846852
// Since this is intended to look through dynamic subclasses, it's

lib/AST/Decl.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "swift/Basic/Fallthrough.h"
3838

3939
#include "clang/Basic/CharInfo.h"
40+
#include "clang/AST/Attr.h"
4041
#include "clang/AST/DeclObjC.h"
4142

4243
#include <algorithm>
@@ -2286,6 +2287,11 @@ static StringRef mangleObjCRuntimeName(const NominalTypeDecl *nominal,
22862287

22872288
StringRef ClassDecl::getObjCRuntimeName(
22882289
llvm::SmallVectorImpl<char> &buffer) const {
2290+
// If there is a Clang declaration, use it's runtime name.
2291+
if (auto objcClass
2292+
= dyn_cast_or_null<clang::ObjCInterfaceDecl>(getClangDecl()))
2293+
return objcClass->getObjCRuntimeNameAsString();
2294+
22892295
// If there is an 'objc' attribute with a name, use that name.
22902296
if (auto objc = getAttrs().getAttribute<ObjCAttr>()) {
22912297
if (auto name = objc->getName())
@@ -2296,6 +2302,19 @@ StringRef ClassDecl::getObjCRuntimeName(
22962302
return mangleObjCRuntimeName(this, buffer);
22972303
}
22982304

2305+
bool ClassDecl::isOnlyObjCRuntimeVisible() const {
2306+
auto clangDecl = getClangDecl();
2307+
if (!clangDecl) return false;
2308+
2309+
auto objcClass = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl);
2310+
if (!objcClass) return false;
2311+
2312+
if (auto def = objcClass->getDefinition())
2313+
objcClass = def;
2314+
2315+
return objcClass->hasAttr<clang::ObjCRuntimeVisibleAttr>();
2316+
}
2317+
22992318
ArtificialMainKind ClassDecl::getArtificialMainKind() const {
23002319
if (getAttrs().hasAttribute<UIApplicationMainAttr>())
23012320
return ArtificialMainKind::UIApplicationMain;

lib/IRGen/GenMeta.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,9 @@ llvm::Constant *irgen::tryEmitConstantHeapMetadataRef(IRGenModule &IGM,
256256
if (doesClassMetadataRequireDynamicInitialization(IGM, theDecl))
257257
return nullptr;
258258

259-
// Otherwise, just respect genericity.
260-
} else if (theDecl->isGenericContext()) {
259+
// Otherwise, just respect genericity and Objective-C runtime visibility.
260+
} else if (theDecl->isGenericContext() ||
261+
theDecl->isOnlyObjCRuntimeVisible()) {
261262
return nullptr;
262263
}
263264

@@ -291,6 +292,15 @@ irgen::tryEmitConstantTypeMetadataRef(IRGenModule &IGM, CanType type,
291292
llvm::Value *irgen::emitObjCHeapMetadataRef(IRGenFunction &IGF,
292293
ClassDecl *theClass,
293294
bool allowUninitialized) {
295+
// If the class is visible only through the Objective-C runtime, form the
296+
// appropriate runtime call.
297+
if (theClass->isOnlyObjCRuntimeVisible()) {
298+
SmallString<64> scratch;
299+
auto className =
300+
IGF.IGM.getAddrOfGlobalString(theClass->getObjCRuntimeName(scratch));
301+
return IGF.Builder.CreateCall(IGF.IGM.getLookUpClassFn(), className);
302+
}
303+
294304
auto classObject = IGF.IGM.getAddrOfObjCClass(theClass, NotForDefinition);
295305
if (allowUninitialized) return classObject;
296306

lib/Sema/TypeCheckDecl.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2090,9 +2090,14 @@ static Optional<ObjCReason> shouldMarkAsObjC(TypeChecker &TC,
20902090
// make it implicitly @objc. However, if the declaration cannot be represented
20912091
// as @objc, don't diagnose.
20922092
Type contextTy = VD->getDeclContext()->getDeclaredTypeInContext();
2093-
if (auto classDecl = contextTy->getClassOrBoundGenericClass())
2093+
if (auto classDecl = contextTy->getClassOrBoundGenericClass()) {
2094+
// One cannot define @objc members of Objective-C runtime visible classes.
2095+
if (classDecl->isOnlyObjCRuntimeVisible())
2096+
return None;
2097+
20942098
if (classDecl->checkObjCAncestry() != ObjCClassKind::NonObjC)
20952099
return ObjCReason::DoNotDiagnose;
2100+
}
20962101

20972102
return None;
20982103
}
@@ -3835,6 +3840,11 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
38353840
TC.diagnose(CD, diag::inheritance_from_objc_generic_class,
38363841
Super->getName());
38373842
}
3843+
3844+
if (Super->isOnlyObjCRuntimeVisible()) {
3845+
TC.diagnose(CD, diag::inheritance_from_objc_runtime_visible_class,
3846+
Super->getName());
3847+
}
38383848
}
38393849

38403850
checkAccessibility(TC, CD);

lib/Sema/TypeCheckType.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,7 +2494,8 @@ static bool isParamListRepresentableInObjC(TypeChecker &TC,
24942494
}
24952495

24962496
/// Check whether the given declaration occurs within a constrained
2497-
/// extension, or an extension of a class with generic ancestry, and
2497+
/// extension, or an extension of a class with generic ancestry, or an
2498+
/// extension of an Objective-C runtime visible class, and
24982499
/// therefore is not representable in Objective-C.
24992500
static bool checkObjCInExtensionContext(TypeChecker &tc,
25002501
const ValueDecl *value,
@@ -2521,11 +2522,19 @@ static bool checkObjCInExtensionContext(TypeChecker &tc,
25212522

25222523
if (!CD->hasClangNode() && CD->getGenericParams()) {
25232524
if (diagnose) {
2524-
tc.diagnose(value->getLoc(), diag::objc_in_generic_extension);
2525+
tc.diagnose(value, diag::objc_in_generic_extension);
25252526
}
25262527
return true;
25272528
}
25282529

2530+
// Cannot define @objc members of an Objective-C runtime visible class,
2531+
// because doing so would create a category.
2532+
if (CD->isOnlyObjCRuntimeVisible()) {
2533+
if (diagnose)
2534+
tc.diagnose(value, diag::objc_in_objc_runtime_visible);
2535+
return true;
2536+
}
2537+
25292538
extendedTy = CD->getSuperclass();
25302539
}
25312540
}

test/IRGen/objc_runtime_visible.sil

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// REQUIRES: objc_interop
2+
3+
// RUN: %target-swift-frontend -I %S/../Inputs/custom-modules %s -emit-ir | FileCheck %s
4+
5+
sil_stage raw
6+
7+
import Builtin
8+
import Swift
9+
import SwiftShims
10+
import ObjCRuntimeVisible
11+
12+
// CHECK: [[CLASS_NAME:@.*]] = private unnamed_addr constant [22 x i8] c"MyRuntimeVisibleClass\00"
13+
14+
// CHECK: define %objc_class* @getClassA() #0 {
15+
sil @getClassA : $@convention(thin) () -> @objc_metatype A.Type {
16+
bb0:
17+
// CHECK: call %objc_class* @objc_lookUpClass(i8* getelementptr inbounds ([22 x i8], [22 x i8]* [[CLASS_NAME]], i64 0, i64 0))
18+
%0 = metatype $@objc_metatype A.Type
19+
20+
// CHECK-NEXT: ret %objc_class*
21+
return %0 : $@objc_metatype A.Type
22+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@interface Root
2+
+(Class)class;
3+
@end
4+
5+
__attribute__((objc_runtime_visible))
6+
__attribute__((objc_runtime_name("MyRuntimeVisibleClass")))
7+
@interface A : Root
8+
@end

test/Inputs/custom-modules/module.map

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module ObjCRuntimeVisible {
2+
header "ObjCRuntimeVisible.h"
3+
}

test/attr/attr_objc_clang.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
// RUN: %target-parse-verify-swift -sdk %S/Inputs -I %S/Inputs/custom-modules
2-
// RUN: %target-swift-ide-test -print-ast-typechecked -source-filename %s -sdk %S/Inputs -I %S/Inputs/custom-modules -function-definitions=true -prefer-type-repr=false -print-implicit-attrs=true -explode-pattern-binding-decls=true -disable-objc-attr-requires-foundation-module | FileCheck %s
1+
// RUN: %target-parse-verify-swift -sdk %S/Inputs -I %S/Inputs/custom-modules -I %S/../Inputs/custom-modules
2+
// RUN: %target-swift-ide-test -print-ast-typechecked -source-filename %s -sdk %S/Inputs -I %S/Inputs/custom-modules -I %S/../Inputs/custom-modules -function-definitions=true -prefer-type-repr=false -print-implicit-attrs=true -explode-pattern-binding-decls=true -disable-objc-attr-requires-foundation-module | FileCheck %s
33

44
// REQUIRES: objc_interop
55

66
import AttrObjc_FooClangModule
7+
import ObjCRuntimeVisible
78

89
@objc
910
class infer_instanceVar1 {
@@ -35,3 +36,8 @@ func ==(lhs: ObjC_Class1, rhs: ObjC_Class1) -> Bool {
3536
func ==(lhs: ObjC_Class2, rhs: ObjC_Class2) -> Bool {
3637
return true
3738
}
39+
40+
extension A {
41+
// CHECK: {{^}} func foo()
42+
func foo() { }
43+
}

0 commit comments

Comments
 (0)