From cd3e96a320497c40995a33eb52633e8a7af171a8 Mon Sep 17 00:00:00 2001 From: Jamie <2119834+jamieQ@users.noreply.github.com> Date: Fri, 1 Aug 2025 05:48:37 -0500 Subject: [PATCH] [Sema]: fix @discardableResult interaction with implicit @Sendable conversions Updates existing diagnostic handling to look through function conversions when suppressing diagnostics for unused functions that are return values from callees marked with @discardableResult. --- lib/Sema/TypeCheckStmt.cpp | 21 ++++++++++++++++++- .../attr_discardableResult_async_await.swift | 20 ++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) 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() +}