From 8804eedfa4c948abfc6c838449e7bd40e8872b9a Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Mon, 24 Aug 2026 19:50:35 -0300 Subject: [PATCH 1/2] Propagate async closure output into its body Async closure bodies are checked as coroutines, but their expected return type was lost before body checking. Reuse the matching coroutine signature. Keep this path limited to async closures because gen closures have a different input shape. --- compiler/rustc_hir_typeck/src/closure.rs | 20 ++++++++++++++++++ ...utput-mismatch-issue-161619.current.stderr | 21 +++++++++++++++++++ ...e-output-mismatch-issue-161619.next.stderr | 21 +++++++++++++++++++ ...nc-closure-output-mismatch-issue-161619.rs | 18 ++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.current.stderr create mode 100644 tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.next.stderr create mode 100644 tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.rs diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index f6a97f04d99e4..f7fcfb6ea80d3 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -62,6 +62,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (expected_sig, expected_kind) = match expected.to_option(self) { Some(ty) => self.deduce_closure_signature( self.deeply_resolve_ignoring_regions_with_obligations(ty), + closure.def_id, closure.kind, ), None => (None, None), @@ -291,9 +292,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn deduce_closure_signature( &self, expected_ty: Ty<'tcx>, + closure_def_id: LocalDefId, closure_kind: hir::ClosureKind, ) -> (Option>, Option) { match *expected_ty.kind() { + ty::Coroutine(def_id, args) + if def_id == closure_def_id.to_def_id() + && matches!( + closure_kind, + hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Closure, + )) + ) => + { + // An async closure's body is itself a coroutine. Propagate the signature + // inferred for that coroutine into its body. + let args = args.as_coroutine(); + let sig = ty::Binder::dummy( + self.tcx.mk_fn_sig_safe_rust_abi([args.resume_ty()], args.return_ty()), + ); + (Some(ExpectedSig { cause_span: None, sig }), None) + } ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => self .deduce_closure_signature_from_predicates( expected_ty, diff --git a/tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.current.stderr b/tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.current.stderr new file mode 100644 index 0000000000000..62ee5193400c9 --- /dev/null +++ b/tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.current.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/async-closure-output-mismatch-issue-161619.rs:16:27 + | +LL | for_each(async |_| Ok(String::new())); + | -- ^^^^^^^^^^^^^ expected `()`, found `String` + | | + | arguments to this enum variant are incorrect + | +help: the type constructed contains `String` due to the type of the argument passed + --> $DIR/async-closure-output-mismatch-issue-161619.rs:16:24 + | +LL | for_each(async |_| Ok(String::new())); + | ^^^-------------^ + | | + | this argument influences the type of `Ok` +note: tuple variant defined here + --> $SRC_DIR/core/src/result.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.next.stderr b/tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.next.stderr new file mode 100644 index 0000000000000..62ee5193400c9 --- /dev/null +++ b/tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.next.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/async-closure-output-mismatch-issue-161619.rs:16:27 + | +LL | for_each(async |_| Ok(String::new())); + | -- ^^^^^^^^^^^^^ expected `()`, found `String` + | | + | arguments to this enum variant are incorrect + | +help: the type constructed contains `String` due to the type of the argument passed + --> $DIR/async-closure-output-mismatch-issue-161619.rs:16:24 + | +LL | for_each(async |_| Ok(String::new())); + | ^^^-------------^ + | | + | this argument influences the type of `Ok` +note: tuple variant defined here + --> $SRC_DIR/core/src/result.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.rs b/tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.rs new file mode 100644 index 0000000000000..aa26814ff5dbc --- /dev/null +++ b/tests/ui/async-await/async-closures/async-closure-output-mismatch-issue-161619.rs @@ -0,0 +1,18 @@ +//@ edition: 2024 +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +use std::future::Future; + +fn for_each(_: F) +where + F: FnMut(()) -> Fut, + Fut: Future>, +{ +} + +fn main() { + for_each(async |_| Ok(String::new())); + //~^ ERROR mismatched types +} From 1b7d358443fdfc6a047214544d53cf38e32ebf1b Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Mon, 24 Aug 2026 20:33:09 -0300 Subject: [PATCH 2/2] Update async closure suggestion expectations --- .../suggest-create-closure-issue-150701.fixed | 1 - .../suggest-create-closure-issue-150701.rs | 1 - ...suggest-create-closure-issue-150701.stderr | 25 ++++--------------- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/tests/ui/suggestions/suggest-create-closure-issue-150701.fixed b/tests/ui/suggestions/suggest-create-closure-issue-150701.fixed index f3e226c9e5681..d19ab19f98012 100644 --- a/tests/ui/suggestions/suggest-create-closure-issue-150701.fixed +++ b/tests/ui/suggestions/suggest-create-closure-issue-150701.fixed @@ -9,7 +9,6 @@ fn f(_c: impl Future) {} fn main() { f((async || {})()); //~ ERROR: expected function, found `()` - //~^ ERROR: is not a future f(async {}); //~^ ERROR: is not a future } diff --git a/tests/ui/suggestions/suggest-create-closure-issue-150701.rs b/tests/ui/suggestions/suggest-create-closure-issue-150701.rs index e7a5076d8f1d9..bbb5f4d0bb6bd 100644 --- a/tests/ui/suggestions/suggest-create-closure-issue-150701.rs +++ b/tests/ui/suggestions/suggest-create-closure-issue-150701.rs @@ -9,7 +9,6 @@ fn f(_c: impl Future) {} fn main() { f(async || {}()); //~ ERROR: expected function, found `()` - //~^ ERROR: is not a future f(async || {}); //~^ ERROR: is not a future } diff --git a/tests/ui/suggestions/suggest-create-closure-issue-150701.stderr b/tests/ui/suggestions/suggest-create-closure-issue-150701.stderr index 0f991169d6772..8f1f721f97f35 100644 --- a/tests/ui/suggestions/suggest-create-closure-issue-150701.stderr +++ b/tests/ui/suggestions/suggest-create-closure-issue-150701.stderr @@ -11,30 +11,15 @@ help: if you meant to create this closure and immediately call it, surround the LL | f((async || {})()); | + + -error[E0277]: `{async closure@$DIR/suggest-create-closure-issue-150701.rs:11:7: 11:15}` is not a future - --> $DIR/suggest-create-closure-issue-150701.rs:11:7 - | -LL | f(async || {}()); - | - ^^^^^^^^^^^^^ `{async closure@$DIR/suggest-create-closure-issue-150701.rs:11:7: 11:15}` is not a future - | | - | required by a bound introduced by this call - | - = help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-create-closure-issue-150701.rs:11:7: 11:15}` -note: required by a bound in `f` - --> $DIR/suggest-create-closure-issue-150701.rs:8:15 - | -LL | fn f(_c: impl Future) {} - | ^^^^^^^^^^^^^^^^^^^ required by this bound in `f` - -error[E0277]: `{async closure@$DIR/suggest-create-closure-issue-150701.rs:13:7: 13:15}` is not a future - --> $DIR/suggest-create-closure-issue-150701.rs:13:7 +error[E0277]: `{async closure@$DIR/suggest-create-closure-issue-150701.rs:12:7: 12:15}` is not a future + --> $DIR/suggest-create-closure-issue-150701.rs:12:7 | LL | f(async || {}); - | - ^^^^^^^^^^^ `{async closure@$DIR/suggest-create-closure-issue-150701.rs:13:7: 13:15}` is not a future + | - ^^^^^^^^^^^ `{async closure@$DIR/suggest-create-closure-issue-150701.rs:12:7: 12:15}` is not a future | | | required by a bound introduced by this call | - = help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-create-closure-issue-150701.rs:13:7: 13:15}` + = help: the trait `Future` is not implemented for `{async closure@$DIR/suggest-create-closure-issue-150701.rs:12:7: 12:15}` note: required by a bound in `f` --> $DIR/suggest-create-closure-issue-150701.rs:8:15 | @@ -46,7 +31,7 @@ LL - f(async || {}); LL + f(async {}); | -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0618. For more information about an error, try `rustc --explain E0277`.