From 2e901a1f10c052bc90d098612d65ab1584afcbbb Mon Sep 17 00:00:00 2001 From: Jamie <2119834+jamieQ@users.noreply.github.com> Date: Thu, 20 Nov 2025 06:21:03 -0600 Subject: [PATCH 1/2] [Sema]: suppress unexpected type warning on some async-let patterns It is somewhat common to use an async-let binding to run a synchronous, void-returning function in an async context. In such cases, the binding's type being inferred as Void is expected, and requiring programmers to explicitly write this to suppress a warning seems like unnecessary boilerplate. This patch changes the diagnostic logic to exempt async-let bindings from the existing diagnostic when the inferred type is Void. --- lib/Sema/TypeCheckPattern.cpp | 9 ++++++++- test/decl/var/async_let.swift | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index c13bd31ac8f73..8953c0ca2fb01 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1230,7 +1230,14 @@ Pattern *TypeChecker::coercePatternToType( // If the whole pattern is implicit, the user didn't write it. // Assume the compiler knows what it's doing. } else if (diagTy->isEqual(Context.TheEmptyTupleType)) { - shouldRequireType = true; + // Async-let bindings are commonly used to run a Void-returning + // synchronous function in an async context. As a policy choice, don't + // diagnose a Void result on these bindings as potentially unexpected. + if (!isOptional && var->isAsyncLet()) { + shouldRequireType = false; + } else { + shouldRequireType = true; + } } else if (auto MTT = diagTy->getAs()) { if (MTT->getInstanceType()->isAnyObject()) shouldRequireType = true; diff --git a/test/decl/var/async_let.swift b/test/decl/var/async_let.swift index 5461e510484f4..e37f12c5976b1 100644 --- a/test/decl/var/async_let.swift +++ b/test/decl/var/async_let.swift @@ -42,3 +42,34 @@ func testInterpolation() async { async let y = "\(12345)" _ = await y } + +// https://forums.swift.org/t/disable-constant-inferred-to-have-type-warning-when-using-async-let/83025 + +func testVoidResultTypeDiagnostics() async { + async let void = print("hello") + await void + async let void2 = () + await void2 + async let void3 = Void() + await void3 + async let void4 = { _ = 42 }() + await void4 + + @Sendable func syncVoid() {} + async let void5 = syncVoid() + await void5 + + @Sendable func asyncVoid() async {} + async let void6 = asyncVoid() + await void6 + + // expected-warning @+2 {{constant 'maybeVoid' inferred to have type '()?', which may be unexpected}} + // expected-note @+1 {{add an explicit type annotation to silence this warning}} + async let maybeVoid = { Bool.random() ? () : nil }() + await maybeVoid + + // expected-warning @+2 {{constant 'boxOVoid' inferred to have type '[()]', which may be unexpected}} + // expected-note @+1 {{add an explicit type annotation to silence this warning}} + async let boxOVoid = { [(), (), ()] }() + await boxOVoid // expected-warning {{expression of type '[()]' is unused}} +} From 9c48c9e0ba2b200003713c9e81503cedca5449ba Mon Sep 17 00:00:00 2001 From: Jamie <2119834+jamieQ@users.noreply.github.com> Date: Fri, 21 Nov 2025 05:27:04 -0600 Subject: [PATCH 2/2] [Sema]: don't warn about optional Void async-lets either --- lib/Sema/TypeCheckPattern.cpp | 9 +++------ test/decl/var/async_let.swift | 9 +++++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 8953c0ca2fb01..ac8c593babe12 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1232,12 +1232,9 @@ Pattern *TypeChecker::coercePatternToType( } else if (diagTy->isEqual(Context.TheEmptyTupleType)) { // Async-let bindings are commonly used to run a Void-returning // synchronous function in an async context. As a policy choice, don't - // diagnose a Void result on these bindings as potentially unexpected. - if (!isOptional && var->isAsyncLet()) { - shouldRequireType = false; - } else { - shouldRequireType = true; - } + // diagnose an inferred Void type (or optional thereof) on such bindings + // as potentially unexpected. + shouldRequireType = var->isAsyncLet() ? false : true; } else if (auto MTT = diagTy->getAs()) { if (MTT->getInstanceType()->isAnyObject()) shouldRequireType = true; diff --git a/test/decl/var/async_let.swift b/test/decl/var/async_let.swift index e37f12c5976b1..dc31ff6aa3bd9 100644 --- a/test/decl/var/async_let.swift +++ b/test/decl/var/async_let.swift @@ -63,11 +63,16 @@ func testVoidResultTypeDiagnostics() async { async let void6 = asyncVoid() await void6 - // expected-warning @+2 {{constant 'maybeVoid' inferred to have type '()?', which may be unexpected}} - // expected-note @+1 {{add an explicit type annotation to silence this warning}} async let maybeVoid = { Bool.random() ? () : nil }() await maybeVoid + do { + final class C: Sendable { func doit() {} } + let c: C? = nil + async let maybeVoid2 = c?.doit() + let _: ()? = await maybeVoid2 + } + // expected-warning @+2 {{constant 'boxOVoid' inferred to have type '[()]', which may be unexpected}} // expected-note @+1 {{add an explicit type annotation to silence this warning}} async let boxOVoid = { [(), (), ()] }()