Skip to content

Commit 865de72

Browse files
committed
Emit hop_to_executor instructors for foreign async calls.
Fix a miscompile where a foreign `async` call would fail to emit an appropriate `hop_to_executor` instruction to hop back to the current executor afterward, meaning that subsequent code would not execute on the correct actor. Fixes rdar://81468980.
1 parent f9c3475 commit 865de72

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4460,7 +4460,7 @@ RValue SILGenFunction::emitApply(
44604460
}
44614461

44624462
ExecutorBreadcrumb breadcrumb;
4463-
4463+
44644464
// The presence of `implicitActorHopTarget` indicates that the callee is a
44654465
// synchronous function isolated to an actor other than our own.
44664466
// Such functions require the caller to hop to the callee's executor
@@ -4486,7 +4486,8 @@ RValue SILGenFunction::emitApply(
44864486
}
44874487

44884488
breadcrumb = emitHopToTargetExecutor(loc, executor);
4489-
} else if (ExpectedExecutor && substFnType->isAsync()) {
4489+
} else if (ExpectedExecutor &&
4490+
(substFnType->isAsync() || calleeTypeInfo.foreign.async)) {
44904491
// Otherwise, if we're in an actor method ourselves, and we're calling into
44914492
// any sort of async function, we'll want to make sure to hop back to our
44924493
// own executor afterward, since the callee could have made arbitrary hops
@@ -4503,8 +4504,10 @@ RValue SILGenFunction::emitApply(
45034504
rawDirectResult = rawDirectResults[0];
45044505
}
45054506

4506-
// hop back to the current executor
4507-
breadcrumb.emit(*this, loc);
4507+
if (!calleeTypeInfo.foreign.async) {
4508+
// hop back to the current executor
4509+
breadcrumb.emit(*this, loc);
4510+
}
45084511

45094512
// For objc async calls, lifetime extend the args until the result plan which
45104513
// generates `await_async_continuation`.
@@ -4616,6 +4619,9 @@ RValue SILGenFunction::emitApply(
46164619
B.emitFixLifetime(loc, value);
46174620
B.emitDestroyOperation(loc, value);
46184621
}
4622+
4623+
// hop back to the current executor
4624+
breadcrumb.emit(*this, loc);
46194625
}
46204626

46214627
return result;

test/SILGen/objc_async.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,30 @@ func testGeneric2<T: AnyObject, U>(x: GenericObject<T>, y: U) async throws {
168168
// CHECK: [[RESULT_1_BUF:%.*]] = tuple_element_addr [[RESULT_BUF]] {{.*}}, 1
169169
// CHECK: store %2 to [trivial] [[RESULT_1_BUF]]
170170

171+
// CHECK-LABEL: sil {{.*}}@${{.*}}22testSlowServerFromMain
172+
@MainActor
173+
func testSlowServerFromMain(slowServer: SlowServer) async throws {
174+
// CHECK: hop_to_executor %6 : $MainActor
175+
// CHECK: [[RESUME_BUF:%.*]] = alloc_stack $Int
176+
// CHECK: [[STRINGINIT:%.*]] = function_ref @$sSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF :
177+
// CHECK: [[ARG:%.*]] = apply [[STRINGINIT]]
178+
// CHECK: [[METHOD:%.*]] = objc_method {{.*}} $@convention(objc_method) (NSString, @convention(block) (Int) -> (), SlowServer) -> ()
179+
// CHECK: [[CONT:%.*]] = get_async_continuation_addr Int, [[RESUME_BUF]]
180+
// CHECK: [[WRAPPED:%.*]] = struct $UnsafeContinuation<Int, Never> ([[CONT]] : $Builtin.RawUnsafeContinuation)
181+
// CHECK: [[BLOCK_STORAGE:%.*]] = alloc_stack $@block_storage UnsafeContinuation<Int, Never>
182+
// CHECK: [[CONT_SLOT:%.*]] = project_block_storage [[BLOCK_STORAGE]]
183+
// CHECK: store [[WRAPPED]] to [trivial] [[CONT_SLOT]]
184+
// CHECK: [[BLOCK_IMPL:%.*]] = function_ref @[[INT_COMPLETION_BLOCK:.*]] : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation<Int, Never>, Int) -> ()
185+
// CHECK: [[BLOCK:%.*]] = init_block_storage_header [[BLOCK_STORAGE]] {{.*}}, invoke [[BLOCK_IMPL]]
186+
// CHECK: apply [[METHOD]]([[ARG]], [[BLOCK]], %0)
187+
// CHECK: [[COPY:%.*]] = copy_value [[ARG]]
188+
// CHECK: destroy_value [[ARG]]
189+
// CHECK: await_async_continuation [[CONT]] {{.*}}, resume [[RESUME:bb[0-9]+]]
190+
// CHECK: [[RESUME]]:
191+
// CHECK: [[RESULT:%.*]] = load [trivial] [[RESUME_BUF]]
192+
// CHECK: fix_lifetime [[COPY]]
193+
// CHECK: destroy_value [[COPY]]
194+
// CHECK: hop_to_executor %6 : $MainActor
195+
// CHECK: dealloc_stack [[RESUME_BUF]]
196+
let _: Int = await slowServer.doSomethingSlow("mail")
197+
}

0 commit comments

Comments
 (0)