diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index fbe6f454dbc92..ba43b9c0f7063 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -183,16 +183,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: TypeVariableOriginKind::TypeInference, span: callee_expr.span, }); + // We may actually receive a coroutine back whose kind is different + // from the closure that this dispatched from. This is because when + // we have no captures, we automatically implement `FnOnce`. This + // impl forces the closure kind to `FnOnce` i.e. `u8`. + let kind_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span: callee_expr.span, + }); let call_sig = self.tcx.mk_fn_sig( [coroutine_closure_sig.tupled_inputs_ty], coroutine_closure_sig.to_coroutine( self.tcx, closure_args.parent_args(), - // Inherit the kind ty of the closure, since we're calling this - // coroutine with the most relaxed `AsyncFn*` trait that we can. - // We don't necessarily need to do this here, but it saves us - // computing one more infer var that will get constrained later. - closure_args.kind_ty(), + kind_ty, self.tcx.coroutine_for_closure(def_id), tupled_upvars_ty, ), @@ -263,14 +267,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Try the options that are least restrictive on the caller first. for (opt_trait_def_id, method_name, borrow) in [ (self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true), - (self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true), - (self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false), (self.tcx.lang_items().async_fn_trait(), Ident::with_dummy_span(sym::async_call), true), + (self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true), ( self.tcx.lang_items().async_fn_mut_trait(), Ident::with_dummy_span(sym::async_call_mut), true, ), + (self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false), ( self.tcx.lang_items().async_fn_once_trait(), Ident::with_dummy_span(sym::async_call_once), diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 9f7dc98daa1b8..101ef6aadc574 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -336,11 +336,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let is_const = self.tcx().is_const_fn_raw(def_id); match self.infcx.closure_kind(self_ty) { Some(closure_kind) => { - let no_borrows = self + let no_borrows = match self .infcx .shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()) - .tuple_fields() - .is_empty(); + .kind() + { + ty::Tuple(tys) => tys.is_empty(), + ty::Error(_) => false, + _ => bug!("tuple_fields called on non-tuple"), + }; if no_borrows && closure_kind.extends(kind) { candidates.vec.push(ClosureCandidate { is_const }); } else if kind == ty::ClosureKind::FnOnce { diff --git a/tests/ui/async-await/async-closures/def-path.stderr b/tests/ui/async-await/async-closures/def-path.stderr index dae45825f370d..ae34d920e3858 100644 --- a/tests/ui/async-await/async-closures/def-path.stderr +++ b/tests/ui/async-await/async-closures/def-path.stderr @@ -5,11 +5,11 @@ LL | let x = async || {}; | -- the expected `async` closure body LL | LL | let () = x(); - | ^^ --- this expression has type `{static main::{closure#0}::{closure#0} upvar_tys=?15t witness=?6t}` + | ^^ --- this expression has type `{static main::{closure#0}::{closure#0} upvar_tys=?15t witness=?6t}` | | | expected `async` closure body, found `()` | - = note: expected `async` closure body `{static main::{closure#0}::{closure#0} upvar_tys=?15t witness=?6t}` + = note: expected `async` closure body `{static main::{closure#0}::{closure#0} upvar_tys=?15t witness=?6t}` found unit type `()` error: aborting due to 1 previous error diff --git a/tests/ui/async-await/async-closures/is-not-fn.rs b/tests/ui/async-await/async-closures/is-not-fn.rs index 94c8e8563bd9e..40b0febbf0692 100644 --- a/tests/ui/async-await/async-closures/is-not-fn.rs +++ b/tests/ui/async-await/async-closures/is-not-fn.rs @@ -5,8 +5,5 @@ fn main() { fn needs_fn(x: impl FnOnce()) {} needs_fn(async || {}); - //~^ ERROR expected a `FnOnce()` closure, found `{coroutine-closure@ - // FIXME(async_closures): This should explain in more detail how async fns don't - // implement the regular `Fn` traits. Or maybe we should just fix it and make them - // when there are no upvars or whatever. + //~^ ERROR expected `{coroutine-closure@is-not-fn.rs:7:14}` to be a closure that returns `()` } diff --git a/tests/ui/async-await/async-closures/is-not-fn.stderr b/tests/ui/async-await/async-closures/is-not-fn.stderr index 12da4b1fc6fb7..6169cee85fd32 100644 --- a/tests/ui/async-await/async-closures/is-not-fn.stderr +++ b/tests/ui/async-await/async-closures/is-not-fn.stderr @@ -1,13 +1,13 @@ -error[E0277]: expected a `FnOnce()` closure, found `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}` +error[E0271]: expected `{coroutine-closure@is-not-fn.rs:7:14}` to be a closure that returns `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:7:23: 7:25}` --> $DIR/is-not-fn.rs:7:14 | LL | needs_fn(async || {}); - | -------- ^^^^^^^^^^^ expected an `FnOnce()` closure, found `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}` + | -------- ^^^^^^^^^^^ expected `()`, found `async` closure body | | | required by a bound introduced by this call | - = help: the trait `FnOnce<()>` is not implemented for `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}` - = note: wrap the `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}` in a closure with no arguments: `|| { /* code */ }` + = note: expected unit type `()` + found `async` closure body `{async closure body@$DIR/is-not-fn.rs:7:23: 7:25}` note: required by a bound in `needs_fn` --> $DIR/is-not-fn.rs:6:25 | @@ -16,4 +16,4 @@ LL | fn needs_fn(x: impl FnOnce()) {} error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/async-await/async-closures/move-consuming-capture.stderr b/tests/ui/async-await/async-closures/move-consuming-capture.stderr index 2c2a0d1162d17..a139a2686a55a 100644 --- a/tests/ui/async-await/async-closures/move-consuming-capture.stderr +++ b/tests/ui/async-await/async-closures/move-consuming-capture.stderr @@ -5,12 +5,15 @@ LL | let x = async move || { | - move occurs because `x` has type `{coroutine-closure@$DIR/move-consuming-capture.rs:13:17: 13:30}`, which does not implement the `Copy` trait ... LL | x().await; - | --- `x` moved due to this method call + | --- `x` moved due to this call LL | x().await; | ^ value used here after move | -note: `async_call_once` takes ownership of the receiver `self`, which moves `x` - --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/move-consuming-capture.rs:16:9 + | +LL | x().await; + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/async-await/async-fn/dyn-pos.stderr b/tests/ui/async-await/async-fn/dyn-pos.stderr index c93235265160b..488c5d06938f0 100644 --- a/tests/ui/async-await/async-fn/dyn-pos.stderr +++ b/tests/ui/async-await/async-fn/dyn-pos.stderr @@ -8,6 +8,9 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL | = note: the trait cannot be made into an object because it contains the generic associated type `CallFuture` + = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFn` for this new enum and using it instead: + &F + std::boxed::Box error[E0038]: the trait `AsyncFnMut` cannot be made into an object --> $DIR/dyn-pos.rs:5:16 @@ -19,6 +22,10 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL | = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture` + = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFnMut` for this new enum and using it instead: + &F + &mut F + std::boxed::Box error[E0038]: the trait `AsyncFn` cannot be made into an object --> $DIR/dyn-pos.rs:5:16 @@ -30,6 +37,9 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL | = note: the trait cannot be made into an object because it contains the generic associated type `CallFuture` + = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFn` for this new enum and using it instead: + &F + std::boxed::Box = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0038]: the trait `AsyncFnMut` cannot be made into an object @@ -42,6 +52,10 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL | = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture` + = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFnMut` for this new enum and using it instead: + &F + &mut F + std::boxed::Box = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0038]: the trait `AsyncFn` cannot be made into an object @@ -54,6 +68,9 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL | = note: the trait cannot be made into an object because it contains the generic associated type `CallFuture` + = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFn` for this new enum and using it instead: + &F + std::boxed::Box = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0038]: the trait `AsyncFnMut` cannot be made into an object @@ -66,6 +83,10 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL | = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture` + = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFnMut` for this new enum and using it instead: + &F + &mut F + std::boxed::Box = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0038]: the trait `AsyncFn` cannot be made into an object @@ -81,6 +102,9 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all ::: $SRC_DIR/core/src/ops/async_function.rs:LL:COL | = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture` + = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFn` for this new enum and using it instead: + &F + std::boxed::Box error: aborting due to 7 previous errors