Make coroutine-closures possible to be cloned
This commit is contained in:
parent
2d5a628a1d
commit
d5656059a1
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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() => {
|
||||
|
24
tests/ui/async-await/async-closures/clone-closure.rs
Normal file
24
tests/ui/async-await/async-closures/clone-closure.rs
Normal 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;
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
Hello, world
|
||||
Hello, world2
|
@ -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
|
||||
|
||||
|
36
tests/ui/async-await/async-closures/not-clone-closure.rs
Normal file
36
tests/ui/async-await/async-closures/not-clone-closure.rs
Normal 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() {}
|
20
tests/ui/async-await/async-closures/not-clone-closure.stderr
Normal file
20
tests/ui/async-await/async-closures/not-clone-closure.stderr
Normal 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`.
|
@ -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);
|
||||
|
@ -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`.
|
||||
|
Loading…
x
Reference in New Issue
Block a user