Skip to content

Commit f66982a

Browse files
committed
SILGen: do not emit hop_to_executor in some objc thunks
An @objc async thunk does not need to perform a hop_to_executor if the native method the thunk is wrapping is async, since the native method will do its own hop. This redundant hop was causing a crash in SILGen with async actor-instance isolated @objc methods declared in an actor, because SILGen was not prepared to add the hop to to `self` within the @objc thunk. resolves rdar://80130628
1 parent 821ae4f commit f66982a

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

lib/SILGen/SILGenBridging.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,8 +1568,10 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
15681568
if (thunk.hasDecl()) {
15691569
isolation = getActorIsolation(thunk.getDecl());
15701570
}
1571-
1572-
if (isolation) {
1571+
1572+
// A hop is only needed in the thunk if it is global-actor isolated.
1573+
// Native, instance-isolated async methods will hop in the prologue.
1574+
if (isolation && isolation->isGlobalActor()) {
15731575
emitHopToTargetActor(loc, *isolation, None);
15741576
}
15751577
}

test/Concurrency/actor_isolation_objc.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,15 @@ func outside(a : A) async {
5353
await a.i() // expected-warning {{no 'async' operations occur within 'await' expression}}
5454
}
5555

56+
actor Dril: NSObject {
57+
// expected-note@+2 {{add 'async' to function 'postSynchronouslyTo(twitter:)' to make it asynchronous}}
58+
// expected-error@+1 {{actor-isolated instance method 'postSynchronouslyTo(twitter:)' cannot be @objc}}
59+
@objc func postSynchronouslyTo(twitter msg: String) -> Bool {
60+
return true
61+
}
5662

57-
63+
@MainActor
64+
@objc func postFromMainActorTo(twitter msg: String) -> Bool {
65+
return true
66+
}
67+
}

test/SILGen/objc_async_from_swift.swift

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -I %S/Inputs/custom-modules -enable-experimental-concurrency -disable-availability-checking %s -verify | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -I %S/Inputs/custom-modules -enable-experimental-concurrency -disable-availability-checking %s -verify | %FileCheck --implicit-check-not=hop_to_executor --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s
22
// REQUIRES: concurrency
33
// REQUIRES: objc_interop
44

@@ -114,9 +114,44 @@ class SlowServerlet: SlowServer {
114114

115115
@FooActor
116116
class ActorConstrained: NSObject {
117-
// CHECK-LABEL: sil shared [thunk] [ossa] @$s{{.*}}16ActorConstrainedC3foo{{.*}}U_To
117+
// ActorConstrained.foo()
118+
// CHECK-LABEL: sil hidden [ossa] @$s{{.*}}16ActorConstrainedC3foo{{.*}} : $@convention(method) @async (@guaranteed ActorConstrained) -> Bool {
118119
// CHECK: hop_to_executor {{%.*}} : $FooActor
120+
121+
// @objc ActorConstrained.foo()
122+
// CHECK-LABEL: sil hidden [thunk] [ossa] @$s{{.*}}16ActorConstrainedC3foo{{.*}}To : $@convention(objc_method) (@convention(block) (Bool) -> (), ActorConstrained) -> () {
123+
// CHECK: [[ASYNC_CLOS:%[0-9]+]] = function_ref @$s{{.*}}16ActorConstrainedC3foo{{.*}}U_To : $@convention(thin) @Sendable @async (@convention(block) (Bool) -> (), ActorConstrained) -> ()
124+
// CHECK: [[PRIMED_CLOS:%[0-9]+]] = partial_apply [callee_guaranteed] [[ASYNC_CLOS]](
125+
// CHECK: [[TASK_RUNNER:%[0-9]+]] = function_ref @$ss29_runTaskForBridgedAsyncMethodyyyyYaYbcnF
126+
// CHECK: apply [[TASK_RUNNER]]([[PRIMED_CLOS]])
127+
128+
// @objc closure #1 in ActorConstrained.foo()
129+
// CHECK-LABEL: sil shared [thunk] [ossa] @$s{{.*}}16ActorConstrainedC3foo{{.*}}U_To : $@convention(thin) @Sendable @async (@convention(block) (Bool) -> (), ActorConstrained) -> () {
130+
// CHECK: hop_to_executor {{%.*}} : $FooActor
119131
@objc func foo() async -> Bool {
120132
return true
121133
}
122134
}
135+
136+
137+
actor Dril: NSObject {
138+
// Dril.postTo(twitter:)
139+
// CHECK-LABEL: sil hidden [ossa] @$s{{.*}}4DrilC6postTo7twitter{{.*}} : $@convention(method) @async (@guaranteed String, @guaranteed Dril) -> Bool {
140+
// CHECK: hop_to_executor {{%.*}} : $Dril
141+
142+
// @objc Dril.postTo(twitter:)
143+
// CHECK-LABEL: sil hidden [thunk] [ossa] @$s{{.*}}4DrilC6postTo7twitter{{.*}}To : $@convention(objc_method) (NSString, @convention(block) (Bool) -> (), Dril) -> () {
144+
// CHECK: [[ASYNC_CLOS:%[0-9]+]] = function_ref @$s{{.*}}4DrilC6postTo7twitter{{.*}}U_To : $@convention(thin) @Sendable @async (NSString, @convention(block) (Bool) -> (), Dril) -> ()
145+
// CHECK: [[PRIMED_CLOS:%[0-9]+]] = partial_apply [callee_guaranteed] [[ASYNC_CLOS]](
146+
// CHECK: [[TASK_RUNNER:%[0-9]+]] = function_ref @$ss29_runTaskForBridgedAsyncMethodyyyyYaYbcnF
147+
// CHECK: apply [[TASK_RUNNER]]([[PRIMED_CLOS]])
148+
@objc func postTo(twitter msg: String) async -> Bool {
149+
return true
150+
}
151+
152+
// this is known to not emit a hop in the objc thunk (rdar://80972126)
153+
@MainActor
154+
@objc func postFromMainActorTo(twitter msg: String) -> Bool {
155+
return true
156+
}
157+
}

0 commit comments

Comments
 (0)