Auto merge of #120712 - compiler-errors:async-closures-harmonize, r=oli-obk

Harmonize `AsyncFn` implementations, make async closures conditionally impl `Fn*` traits

This PR implements several changes to the built-in and libcore-provided implementations of `Fn*` and `AsyncFn*` to address two problems:
1. async closures do not implement the `Fn*` family traits, leading to breakage: https://crater-reports.s3.amazonaws.com/pr-120361/index.html
2. *references* to async closures do not implement `AsyncFn*`, as a consequence of the existing blanket impls of the shape `AsyncFn for F where F: Fn, F::Output: Future`.

In order to fix (1.), we implement `Fn` traits appropriately for async closures. It turns out that async closures can:
* always implement `FnOnce`, meaning that they're drop-in compatible with `FnOnce`-bound combinators like `Option::map`.
* conditionally implement `Fn`/`FnMut` if they have no captures, which means that existing usages of async closures should *probably* work without breakage (crater checking this: https://github.com/rust-lang/rust/pull/120712#issuecomment-1930587805).

In order to fix (2.), we make all of the built-in callables implement `AsyncFn*` via built-in impls, and instead adjust the blanket impls for `AsyncFn*` provided by libcore to match the blanket impls for `Fn*`.
This commit is contained in:
bors 2024-02-10 07:15:15 +00:00
commit 757b8efed4
21 changed files with 706 additions and 263 deletions

View File

@ -261,23 +261,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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| {
@ -294,7 +311,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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),

View File

@ -56,11 +56,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// It's always helpful for inference if we know the kind of
// closure sooner rather than later, so first examine the expected
// type, and see if can glean a closure kind from there.
let (expected_sig, expected_kind) = match expected.to_option(self) {
Some(ty) => {
self.deduce_closure_signature(self.try_structurally_resolve_type(expr_span, ty))
}
None => (None, None),
let (expected_sig, expected_kind) = match closure.kind {
hir::ClosureKind::Closure => match expected.to_option(self) {
Some(ty) => {
self.deduce_closure_signature(self.try_structurally_resolve_type(expr_span, ty))
}
None => (None, None),
},
// We don't want to deduce a signature from `Fn` bounds for coroutines
// or coroutine-closures, because the former does not implement `Fn`
// ever, and the latter's signature doesn't correspond to the coroutine
// type that it returns.
hir::ClosureKind::Coroutine(_) | hir::ClosureKind::CoroutineClosure(_) => (None, None),
};
let ClosureSignatures { bound_sig, mut liberated_sig } =

View File

@ -177,12 +177,13 @@ pub fn report_object_safety_error<'tcx>(
)));
}
impls => {
let types = impls
let mut types = impls
.iter()
.map(|t| {
with_no_trimmed_paths!(format!(" {}", tcx.type_of(*t).instantiate_identity(),))
})
.collect::<Vec<_>>();
types.sort();
err.help(format!(
"the following types implement the trait, consider defining an enum where each \
variant holds one of these types, implementing `{}` for this new enum and using \

View File

@ -323,34 +323,27 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
self_ty: Ty<'tcx>,
goal_kind: ty::ClosureKind,
env_region: ty::Region<'tcx>,
) -> Result<
(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Option<ty::Predicate<'tcx>>),
NoSolution,
> {
) -> Result<(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Vec<ty::Predicate<'tcx>>), NoSolution>
{
match *self_ty.kind() {
ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
let sig = args.coroutine_closure_sig().skip_binder();
let mut nested = vec![];
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
return Err(NoSolution);
}
Ok((
args.coroutine_closure_sig().map_bound(|sig| {
let coroutine_ty = sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
goal_kind,
env_region,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
);
(sig.tupled_inputs_ty, sig.return_ty, coroutine_ty)
}),
None,
))
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
goal_kind,
env_region,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
)
} else {
let async_fn_kind_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
@ -367,42 +360,117 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
// will project to the right upvars for the generator, appending the inputs and
// coroutine upvars respecting the closure kind.
Ok((
args.coroutine_closure_sig().map_bound(|sig| {
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
let coroutine_ty = sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
);
(sig.tupled_inputs_ty, sig.return_ty, coroutine_ty)
}),
Some(
ty::TraitRef::new(
tcx,
async_fn_kind_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
)
.to_predicate(tcx),
),
))
}
nested.push(
ty::TraitRef::new(
tcx,
async_fn_kind_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
)
.to_predicate(tcx),
);
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
)
};
Ok((
args.coroutine_closure_sig().rebind((
sig.tupled_inputs_ty,
sig.return_ty,
coroutine_ty,
)),
nested,
))
}
ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) => Err(NoSolution),
ty::FnDef(..) | ty::FnPtr(..) => {
let bound_sig = self_ty.fn_sig(tcx);
let sig = bound_sig.skip_binder();
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
// `FnDef` and `FnPtr` only implement `AsyncFn*` when their
// return type implements `Future`.
let nested = vec![
bound_sig
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
.to_predicate(tcx),
];
let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
Ok((
bound_sig.rebind((Ty::new_tup(tcx, sig.inputs()), sig.output(), future_output_ty)),
nested,
))
}
ty::Closure(_, args) => {
let args = args.as_closure();
let bound_sig = args.sig();
let sig = bound_sig.skip_binder();
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
// `Closure`s only implement `AsyncFn*` when their return type
// implements `Future`.
let mut nested = vec![
bound_sig
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
.to_predicate(tcx),
];
// Additionally, we need to check that the closure kind
// is still compatible.
let kind_ty = args.kind_ty();
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
return Err(NoSolution);
}
} else {
let async_fn_kind_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
// When we don't know the closure kind (and therefore also the closure's upvars,
// which are computed at the same time), we must delay the computation of the
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
// goal functions similarly to the old `ClosureKind` predicate, and ensures that
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
// will project to the right upvars for the generator, appending the inputs and
// coroutine upvars respecting the closure kind.
nested.push(
ty::TraitRef::new(
tcx,
async_fn_kind_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
)
.to_predicate(tcx),
);
}
let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
Ok((bound_sig.rebind((sig.inputs()[0], sig.output(), future_output_ty)), nested))
}
ty::Bool
| ty::Char

