Prefer AsyncFn* over Fn* for coroutine-closures
This commit is contained in:
parent
b8c93f1223
commit
3bb384aad6
@ -260,23 +260,40 @@ fn try_overloaded_call_traits(
|
||||
adjusted_ty: Ty<'tcx>,
|
||||
opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>,
|
||||
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
|
||||
// HACK(async_closures): For async closures, prefer `AsyncFn*`
|
||||
// over `Fn*`, since all async closures implement `FnOnce`, but
|
||||
// choosing that over `AsyncFn`/`AsyncFnMut` would be more restrictive.
|
||||
// For other callables, just prefer `Fn*` for perf reasons.
|
||||
//
|
||||
// The order of trait choices here is not that big of a deal,
|
||||
// since it just guides inference (and our choice of autoref).
|
||||
// Though in the future, I'd like typeck to choose:
|
||||
// `Fn > AsyncFn > FnMut > AsyncFnMut > FnOnce > AsyncFnOnce`
|
||||
// ...or *ideally*, we just have `LendingFn`/`LendingFnMut`, which
|
||||
// would naturally unify these two trait hierarchies in the most
|
||||
// general way.
|
||||
let call_trait_choices = if self.shallow_resolve(adjusted_ty).is_coroutine_closure() {
|
||||
[
|
||||
(self.tcx.lang_items().async_fn_trait(), sym::async_call, true),
|
||||
(self.tcx.lang_items().async_fn_mut_trait(), sym::async_call_mut, true),
|
||||
(self.tcx.lang_items().async_fn_once_trait(), sym::async_call_once, false),
|
||||
(self.tcx.lang_items().fn_trait(), sym::call, true),
|
||||
(self.tcx.lang_items().fn_mut_trait(), sym::call_mut, true),
|
||||
(self.tcx.lang_items().fn_once_trait(), sym::call_once, false),
|
||||
]
|
||||
} else {
|
||||
[
|
||||
(self.tcx.lang_items().fn_trait(), sym::call, true),
|
||||
(self.tcx.lang_items().fn_mut_trait(), sym::call_mut, true),
|
||||
(self.tcx.lang_items().fn_once_trait(), sym::call_once, false),
|
||||
(self.tcx.lang_items().async_fn_trait(), sym::async_call, true),
|
||||
(self.tcx.lang_items().async_fn_mut_trait(), sym::async_call_mut, true),
|
||||
(self.tcx.lang_items().async_fn_once_trait(), sym::async_call_once, false),
|
||||
]
|
||||
};
|
||||
|
||||
// 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().async_fn_mut_trait(),
|
||||
Ident::with_dummy_span(sym::async_call_mut),
|
||||
true,
|
||||
),
|
||||
(
|
||||
self.tcx.lang_items().async_fn_once_trait(),
|
||||
Ident::with_dummy_span(sym::async_call_once),
|
||||
false,
|
||||
),
|
||||
] {
|
||||
for (opt_trait_def_id, method_name, borrow) in call_trait_choices {
|
||||
let Some(trait_def_id) = opt_trait_def_id else { continue };
|
||||
|
||||
let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
|
||||
@ -293,7 +310,7 @@ fn try_overloaded_call_traits(
|
||||
|
||||
if let Some(ok) = self.lookup_method_in_trait(
|
||||
self.misc(call_expr.span),
|
||||
method_name,
|
||||
Ident::with_dummy_span(method_name),
|
||||
trait_def_id,
|
||||
adjusted_ty,
|
||||
opt_input_type.as_ref().map(slice::from_ref),
|
||||
|
@ -336,11 +336,23 @@ fn assemble_closure_candidates(
|
||||
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"),
|
||||
};
|
||||
// A coroutine-closure implements `FnOnce` *always*, since it may
|
||||
// always be called once. It additionally implements `Fn`/`FnMut`
|
||||
// only if it has no upvars (therefore no borrows from the closure
|
||||
// that would need to be represented with a lifetime) and if the
|
||||
// closure kind permits it.
|
||||
// FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
|
||||
// if it takes all of its upvars by copy, and none by ref. This would
|
||||
// require us to record a bit more information during upvar analysis.
|
||||
if no_borrows && closure_kind.extends(kind) {
|
||||
candidates.vec.push(ClosureCandidate { is_const });
|
||||
} else if kind == ty::ClosureKind::FnOnce {
|
||||
|
@ -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 `()`
|
||||
}
|
||||
|
@ -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`.
|
||||
|
@ -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<F, A>
|
||||
|
||||
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<F, A>
|
||||
|
||||
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<F, A>
|
||||
= 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<F, A>
|
||||
= 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<F, A>
|
||||
= 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<F, A>
|
||||
= 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<F, A>
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user