Skip to content

Commit

Permalink
Make coroutine-closures possible to be cloned
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Jul 25, 2024
1 parent 2d8c215 commit 3851fe0
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 21 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_mir_transform/src/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,9 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
match self_ty.kind() {
ty::FnDef(..) | ty::FnPtr(_) => builder.copy_shim(),
ty::Closure(_, args) => builder.tuple_like_shim(dest, src, args.as_closure().upvar_tys()),
ty::CoroutineClosure(_, args) => {
builder.tuple_like_shim(dest, src, args.as_coroutine_closure().upvar_tys())
}
ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
ty::Coroutine(coroutine_def_id, args) => {
assert_eq!(tcx.coroutine_movability(*coroutine_def_id), hir::Movability::Movable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,10 @@ where
// impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone
ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),

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

// only when `coroutine_clone` is enabled and the coroutine is movable
// impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses)
Expand Down
17 changes: 15 additions & 2 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2262,8 +2262,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}
}

// FIXME(async_closures): These are never clone, for now.
ty::CoroutineClosure(_, _) => None,
ty::CoroutineClosure(_, args) => {
// (*) binder moved here
let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty());
if let ty::Infer(ty::TyVar(_)) = ty.kind() {
// Not yet resolved.
Ambiguous
} else {
Where(
obligation
.predicate
.rebind(args.as_coroutine_closure().upvar_tys().to_vec()),
)
}
}

// `Copy` and `Clone` are automatically implemented for an anonymous adt
// if all of its fields are `Copy` and `Clone`
ty::Adt(adt, args) if adt.is_anonymous() => {
Expand Down
24 changes: 24 additions & 0 deletions tests/ui/async-await/async-closures/clone-closure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//@ aux-build:block-on.rs
//@ edition:2021
//@ run-pass
//@ check-run-results

#![feature(async_closure)]

extern crate block_on;

async fn for_each(f: impl async FnOnce(&str) + Clone) {
f.clone()("world").await;
f.clone()("world2").await;
}

fn main() {
block_on::block_on(async_main());
}

async fn async_main() {
let x = String::from("Hello,");
for_each(async move |s| {
println!("{x} {s}");
}).await;
}
2 changes: 2 additions & 0 deletions tests/ui/async-await/async-closures/clone-closure.run.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello, world
Hello, world2
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ LL | x().await;
|
note: `async_call_once` takes ownership of the receiver `self`, which moves `x`
--> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
help: you could `clone` the value and consume it, if the `NoCopy: Clone` trait bound could be satisfied
|
LL | x.clone()().await;
| ++++++++
help: consider annotating `NoCopy` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | struct NoCopy;
|

error: aborting due to 1 previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn simple<'a>(x: &'a i32) {

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

struct S<'a>(&'a i32);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,6 @@ LL | outlives::<'a>(call_once(c));
LL | }
| - `c` dropped here while still borrowed

error[E0505]: cannot move out of `c` because it is borrowed
--> $DIR/without-precise-captures-we-are-powerless.rs:22:30
|
LL | fn simple<'a>(x: &'a i32) {
| -- lifetime `'a` defined here
...
LL | let c = async move || { println!("{}", *x); };
| - binding `c` declared here
LL | outlives::<'a>(c());
| ---
| |
| borrow of `c` occurs here
| argument requires that `c` is borrowed for `'a`
LL | outlives::<'a>(call_once(c));
| ^ move out of `c` occurs here

error[E0597]: `x` does not live long enough
--> $DIR/without-precise-captures-we-are-powerless.rs:28:13
|
Expand Down Expand Up @@ -72,6 +56,11 @@ LL | outlives::<'a>(call_once(c));
LL |
LL | let c = async move || { println!("{}", *x.0); };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here
|
help: consider cloning the value if the performance cost is acceptable
|
LL | let c = async || { println!("{}", *x.0); }.clone();
| ++++++++

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

error: aborting due to 10 previous errors
error: aborting due to 9 previous errors

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

0 comments on commit 3851fe0

Please sign in to comment.