View File

@ -2087,7 +2087,9 @@ fn confirm_select_candidate<'cx, 'tcx>(
} else if lang_items.async_iterator_trait() == Some(trait_def_id) {
confirm_async_iterator_candidate(selcx, obligation, data)
} else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() {
if obligation.predicate.self_ty().is_closure() {
if obligation.predicate.self_ty().is_closure()
|| obligation.predicate.self_ty().is_coroutine_closure()
{
confirm_closure_candidate(selcx, obligation, data)
} else {
confirm_fn_pointer_candidate(selcx, obligation, data)
@ -2410,11 +2412,75 @@ fn confirm_closure_candidate<'cx, 'tcx>(
obligation: &ProjectionTyObligation<'tcx>,
nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
let tcx = selcx.tcx();
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let ty::Closure(_, args) = self_ty.kind() else {
unreachable!("expected closure self type for closure candidate, found {self_ty}")
let closure_sig = match *self_ty.kind() {
ty::Closure(_, args) => args.as_closure().sig(),
// Construct a "normal" `FnOnce` signature for coroutine-closure. This is
// basically duplicated with the `AsyncFnOnce::CallOnce` confirmation, but
// I didn't see a good way to unify those.
ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
args.coroutine_closure_sig().map_bound(|sig| {
// If we know the kind and upvars, use that directly.
// Otherwise, defer to `AsyncFnKindHelper::Upvars` to delay
// the projection, like the `AsyncFn*` traits do.
let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind() {
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
ty::ClosureKind::FnOnce,
tcx.lifetimes.re_static,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
)
} else {
let async_fn_kind_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
let upvars_projection_def_id = tcx
.associated_items(async_fn_kind_trait_def_id)
.filter_by_name_unhygienic(sym::Upvars)
.next()
.unwrap()
.def_id;
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce).into(),
tcx.lifetimes.re_static.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
)
};
tcx.mk_fn_sig(
[sig.tupled_inputs_ty],
output_ty,
sig.c_variadic,
sig.unsafety,
sig.abi,
)
})
}
_ => {
unreachable!("expected closure self type for closure candidate, found {self_ty}");
}
};
let closure_sig = args.as_closure().sig();
let Normalized { value: closure_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
@ -2470,126 +2536,171 @@ fn confirm_callable_candidate<'cx, 'tcx>(
fn confirm_async_closure_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
mut nested: Vec<PredicateObligation<'tcx>>,
nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let ty::CoroutineClosure(def_id, args) = *self_ty.kind() else {
unreachable!(
"expected coroutine-closure self type for coroutine-closure candidate, found {self_ty}"
)
};
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
let tcx = selcx.tcx();
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let goal_kind =
tcx.async_fn_trait_kind_from_def_id(obligation.predicate.trait_def_id(tcx)).unwrap();
let async_fn_kind_helper_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
nested.push(obligation.with(
tcx,
ty::TraitRef::new(
tcx,
async_fn_kind_helper_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
),
));
let env_region = match goal_kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => obligation.predicate.args.region_at(2),
ty::ClosureKind::FnOnce => tcx.lifetimes.re_static,
};
let item_name = tcx.item_name(obligation.predicate.def_id);
let upvars_projection_def_id = tcx
.associated_items(async_fn_kind_helper_trait_def_id)
.filter_by_name_unhygienic(sym::Upvars)
.next()
.unwrap()
.def_id;
let poly_cache_entry = match *self_ty.kind() {
ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure();
let kind_ty = args.kind_ty();
let sig = args.coroutine_closure_sig().skip_binder();
// FIXME(async_closures): Confirmation is kind of a mess here. Ideally,
// we'd short-circuit when we know that the goal_kind >= closure_kind, and not
// register a nested predicate or create a new projection ty here. But I'm too
// lazy to make this more efficient atm, and we can always tweak it later,
// since all this does is make the solver do more work.
//
// The code duplication due to the different length args is kind of weird, too.
//
// See the logic in `structural_traits` in the new solver to understand a bit
// more clearly how this *should* look.
let poly_cache_entry = args.coroutine_closure_sig().map_bound(|sig| {
let (projection_ty, term) = match tcx.item_name(obligation.predicate.def_id) {
sym::CallOnceFuture => {
let tupled_upvars_ty = Ty::new_projection(
let term = match item_name {
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => {
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
bug!("we should not be confirming if the closure kind is not met");
}
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(def_id),
goal_kind,
env_region,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
)
} else {
let async_fn_kind_trait_def_id =
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
let upvars_projection_def_id = tcx
.associated_items(async_fn_kind_trait_def_id)
.filter_by_name_unhygienic(sym::Upvars)
.next()
.unwrap()
.def_id;
// When we don't know the closure kind (and therefore also the closure's upvars,
// which are computed at the same time), we must delay the computation of the
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
// goal functions similarly to the old `ClosureKind` predicate, and ensures that
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
// will project to the right upvars for the generator, appending the inputs and
// coroutine upvars respecting the closure kind.
// N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`.
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
)
}
}
sym::Output => sig.return_ty,
name => bug!("no such associated type: {name}"),
};
let projection_ty = match item_name {
sym::CallOnceFuture | sym::Output => ty::AliasTy::new(
tcx,
upvars_projection_def_id,
obligation.predicate.def_id,
[self_ty, sig.tupled_inputs_ty],
),
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()],
),
name => bug!("no such associated type: {name}"),
};
args.coroutine_closure_sig()
.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
}
ty::FnDef(..) | ty::FnPtr(..) => {
let bound_sig = self_ty.fn_sig(tcx);
let sig = bound_sig.skip_binder();
let term = match item_name {
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(),
sym::Output => {
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
Ty::new_projection(tcx, future_output_def_id, [sig.output()])
}
name => bug!("no such associated type: {name}"),
};
let projection_ty = match item_name {
sym::CallOnceFuture | sym::Output => ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[self_ty, Ty::new_tup(tcx, sig.inputs())],
),
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
ty::GenericArg::from(self_ty),
Ty::new_tup(tcx, sig.inputs()).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
let coroutine_ty = sig.to_coroutine(
),
name => bug!("no such associated type: {name}"),
};
bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
}
ty::Closure(_, args) => {
let args = args.as_closure();
let bound_sig = args.sig();
let sig = bound_sig.skip_binder();
let term = match item_name {
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(),
sym::Output => {
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
let future_output_def_id = tcx
.associated_items(future_trait_def_id)
.filter_by_name_unhygienic(sym::Output)
.next()
.unwrap()
.def_id;
Ty::new_projection(tcx, future_output_def_id, [sig.output()])
}
name => bug!("no such associated type: {name}"),
};
let projection_ty = match item_name {
sym::CallOnceFuture | sym::Output => {
ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.inputs()[0]])
}
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
);
(
ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[self_ty, sig.tupled_inputs_ty],
),
coroutine_ty.into(),
)
}
sym::CallMutFuture | sym::CallFuture => {
let tupled_upvars_ty = Ty::new_projection(
tcx,
upvars_projection_def_id,
[
ty::GenericArg::from(kind_ty),
Ty::from_closure_kind(tcx, goal_kind).into(),
env_region.into(),
sig.tupled_inputs_ty.into(),
args.tupled_upvars_ty().into(),
args.coroutine_captures_by_ref_ty().into(),
],
);
let coroutine_ty = sig.to_coroutine(
tcx,
args.parent_args(),
Ty::from_closure_kind(tcx, goal_kind),
tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
);
(
ty::AliasTy::new(
tcx,
obligation.predicate.def_id,
[
ty::GenericArg::from(self_ty),
sig.tupled_inputs_ty.into(),
env_region.into(),
],
),
coroutine_ty.into(),
)
}
sym::Output => (
ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.tupled_inputs_ty]),
sig.return_ty.into(),
),
name => bug!("no such associated type: {name}"),
};
ty::ProjectionPredicate { projection_ty, term }
});
obligation.predicate.def_id,
[ty::GenericArg::from(self_ty), sig.inputs()[0].into(), env_region.into()],
),
name => bug!("no such associated type: {name}"),
};
bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
}
_ => bug!("expected callable type for AsyncFn candidate"),
};
confirm_param_env_candidate(selcx, obligation, poly_cache_entry, true)
.with_addl_obligations(nested)

