Skip to content

Commit 6406f08

Browse files
committed
[SE-0306] Disallow actor inheritance from NSObject, but allow @objc actor.
Allow an actor to be exposed to Objective-C via `@objc` without inheriting from `NSObject`, and remove the loophole that allowed actors to inherit from `NSObject`. Fixes rdar://78333614
1 parent 83827e7 commit 6406f08

File tree

8 files changed

+32
-36
lines changed

8 files changed

+32
-36
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4351,6 +4351,8 @@ ERROR(async_objc_dynamic_self,none,
43514351
ERROR(actor_inheritance,none,
43524352
"%select{actor|distributed actor}0 types do not support inheritance",
43534353
(bool))
4354+
NOTE(actor_inheritance_nsobject,none,
4355+
"use '@objc' to expose actor %0 to Objective-C", (DeclName))
43544356

43554357
ERROR(actor_protocol_illegal_inheritance,none,
43564358
"non-actor type %0 cannot conform to the 'Actor' protocol",

lib/AST/Decl.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8289,15 +8289,14 @@ bool ClassDecl::isRootDefaultActor(ModuleDecl *M,
82898289
}
82908290

82918291
bool ClassDecl::isNativeNSObjectSubclass() const {
8292-
// Only if we inherit from NSObject.
8293-
auto superclass = getSuperclassDecl();
8294-
if (!superclass || !superclass->isNSObject())
8295-
return false;
8292+
// @objc actors implicitly inherit from NSObject.
8293+
if (isActor() && getAttrs().hasAttribute<ObjCAttr>())
8294+
return true;
82968295

8297-
// For now, only actors (regardless of whether they're default actors).
8298-
// Eventually we should roll this out to more classes, but we have to
8299-
// do it with ABI compatibility.
8300-
return isActor();
8296+
// For now, non-actor classes cannot use the native NSObject subclass.
8297+
// Eventually we should roll this out to more classes that directly
8298+
// inherit NSObject, but we have to do it with ABI compatibility.
8299+
return false;
83018300
}
83028301

83038302
bool ClassDecl::isNSObject() const {

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,10 +1271,11 @@ static Optional<ObjCReason> shouldMarkClassAsObjC(const ClassDecl *CD) {
12711271
reason.describe(CD);
12721272
}
12731273

1274-
// Only allow ObjC-rooted classes to be @objc.
1274+
// Only allow actors and ObjC-rooted classes to be @objc.
12751275
// (Leave a hole for test cases.)
12761276
if (ancestry.contains(AncestryFlags::ObjC) &&
1277-
!ancestry.contains(AncestryFlags::ClangImported)) {
1277+
!ancestry.contains(AncestryFlags::ClangImported) &&
1278+
!CD->isActor()) {
12781279
if (ctx.LangOpts.EnableObjCAttrRequiresFoundation) {
12791280
swift::diagnoseAndRemoveAttr(CD, attr,
12801281
diag::invalid_objc_swift_rooted_class)

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,12 +2398,18 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
23982398

23992399
if (auto superclass = CD->getSuperclassDecl()) {
24002400
// Actors cannot have superclasses, nor can they be superclasses.
2401-
if (CD->isActor() && !superclass->isNSObject())
2401+
if (CD->isActor()) {
24022402
CD->diagnose(diag::actor_inheritance,
24032403
/*distributed=*/CD->isDistributedActor());
2404-
else if (superclass->isActor())
2404+
if (superclass->isNSObject() && !CD->isDistributedActor()) {
2405+
CD->diagnose(diag::actor_inheritance_nsobject, CD->getName())
2406+
.fixItInsert(CD->getAttributeInsertionLoc(/*forModifier=*/false),
2407+
"@objc ");
2408+
}
2409+
} else if (superclass->isActor()) {
24052410
CD->diagnose(diag::actor_inheritance,
24062411
/*distributed=*/CD->isDistributedActor());
2412+
}
24072413
}
24082414

24092415
// Force lowering of stored properties.

test/IRGen/actor_class_objc.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import Foundation
99

1010
// CHECK-LABEL: @"OBJC_METACLASS_$__TtC16actor_class_objc7MyClass" = global
1111
// Metaclass is an instance of the root class.
12-
// CHECK-SAME: %objc_class* {{.*}}@"OBJC_METACLASS_$_NSObject{{(.ptrauth)?}}"
13-
// Metaclass superclass is the metaclass of the superclass.
1412
// CHECK-SAME: %objc_class* {{.*}}@"OBJC_METACLASS_$_SwiftNativeNSObject{{(.ptrauth)?}}"
1513

1614
// CHECK: @"$s16actor_class_objc7MyClassCMf" = internal global
@@ -29,9 +27,9 @@ import Foundation
2927
// CHECK-64-SAME: i64 112,
3028
// CHECK-32-SAME: i32 56,
3129

32-
public actor MyClass: NSObject {
30+
@objc public actor MyClass {
3331
public var x: Int
34-
public override init() { self.x = 0 }
32+
public init() { self.x = 0 }
3533
}
3634

3735
// CHECK-LABEL: define {{.*}} @"$s16actor_class_objc7MyClassC1xSivg"

test/Interpreter/actor_class_forbid_objc_assoc_objects.swift

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
// UNSUPPORTED: back_deployment_runtime
1111

1212
import ObjectiveC
13+
import Foundation
1314
import _Concurrency
1415
import StdlibUnittest
1516

@@ -70,7 +71,7 @@ if #available(macOS 10.4.4, iOS 12.2, watchOS 5.2, tvOS 12.2, *) {
7071
}
7172

7273
@available(macOS 10.4.4, iOS 12.2, watchOS 5.2, tvOS 12.2, *)
73-
actor ActorNSObjectSubKlass : NSObject {}
74+
@objc actor ActorNSObjectSubKlass {}
7475

7576
if #available(macOS 10.4.4, iOS 12.2, watchOS 5.2, tvOS 12.2, *) {
7677
Tests.test("no crash when inherit from nsobject")
@@ -79,17 +80,3 @@ if #available(macOS 10.4.4, iOS 12.2, watchOS 5.2, tvOS 12.2, *) {
7980
objc_setAssociatedObject(x, "myKey", "myValue", .OBJC_ASSOCIATION_RETAIN)
8081
}
8182
}
82-
83-
@available(macOS 10.4.4, iOS 12.2, watchOS 5.2, tvOS 12.2, *)
84-
actor ActorNSObjectSubKlassGeneric<T> : NSObject {
85-
var state: T
86-
init(state: T) { self.state = state }
87-
}
88-
89-
if #available(macOS 10.4.4, iOS 12.2, watchOS 5.2, tvOS 12.2, *) {
90-
Tests.test("no crash when generic inherit from nsobject")
91-
.code {
92-
let x = ActorNSObjectSubKlassGeneric(state: 5)
93-
objc_setAssociatedObject(x, "myKey", "myValue", .OBJC_ASSOCIATION_RETAIN)
94-
}
95-
}

test/ModuleInterface/actor_objc.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import Foundation
1414

15-
// CHECK-LABEL: @objc @_inheritsConvenienceInitializers public actor SomeActor : ObjectiveC.NSObject {
16-
// CHECK: @objc override public init()
17-
public actor SomeActor: NSObject {
15+
// CHECK-LABEL: @objc public actor SomeActor {
16+
// CHECK-NOT: @objc override public init()
17+
@objc public actor SomeActor {
1818
}

test/attr/attr_objc_async.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ actor class MyActor2 { }
5151
// expected-error@-1 {{keyword 'class' cannot be used as an identifier here}}
5252

5353
// CHECK: @objc actor MyObjCActor
54-
@objc actor MyObjCActor: NSObject { }
54+
@objc actor MyObjCActor { }
5555

56-
@objc actor class MyObjCActor2: NSObject {}
56+
@objc actor class MyObjCActor2 {}
5757
// expected-error@-1 {{keyword 'class' cannot be used as an identifier here}}
58+
59+
actor MyObjCActor3: NSObject { } // expected-error{{actor types do not support inheritance}}
60+
// expected-note@-1{{use '@objc' to expose actor 'MyObjCActor3' to Objective-C}}

0 commit comments

Comments
 (0)