diff options
| author | Trevor Gross <t.gross35@gmail.com> | 2024-07-26 19:03:05 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-26 19:03:05 -0400 |
| commit | bd18f88dabdf4af5de1cfa180596fab3d503a734 (patch) | |
| tree | 5533d5316374f22a8870b66b2a232a61b3850712 /tests | |
| parent | 86721a4c90531cbf2d8e1c7124b527e30489782b (diff) | |
| parent | 5a9959fd9daa17e77d609f3c2db4d18554484447 (diff) | |
| download | rust-bd18f88dabdf4af5de1cfa180596fab3d503a734.tar.gz rust-bd18f88dabdf4af5de1cfa180596fab3d503a734.zip | |
Rollup merge of #128201 - compiler-errors:closure-clone, r=oli-obk
Implement `Copy`/`Clone` for async closures We can do so in the same cases that regular closures do. For the purposes of cloning, coroutine-closures are actually precisely the same as regular closures, specifically in the aspect that `Clone` impls care about which is the upvars. The only difference b/w coroutine-closures and regular closures is the type that they *return*, but this type has not been *created* yet, so we don't really have a problem. IDK why I didn't add this impl initially -- I went back and forth a bit on the internal representation for coroutine-closures before settling on a design which largely models regular closures. Previous (not published) iterations of coroutine-closures used to be represented as a special (read: cursed) kind of coroutine, which would probably suffer from the pitfalls that coroutines have that oli mentioned below in https://github.com/rust-lang/rust/pull/128201#issuecomment-2251230274. r? oli-obk
Diffstat (limited to 'tests')
7 files changed, 93 insertions, 18 deletions
diff --git a/tests/ui/async-await/async-closures/clone-closure.rs b/tests/ui/async-await/async-closures/clone-closure.rs new file mode 100644 index 00000000000..807897e3e03 --- /dev/null +++ b/tests/ui/async-await/async-closures/clone-closure.rs @@ -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; +} diff --git a/tests/ui/async-await/async-closures/clone-closure.run.stdout b/tests/ui/async-await/async-closures/clone-closure.run.stdout new file mode 100644 index 00000000000..0cfcf1923da --- /dev/null +++ b/tests/ui/async-await/async-closures/clone-closure.run.stdout @@ -0,0 +1,2 @@ +Hello, world +Hello, world2 diff --git a/tests/ui/async-await/async-closures/move-consuming-capture.stderr b/tests/ui/async-await/async-closures/move-consuming-capture.stderr index 45c1eac8f8f..4ce71ec49d6 100644 --- a/tests/ui/async-await/async-closures/move-consuming-capture.stderr +++ b/tests/ui/async-await/async-closures/move-consuming-capture.stderr @@ -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 diff --git a/tests/ui/async-await/async-closures/not-clone-closure.rs b/tests/ui/async-await/async-closures/not-clone-closure.rs new file mode 100644 index 00000000000..2776ce4690f --- /dev/null +++ b/tests/ui/async-await/async-closures/not-clone-closure.rs @@ -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() {} diff --git a/tests/ui/async-await/async-closures/not-clone-closure.stderr b/tests/ui/async-await/async-closures/not-clone-closure.stderr new file mode 100644 index 00000000000..aea48a455c2 --- /dev/null +++ b/tests/ui/async-await/async-closures/not-clone-closure.stderr @@ -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`. diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs index 17681161e20..18f16ca4b2d 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs @@ -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); diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr index 569028934cb..1df5abdbb18 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -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 | @@ -146,7 +130,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`. |
