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>,
|
adjusted_ty: Ty<'tcx>,
|
||||||
opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>,
|
opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>,
|
||||||
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'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.
|
// Try the options that are least restrictive on the caller first.
|
||||||
for (opt_trait_def_id, method_name, borrow) in [
|
for (opt_trait_def_id, method_name, borrow) in call_trait_choices {
|
||||||
(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,
|
|
||||||
),
|
|
||||||
] {
|
|
||||||
let Some(trait_def_id) = opt_trait_def_id else { continue };
|
let Some(trait_def_id) = opt_trait_def_id else { continue };
|
||||||
|
|
||||||
let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
|
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(
|
if let Some(ok) = self.lookup_method_in_trait(
|
||||||
self.misc(call_expr.span),
|
self.misc(call_expr.span),
|
||||||
method_name,
|
Ident::with_dummy_span(method_name),
|
||||||
trait_def_id,
|
trait_def_id,
|
||||||
adjusted_ty,
|
adjusted_ty,
|
||||||
opt_input_type.as_ref().map(slice::from_ref),
|
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);
|
let is_const = self.tcx().is_const_fn_raw(def_id);
|
||||||
match self.infcx.closure_kind(self_ty) {
|
match self.infcx.closure_kind(self_ty) {
|
||||||
Some(closure_kind) => {
|
Some(closure_kind) => {
|
||||||
let no_borrows = self
|
let no_borrows = match self
|
||||||
.infcx
|
.infcx
|
||||||
.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty())
|
.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty())
|
||||||
.tuple_fields()
|
.kind()
|
||||||
.is_empty();
|
{
|
||||||
|
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) {
|
if no_borrows && closure_kind.extends(kind) {
|
||||||
candidates.vec.push(ClosureCandidate { is_const });
|
candidates.vec.push(ClosureCandidate { is_const });
|
||||||
} else if kind == ty::ClosureKind::FnOnce {
|
} else if kind == ty::ClosureKind::FnOnce {
|
||||||
|
@ -5,8 +5,5 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
fn needs_fn(x: impl FnOnce()) {}
|
fn needs_fn(x: impl FnOnce()) {}
|
||||||
needs_fn(async || {});
|
needs_fn(async || {});
|
||||||
//~^ ERROR expected a `FnOnce()` closure, found `{coroutine-closure@
|
//~^ ERROR expected `{coroutine-closure@is-not-fn.rs:7:14}` to be a closure that returns `()`
|
||||||
// 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.
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
--> $DIR/is-not-fn.rs:7:14
|
||||||
|
|
|
|
||||||
LL | needs_fn(async || {});
|
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
|
| 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: expected unit type `()`
|
||||||
= note: wrap the `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}` in a closure with no arguments: `|| { /* code */ }`
|
found `async` closure body `{async closure body@$DIR/is-not-fn.rs:7:23: 7:25}`
|
||||||
note: required by a bound in `needs_fn`
|
note: required by a bound in `needs_fn`
|
||||||
--> $DIR/is-not-fn.rs:6:25
|
--> $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
|
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
|
--> $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`
|
= 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
|
error[E0038]: the trait `AsyncFnMut` cannot be made into an object
|
||||||
--> $DIR/dyn-pos.rs:5:16
|
--> $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
|
--> $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`
|
= 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
|
error[E0038]: the trait `AsyncFn` cannot be made into an object
|
||||||
--> $DIR/dyn-pos.rs:5:16
|
--> $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
|
--> $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`
|
= 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`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
error[E0038]: the trait `AsyncFnMut` cannot be made into an object
|
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
|
--> $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`
|
= 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`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
error[E0038]: the trait `AsyncFn` cannot be made into an object
|
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
|
--> $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`
|
= 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`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
error[E0038]: the trait `AsyncFnMut` cannot be made into an object
|
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
|
--> $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`
|
= 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`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
error[E0038]: the trait `AsyncFn` cannot be made into an object
|
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
|
::: $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`
|
= 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
|
error: aborting due to 7 previous errors
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user