Skip to content

Commit 102b458

Browse files
Support {async closure}: Fn in new solver
1 parent fc3800f commit 102b458

File tree

3 files changed

+101
-3
lines changed

3 files changed

+101
-3
lines changed

compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs

+73-1
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,79 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
281281
}
282282

283283
// Coroutine-closures don't implement `Fn` traits the normal way.
284-
ty::CoroutineClosure(..) => Err(NoSolution),
284+
// Instead, they always implement `FnOnce`, but only implement
285+
// `FnMut`/`Fn` if they capture no upvars, since those may borrow
286+
// from the closure.
287+
ty::CoroutineClosure(def_id, args) => {
288+
let args = args.as_coroutine_closure();
289+
let kind_ty = args.kind_ty();
290+
let sig = args.coroutine_closure_sig().skip_binder();
291+
292+
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
293+
if !closure_kind.extends(goal_kind) {
294+
return Err(NoSolution);
295+
}
296+
297+
// If `Fn`/`FnMut`, we only implement this goal if we
298+
// have no captures.
299+
let no_borrows = match args.tupled_upvars_ty().kind() {
300+
ty::Tuple(tys) => tys.is_empty(),
301+
ty::Error(_) => false,
302+
_ => bug!("tuple_fields called on non-tuple"),
303+
};
304+
if closure_kind != ty::ClosureKind::FnOnce && !no_borrows {
305+
return Err(NoSolution);
306+
}
307+
308+
sig.to_coroutine_given_kind_and_upvars(
309+
tcx,
310+
args.parent_args(),
311+
tcx.coroutine_for_closure(def_id),
312+
goal_kind,
313+
// No captures by ref, so this doesn't matter.
314+
tcx.lifetimes.re_static,
315+
args.tupled_upvars_ty(),
316+
args.coroutine_captures_by_ref_ty(),
317+
)
318+
} else {
319+
// Closure kind is not yet determined, so we return ambiguity unless
320+
// the expected kind is `FnOnce` as that is always implemented.
321+
if goal_kind != ty::ClosureKind::FnOnce {
322+
return Ok(None);
323+
}
324+
325+
let async_fn_kind_trait_def_id =
326+
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
327+
let upvars_projection_def_id = tcx
328+
.associated_items(async_fn_kind_trait_def_id)
329+
.filter_by_name_unhygienic(sym::Upvars)
330+
.next()
331+
.unwrap()
332+
.def_id;
333+
let tupled_upvars_ty = Ty::new_projection(
334+
tcx,
335+
upvars_projection_def_id,
336+
[
337+
ty::GenericArg::from(kind_ty),
338+
Ty::from_closure_kind(tcx, goal_kind).into(),
339+
// No captures by ref, so this doesn't matter.
340+
tcx.lifetimes.re_static.into(),
341+
sig.tupled_inputs_ty.into(),
342+
args.tupled_upvars_ty().into(),
343+
args.coroutine_captures_by_ref_ty().into(),
344+
],
345+
);
346+
sig.to_coroutine(
347+
tcx,
348+
args.parent_args(),
349+
Ty::from_closure_kind(tcx, goal_kind),
350+
tcx.coroutine_for_closure(def_id),
351+
tupled_upvars_ty,
352+
)
353+
};
354+
355+
Ok(Some(args.coroutine_closure_sig().rebind((sig.tupled_inputs_ty, coroutine_ty))))
356+
}
285357

286358
ty::Bool
287359
| ty::Char
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ aux-build:block-on.rs
2+
//@ edition:2021
3+
//@ build-pass
4+
//@ revisions: current next
5+
//@[next] compile-flags: -Znext-solver
6+
7+
#![feature(async_closure)]
8+
9+
use std::future::Future;
10+
11+
extern crate block_on;
12+
13+
// Check that closures that don't capture any state may implement `Fn`.
14+
15+
fn main() {
16+
block_on::block_on(async {
17+
async fn call_once<F: Future>(x: impl FnOnce(&'static str) -> F) -> F::Output {
18+
x("hello, world").await
19+
}
20+
call_once(async |x: &'static str| {
21+
println!("hello, {x}");
22+
}).await
23+
});
24+
}

tests/ui/async-await/async-closures/once.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
//@ aux-build:block-on.rs
22
//@ edition:2021
33
//@ build-pass
4+
//@ revisions: current next
5+
//@[next] compile-flags: -Znext-solver
46

57
#![feature(async_closure)]
68

79
use std::future::Future;
810

911
extern crate block_on;
1012

11-
struct NoCopy;
13+
// Check that async closures always implement `FnOnce`
1214

1315
fn main() {
1416
block_on::block_on(async {
15-
async fn call_once<F: Future>(x: impl Fn(&'static str) -> F) -> F::Output {
17+
async fn call_once<F: Future>(x: impl FnOnce(&'static str) -> F) -> F::Output {
1618
x("hello, world").await
1719
}
1820
call_once(async |x: &'static str| {

0 commit comments

Comments
 (0)