Make coroutine-closures possible to be cloned

This commit is contained in:
Michael Goulet 2024-07-25 14:02:33 -04:00
parent 2d5a628a1d
commit d5656059a1
10 changed files with 120 additions and 21 deletions

View File

@ -435,6 +435,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);

View File

@ -217,7 +217,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)

View File

@ -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() => {

View File

@ -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;
}

View File

@ -0,0 +1,2 @@
Hello, world
Hello, world2

View File

@ -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

View File

@ -0,0 +1,36 @@
//@ edition: 2021
#![feature(async_closure)]
struct NotClonableArg;
#[derive(Default)]
struct NotClonableReturnType;
// Verify that the only components that we care about are the upvars, not the signature.
fn we_are_okay_with_not_clonable_signature() {
let x = async |x: NotClonableArg| -> NotClonableReturnType { Default::default() };
x.clone(); // Okay
}
#[derive(Debug)]
struct NotClonableUpvar;
fn we_only_care_about_clonable_upvars() {
let x = NotClonableUpvar;
// Notably, this is clone because we capture `&x`.
let yes_clone = async || {
println!("{x:?}");
};
yes_clone.clone(); // Okay
let z = NotClonableUpvar;
// However, this is not because the closure captures `z` by move.
// (Even though the future that is lent out captures `z by ref!)
let not_clone = async move || {
println!("{z:?}");
};
not_clone.clone();
//~^ ERROR the trait bound `NotClonableUpvar: Clone` is not satisfied
}
fn main() {}

View File

@ -0,0 +1,20 @@
error[E0277]: the trait bound `NotClonableUpvar: Clone` is not satisfied in `{async closure@$DIR/not-clone-closure.rs:29:21: 29:34}`
--> $DIR/not-clone-closure.rs:32:15
|
LL | not_clone.clone();
| ^^^^^ within `{async closure@$DIR/not-clone-closure.rs:29:21: 29:34}`, the trait `Clone` is not implemented for `NotClonableUpvar`, which is required by `{async closure@$DIR/not-clone-closure.rs:29:21: 29:34}: Clone`
|
note: required because it's used within this closure
--> $DIR/not-clone-closure.rs:29:21
|
LL | let not_clone = async move || {
| ^^^^^^^^^^^^^
help: consider annotating `NotClonableUpvar` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | struct NotClonableUpvar;
|
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -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);

View File

@ -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
|
@ -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
@ -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`.