Skip to content

Commit 43f1581

Browse files
committed
Emit a separate vtable entry + override thunk for async overrides that drop throws
Unlike with synchronous functions, an async 'throws' function and an async non-throwing function are not ABI-compatible. This would lead to an ABI mismatch and runtime crash when overriding an `async throws` function with an `async` one. Deal with this ABI incompatibility by producing an appropriate thunk. Fixes SR-15766 / rdar://problem/88035174.
1 parent e2a49f4 commit 43f1581

File tree

4 files changed

+25
-3
lines changed

4 files changed

+25
-3
lines changed

lib/AST/Type.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3000,10 +3000,15 @@ static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2,
30003000
auto ext1 = fn1->getExtInfo();
30013001
auto ext2 = fn2->getExtInfo();
30023002
if (matchMode.contains(TypeMatchFlags::AllowOverride)) {
3003-
if (ext2.isThrowing()) {
3003+
// Removing 'throwing' is ABI-compatible for synchronous functions, but
3004+
// not for async ones.
3005+
if (ext2.isThrowing() &&
3006+
!(ext2.isAsync() &&
3007+
matchMode.contains(TypeMatchFlags::AllowABICompatible))) {
30043008
ext1 = ext1.withThrows(true);
30053009
}
30063010
}
3011+
30073012
// If specified, allow an escaping function parameter to override a
30083013
// non-escaping function parameter when the parameter is optional.
30093014
// Note that this is checking 'ext2' rather than 'ext1' because parameters

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4390,8 +4390,9 @@ SILFunctionType::isABICompatibleWith(CanSILFunctionType other,
43904390
if (getRepresentation() != other->getRepresentation())
43914391
return ABICompatibilityCheckResult::DifferentFunctionRepresentations;
43924392

4393-
// `() async -> ()` is not compatible with `() async -> @error Error`.
4394-
if (!hasErrorResult() && other->hasErrorResult() && isAsync()) {
4393+
// `() async -> ()` is not compatible with `() async -> @error Error` and
4394+
// vice versa.
4395+
if (hasErrorResult() != other->hasErrorResult() && isAsync()) {
43954396
return ABICompatibilityCheckResult::DifferentErrorResultConventions;
43964397
}
43974398

lib/SIL/IR/TypeLowering.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3407,6 +3407,12 @@ TypeConverter::checkFunctionForABIDifferences(SILModule &M,
34073407
return ABIDifference::NeedsThunk;
34083408
}
34093409

3410+
// Asynchronous functions require a thunk if they differ in whether they
3411+
// have an error result.
3412+
if (fnTy1->hasErrorResult() != fnTy2->hasErrorResult() &&
3413+
(fnTy1->isAsync() || fnTy2->isAsync()))
3414+
return ABIDifference::NeedsThunk;
3415+
34103416
for (unsigned i = 0, e = fnTy1->getParameters().size(); i < e; ++i) {
34113417
auto param1 = fnTy1->getParameters()[i], param2 = fnTy2->getParameters()[i];
34123418

test/SILGen/async_vtable_thunk.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,21 @@
33

44
class BaseClass<T> {
55
func wait() async -> T {}
6+
func waitOrDie() async throws -> T {}
67
}
78

89
class Derived : BaseClass<Int> {
910
override func wait() async -> Int {}
11+
override func waitOrDie() async -> Int {}
1012
}
1113

1214
// CHECK-LABEL: sil private [thunk] [ossa] @$s18async_vtable_thunk7DerivedC4waitSiyYaFAA9BaseClassCADxyYaFTV : $@convention(method) @async (@guaranteed Derived) -> @out Int {
1315

16+
// CHECK-LABEL: sil_vtable Derived {
17+
// CHECK: #BaseClass.wait: <T> (BaseClass<T>) -> () async -> T : @$s18async_vtable_thunk7DerivedC4waitSiyYaFAA9BaseClassCADxyYaFTV [override]
18+
// CHECK-NEXT: #BaseClass.waitOrDie: <T> (BaseClass<T>) -> () async throws -> T : @$s18async_vtable_thunk7DerivedC9waitOrDieSiyYaFAA9BaseClassCADxyYaKFTV [override]
19+
// CHECK-NEXT: #BaseClass.init!allocator: <T> (BaseClass<T>.Type) -> () -> BaseClass<T> : @$s18async_vtable_thunk7DerivedCACycfC [override]
20+
// CHECK-NEXT: #Derived.waitOrDie: (Derived) -> () async -> Int : @$s18async_vtable_thunk7DerivedC9waitOrDieSiyYaF
21+
// CHECK-NEXT: #Derived.deinit!deallocator: @$s18async_vtable_thunk7DerivedCfD
22+
// CHECK-NEXT: }
23+

0 commit comments

Comments
 (0)