View File

@ -374,6 +374,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
}
ty::CoroutineClosure(def_id, args) => {
let is_const = self.tcx().is_const_fn_raw(def_id);
match self.infcx.closure_kind(self_ty) {
Some(closure_kind) => {
let no_borrows = match self
.infcx
.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty())
.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 {
candidates.vec.push(ClosureCandidate { is_const });
}
}
None => {
if kind == ty::ClosureKind::FnOnce {
candidates.vec.push(ClosureCandidate { is_const });
} else {
// This stays ambiguous until kind+upvars are determined.
candidates.ambiguous = true;
}
}
}
}
ty::Infer(ty::TyVar(_)) => {
debug!("assemble_unboxed_closure_candidates: ambiguous self-type");
candidates.ambiguous = true;
@ -403,8 +440,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
candidates.vec.push(AsyncClosureCandidate);
}
ty::Infer(ty::TyVar(_)) => {
candidates.ambiguous = true;
// Closures and fn pointers implement `AsyncFn*` if their return types
// implement `Future`, which is checked later.
ty::Closure(_, args) => {
if let Some(closure_kind) = args.as_closure().kind_ty().to_opt_closure_kind()
&& !closure_kind.extends(goal_kind)
{
return;
}
candidates.vec.push(AsyncClosureCandidate);
}
ty::FnDef(..) | ty::FnPtr(..) => {
candidates.vec.push(AsyncClosureCandidate);
}
_ => {}
}

