Skip to content

[6.0🍒][SILGen] Create thunks for non-throwing -> indirect error result conversions #73177

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/SIL/IR/TypeLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4664,6 +4664,12 @@ TypeConverter::checkFunctionForABIDifferences(SILModule &M,
return ABIDifference::NeedsThunk;
}

// Non-throwing functions and functions with indirect error result need thunk
// because of the trailing error result storage parameter.
if (fnTy1->hasIndirectErrorResult() != fnTy2->hasIndirectErrorResult()) {
return ABIDifference::NeedsThunk;
}

// Asynchronous functions require a thunk if they differ in whether they
// have an error result.
if (fnTy1->hasErrorResult() != fnTy2->hasErrorResult() &&
Expand Down
139 changes: 139 additions & 0 deletions test/SILGen/typed_throws_thunk.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// RUN: %target-swift-emit-silgen %s | %FileCheck %s

struct ConcreteError: Error {}

func existentialPassThrough(_ body: () throws(Error) -> Void) throws(Error) {
try body()
}

func concretePassThrough(_ body: () throws(ConcreteError) -> Void) throws(ConcreteError) {
try body()
}

func genericPassThrough<X>(_ body: () throws(X) -> Void) throws(X) {
try body()
}

func neverThrow() {}
func throwConcrete() throws(ConcreteError) {}
func throwExistential() throws {}

// CHECK-LABEL: sil{{.*}} @$s18typed_throws_thunk5test1yyKF
func test1() throws {
// No thunk needed here because no indirect error result
// CHECK-NOT: function_ref thunk for
// CHECK: [[FN:%[0-9]+]] = function_ref @$s18typed_throws_thunk22existentialPassThroughyyyyKXEKF
// CHECK-NEXT: try_apply [[FN]]
try existentialPassThrough(neverThrow)
}

// CHECK-LABEL: sil{{.*}} @$s18typed_throws_thunk5test2yyKF
func test2() throws {
// No thunk needed here because no indirect error result
// CHECK-NOT: function_ref thunk for
// CHECK: [[FN:%[0-9]+]] = function_ref @$s18typed_throws_thunk19concretePassThroughyyyyAA13ConcreteErrorVYKXEADYKF
// CHECK-NEXT: try_apply [[FN]]
try concretePassThrough(neverThrow)
}

// CHECK-LABEL: sil{{.*}} @$s18typed_throws_thunk5test3yyF
func test3() {
// CHECK: [[T1:%[0-9]+]] = function_ref @$s18typed_throws_thunk10neverThrowyyF
// CHECK-NEXT: [[T2:%[0-9]+]] = thin_to_thick_function [[T1]]
// CHECK-NEXT: [[T3:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[T2]]
// CHECK-NEXT: function_ref thunk for
// CHECK-NEXT: [[THUNK:%[0-9]+]] = function_ref @$sIg_s5NeverOIegzr_TR
// CHECK-NEXT: [[T4:%[0-9]+]] = partial_apply [callee_guaranteed] [[THUNK]]([[T3]])
// CHECK-NEXT: [[T5:%[0-9]+]] = convert_function [[T4]]
// CHECK-NEXT: [[T6:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[T5]]
// CHECK-NEXT: // function_ref genericPassThrough<A>(_:)
// CHECK-NEXT: [[T7:%[0-9]+]] = function_ref @$s18typed_throws_thunk18genericPassThroughyyyyxYKXExYKs5ErrorRzlF
// CHECK-NEXT: [[T8:%[0-9]+]] = alloc_stack $Never
// CHECK-NEXT: try_apply [[T7]]<Never>([[T8]], [[T6]])
genericPassThrough(neverThrow)
}

// CHECK-LABEL: sil{{.*}} @$s18typed_throws_thunk5test4yyyyxYKXEKs5ErrorRzlF
// Indirect -> Indirect should not require a thunk
func test4<T: Error>(_ x: () throws(T) -> Void) throws {
// CHECK-NOT: function_ref thunk for
// CHECK: [[FN:%[0-9]+]] = function_ref @$s18typed_throws_thunk18genericPassThroughyyyyxYKXExYKs5ErrorRzlF
// CHECK: try_apply [[FN]]
try genericPassThrough(x)
}

// CHECK-LABEL: sil{{.*}} @$s18typed_throws_thunk5test5yyyyxYKXEKs5ErrorRzlF
// Indirect -> Existential should require a thunk
func test5<T: Error>(_ x: () throws(T) -> Void) throws {
// CHECK: bb0([[T0:%[0-9]+]]
// CHECK: [[T3:%[0-9]+]] = copy_value [[T0]]
// CHECK-NEXT: [[T4:%[0-9]+]] = convert_function [[T3]]
// CHECK-NEXT: // function_ref thunk for @callee_guaranteed () -> (@error @out A)
// CHECK-NEXT: [[T5:%[0-9]+]] = function_ref @$sxIgzr_s5Error_pIegzo_sAARzlTR
// CHECK-NEXT: [[T6:%[0-9]+]] = partial_apply [callee_guaranteed] [[T5]]<T>([[T4]])
// CHECK-NEXT: [[T7:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[T6]]
// CHECK-NEXT: // function_ref existentialPassThrough(_:)
// CHECK-NEXT: [[T8:%[0-9]+]] = function_ref @$s18typed_throws_thunk22existentialPassThroughyyyyKXEKF
// CHECK-NEXT: try_apply [[T8]]([[T7]])
try existentialPassThrough(x)
}

// CHECK-LABEL: sil{{.*}} @$s18typed_throws_thunk5test6yyKF
// Concrete -> Existential should require a thunk
func test6() throws {
// CHECK: [[T1:%[0-9]+]] = function_ref @$s18typed_throws_thunk13throwConcreteyyAA0E5ErrorVYKF
// CHECK-NEXT: [[T2:%[0-9]+]] = thin_to_thick_function [[T1]]
// CHECK-NEXT: // function_ref thunk for @escaping @callee_guaranteed () -> (@error @owned ConcreteError)
// CHECK-NEXT: [[T3:%[0-9]+]] = function_ref @$s18typed_throws_thunk13ConcreteErrorVIegzo_s0E0_pIegzo_TR
// CHECK-NEXT: [[T4:%[0-9]+]] = partial_apply [callee_guaranteed] [[T3]]([[T2]])
// CHECK-NEXT: [[T5:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[T4]]
// CHECK-NEXT: // function_ref existentialPassThrough(_:)
// CHECK-NEXT: [[T6:%[0-9]+]] = function_ref @$s18typed_throws_thunk22existentialPassThroughyyyyKXEKF
// CHECK-NEXT: try_apply [[T6]]([[T5]])
try existentialPassThrough(throwConcrete)
}

// CHECK-LABEL: sil{{.*}} @$s18typed_throws_thunk5test7yyKF
// Concrete -> Concrete should not require a thunk
func test7() throws {
// CHECK-NOT: function_ref thunk for
// CHECK: [[FN:%[0-9]+]] = function_ref @$s18typed_throws_thunk19concretePassThroughyyyyAA13ConcreteErrorVYKXEADYKF
// CHECK: try_apply [[FN]]
try concretePassThrough(throwConcrete)
}

// CHECK-LABEL: sil{{.*}} @$s18typed_throws_thunk5test8yyKF
// Concrete -> Indirect should require a thunk
func test8() throws {
// CHECK: [[T1:%[0-9]+]] = function_ref @$s18typed_throws_thunk13throwConcreteyyAA0E5ErrorVYKF
// CHECK-NEXT: [[T2:%[0-9]+]] = thin_to_thick_function [[T1]]
// CHECK-NEXT: [[T3:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[T2]]
// CHECK-NEXT: // function_ref thunk for @callee_guaranteed () -> (@error @owned ConcreteError)
// CHECK-NEXT: [[T4:%[0-9]+]] = function_ref @$s18typed_throws_thunk13ConcreteErrorVIgzo_ACIegzr_TR
// CHECK-NEXT: [[T5:%[0-9]+]] = partial_apply [callee_guaranteed] [[T4]]([[T3]])
// CHECK-NEXT: [[T6:%[0-9]+]] = convert_function [[T5]]
// CHECK-NEXT: [[T7:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[T6]]
// CHECK-NEXT: // function_ref genericPassThrough<A>(_:)
// CHECK-NEXT: [[T8:%[0-9]+]] = function_ref @$s18typed_throws_thunk18genericPassThroughyyyyxYKXExYKs5ErrorRzlF
// CHECK-NEXT: [[T9:%[0-9]+]] = alloc_stack $ConcreteError
// CHECK-NEXT: try_apply [[T8]]<ConcreteError>([[T9]], [[T7]])
try genericPassThrough(throwConcrete)
}

// CHECK-LABEL: sil{{.*}} @$s18typed_throws_thunk5test9yyKF
// Existential -> Indirect should require a thunk
func test9() throws {
// CHECK: [[T1:%[0-9]+]] = function_ref @$s18typed_throws_thunk16throwExistentialyyKF
// CHECK-NEXT: [[T2:%[0-9]+]] = thin_to_thick_function [[T1]]
// CHECK-NEXT: [[T3:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[T2]]
// CHECK-NEXT: // function_ref thunk for @callee_guaranteed () -> (@error @owned Error)
// CHECK-NEXT: [[T4:%[0-9]+]] = function_ref @$ss5Error_pIgzo_sAA_pIegzr_TR
// CHECK-NEXT: [[T5:%[0-9]+]] = partial_apply [callee_guaranteed] [[T4]]([[T3]])
// CHECK-NEXT: [[T6:%[0-9]+]] = convert_function [[T5]]
// CHECK-NEXT: [[T7:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[T6]]
// CHECK-NEXT: // function_ref genericPassThrough<A>(_:)
// CHECK-NEXT: [[T8:%[0-9]+]] = function_ref @$s18typed_throws_thunk18genericPassThroughyyyyxYKXExYKs5ErrorRzlF
// CHECK-NEXT: [[T9:%[0-9]+]] = alloc_stack $any Error
// CHECK-NEXT: try_apply [[T8]]<any Error>([[T9]], [[T7]])
try genericPassThrough(throwExistential)
}