Skip to content

Commit f157c79

Browse files
committed
[Concurrency] Loosen isolation checking for overrides/witnesses to ObjC.
When overriding a method or witnessing a requirement that comes from Objective-C and has no actor annotation, allow the overriding method or witness to specify a global actor (any global actor, but probably almost always the main actor) without triggering the actor-isolation errors one would normally get if both entities were written in Swift. This opens up a hole in actor-isolation checking, because nothing guarantees that code won't call these methods through the superclass or protocol from the wrong actor. On the other hand, it's a very convenient hole, because it allows us to state when we "know" that an Objective-C framework will only call a method on (e.g.) the main actor, and make that known to Swift to be enforced everywhere else in Swift. If this is a good idea, it's plausible to introduce runtime assertions of some form to tell the user when such an annotation is wrong.
1 parent 5e9c24e commit f157c79

File tree

3 files changed

+31
-1
lines changed

3 files changed

+31
-1
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,12 @@ void swift::checkOverrideActorIsolation(ValueDecl *value) {
19071907
if (isolation == overriddenIsolation)
19081908
return;
19091909

1910+
// If the overridden declaration is from Objective-C with no actor annotation,
1911+
// and the overriding declaration has been placed in a global actor, allow it.
1912+
if (overridden->hasClangNode() && !overriddenIsolation &&
1913+
isolation.getKind() == ActorIsolation::GlobalActor)
1914+
return;
1915+
19101916
// Isolation mismatch. Diagnose it.
19111917
value->diagnose(
19121918
diag::actor_isolation_override_mismatch, isolation,

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2726,7 +2726,11 @@ bool ConformanceChecker::checkActorIsolation(
27262726

27272727
// If the witness has a global actor but the requirement does not, we have
27282728
// an isolation error.
2729-
if (witnessGlobalActor && !requirementGlobalActor) {
2729+
//
2730+
// However, we allow this case when the requirement was imported, because
2731+
// it might not have been annotated.
2732+
if (witnessGlobalActor && !requirementGlobalActor &&
2733+
!requirement->hasClangNode()) {
27302734
witness->diagnose(
27312735
diag::global_actor_isolated_witness, witness->getDescriptiveKind(),
27322736
witness->getName(), witnessGlobalActor, Proto->getName());

test/decl/protocol/conforms/objc_async.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// REQUIRES: objc_interop
44
// REQUIRES: concurrency
55
import Foundation
6+
import ObjectiveC
67
import ObjCConcurrency
78

89
// Conform via async method
@@ -51,3 +52,22 @@ extension C5: ConcurrentProtocol {
5152
completionHandler?("hello")
5253
}
5354
}
55+
56+
// Global actors.
57+
actor class SomeActor { }
58+
59+
@globalActor
60+
struct SomeGlobalActor {
61+
static let shared = SomeActor()
62+
}
63+
64+
class C6: ConcurrentProtocol {
65+
@SomeGlobalActor
66+
func askUser(toSolvePuzzle puzzle: String) async throws -> String { "" }
67+
68+
func askUser(toJumpThroughHoop hoop: String) async -> String { "hello" }
69+
}
70+
71+
class C7: NSObject {
72+
@SomeGlobalActor override var description: String { "on an actor" }
73+
}

0 commit comments

Comments
 (0)