Support {async closure}: Fn
in new solver
This commit is contained in:
parent
b6e4299415
commit
a6727bad88
3 changed files with 101 additions and 3 deletions
|
@ -281,7 +281,79 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Coroutine-closures don't implement `Fn` traits the normal way.
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
sig.to_coroutine_given_kind_and_upvars(
|
||||||
|
tcx,
|
||||||
|
args.parent_args(),
|
||||||
|
tcx.coroutine_for_closure(def_id),
|
||||||
|
goal_kind,
|
||||||
|
// No captures by ref, so this doesn't matter.
|
||||||
|
tcx.lifetimes.re_static,
|
||||||
|
args.tupled_upvars_ty(),
|
||||||
|
args.coroutine_captures_by_ref_ty(),
|
||||||
|
)
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, goal_kind).into(),
|
||||||
|
// No captures by ref, so this doesn't matter.
|
||||||
|
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, goal_kind),
|
||||||
|
tcx.coroutine_for_closure(def_id),
|
||||||
|
tupled_upvars_ty,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(args.coroutine_closure_sig().rebind((sig.tupled_inputs_ty, coroutine_ty))))
|
||||||
|
}
|
||||||
|
|
||||||
ty::Bool
|
ty::Bool
|
||||||
| ty::Char
|
| ty::Char
|
||||||
|
|
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
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
//@ aux-build:block-on.rs
|
//@ aux-build:block-on.rs
|
||||||
//@ edition:2021
|
//@ edition:2021
|
||||||
//@ build-pass
|
//@ build-pass
|
||||||
|
//@ revisions: current next
|
||||||
|
//@[next] compile-flags: -Znext-solver
|
||||||
|
|
||||||
#![feature(async_closure)]
|
#![feature(async_closure)]
|
||||||
|
|
||||||
|
@ -8,11 +10,11 @@ use std::future::Future;
|
||||||
|
|
||||||
extern crate block_on;
|
extern crate block_on;
|
||||||
|
|
||||||
struct NoCopy;
|
// Check that async closures always implement `FnOnce`
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
block_on::block_on(async {
|
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
|
x("hello, world").await
|
||||||
}
|
}
|
||||||
call_once(async |x: &'static str| {
|
call_once(async |x: &'static str| {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue