diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 7e78556aafb6f..bff6cec1bee7e 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1886,7 +1886,26 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { : valueE; if (auto *Fn = dyn_cast(expr)) { - if (auto *calledValue = Fn->getCalledValue()) { + /// Some concurrency-related things may have intermediate conversions that + /// we want to look through, e.g. + /// + /// ```swift + /// @discardableResult + /// @MainActor + /// func foo() -> () -> Void { + /// return {} + /// } + /// + /// @MainActor + /// func test() { + /// foo() + /// // ^ return value implicitly wapped in a `@Sendable` conversion + /// } + /// ``` + /// Attempt to look through function conversions when resolving the + /// called value. + if (auto *calledValue = + Fn->getCalledValue(/*skipFunctionConversions=*/true)) { if (auto *FD = dyn_cast(calledValue)) { if (FD->getAttrs().hasAttribute()) { isDiscardable = true; diff --git a/test/Concurrency/attr_discardableResult_async_await.swift b/test/Concurrency/attr_discardableResult_async_await.swift index d8f3e7f822cc6..d3475bad09248 100644 --- a/test/Concurrency/attr_discardableResult_async_await.swift +++ b/test/Concurrency/attr_discardableResult_async_await.swift @@ -14,3 +14,23 @@ func mainActorAsyncDiscardable() async -> Int { 0 } func consumesMainActorAsyncDiscardable() async { await mainActorAsyncDiscardable() // ok } + +// https://github.com/swiftlang/swift/issues/83463 + +@MainActor +@discardableResult +func returnsDiscardableFunc() -> () -> Void { return {} } + +@MainActor +func testDiscardsSyncFuncWithImplicitSendableConversion() { + returnsDiscardableFunc() +} + +@MainActor +@discardableResult +func mainActorAsyncReturnsDiscardableFunc() async -> () -> Void { return {} } + +@MainActor +func discardsAsyncFuncWithImplicitSendableConversion() async { + await mainActorAsyncReturnsDiscardableFunc() +}