Rollup merge of #121654 - compiler-errors:async-fn-for-fn-def, r=oli-obk
Fix `async Fn` confirmation for `FnDef`/`FnPtr`/`Closure` types Fixes three issues: 1. The code in `extract_tupled_inputs_and_output_from_async_callable` was accidentally getting the *future* type and the *output* type (returned by the future) messed up for fnptr/fndef/closure types. :/ 2. We have a (class of) bug(s) in the old solver where we don't really support higher ranked built-in `Future` goals for generators. This is not possible to hit on stable code, but [can be hit with `unboxed_closures`](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=e935de7181e37e13515ad01720bcb899) (#121653). * I'm opting not to fix that in this PR. Instead, I just instantiate placeholders when confirming `async Fn` goals. 4. Fixed a bug when generating `FnPtr` shims for `async Fn` trait goals. r? oli-obk
This commit is contained in:
commit
5978b6ff83
@ -37,7 +37,11 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
|
||||
}
|
||||
ty::InstanceDef::FnPtrShim(def_id, ty) => {
|
||||
let trait_ = tcx.trait_of_item(def_id).unwrap();
|
||||
let adjustment = match tcx.fn_trait_kind_from_def_id(trait_) {
|
||||
// Supports `Fn` or `async Fn` traits.
|
||||
let adjustment = match tcx
|
||||
.fn_trait_kind_from_def_id(trait_)
|
||||
.or_else(|| tcx.async_fn_trait_kind_from_def_id(trait_))
|
||||
{
|
||||
Some(ty::ClosureKind::FnOnce) => Adjustment::Identity,
|
||||
Some(ty::ClosureKind::Fn) => Adjustment::Deref { source: DerefSource::ImmRef },
|
||||
Some(ty::ClosureKind::FnMut) => Adjustment::Deref { source: DerefSource::MutRef },
|
||||
|
@ -281,7 +281,58 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
|
||||
}
|
||||
|
||||
// Coroutine-closures don't implement `Fn` traits the normal way.
|
||||
ty::CoroutineClosure(..) => Err(NoSolution),
|
||||
// Instead, they always implement `FnOnce`, but only implement
|
||||
// `FnMut`/`Fn` if they capture no upvars, since those may borrow
|
||||
// from the closure.
|
||||
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();
|
||||
|
||||
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
|
||||
if !closure_kind.extends(goal_kind) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// If `Fn`/`FnMut`, we only implement this goal if we
|
||||
// have no captures.
|
||||
let no_borrows = match args.tupled_upvars_ty().kind() {
|
||||
ty::Tuple(tys) => tys.is_empty(),
|
||||
ty::Error(_) => false,
|
||||
_ => bug!("tuple_fields called on non-tuple"),
|
||||
};
|
||||
if closure_kind != ty::ClosureKind::FnOnce && !no_borrows {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
coroutine_closure_to_certain_coroutine(
|
||||
tcx,
|
||||
goal_kind,
|
||||
// No captures by ref, so this doesn't matter.
|
||||
tcx.lifetimes.re_static,
|
||||
def_id,
|
||||
args,
|
||||
sig,
|
||||
)
|
||||
} else {
|
||||
// Closure kind is not yet determined, so we return ambiguity unless
|
||||
// the expected kind is `FnOnce` as that is always implemented.
|
||||
if goal_kind != ty::ClosureKind::FnOnce {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
coroutine_closure_to_ambiguous_coroutine(
|
||||
tcx,
|
||||
goal_kind, // No captures by ref, so this doesn't matter.
|
||||
tcx.lifetimes.re_static,
|
||||
def_id,
|
||||
args,
|
||||
sig,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Some(args.coroutine_closure_sig().rebind((sig.tupled_inputs_ty, coroutine_ty))))
|
||||
}
|
||||
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
@ -313,6 +364,19 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Relevant types for an async callable, including its inputs, output,
|
||||
/// and the return type you get from awaiting the output.
|
||||
#[derive(Copy, Clone, Debug, TypeVisitable, TypeFoldable)]
|
||||
pub(in crate::solve) struct AsyncCallableRelevantTypes<'tcx> {
|
||||
pub tupled_inputs_ty: Ty<'tcx>,
|
||||
/// Type returned by calling the closure
|
||||
/// i.e. `f()`.
|
||||
pub output_coroutine_ty: Ty<'tcx>,
|
||||
/// Type returned by `await`ing the output
|
||||
/// i.e. `f().await`.
|
||||
pub coroutine_return_ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
// Returns a binder of the tupled inputs types, output type, and coroutine type
|
||||
// from a builtin coroutine-closure type. If we don't yet know the closure kind of
|
||||
// the coroutine-closure, emit an additional trait predicate for `AsyncFnKindHelper`
|
||||
@ -323,8 +387,10 @@ 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>)>, Vec<ty::Predicate<'tcx>>), NoSolution>
|
||||
{
|
||||
) -> Result<
|
||||
(ty::Binder<'tcx, AsyncCallableRelevantTypes<'tcx>>, Vec<ty::Predicate<'tcx>>),
|
||||
NoSolution,
|
||||
> {
|
||||
match *self_ty.kind() {
|
||||
ty::CoroutineClosure(def_id, args) => {
|
||||
let args = args.as_coroutine_closure();
|
||||
@ -335,24 +401,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
|
||||
if !closure_kind.extends(goal_kind) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
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(),
|
||||
|
||||
coroutine_closure_to_certain_coroutine(
|
||||
tcx, goal_kind, env_region, def_id, args, sig,
|
||||
)
|
||||
} 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
|
||||
@ -363,38 +416,23 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
|
||||
nested.push(
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
async_fn_kind_trait_def_id,
|
||||
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None),
|
||||
[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,
|
||||
|
||||
coroutine_closure_to_ambiguous_coroutine(
|
||||
tcx, goal_kind, env_region, def_id, args, sig,
|
||||
)
|
||||
};
|
||||
|
||||
Ok((
|
||||
args.coroutine_closure_sig().rebind((
|
||||
sig.tupled_inputs_ty,
|
||||
sig.return_ty,
|
||||
coroutine_ty,
|
||||
)),
|
||||
args.coroutine_closure_sig().rebind(AsyncCallableRelevantTypes {
|
||||
tupled_inputs_ty: sig.tupled_inputs_ty,
|
||||
output_coroutine_ty: coroutine_ty,
|
||||
coroutine_return_ty: sig.return_ty,
|
||||
}),
|
||||
nested,
|
||||
))
|
||||
}
|
||||
@ -418,7 +456,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
|
||||
.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)),
|
||||
bound_sig.rebind(AsyncCallableRelevantTypes {
|
||||
tupled_inputs_ty: Ty::new_tup(tcx, sig.inputs()),
|
||||
output_coroutine_ty: sig.output(),
|
||||
coroutine_return_ty: future_output_ty,
|
||||
}),
|
||||
nested,
|
||||
))
|
||||
}
|
||||
@ -469,7 +511,14 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
|
||||
.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))
|
||||
Ok((
|
||||
bound_sig.rebind(AsyncCallableRelevantTypes {
|
||||
tupled_inputs_ty: sig.inputs()[0],
|
||||
output_coroutine_ty: sig.output(),
|
||||
coroutine_return_ty: future_output_ty,
|
||||
}),
|
||||
nested,
|
||||
))
|
||||
}
|
||||
|
||||
ty::Bool
|
||||
@ -502,6 +551,68 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a coroutine-closure, project to its returned coroutine when we are *certain*
|
||||
/// that the closure's kind is compatible with the goal.
|
||||
fn coroutine_closure_to_certain_coroutine<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
goal_kind: ty::ClosureKind,
|
||||
goal_region: ty::Region<'tcx>,
|
||||
def_id: DefId,
|
||||
args: ty::CoroutineClosureArgs<'tcx>,
|
||||
sig: ty::CoroutineClosureSignature<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
sig.to_coroutine_given_kind_and_upvars(
|
||||
tcx,
|
||||
args.parent_args(),
|
||||
tcx.coroutine_for_closure(def_id),
|
||||
goal_kind,
|
||||
goal_region,
|
||||
args.tupled_upvars_ty(),
|
||||
args.coroutine_captures_by_ref_ty(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Given a coroutine-closure, project to its returned coroutine when we are *not certain*
|
||||
/// that the closure's kind is compatible with the goal, and therefore also don't know
|
||||
/// yet what the closure's upvars are.
|
||||
///
|
||||
/// Note that we do not also push a `AsyncFnKindHelper` goal here.
|
||||
fn coroutine_closure_to_ambiguous_coroutine<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
goal_kind: ty::ClosureKind,
|
||||
goal_region: ty::Region<'tcx>,
|
||||
def_id: DefId,
|
||||
args: ty::CoroutineClosureArgs<'tcx>,
|
||||
sig: ty::CoroutineClosureSignature<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
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(args.kind_ty()),
|
||||
Ty::from_closure_kind(tcx, goal_kind).into(),
|
||||
goal_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,
|
||||
)
|
||||
}
|
||||
|
||||
/// Assemble a list of predicates that would be present on a theoretical
|
||||
/// user impl for an object type. These predicates must be checked any time
|
||||
/// we assemble a built-in object candidate for an object type, since they
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::traits::{check_args_compatible, specialization_graph};
|
||||
|
||||
use super::assembly::structural_traits::AsyncCallableRelevantTypes;
|
||||
use super::assembly::{self, structural_traits, Candidate};
|
||||
use super::{EvalCtxt, GoalSource};
|
||||
use rustc_hir::def::DefKind;
|
||||
@ -392,46 +393,56 @@ fn consider_builtin_async_fn_trait_candidates(
|
||||
goal_kind,
|
||||
env_region,
|
||||
)?;
|
||||
let output_is_sized_pred =
|
||||
tupled_inputs_and_output_and_coroutine.map_bound(|(_, output, _)| {
|
||||
ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
|
||||
});
|
||||
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
|
||||
|AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| {
|
||||
ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output_ty])
|
||||
},
|
||||
);
|
||||
|
||||
let pred = tupled_inputs_and_output_and_coroutine
|
||||
.map_bound(|(inputs, output, coroutine)| {
|
||||
let (projection_ty, term) = match tcx.item_name(goal.predicate.def_id()) {
|
||||
sym::CallOnceFuture => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[goal.predicate.self_ty(), inputs],
|
||||
.map_bound(
|
||||
|AsyncCallableRelevantTypes {
|
||||
tupled_inputs_ty,
|
||||
output_coroutine_ty,
|
||||
coroutine_return_ty,
|
||||
}| {
|
||||
let (projection_ty, term) = match tcx.item_name(goal.predicate.def_id()) {
|
||||
sym::CallOnceFuture => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[goal.predicate.self_ty(), tupled_inputs_ty],
|
||||
),
|
||||
output_coroutine_ty.into(),
|
||||
),
|
||||
coroutine.into(),
|
||||
),
|
||||
sym::CallMutFuture | sym::CallFuture => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[
|
||||
ty::GenericArg::from(goal.predicate.self_ty()),
|
||||
inputs.into(),
|
||||
env_region.into(),
|
||||
],
|
||||
sym::CallMutFuture | sym::CallFuture => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[
|
||||
ty::GenericArg::from(goal.predicate.self_ty()),
|
||||
tupled_inputs_ty.into(),
|
||||
env_region.into(),
|
||||
],
|
||||
),
|
||||
output_coroutine_ty.into(),
|
||||
),
|
||||
coroutine.into(),
|
||||
),
|
||||
sym::Output => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[ty::GenericArg::from(goal.predicate.self_ty()), inputs.into()],
|
||||
sym::Output => (
|
||||
ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[
|
||||
ty::GenericArg::from(goal.predicate.self_ty()),
|
||||
tupled_inputs_ty.into(),
|
||||
],
|
||||
),
|
||||
coroutine_return_ty.into(),
|
||||
),
|
||||
output.into(),
|
||||
),
|
||||
name => bug!("no such associated type: {name}"),
|
||||
};
|
||||
ty::ProjectionPredicate { projection_ty, term }
|
||||
})
|
||||
name => bug!("no such associated type: {name}"),
|
||||
};
|
||||
ty::ProjectionPredicate { projection_ty, term }
|
||||
},
|
||||
)
|
||||
.to_predicate(tcx);
|
||||
|
||||
// A built-in `AsyncFn` impl only holds if the output is sized.
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use crate::traits::supertrait_def_ids;
|
||||
|
||||
use super::assembly::structural_traits::AsyncCallableRelevantTypes;
|
||||
use super::assembly::{self, structural_traits, Candidate};
|
||||
use super::{EvalCtxt, GoalSource, SolverMode};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
@ -327,14 +328,19 @@ fn consider_builtin_async_fn_trait_candidates(
|
||||
// This region doesn't matter because we're throwing away the coroutine type
|
||||
tcx.lifetimes.re_static,
|
||||
)?;
|
||||
let output_is_sized_pred =
|
||||
tupled_inputs_and_output_and_coroutine.map_bound(|(_, output, _)| {
|
||||
ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
|
||||
});
|
||||
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
|
||||
|AsyncCallableRelevantTypes { output_coroutine_ty, .. }| {
|
||||
ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output_coroutine_ty])
|
||||
},
|
||||
);
|
||||
|
||||
let pred = tupled_inputs_and_output_and_coroutine
|
||||
.map_bound(|(inputs, _, _)| {
|
||||
ty::TraitRef::new(tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
|
||||
.map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| {
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[goal.predicate.self_ty(), tupled_inputs_ty],
|
||||
)
|
||||
})
|
||||
.to_predicate(tcx);
|
||||
// A built-in `AsyncFn` impl only holds if the output is sized.
|
||||
|
@ -923,14 +923,22 @@ fn confirm_async_closure_candidate(
|
||||
[self_ty, Ty::new_tup(tcx, sig.inputs())],
|
||||
)
|
||||
});
|
||||
|
||||
// We must additionally check that the return type impls `Future`.
|
||||
|
||||
// FIXME(async_closures): Investigate this before stabilization.
|
||||
// We instantiate this binder eagerly because the `confirm_future_candidate`
|
||||
// method doesn't support higher-ranked futures, which the `AsyncFn`
|
||||
// traits expressly allow the user to write. To fix this correctly,
|
||||
// we'd need to instantiate trait bounds before we get to selection,
|
||||
// like the new trait solver does.
|
||||
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
|
||||
let placeholder_output_ty = self.infcx.enter_forall_and_leak_universe(sig.output());
|
||||
nested.push(obligation.with(
|
||||
tcx,
|
||||
sig.map_bound(|sig| {
|
||||
ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])
|
||||
}),
|
||||
ty::TraitRef::new(tcx, future_trait_def_id, [placeholder_output_ty]),
|
||||
));
|
||||
|
||||
(trait_ref, Ty::from_closure_kind(tcx, ty::ClosureKind::Fn))
|
||||
}
|
||||
ty::Closure(_, args) => {
|
||||
@ -943,14 +951,16 @@ fn confirm_async_closure_candidate(
|
||||
[self_ty, sig.inputs()[0]],
|
||||
)
|
||||
});
|
||||
|
||||
// We must additionally check that the return type impls `Future`.
|
||||
// See FIXME in last branch for why we instantiate the binder eagerly.
|
||||
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
|
||||
let placeholder_output_ty = self.infcx.enter_forall_and_leak_universe(sig.output());
|
||||
nested.push(obligation.with(
|
||||
tcx,
|
||||
sig.map_bound(|sig| {
|
||||
ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()])
|
||||
}),
|
||||
ty::TraitRef::new(tcx, future_trait_def_id, [placeholder_output_ty]),
|
||||
));
|
||||
|
||||
(trait_ref, args.kind_ty())
|
||||
}
|
||||
_ => bug!("expected callable type for AsyncFn candidate"),
|
||||
|
@ -8,11 +8,15 @@
|
||||
|
||||
fn main() {
|
||||
block_on::block_on(async {
|
||||
let x = async || {};
|
||||
|
||||
async fn needs_async_fn_once(x: impl async FnOnce()) {
|
||||
x().await;
|
||||
}
|
||||
needs_async_fn_once(x).await;
|
||||
|
||||
needs_async_fn_once(async || {}).await;
|
||||
|
||||
needs_async_fn_once(|| async {}).await;
|
||||
|
||||
async fn foo() {}
|
||||
needs_async_fn_once(foo).await;
|
||||
});
|
||||
}
|
||||
|
24
tests/ui/async-await/async-closures/is-fn.rs
Normal file
24
tests/ui/async-await/async-closures/is-fn.rs
Normal file
@ -0,0 +1,24 @@
|
||||
//@ aux-build:block-on.rs
|
||||
//@ edition:2021
|
||||
//@ build-pass
|
||||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
#![feature(async_closure)]
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
extern crate block_on;
|
||||
|
||||
// Check that closures that don't capture any state may implement `Fn`.
|
||||
|
||||
fn main() {
|
||||
block_on::block_on(async {
|
||||
async fn call_once<F: Future>(x: impl FnOnce(&'static str) -> F) -> F::Output {
|
||||
x("hello, world").await
|
||||
}
|
||||
call_once(async |x: &'static str| {
|
||||
println!("hello, {x}");
|
||||
}).await
|
||||
});
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
//@ edition:2021
|
||||
//@ build-pass
|
||||
|
||||
#![feature(async_closure)]
|
||||
#![feature(async_closure, async_fn_traits)]
|
||||
|
||||
extern crate block_on;
|
||||
|
||||
@ -15,7 +15,11 @@ fn main() {
|
||||
c().await;
|
||||
c().await;
|
||||
|
||||
fn is_static<T: 'static>(_: T) {}
|
||||
is_static(c);
|
||||
fn is_static<T: 'static>(_: &T) {}
|
||||
is_static(&c);
|
||||
|
||||
// Check that `<{async fn} as AsyncFnOnce>::CallOnceFuture` owns its captures.
|
||||
fn call_once<F: async FnOnce()>(f: F) -> F::CallOnceFuture { f() }
|
||||
is_static(&call_once(c));
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
//@ aux-build:block-on.rs
|
||||
//@ edition:2021
|
||||
//@ build-pass
|
||||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
#![feature(async_closure)]
|
||||
|
||||
@ -8,11 +10,11 @@
|
||||
|
||||
extern crate block_on;
|
||||
|
||||
struct NoCopy;
|
||||
// Check that async closures always implement `FnOnce`
|
||||
|
||||
fn main() {
|
||||
block_on::block_on(async {
|
||||
async fn call_once<F: Future>(x: impl Fn(&'static str) -> F) -> F::Output {
|
||||
async fn call_once<F: Future>(x: impl FnOnce(&'static str) -> F) -> F::Output {
|
||||
x("hello, world").await
|
||||
}
|
||||
call_once(async |x: &'static str| {
|
||||
|
@ -2,8 +2,6 @@
|
||||
//@ edition:2021
|
||||
//@ build-pass
|
||||
|
||||
// check that `&{async-closure}` implements `AsyncFn`.
|
||||
|
||||
#![feature(async_closure)]
|
||||
|
||||
extern crate block_on;
|
||||
@ -13,6 +11,15 @@
|
||||
fn main() {
|
||||
block_on::block_on(async {
|
||||
async fn call_once(x: impl async Fn()) { x().await }
|
||||
call_once(&async || {}).await
|
||||
|
||||
// check that `&{async-closure}` implements `async Fn`.
|
||||
call_once(&async || {}).await;
|
||||
|
||||
// check that `&{closure}` implements `async Fn`.
|
||||
call_once(&|| async {}).await;
|
||||
|
||||
// check that `&fndef` implements `async Fn`.
|
||||
async fn foo() {}
|
||||
call_once(&foo).await;
|
||||
});
|
||||
}
|
||||
|
31
tests/ui/async-await/async-fn/higher-ranked-async-fn.rs
Normal file
31
tests/ui/async-await/async-fn/higher-ranked-async-fn.rs
Normal file
@ -0,0 +1,31 @@
|
||||
//@ aux-build:block-on.rs
|
||||
//@ edition:2018
|
||||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ build-pass (since it ICEs during mono)
|
||||
|
||||
#![feature(async_closure)]
|
||||
|
||||
extern crate block_on;
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
async fn f(arg: &i32) {}
|
||||
|
||||
async fn func<F>(f: F)
|
||||
where
|
||||
F: async for<'a> Fn(&'a i32),
|
||||
{
|
||||
let x: i32 = 0;
|
||||
f(&x).await;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
block_on::block_on(async {
|
||||
// Function
|
||||
func(f).await;
|
||||
|
||||
// Regular closure (doesn't capture)
|
||||
func(|x: &i32| async {});
|
||||
});
|
||||
}
|
12
tests/ui/async-await/async-fn/project.rs
Normal file
12
tests/ui/async-await/async-fn/project.rs
Normal file
@ -0,0 +1,12 @@
|
||||
//@ edition:2018
|
||||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ check-pass
|
||||
|
||||
#![feature(async_closure, unboxed_closures, async_fn_traits)]
|
||||
|
||||
fn project<F: async Fn<()>>(_: F) -> Option<F::Output> { None }
|
||||
|
||||
fn main() {
|
||||
let x: Option<i32> = project(|| async { 1i32 });
|
||||
}
|
Loading…
Reference in New Issue
Block a user