From cfbe6af4f25d12f7eade4106f978363d11c4d125 Mon Sep 17 00:00:00 2001 From: cijiugechu Date: Thu, 28 May 2026 10:15:29 +0800 Subject: [PATCH] Fix tupled closure signature in AsyncFn arg mismatch diagnostic --- .../rustc_middle/src/middle/lang_items.rs | 19 +++++++++++++++ .../src/error_reporting/traits/suggestions.rs | 2 +- .../async-fn/closure-arg-type-mismatch.rs | 9 +++++++ .../async-fn/closure-arg-type-mismatch.stderr | 24 +++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/ui/async-await/async-fn/closure-arg-type-mismatch.rs create mode 100644 tests/ui/async-await/async-fn/closure-arg-type-mismatch.stderr diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index a0e4c288c4a85..8a2595e2938b8 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -55,6 +55,25 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Given a [`DefId`], returns whether it is one of the built-in callable + /// traits: `Fn`/`FnMut`/`FnOnce` or `AsyncFn`/`AsyncFnMut`/`AsyncFnOnce`. + /// + /// These built-in callable traits all model their inputs using the + /// `rust-call` ABI, which is tupled at the type level. + pub fn is_callable_trait(self, id: DefId) -> bool { + matches!( + self.as_lang_item(id), + Some( + LangItem::Fn + | LangItem::FnMut + | LangItem::FnOnce + | LangItem::AsyncFn + | LangItem::AsyncFnMut + | LangItem::AsyncFnOnce + ) + ) + } + /// Given a [`ty::ClosureKind`], get the [`DefId`] of its corresponding `Fn`-family /// trait, if it is defined. pub fn fn_trait_kind_to_def_id(self, kind: ty::ClosureKind) -> Option { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 66eaa49cbd5d4..beea76434f1b3 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2346,7 +2346,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let inputs = trait_ref.args.type_at(1); let sig = match inputs.kind() { - ty::Tuple(inputs) if infcx.tcx.is_fn_trait(trait_ref.def_id) => { + ty::Tuple(inputs) if infcx.tcx.is_callable_trait(trait_ref.def_id) => { infcx.tcx.mk_fn_sig_safe_rust_abi(*inputs, infcx.next_ty_var(DUMMY_SP)) } _ => infcx.tcx.mk_fn_sig_safe_rust_abi([inputs], infcx.next_ty_var(DUMMY_SP)), diff --git a/tests/ui/async-await/async-fn/closure-arg-type-mismatch.rs b/tests/ui/async-await/async-fn/closure-arg-type-mismatch.rs new file mode 100644 index 0000000000000..be1ad20e4c221 --- /dev/null +++ b/tests/ui/async-await/async-fn/closure-arg-type-mismatch.rs @@ -0,0 +1,9 @@ +//! Regression test for +//@ edition:2021 + +fn foo(_: impl AsyncFn(&mut i32)) {} + +fn main() { + foo(|_: i32| async {}); + //~^ ERROR type mismatch in closure arguments +} diff --git a/tests/ui/async-await/async-fn/closure-arg-type-mismatch.stderr b/tests/ui/async-await/async-fn/closure-arg-type-mismatch.stderr new file mode 100644 index 0000000000000..a3b47bb448d17 --- /dev/null +++ b/tests/ui/async-await/async-fn/closure-arg-type-mismatch.stderr @@ -0,0 +1,24 @@ +error[E0631]: type mismatch in closure arguments + --> $DIR/closure-arg-type-mismatch.rs:7:5 + | +LL | foo(|_: i32| async {}); + | ^^^^--------^^^^^^^^^^ + | | | + | | found signature defined here + | expected due to this + | + = note: expected closure signature `for<'a> fn(&'a mut _) -> _` + found closure signature `fn(_) -> _` +note: required by a bound in `foo` + --> $DIR/closure-arg-type-mismatch.rs:4:16 + | +LL | fn foo(_: impl AsyncFn(&mut i32)) {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `foo` +help: consider adjusting the signature so it borrows its argument + | +LL | foo(|_: &mut i32| async {}); + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0631`.