View File

@ -872,17 +872,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// touch bound regions, they just capture the in-scope
// type/region parameters.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
let ty::Closure(closure_def_id, args) = *self_ty.kind() else {
bug!("closure candidate for non-closure {:?}", obligation);
let trait_ref = match *self_ty.kind() {
ty::Closure(_, args) => {
self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_)
}
ty::CoroutineClosure(_, args) => {
args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
ty::TraitRef::new(
self.tcx(),
obligation.predicate.def_id(),
[self_ty, sig.tupled_inputs_ty],
)
})
}
_ => {
bug!("closure candidate for non-closure {:?}", obligation);
}
};
let trait_ref =
self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_);
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");
Ok(nested)
self.confirm_poly_trait_refs(obligation, trait_ref)
}
#[instrument(skip(self), level = "debug")]
@ -890,40 +898,86 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
obligation: &PolyTraitObligation<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// Okay to skip binder because the args on closure types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
let tcx = self.tcx();
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
let ty::CoroutineClosure(closure_def_id, args) = *self_ty.kind() else {
bug!("async closure candidate for non-coroutine-closure {:?}", obligation);
let mut nested = vec![];
let (trait_ref, kind_ty) = match *self_ty.kind() {
ty::CoroutineClosure(_, args) => {
let args = args.as_coroutine_closure();
let trait_ref = args.coroutine_closure_sig().map_bound(|sig| {
ty::TraitRef::new(
self.tcx(),
obligation.predicate.def_id(),
[self_ty, sig.tupled_inputs_ty],
)
});
(trait_ref, args.kind_ty())
}
ty::FnDef(..) | ty::FnPtr(..) => {
let sig = self_ty.fn_sig(tcx);
let trait_ref = sig.map_bound(|sig| {
ty::TraitRef::new(
self.tcx(),
obligation.predicate.def_id(),
[self_ty, Ty::new_tup(tcx, sig.inputs())],
)
});
// We must additionally check that the return type impls `Future`.
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
nested.push(obligation.with(
tcx,
sig.map_bound(|sig| {
ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])
}),
));
(trait_ref, Ty::from_closure_kind(tcx, ty::ClosureKind::Fn))
}
ty::Closure(_, args) => {
let sig = args.as_closure().sig();
let trait_ref = sig.map_bound(|sig| {
ty::TraitRef::new(
self.tcx(),
obligation.predicate.def_id(),
[self_ty, sig.inputs()[0]],
)
});
// We must additionally check that the return type impls `Future`.
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
nested.push(obligation.with(
tcx,
sig.map_bound(|sig| {
ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])
}),
));
(trait_ref, Ty::from_closure_kind(tcx, ty::ClosureKind::Fn))
}
_ => bug!("expected callable type for AsyncFn candidate"),
};
let trait_ref = args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
ty::TraitRef::new(
self.tcx(),
obligation.predicate.def_id(),
[self_ty, sig.tupled_inputs_ty],
)
});
let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
nested.extend(self.confirm_poly_trait_refs(obligation, trait_ref)?);
let goal_kind =
self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap();
nested.push(obligation.with(
self.tcx(),
ty::TraitRef::from_lang_item(
self.tcx(),
LangItem::AsyncFnKindHelper,
obligation.cause.span,
[
args.as_coroutine_closure().kind_ty(),
Ty::from_closure_kind(self.tcx(), goal_kind),
],
),
));
debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");
// If we have not yet determiend the `ClosureKind` of the closure or coroutine-closure,
// then additionally register an `AsyncFnKindHelper` goal which will fail if the kind
// is constrained to an insufficient type later on.
if let Some(closure_kind) = self.infcx.shallow_resolve(kind_ty).to_opt_closure_kind() {
if !closure_kind.extends(goal_kind) {
return Err(SelectionError::Unimplemented);
}
} else {
nested.push(obligation.with(
self.tcx(),
ty::TraitRef::from_lang_item(
self.tcx(),
LangItem::AsyncFnKindHelper,
obligation.cause.span,
[kind_ty, Ty::from_closure_kind(self.tcx(), goal_kind)],
),
));
}
Ok(nested)
}

