Skip to content

Commit 3851fe0

Browse files
Make coroutine-closures possible to be cloned
1 parent 2d8c215 commit 3851fe0

File tree

8 files changed

+64
-21
lines changed

8 files changed

+64
-21
lines changed

Diff for: compiler/rustc_mir_transform/src/shim.rs

+3
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,9 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
434434
match self_ty.kind() {
435435
ty::FnDef(..) | ty::FnPtr(_) => builder.copy_shim(),
436436
ty::Closure(_, args) => builder.tuple_like_shim(dest, src, args.as_closure().upvar_tys()),
437+
ty::CoroutineClosure(_, args) => {
438+
builder.tuple_like_shim(dest, src, args.as_coroutine_closure().upvar_tys())
439+
}
437440
ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
438441
ty::Coroutine(coroutine_def_id, args) => {
439442
assert_eq!(tcx.coroutine_movability(*coroutine_def_id), hir::Movability::Movable);

Diff for: compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ where
216216
// impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone
217217
ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
218218

219-
ty::CoroutineClosure(..) => Err(NoSolution),
219+
// impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone
220+
ty::CoroutineClosure(_, args) => {
221+
Ok(vec![ty::Binder::dummy(args.as_coroutine_closure().tupled_upvars_ty())])
222+
}
220223

221224
// only when `coroutine_clone` is enabled and the coroutine is movable
222225
// impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses)

Diff for: compiler/rustc_trait_selection/src/traits/select/mod.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -2262,8 +2262,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
22622262
}
22632263
}
22642264

2265-
// FIXME(async_closures): These are never clone, for now.
2266-
ty::CoroutineClosure(_, _) => None,
2265+
ty::CoroutineClosure(_, args) => {
2266+
// (*) binder moved here
2267+
let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty());
2268+
if let ty::Infer(ty::TyVar(_)) = ty.kind() {
2269+
// Not yet resolved.
2270+
Ambiguous
2271+
} else {
2272+
Where(
2273+
obligation
2274+
.predicate
2275+
.rebind(args.as_coroutine_closure().upvar_tys().to_vec()),
2276+
)
2277+
}
2278+
}
2279+
22672280
// `Copy` and `Clone` are automatically implemented for an anonymous adt
22682281
// if all of its fields are `Copy` and `Clone`
22692282
ty::Adt(adt, args) if adt.is_anonymous() => {

Diff for: tests/ui/async-await/async-closures/clone-closure.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ aux-build:block-on.rs
2+
//@ edition:2021
3+
//@ run-pass
4+
//@ check-run-results
5+
6+
#![feature(async_closure)]
7+
8+
extern crate block_on;
9+
10+
async fn for_each(f: impl async FnOnce(&str) + Clone) {
11+
f.clone()("world").await;
12+
f.clone()("world2").await;
13+
}
14+
15+
fn main() {
16+
block_on::block_on(async_main());
17+
}
18+
19+
async fn async_main() {
20+
let x = String::from("Hello,");
21+
for_each(async move |s| {
22+
println!("{x} {s}");
23+
}).await;
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Hello, world
2+
Hello, world2

Diff for: tests/ui/async-await/async-closures/move-consuming-capture.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ LL | x().await;
1111
|
1212
note: `async_call_once` takes ownership of the receiver `self`, which moves `x`
1313
--> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
14+
help: you could `clone` the value and consume it, if the `NoCopy: Clone` trait bound could be satisfied
15+
|
16+
LL | x.clone()().await;
17+
| ++++++++
18+
help: consider annotating `NoCopy` with `#[derive(Clone)]`
19+
|
20+
LL + #[derive(Clone)]
21+
LL | struct NoCopy;
22+
|
1423

1524
error: aborting due to 1 previous error
1625

Diff for: tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn simple<'a>(x: &'a i32) {
1919

2020
let c = async move || { println!("{}", *x); };
2121
outlives::<'a>(c()); //~ ERROR `c` does not live long enough
22-
outlives::<'a>(call_once(c)); //~ ERROR cannot move out of `c`
22+
outlives::<'a>(call_once(c));
2323
}
2424

2525
struct S<'a>(&'a i32);

Diff for: tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr

+6-17
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,6 @@ LL | outlives::<'a>(call_once(c));
2929
LL | }
3030
| - `c` dropped here while still borrowed
3131

32-
error[E0505]: cannot move out of `c` because it is borrowed
33-
--> $DIR/without-precise-captures-we-are-powerless.rs:22:30
34-
|
35-
LL | fn simple<'a>(x: &'a i32) {
36-
| -- lifetime `'a` defined here
37-
...
38-
LL | let c = async move || { println!("{}", *x); };
39-
| - binding `c` declared here
40-
LL | outlives::<'a>(c());
41-
| ---
42-
| |
43-
| borrow of `c` occurs here
44-
| argument requires that `c` is borrowed for `'a`
45-
LL | outlives::<'a>(call_once(c));
46-
| ^ move out of `c` occurs here
47-
4832
error[E0597]: `x` does not live long enough
4933
--> $DIR/without-precise-captures-we-are-powerless.rs:28:13
5034
|
@@ -72,6 +56,11 @@ LL | outlives::<'a>(call_once(c));
7256
LL |
7357
LL | let c = async move || { println!("{}", *x.0); };
7458
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here
59+
|
60+
help: consider cloning the value if the performance cost is acceptable
61+
|
62+
LL | let c = async || { println!("{}", *x.0); }.clone();
63+
| ++++++++
7564

7665
error[E0597]: `c` does not live long enough
7766
--> $DIR/without-precise-captures-we-are-powerless.rs:33:20
@@ -146,7 +135,7 @@ LL | // outlives::<'a>(call_once(c)); // FIXME(async_closures): Figure out w
146135
LL | }
147136
| - `c` dropped here while still borrowed
148137

149-
error: aborting due to 10 previous errors
138+
error: aborting due to 9 previous errors
150139

151140
Some errors have detailed explanations: E0505, E0597, E0621.
152141
For more information about an error, try `rustc --explain E0505`.

0 commit comments

Comments
 (0)