Skip to content

Commit 0edbca0

Browse files
committed
IRGen: Don't ignore the error parameter in some async functions
Calls that emit the result to memory follow a different path that we missed to update with error handling code. rdar://76599021
1 parent 1e544b0 commit 0edbca0

File tree

2 files changed

+115
-3
lines changed

2 files changed

+115
-3
lines changed

lib/IRGen/GenCall.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2507,14 +2507,16 @@ class AsyncCallEmission final : public CallEmission {
25072507
}
25082508
void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override {
25092509
// Bail out on a void result type.
2510+
auto &IGM = IGF.IGM;
25102511
llvm::Value *result = call;
25112512
auto *suspendResultTy = cast<llvm::StructType>(result->getType());
25122513
auto numAsyncContextParams =
2513-
getCalleeFunctionPointer().getSignature().getAsyncContextIndex() + 1;
2514+
Signature::forAsyncReturn(IGM, getCallee().getSubstFunctionType())
2515+
.getAsyncContextIndex() +
2516+
1;
25142517
if (suspendResultTy->getNumElements() == numAsyncContextParams)
25152518
return;
25162519

2517-
auto &IGM = IGF.IGM;
25182520
auto &Builder = IGF.Builder;
25192521

25202522
auto resultTys =
@@ -2683,7 +2685,29 @@ void CallEmission::emitToUnmappedMemory(Address result) {
26832685
LastArgWritten = 0; // appease an assert
26842686
#endif
26852687

2686-
emitCallSite();
2688+
auto call = emitCallSite();
2689+
2690+
// Async calls need to store the error result that is passed as a parameter.
2691+
if (IGF.isAsync()) {
2692+
auto &IGM = IGF.IGM;
2693+
auto &Builder = IGF.Builder;
2694+
auto numAsyncContextParams =
2695+
Signature::forAsyncReturn(IGM, CurCallee.getSubstFunctionType())
2696+
.getAsyncContextIndex() +
2697+
1;
2698+
2699+
auto substCalleeType = CurCallee.getSubstFunctionType();
2700+
SILFunctionConventions substConv(substCalleeType, IGF.getSILModule());
2701+
auto hasError = substCalleeType->hasErrorResult();
2702+
SILType errorType;
2703+
if (hasError) {
2704+
errorType =
2705+
substConv.getSILErrorType(IGM.getMaximalTypeExpansionContext());
2706+
auto result = Builder.CreateExtractValue(call, numAsyncContextParams);
2707+
Address errorAddr = IGF.getCalleeErrorResultSlot(errorType);
2708+
Builder.CreateStore(result, errorAddr);
2709+
}
2710+
}
26872711
}
26882712

26892713
/// The private routine to ultimately emit a call or invoke instruction.

test/Concurrency/throwing.swift

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency -parse-as-library)
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
// UNSUPPORTED: use_os_stdlib
6+
// UNSUPPORTED: back_deployment_runtime
7+
8+
import _Concurrency
9+
import StdlibUnittest
10+
11+
class P<T> {
12+
var t: T
13+
init(_ v: T) {
14+
t = v
15+
}
16+
}
17+
18+
class A {}
19+
class B {}
20+
class C {}
21+
22+
enum E : Error {
23+
case err
24+
}
25+
26+
protocol MP { }
27+
28+
class M : MP {
29+
30+
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
31+
func throwWithIndirectResult<T>(_ a: P<T>) async throws -> T {
32+
throw E.err
33+
}
34+
}
35+
36+
extension MP {
37+
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
38+
func l<A, B, C, D, E2, F> (_ a : P<A>, _ b: P<B>, _ c: P<C>, _ d : P<D>, _ e: P<E2>, _ f: P<F>) async throws -> (A, B, C, D, E2, F) {
39+
throw E.err
40+
}
41+
}
42+
43+
@main struct Main {
44+
static func main() async {
45+
var tests = TestSuite("Async Throw")
46+
47+
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
48+
tests.test("throwing of naturally direct but indirect reabstration") {
49+
let task2 = detach {
50+
let m = M()
51+
await verifyCancelled {
52+
try await m.l(P(A()), P(B()), P(C()), P(A()), P(B()), P(C()))
53+
}
54+
func verifyCancelled<T>(execute operation: () async throws -> T) async {
55+
do {
56+
let _ = try await operation()
57+
assertionFailure("operation() threw")
58+
}
59+
catch _ as E {
60+
// This is what we expect to happen
61+
}
62+
catch {
63+
assertionFailure("unknown error thrown")
64+
}
65+
}
66+
}
67+
_ = await task2.get()
68+
}
69+
tests.test("throwing with indirect result") {
70+
let task2 = detach {
71+
let m = M()
72+
do {
73+
let _ = try await m.throwWithIndirectResult(P(A()))
74+
assertionFailure("operation() threw")
75+
}
76+
catch _ as E {
77+
// This is what we expect to happen
78+
}
79+
catch {
80+
assertionFailure("unknown error thrown")
81+
}
82+
}
83+
_ = await task2.get()
84+
}
85+
}
86+
await runAllTestsAsync()
87+
}
88+
}

0 commit comments

Comments
 (0)