View File

@ -278,6 +278,24 @@ fn resolve_associated_item<'tcx>(
def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)),
args: rcvr_args,
}),
ty::CoroutineClosure(coroutine_closure_def_id, args) => {
// When a coroutine-closure implements the `Fn` traits, then it
// always dispatches to the `FnOnce` implementation. This is to
// ensure that the `closure_kind` of the resulting closure is in
// sync with the built-in trait implementations (since all of the
// implementations return `FnOnce::Output`).
if ty::ClosureKind::FnOnce == args.as_coroutine_closure().kind() {
Some(Instance::new(coroutine_closure_def_id, args))
} else {
Some(Instance {
def: ty::InstanceDef::ConstructCoroutineInClosureShim {
coroutine_closure_def_id,
target_kind: ty::ClosureKind::FnOnce,
},
args,
})
}
}
_ => bug!(
"no built-in definition for `{trait_ref}::{}` for non-fn type",
tcx.item_name(trait_item_id)
@ -306,6 +324,19 @@ fn resolve_associated_item<'tcx>(
Some(Instance::new(coroutine_closure_def_id, args))
}
}
ty::Closure(closure_def_id, args) => {
let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
Some(Instance::resolve_closure(
tcx,
closure_def_id,
args,
trait_closure_kind,
))
}
ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)),
args: rcvr_args,
}),
_ => bug!(
"no built-in definition for `{trait_ref}::{}` for non-lending-closure type",
tcx.item_name(trait_item_id)

View File

@ -159,6 +159,7 @@ use core::iter::FusedIterator;
use core::marker::Tuple;
use core::marker::Unsize;
use core::mem::{self, SizedTypeProperties};
use core::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce};
use core::ops::{
CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, DispatchFromDyn, Receiver,
};
@ -2030,6 +2031,34 @@ impl<Args: Tuple, F: Fn<Args> + ?Sized, A: Allocator> Fn<Args> for Box<F, A> {
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<Args: Tuple, F: AsyncFnOnce<Args> + ?Sized, A: Allocator> AsyncFnOnce<Args> for Box<F, A> {
type Output = F::Output;
type CallOnceFuture = F::CallOnceFuture;
extern "rust-call" fn async_call_once(self, args: Args) -> Self::CallOnceFuture {
F::async_call_once(*self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<Args: Tuple, F: AsyncFnMut<Args> + ?Sized, A: Allocator> AsyncFnMut<Args> for Box<F, A> {
type CallMutFuture<'a> = F::CallMutFuture<'a> where Self: 'a;
extern "rust-call" fn async_call_mut(&mut self, args: Args) -> Self::CallMutFuture<'_> {
F::async_call_mut(self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<Args: Tuple, F: AsyncFn<Args> + ?Sized, A: Allocator> AsyncFn<Args> for Box<F, A> {
type CallFuture<'a> = F::CallFuture<'a> where Self: 'a;
extern "rust-call" fn async_call(&self, args: Args) -> Self::CallFuture<'_> {
F::async_call(self, args)
}
}
#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {}

View File

@ -106,6 +106,7 @@
#![feature(array_windows)]
#![feature(ascii_char)]
#![feature(assert_matches)]
#![feature(async_fn_traits)]
#![feature(async_iterator)]
#![feature(coerce_unsized)]
#![feature(const_align_of_val)]

View File

@ -65,44 +65,67 @@ pub trait AsyncFnOnce<Args: Tuple> {
mod impls {
use super::{AsyncFn, AsyncFnMut, AsyncFnOnce};
use crate::future::Future;
use crate::marker::Tuple;
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<F: Fn<A>, A: Tuple> AsyncFn<A> for F
impl<A: Tuple, F: ?Sized> AsyncFn<A> for &F
where
<F as FnOnce<A>>::Output: Future,
F: AsyncFn<A>,
{
type CallFuture<'a> = <F as FnOnce<A>>::Output where Self: 'a;
type CallFuture<'a> = F::CallFuture<'a> where Self: 'a;
extern "rust-call" fn async_call(&self, args: A) -> Self::CallFuture<'_> {
self.call(args)
F::async_call(*self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<F: FnMut<A>, A: Tuple> AsyncFnMut<A> for F
impl<A: Tuple, F: ?Sized> AsyncFnMut<A> for &F
where
<F as FnOnce<A>>::Output: Future,
F: AsyncFn<A>,
{
type CallMutFuture<'a> = <F as FnOnce<A>>::Output where Self: 'a;
type CallMutFuture<'a> = F::CallFuture<'a> where Self: 'a;
extern "rust-call" fn async_call_mut(&mut self, args: A) -> Self::CallMutFuture<'_> {
self.call_mut(args)
F::async_call(*self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<F: FnOnce<A>, A: Tuple> AsyncFnOnce<A> for F
impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce<A> for &'a F
where
<F as FnOnce<A>>::Output: Future,
F: AsyncFn<A>,
{
type CallOnceFuture = <F as FnOnce<A>>::Output;
type Output = <<F as FnOnce<A>>::Output as Future>::Output;
type Output = F::Output;
type CallOnceFuture = F::CallFuture<'a>;
extern "rust-call" fn async_call_once(self, args: A) -> Self::CallOnceFuture {
self.call_once(args)
F::async_call(self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<A: Tuple, F: ?Sized> AsyncFnMut<A> for &mut F
where
F: AsyncFnMut<A>,
{
type CallMutFuture<'a> = F::CallMutFuture<'a> where Self: 'a;
extern "rust-call" fn async_call_mut(&mut self, args: A) -> Self::CallMutFuture<'_> {
F::async_call_mut(*self, args)
}
}
#[unstable(feature = "async_fn_traits", issue = "none")]
impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce<A> for &'a mut F
where
F: AsyncFnMut<A>,
{
type Output = F::Output;
type CallOnceFuture = F::CallMutFuture<'a>;
extern "rust-call" fn async_call_once(self, args: A) -> Self::CallOnceFuture {
F::async_call_mut(self, args)
}
}
}

View File

@ -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 `()`
}

View File

@ -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`.

View File

@ -0,0 +1,22 @@
// aux-build:block-on.rs
// edition:2021
// build-pass
#![feature(async_closure)]
use std::future::Future;
extern crate block_on;
struct NoCopy;
fn main() {
block_on::block_on(async {
async fn call_once<F: Future>(x: impl Fn(&'static str) -> F) -> F::Output {
x("hello, world").await
}
call_once(async |x: &'static str| {
println!("hello, {x}");
}).await
});
}

View File

@ -0,0 +1,18 @@
// aux-build:block-on.rs
// edition:2021
// build-pass
// check that `&{async-closure}` implements `AsyncFn`.
#![feature(async_closure)]
extern crate block_on;
struct NoCopy;
fn main() {
block_on::block_on(async {
async fn call_once(x: impl async Fn()) { x().await }
call_once(&async || {}).await
});
}

View File

@ -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

View File

@ -1,5 +1,5 @@
// edition: 2021
// check-pass
// build-pass
#![feature(async_fn_traits)]

View File

@ -191,14 +191,7 @@ error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:33:10
|
LL | type H = Fn(u8) -> (u8)::Output;
| ^^^^^^^^^^^^^^^^^^^^^^
|
help: use fully-qualified syntax
|
LL | type H = <(dyn Fn(u8) -> u8 + 'static) as AsyncFnOnce>::Output;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LL | type H = <(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output`
error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:39:19

View File

@ -13,8 +13,8 @@ LL | type A<'a> where Self: 'a;
| ^ ...because it contains the generic associated type `A`
= help: consider moving `A` to another trait
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Foo` for this new enum and using it instead:
Fooy
Fooer<T>
Fooy
error: aborting due to 1 previous error

View File

@ -29,8 +29,8 @@ LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a;
| ^^^^^^^^ ...because it contains the generic associated type `VRefCont`
= help: consider moving `VRefCont` to another trait
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `MapLike` for this new enum and using it instead:
std::collections::BTreeMap<K, V>
Source
std::collections::BTreeMap<K, V>
error[E0038]: the trait `MapLike` cannot be made into an object
--> $DIR/issue-79422.rs:44:13
@ -47,8 +47,8 @@ LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a;
| ^^^^^^^^ ...because it contains the generic associated type `VRefCont`
= help: consider moving `VRefCont` to another trait
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `MapLike` for this new enum and using it instead:
std::collections::BTreeMap<K, V>
Source
std::collections::BTreeMap<K, V>
= note: required for the cast from `Box<BTreeMap<u8, u8>>` to `Box<dyn MapLike<u8, u8, VRefCont = (dyn RefCont<'_, u8> + 'static)>>`
error: aborting due to 3 previous errors

View File

@ -30,8 +30,8 @@ LL | trait Trait: Sized {}
| |
| this trait cannot be made into an object...
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Trait` for this new enum and using it instead:
S
R
S
= note: required for the cast from `&S` to `&dyn Trait`
error[E0038]: the trait `Trait` cannot be made into an object
@ -52,8 +52,8 @@ LL | trait Trait: Sized {}
| |
| this trait cannot be made into an object...
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Trait` for this new enum and using it instead:
S
R
S
= note: required for the cast from `&R` to `&dyn Trait`
error: aborting due to 3 previous errors