Skip to content

Commit 7b1e89e

Browse files
Merge pull request #37180 from aschwaighofer/fix_async_throwing_unnaturally_direct_5.5
[5.5] IRGen: Don't ignore the error parameter in some async functions
2 parents ff56f42 + 02efbe3 commit 7b1e89e

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
@@ -2487,14 +2487,16 @@ class AsyncCallEmission final : public CallEmission {
24872487
}
24882488
void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override {
24892489
// Bail out on a void result type.
2490+
auto &IGM = IGF.IGM;
24902491
llvm::Value *result = call;
24912492
auto *suspendResultTy = cast<llvm::StructType>(result->getType());
24922493
auto numAsyncContextParams =
2493-
getCalleeFunctionPointer().getSignature().getAsyncContextIndex() + 1;
2494+
Signature::forAsyncReturn(IGM, getCallee().getSubstFunctionType())
2495+
.getAsyncContextIndex() +
2496+
1;
24942497
if (suspendResultTy->getNumElements() == numAsyncContextParams)
24952498
return;
24962499

2497-
auto &IGM = IGF.IGM;
24982500
auto &Builder = IGF.Builder;
24992501

25002502
auto resultTys =
@@ -2663,7 +2665,29 @@ void CallEmission::emitToUnmappedMemory(Address result) {
26632665
LastArgWritten = 0; // appease an assert
26642666
#endif
26652667

2666-
emitCallSite();
2668+
auto call = emitCallSite();
2669+
2670+
// Async calls need to store the error result that is passed as a parameter.
2671+
if (CurCallee.getSubstFunctionType()->isAsync()) {
2672+
auto &IGM = IGF.IGM;
2673+
auto &Builder = IGF.Builder;
2674+
auto numAsyncContextParams =
2675+
Signature::forAsyncReturn(IGM, CurCallee.getSubstFunctionType())
2676+
.getAsyncContextIndex() +
2677+
1;
2678+
2679+
auto substCalleeType = CurCallee.getSubstFunctionType();
2680+
SILFunctionConventions substConv(substCalleeType, IGF.getSILModule());
2681+
auto hasError = substCalleeType->hasErrorResult();
2682+
SILType errorType;
2683+
if (hasError) {
2684+
errorType =
2685+
substConv.getSILErrorType(IGM.getMaximalTypeExpansionContext());
2686+
auto result = Builder.CreateExtractValue(call, numAsyncContextParams);
2687+
Address errorAddr = IGF.getCalleeErrorResultSlot(errorType);
2688+
Builder.CreateStore(result, errorAddr);
2689+
}
2690+
}
26672691
}
26682692

26692693
/// 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)