From f7da7ba2330530842e6e349b792ea46fa55a57f5 Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Tue, 11 Mar 2025 23:20:52 +0800 Subject: [PATCH 1/9] changes to tests upfront --- .../clippy/tests/ui/future_not_send.stderr | 18 +- .../clippy/tests/ui/large_futures.stderr | 2 +- .../clippy/tests/ui/needless_lifetimes.fixed | 1 + .../clippy/tests/ui/needless_lifetimes.rs | 1 + .../clippy/tests/ui/needless_lifetimes.stderr | 84 ++++----- tests/codegen/async-fn-debug.rs | 10 +- tests/debuginfo/coroutine-objects.rs | 10 +- ...await.b-{closure#0}.coroutine_resume.0.mir | 48 ++--- ...ny.main-{closure#0}.coroutine_resume.0.mir | 6 +- ...y.run2-{closure#0}.Inline.panic-abort.diff | 156 ++++++++-------- ....run2-{closure#0}.Inline.panic-unwind.diff | 168 ++++++++++-------- tests/ui/async-await/async-drop.rs | 43 +++-- .../ui/async-await/awaiting-unsized-param.rs | 1 + .../async-await/awaiting-unsized-param.stderr | 11 +- .../future-sizes/async-awaiting-fut.stdout | 54 +++--- .../async-await/future-sizes/future-as-arg.rs | 9 +- .../async-await/future-sizes/large-arg.stdout | 71 ++++---- tests/ui/async-await/issue-70818.rs | 1 + tests/ui/async-await/issue-70818.stderr | 20 ++- .../issue-74072-lifetime-name-annotations.rs | 10 +- ...sue-74072-lifetime-name-annotations.stderr | 20 +-- tests/ui/async-await/issue-86507.rs | 1 + tests/ui/async-await/issue-86507.stderr | 29 ++- tests/ui/coroutine/auto-trait-regions.rs | 2 + tests/ui/coroutine/auto-trait-regions.stderr | 28 ++- tests/ui/coroutine/clone-impl.stderr | 44 +++-- .../ref-escapes-but-not-over-yield.rs | 9 +- .../ref-escapes-but-not-over-yield.stderr | 14 +- tests/ui/coroutine/ref-upvar-not-send.rs | 42 +++-- tests/ui/coroutine/ref-upvar-not-send.stderr | 86 ++++++--- .../coroutine/unsized-capture-across-yield.rs | 1 + .../unsized-capture-across-yield.stderr | 13 +- .../recursive-impl-trait-type-indirect.stderr | 6 + tests/ui/mir/lint/storage-live.stderr | 2 +- tests/ui/print_type_sizes/async.stdout | 14 +- tests/ui/print_type_sizes/coroutine.stdout | 17 +- .../ui/stable-mir-print/async-closure.stdout | 12 +- .../hkl_forbidden4.stderr | 4 +- 38 files changed, 632 insertions(+), 436 deletions(-) diff --git a/src/tools/clippy/tests/ui/future_not_send.stderr b/src/tools/clippy/tests/ui/future_not_send.stderr index e366dc2d21958..326a789546204 100644 --- a/src/tools/clippy/tests/ui/future_not_send.stderr +++ b/src/tools/clippy/tests/ui/future_not_send.stderr @@ -13,11 +13,14 @@ LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { LL | async { true }.await | ^^^^^ await occurs here, with `rc` maybe used later = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` -note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - --> tests/ui/future_not_send.rs:8:39 +note: future is not `Send` as this value is used across an await + --> tests/ui/future_not_send.rs:11:20 | LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { - | ^^^^ has type `&std::cell::Cell` which is not `Send`, because `std::cell::Cell` is not `Sync` + | ---- has type `&std::cell::Cell` which is not `Send` +... +LL | async { true }.await + | ^^^^^ await occurs here, with `cell` maybe used later = note: `std::cell::Cell` doesn't implement `std::marker::Sync` = note: `-D clippy::future-not-send` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::future_not_send)]` @@ -92,11 +95,14 @@ error: future cannot be sent between threads safely LL | pub async fn public_future(&self) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future` is not `Send` | -note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - --> tests/ui/future_not_send.rs:49:32 +note: future is not `Send` as this value is used across an await + --> tests/ui/future_not_send.rs:52:31 | LL | pub async fn public_future(&self) { - | ^^^^^ has type `&Dummy` which is not `Send`, because `Dummy` is not `Sync` + | ---- has type `&Dummy` which is not `Send` +... +LL | self.private_future().await; + | ^^^^^ await occurs here, with `self` maybe used later = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely diff --git a/src/tools/clippy/tests/ui/large_futures.stderr b/src/tools/clippy/tests/ui/large_futures.stderr index fd6ba4e3563de..9c39f73ef6d76 100644 --- a/src/tools/clippy/tests/ui/large_futures.stderr +++ b/src/tools/clippy/tests/ui/large_futures.stderr @@ -31,7 +31,7 @@ error: large future with a size of 65540 bytes LL | foo().await; | ^^^^^ help: consider `Box::pin` on it: `Box::pin(foo())` -error: large future with a size of 49159 bytes +error: large future with a size of 32774 bytes --> tests/ui/large_futures.rs:35:5 | LL | calls_fut(fut).await; diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed index d59393fb3f3c6..96caf2917345d 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed +++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed @@ -3,6 +3,7 @@ #![warn(clippy::needless_lifetimes, clippy::elidable_lifetime_names)] #![allow( unused, + clippy::await_holding_lock, clippy::boxed_local, clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index e24907ab5fcdf..b087a12883626 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -3,6 +3,7 @@ #![warn(clippy::needless_lifetimes, clippy::elidable_lifetime_names)] #![allow( unused, + clippy::await_holding_lock, clippy::boxed_local, clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr index 138d0498c43e4..c7669e4d175b4 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.stderr +++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr @@ -1,5 +1,5 @@ error: the following explicit lifetimes could be elided: 'a, 'b - --> tests/ui/needless_lifetimes.rs:19:23 + --> tests/ui/needless_lifetimes.rs:20:23 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^ ^^ ^^ ^^ @@ -13,7 +13,7 @@ LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} | error: the following explicit lifetimes could be elided: 'a, 'b - --> tests/ui/needless_lifetimes.rs:22:24 + --> tests/ui/needless_lifetimes.rs:23:24 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^ ^^ ^^ ^^ @@ -25,7 +25,7 @@ LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:33:15 + --> tests/ui/needless_lifetimes.rs:34:15 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^ ^^ ^^ @@ -37,7 +37,7 @@ LL + fn in_and_out(x: &u8, _y: u8) -> &u8 { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:46:31 + --> tests/ui/needless_lifetimes.rs:47:31 | LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { | ^^ ^^ @@ -49,7 +49,7 @@ LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:54:27 + --> tests/ui/needless_lifetimes.rs:55:27 | LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { | ^^ ^^ @@ -61,7 +61,7 @@ LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:72:26 + --> tests/ui/needless_lifetimes.rs:73:26 | LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { | ^^ ^^ @@ -73,7 +73,7 @@ LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:80:22 + --> tests/ui/needless_lifetimes.rs:81:22 | LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { | ^^ ^^ @@ -85,7 +85,7 @@ LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:90:21 + --> tests/ui/needless_lifetimes.rs:91:21 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^ ^^ ^^ @@ -97,7 +97,7 @@ LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:96:28 + --> tests/ui/needless_lifetimes.rs:97:28 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^ ^^ ^^ @@ -109,7 +109,7 @@ LL + fn where_clause_without_lt(x: &u8, _y: u8) -> Result<&u8, ()> | error: the following explicit lifetimes could be elided: 's - --> tests/ui/needless_lifetimes.rs:127:21 + --> tests/ui/needless_lifetimes.rs:128:21 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^ ^^ ^^ @@ -121,7 +121,7 @@ LL + fn self_and_out(&self) -> &u8 { | error: the following explicit lifetimes could be elided: 't - --> tests/ui/needless_lifetimes.rs:135:30 + --> tests/ui/needless_lifetimes.rs:136:30 | LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { | ^^ ^^ @@ -133,7 +133,7 @@ LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { | error: the following explicit lifetimes could be elided: 's - --> tests/ui/needless_lifetimes.rs:143:26 + --> tests/ui/needless_lifetimes.rs:144:26 | LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { | ^^ ^^ @@ -145,7 +145,7 @@ LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { | error: the following explicit lifetimes could be elided: 's, 't - --> tests/ui/needless_lifetimes.rs:148:29 + --> tests/ui/needless_lifetimes.rs:149:29 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^ ^^ ^^ ^^ @@ -157,7 +157,7 @@ LL + fn distinct_self_and_in(&self, _x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:172:21 + --> tests/ui/needless_lifetimes.rs:173:21 | LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { | ^^ ^^ @@ -169,7 +169,7 @@ LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:188:22 + --> tests/ui/needless_lifetimes.rs:189:22 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^ ^^ ^^ @@ -181,7 +181,7 @@ LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:199:20 + --> tests/ui/needless_lifetimes.rs:200:20 | LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { | ^^ ^^ @@ -193,7 +193,7 @@ LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:204:30 + --> tests/ui/needless_lifetimes.rs:205:30 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^ ^^ ^ @@ -205,7 +205,7 @@ LL + fn named_input_elided_output(_arg: &str) -> &str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:213:19 + --> tests/ui/needless_lifetimes.rs:214:19 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^ ^^ @@ -217,7 +217,7 @@ LL + fn trait_bound_ok>(_: &u8, _: T) { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:249:24 + --> tests/ui/needless_lifetimes.rs:250:24 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^ ^^ @@ -229,7 +229,7 @@ LL + fn needless_lt(x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:254:24 + --> tests/ui/needless_lifetimes.rs:255:24 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^ ^^ @@ -241,7 +241,7 @@ LL + fn needless_lt(_x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:285:55 + --> tests/ui/needless_lifetimes.rs:286:55 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^ ^^ ^^ @@ -253,7 +253,7 @@ LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(& | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:295:26 + --> tests/ui/needless_lifetimes.rs:296:26 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^ ^^ ^^ @@ -265,7 +265,7 @@ LL + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:308:30 + --> tests/ui/needless_lifetimes.rs:309:30 | LL | fn where_clause_elidable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^ ^^ ^^ @@ -277,7 +277,7 @@ LL + fn where_clause_elidable(i: &i32, f: T) -> &i32 | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:324:28 + --> tests/ui/needless_lifetimes.rs:325:28 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^ ^^ ^^ @@ -289,7 +289,7 @@ LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:338:28 + --> tests/ui/needless_lifetimes.rs:339:28 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^ ^^ @@ -301,7 +301,7 @@ LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:342:28 + --> tests/ui/needless_lifetimes.rs:343:28 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^ ^^ @@ -313,7 +313,7 @@ LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:365:21 + --> tests/ui/needless_lifetimes.rs:366:21 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^ ^^ ^^ @@ -325,7 +325,7 @@ LL + fn implicit(&self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:369:25 + --> tests/ui/needless_lifetimes.rs:370:25 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^ ^^ ^^ @@ -337,7 +337,7 @@ LL + fn implicit_mut(&mut self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:374:21 + --> tests/ui/needless_lifetimes.rs:375:21 | LL | fn explicit<'a>(self: &'a Arc) -> &'a () { | ^^ ^^ ^^ @@ -349,7 +349,7 @@ LL + fn explicit(self: &Arc) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:379:25 + --> tests/ui/needless_lifetimes.rs:380:25 | LL | fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { | ^^ ^^ ^^ @@ -361,7 +361,7 @@ LL + fn explicit_mut(self: &mut Rc) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:392:31 + --> tests/ui/needless_lifetimes.rs:393:31 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { | ^^ ^^ ^^ @@ -373,7 +373,7 @@ LL + fn lifetime_elsewhere(self: Box, here: &()) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:399:21 + --> tests/ui/needless_lifetimes.rs:400:21 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^ ^^ ^^ @@ -385,7 +385,7 @@ LL + fn implicit(&self) -> &(); | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:401:30 + --> tests/ui/needless_lifetimes.rs:402:30 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^ ^^ ^^ @@ -397,7 +397,7 @@ LL + fn implicit_provided(&self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:407:21 + --> tests/ui/needless_lifetimes.rs:408:21 | LL | fn explicit<'a>(self: &'a Arc) -> &'a (); | ^^ ^^ ^^ @@ -409,7 +409,7 @@ LL + fn explicit(self: &Arc) -> &(); | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:410:30 + --> tests/ui/needless_lifetimes.rs:411:30 | LL | fn explicit_provided<'a>(self: &'a Arc) -> &'a () { | ^^ ^^ ^^ @@ -421,7 +421,7 @@ LL + fn explicit_provided(self: &Arc) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:421:31 + --> tests/ui/needless_lifetimes.rs:422:31 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^ ^^ ^^ @@ -433,7 +433,7 @@ LL + fn lifetime_elsewhere(self: Box, here: &()) -> &(); | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:423:40 + --> tests/ui/needless_lifetimes.rs:424:40 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^ ^^ ^^ @@ -445,7 +445,7 @@ LL + fn lifetime_elsewhere_provided(self: Box, here: &()) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:433:12 + --> tests/ui/needless_lifetimes.rs:434:12 | LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} | ^^ ^^ @@ -457,7 +457,7 @@ LL + fn foo(x: &u8, y: &'_ u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:436:12 + --> tests/ui/needless_lifetimes.rs:437:12 | LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} | ^^ ^^ @@ -469,7 +469,7 @@ LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:444:18 + --> tests/ui/needless_lifetimes.rs:445:18 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^ ^^ ^^ @@ -481,7 +481,7 @@ LL + fn one_input(x: &u8) -> &u8 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:450:42 + --> tests/ui/needless_lifetimes.rs:451:42 | LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { | ^^ ^^ @@ -493,7 +493,7 @@ LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:467:22 + --> tests/ui/needless_lifetimes.rs:468:22 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^ ^^ ^^ diff --git a/tests/codegen/async-fn-debug.rs b/tests/codegen/async-fn-debug.rs index 7be4ad4566576..2b7d9eb30ecc6 100644 --- a/tests/codegen/async-fn-debug.rs +++ b/tests/codegen/async-fn-debug.rs @@ -37,17 +37,17 @@ async fn async_fn_test() { // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "3", scope: [[VARIANT]], -// CHECK-SAME: file: [[FILE]], line: 12, +// CHECK-SAME: file: [[FILE]], line: 14, // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) -// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "4", scope: [[VARIANT]], -// CHECK-SAME: file: [[FILE]], line: 14, +// CHECK: [[S0:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend0", scope: [[GEN]], // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) -// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]], +// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S0]] // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) -// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]] +// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "4", scope: [[VARIANT]], +// CHECK-SAME: file: [[FILE]], line: 12, // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN]], diff --git a/tests/debuginfo/coroutine-objects.rs b/tests/debuginfo/coroutine-objects.rs index 242c76c2989e3..2142e8f4b859f 100644 --- a/tests/debuginfo/coroutine-objects.rs +++ b/tests/debuginfo/coroutine-objects.rs @@ -10,22 +10,22 @@ // gdb-command:run // gdb-command:print b -// gdb-check:$1 = coroutine_objects::main::{coroutine_env#0}::Unresumed{_ref__a: 0x[...]} +// gdb-check:$1 = coroutine_objects::main::{coroutine_env#0}::Unresumed{a: 0x[...]} // gdb-command:continue // gdb-command:print b -// gdb-check:$2 = coroutine_objects::main::{coroutine_env#0}::Suspend0{c: 6, d: 7, _ref__a: 0x[...]} +// gdb-check:$2 = coroutine_objects::main::{coroutine_env#0}::Suspend0{c: 6, d: 7, a: 0x[...]} // gdb-command:continue // gdb-command:print b -// gdb-check:$3 = coroutine_objects::main::{coroutine_env#0}::Suspend1{c: 7, d: 8, _ref__a: 0x[...]} +// gdb-check:$3 = coroutine_objects::main::{coroutine_env#0}::Suspend1{c: 7, d: 8, a: 0x[...]} // gdb-command:continue // gdb-command:print b -// gdb-check:$4 = coroutine_objects::main::{coroutine_env#0}::Returned{_ref__a: 0x[...]} +// gdb-check:$4 = coroutine_objects::main::{coroutine_env#0}::Returned // === LLDB TESTS ================================================================================== // lldb-command:run // lldb-command:v b -// lldb-check:(coroutine_objects::main::{coroutine_env#0}) b = { value = { _ref__a = 0x[...] } $discr$ = [...] } +// lldb-check:(coroutine_objects::main::{coroutine_env#0}) b = { value = { a = 0x[...] } $discr$ = [...] } // === CDB TESTS =================================================================================== diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir index 109a41d1ef907..fc73c7ba15456 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -1,7 +1,7 @@ // MIR for `b::{closure#0}` 0 coroutine_resume /* coroutine_layout = CoroutineLayout { field_tys: { - _0: CoroutineSavedTy { + corsl_0: CoroutineSavedTy { ty: Coroutine( DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), [ @@ -22,7 +22,7 @@ }, ignore_for_traits: false, }, - _1: CoroutineSavedTy { + corsl_1: CoroutineSavedTy { ty: Coroutine( DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), [ @@ -48,12 +48,12 @@ Unresumed(0): [], Returned (1): [], Panicked (2): [], - Suspend0 (3): [_0], - Suspend1 (4): [_1], + Suspend0 (3): [corsl_1], + Suspend1 (4): [corsl_0], }, storage_conflicts: BitMatrix(2x2) { - (_0, _0), - (_1, _1), + (corsl_0, corsl_0), + (corsl_1, corsl_1), }, } */ @@ -96,14 +96,14 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> let mut _38: &mut std::task::Context<'_>; let mut _39: u32; scope 1 { - debug __awaitee => (((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()}); + debug __awaitee => (((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()}); let _17: (); scope 2 { debug result => _17; } } scope 3 { - debug __awaitee => (((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()}); + debug __awaitee => (((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()}); let _33: (); scope 4 { debug result => _33; @@ -131,7 +131,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageDead(_5); PlaceMention(_4); nop; - (((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()}) = move _4; + (((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()}) = move _4; goto -> bb4; } @@ -141,7 +141,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageLive(_10); StorageLive(_11); StorageLive(_12); - _12 = &mut (((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()}); + _12 = &mut (((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()}); _11 = &mut (*_12); _10 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _11) -> [return: bb5, unwind unreachable]; } @@ -188,7 +188,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageDead(_4); StorageDead(_19); StorageDead(_20); - discriminant((*(_1.0: &mut {async fn body of b()}))) = 3; + discriminant((*(_1.0: &mut {async fn body of b()}))) = 4; return; } @@ -201,7 +201,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageDead(_12); StorageDead(_9); StorageDead(_8); - drop((((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()})) -> [return: bb12, unwind unreachable]; + drop((((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()})) -> [return: bb12, unwind unreachable]; } bb11: { @@ -233,7 +233,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageDead(_22); PlaceMention(_21); nop; - (((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()}) = move _21; + (((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()}) = move _21; goto -> bb16; } @@ -243,7 +243,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageLive(_26); StorageLive(_27); StorageLive(_28); - _28 = &mut (((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()}); + _28 = &mut (((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()}); _27 = &mut (*_28); _26 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _27) -> [return: bb17, unwind unreachable]; } @@ -285,7 +285,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageDead(_21); StorageDead(_35); StorageDead(_36); - discriminant((*(_1.0: &mut {async fn body of b()}))) = 4; + discriminant((*(_1.0: &mut {async fn body of b()}))) = 3; return; } @@ -298,7 +298,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageDead(_28); StorageDead(_25); StorageDead(_24); - drop((((*(_1.0: &mut {async fn body of b()})) as variant#4).0: {async fn body of a()})) -> [return: bb23, unwind unreachable]; + drop((((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()})) -> [return: bb23, unwind unreachable]; } bb22: { @@ -330,6 +330,14 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> } bb27: { + StorageLive(_21); + StorageLive(_35); + StorageLive(_36); + _35 = move _2; + goto -> bb22; + } + + bb28: { StorageLive(_3); StorageLive(_4); StorageLive(_19); @@ -338,14 +346,6 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> goto -> bb11; } - bb28: { - StorageLive(_21); - StorageLive(_35); - StorageLive(_36); - _35 = move _2; - goto -> bb22; - } - bb29: { assert(const false, "`async fn` resumed after completion") -> [success: bb29, unwind unreachable]; } diff --git a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir index f8b3f68d21e63..fe01ea115cd2b 100644 --- a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir @@ -1,7 +1,7 @@ // MIR for `main::{closure#0}` 0 coroutine_resume /* coroutine_layout = CoroutineLayout { field_tys: { - _0: CoroutineSavedTy { + corsl_0: CoroutineSavedTy { ty: HasDrop, source_info: SourceInfo { span: $DIR/coroutine_tiny.rs:22:13: 22:15 (#0), @@ -14,10 +14,10 @@ Unresumed(0): [], Returned (1): [], Panicked (2): [], - Suspend0 (3): [_0], + Suspend0 (3): [corsl_0], }, storage_conflicts: BitMatrix(1x1) { - (_0, _0), + (corsl_0, corsl_0), }, } */ diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index eb97af1e2845f..3beda282fc913 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -38,17 +38,19 @@ + let mut _27: !; + let mut _28: &mut std::task::Context<'_>; + let mut _29: (); -+ let mut _30: (); -+ let mut _31: &mut std::task::Context<'_>; -+ let mut _32: u32; -+ let mut _33: &mut {async fn body of ActionPermit<'_, T>::perform()}; -+ let mut _34: &mut {async fn body of ActionPermit<'_, T>::perform()}; ++ let _30: ActionPermit<'_, T>; ++ let _31: ActionPermit<'_, T>; ++ let mut _32: (); ++ let mut _33: &mut std::task::Context<'_>; ++ let mut _34: u32; + let mut _35: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _36: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _37: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _38: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _39: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _40: &mut {async fn body of ActionPermit<'_, T>::perform()}; ++ let mut _41: &mut {async fn body of ActionPermit<'_, T>::perform()}; ++ let mut _42: &mut {async fn body of ActionPermit<'_, T>::perform()}; + scope 7 { + let mut _15: std::future::Ready<()>; + scope 8 { @@ -58,15 +60,15 @@ + scope 12 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 13 (inlined as Future>::poll) { -+ let mut _42: (); -+ let mut _43: std::option::Option<()>; -+ let mut _44: &mut std::option::Option<()>; -+ let mut _45: &mut std::future::Ready<()>; -+ let mut _46: &mut std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _44: (); ++ let mut _45: std::option::Option<()>; ++ let mut _46: &mut std::option::Option<()>; ++ let mut _47: &mut std::future::Ready<()>; ++ let mut _48: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _47: std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _49: std::pin::Pin<&mut std::future::Ready<()>>; + scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _48: &mut &mut std::future::Ready<()>; ++ let mut _50: &mut &mut std::future::Ready<()>; + scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { @@ -76,22 +78,22 @@ + } + } + scope 19 (inlined Option::<()>::take) { -+ let mut _49: std::option::Option<()>; ++ let mut _51: std::option::Option<()>; + scope 20 (inlined std::mem::replace::>) { + scope 21 { + } + } + } + scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _50: isize; -+ let mut _51: !; ++ let mut _52: isize; ++ let mut _53: !; + scope 23 { + } + } + } + } + scope 10 (inlined ready::<()>) { -+ let mut _41: std::option::Option<()>; ++ let mut _43: std::option::Option<()>; + } + scope 11 (inlined as IntoFuture>::into_future) { + } @@ -136,8 +138,6 @@ + StorageLive(_16); + StorageLive(_25); + StorageLive(_27); -+ StorageLive(_30); -+ StorageLive(_31); + StorageLive(_32); + StorageLive(_33); + StorageLive(_34); @@ -147,9 +147,11 @@ + StorageLive(_38); + StorageLive(_39); + StorageLive(_40); -+ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _32 = discriminant((*_33)); -+ switchInt(move _32) -> [0: bb3, 1: bb10, 3: bb9, otherwise: bb5]; ++ StorageLive(_41); ++ StorageLive(_42); ++ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _34 = discriminant((*_35)); ++ switchInt(move _34) -> [0: bb3, 1: bb10, 3: bb9, otherwise: bb5]; } - bb3: { @@ -159,6 +161,8 @@ + } + + bb2: { ++ StorageDead(_42); ++ StorageDead(_41); + StorageDead(_40); + StorageDead(_39); + StorageDead(_38); @@ -168,8 +172,6 @@ + StorageDead(_34); + StorageDead(_33); + StorageDead(_32); -+ StorageDead(_31); -+ StorageDead(_30); + StorageDead(_27); + StorageDead(_25); + StorageDead(_16); @@ -187,23 +189,28 @@ } + bb3: { -+ _31 = move _9; -+ _34 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ _33 = move _9; ++ StorageLive(_30); ++ StorageLive(_31); ++ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _31 = move (((*_36) as variant#0).0: ActionPermit<'_, T>); ++ _30 = move _31; ++ StorageDead(_31); ++ _42 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ (((*_42) as variant#3).0: ActionPermit<'_, T>) = move _30; + StorageLive(_12); + StorageLive(_13); + StorageLive(_14); + _14 = (); -+ StorageLive(_41); -+ _41 = Option::<()>::Some(copy _14); -+ _13 = std::future::Ready::<()>(move _41); -+ StorageDead(_41); ++ StorageLive(_43); ++ _43 = Option::<()>::Some(copy _14); ++ _13 = std::future::Ready::<()>(move _43); ++ StorageDead(_43); + StorageDead(_14); + _12 = move _13; + StorageDead(_13); -+ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ (((*_37) as variant#3).1: std::future::Ready<()>) = move _12; + goto -> bb4; + } + @@ -215,41 +222,41 @@ + StorageLive(_19); + StorageLive(_20); + StorageLive(_21); -+ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _21 = &mut (((*_38) as variant#3).1: std::future::Ready<()>); + _20 = &mut (*_21); + _19 = Pin::<&mut std::future::Ready<()>> { __pointer: copy _20 }; + StorageDead(_20); + StorageLive(_22); + StorageLive(_23); + StorageLive(_24); -+ _24 = copy _31; ++ _24 = copy _33; + _23 = move _24; + _22 = &mut (*_23); + StorageDead(_24); ++ StorageLive(_47); ++ StorageLive(_48); ++ StorageLive(_51); ++ StorageLive(_53); ++ StorageLive(_44); + StorageLive(_45); + StorageLive(_46); ++ _48 = &mut _19; + StorageLive(_49); -+ StorageLive(_51); -+ StorageLive(_42); -+ StorageLive(_43); -+ StorageLive(_44); -+ _46 = &mut _19; -+ StorageLive(_47); -+ StorageLive(_48); -+ _48 = &mut (_19.0: &mut std::future::Ready<()>); -+ _45 = copy (_19.0: &mut std::future::Ready<()>); -+ StorageDead(_48); -+ _47 = Pin::<&mut std::future::Ready<()>> { __pointer: copy _45 }; -+ StorageDead(_47); -+ _44 = &mut ((*_45).0: std::option::Option<()>); -+ _49 = Option::<()>::None; -+ _43 = copy ((*_45).0: std::option::Option<()>); -+ ((*_45).0: std::option::Option<()>) = copy _49; -+ StorageDead(_44); + StorageLive(_50); -+ _50 = discriminant(_43); -+ switchInt(move _50) -> [0: bb11, 1: bb12, otherwise: bb5]; ++ _50 = &mut (_19.0: &mut std::future::Ready<()>); ++ _47 = copy (_19.0: &mut std::future::Ready<()>); ++ StorageDead(_50); ++ _49 = Pin::<&mut std::future::Ready<()>> { __pointer: copy _47 }; ++ StorageDead(_49); ++ _46 = &mut ((*_47).0: std::option::Option<()>); ++ _51 = Option::<()>::None; ++ _45 = copy ((*_47).0: std::option::Option<()>); ++ ((*_47).0: std::option::Option<()>) = copy _51; ++ StorageDead(_46); ++ StorageLive(_52); ++ _52 = discriminant(_45); ++ switchInt(move _52) -> [0: bb11, 1: bb12, otherwise: bb5]; + } + + bb5: { @@ -269,29 +276,31 @@ + StorageDead(_12); + StorageDead(_28); + StorageDead(_29); -+ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_38)) = 3; ++ StorageDead(_30); ++ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_39)) = 3; + goto -> bb2; + } + + bb7: { + StorageLive(_26); + _26 = copy ((_18 as Ready).0: ()); -+ _30 = copy _26; ++ _32 = copy _26; + StorageDead(_26); + StorageDead(_23); + StorageDead(_21); + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb8, unwind unreachable]; ++ _40 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ drop((((*_40) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb8, unwind unreachable]; + } + + bb8: { -+ _7 = Poll::<()>::Ready(move _30); -+ _40 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_40)) = 1; ++ StorageDead(_30); ++ _7 = Poll::<()>::Ready(move _32); ++ _41 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_41)) = 1; + goto -> bb2; + } + @@ -299,9 +308,10 @@ + StorageLive(_12); + StorageLive(_28); + StorageLive(_29); ++ StorageLive(_30); + _28 = move _9; + StorageDead(_29); -+ _31 = move _28; ++ _33 = move _28; + StorageDead(_28); + _16 = const (); + goto -> bb4; @@ -312,19 +322,19 @@ + } + + bb11: { -+ _51 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; ++ _53 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; + } + + bb12: { -+ _42 = move ((_43 as Some).0: ()); -+ StorageDead(_50); -+ StorageDead(_43); -+ _18 = Poll::<()>::Ready(move _42); -+ StorageDead(_42); -+ StorageDead(_51); -+ StorageDead(_49); -+ StorageDead(_46); ++ _44 = move ((_45 as Some).0: ()); ++ StorageDead(_52); + StorageDead(_45); ++ _18 = Poll::<()>::Ready(move _44); ++ StorageDead(_44); ++ StorageDead(_53); ++ StorageDead(_51); ++ StorageDead(_48); ++ StorageDead(_47); + StorageDead(_22); + StorageDead(_19); + _25 = discriminant(_18); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index eb757e0911453..a6da1fcd9d62f 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -38,11 +38,11 @@ + let mut _27: !; + let mut _28: &mut std::task::Context<'_>; + let mut _29: (); -+ let mut _30: (); -+ let mut _31: &mut std::task::Context<'_>; -+ let mut _32: u32; -+ let mut _33: &mut {async fn body of ActionPermit<'_, T>::perform()}; -+ let mut _34: &mut {async fn body of ActionPermit<'_, T>::perform()}; ++ let _30: ActionPermit<'_, T>; ++ let _31: ActionPermit<'_, T>; ++ let mut _32: (); ++ let mut _33: &mut std::task::Context<'_>; ++ let mut _34: u32; + let mut _35: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _36: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _37: &mut {async fn body of ActionPermit<'_, T>::perform()}; @@ -51,6 +51,8 @@ + let mut _40: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _41: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _42: &mut {async fn body of ActionPermit<'_, T>::perform()}; ++ let mut _43: &mut {async fn body of ActionPermit<'_, T>::perform()}; ++ let mut _44: &mut {async fn body of ActionPermit<'_, T>::perform()}; + scope 7 { + let mut _15: std::future::Ready<()>; + scope 8 { @@ -60,15 +62,15 @@ + scope 12 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 13 (inlined as Future>::poll) { -+ let mut _44: (); -+ let mut _45: std::option::Option<()>; -+ let mut _46: &mut std::option::Option<()>; -+ let mut _47: &mut std::future::Ready<()>; -+ let mut _48: &mut std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _46: (); ++ let mut _47: std::option::Option<()>; ++ let mut _48: &mut std::option::Option<()>; ++ let mut _49: &mut std::future::Ready<()>; ++ let mut _50: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _49: std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _51: std::pin::Pin<&mut std::future::Ready<()>>; + scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _50: &mut &mut std::future::Ready<()>; ++ let mut _52: &mut &mut std::future::Ready<()>; + scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { @@ -78,22 +80,22 @@ + } + } + scope 19 (inlined Option::<()>::take) { -+ let mut _51: std::option::Option<()>; ++ let mut _53: std::option::Option<()>; + scope 20 (inlined std::mem::replace::>) { + scope 21 { + } + } + } + scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _52: isize; -+ let mut _53: !; ++ let mut _54: isize; ++ let mut _55: !; + scope 23 { + } + } + } + } + scope 10 (inlined ready::<()>) { -+ let mut _43: std::option::Option<()>; ++ let mut _45: std::option::Option<()>; + } + scope 11 (inlined as IntoFuture>::into_future) { + } @@ -138,8 +140,6 @@ + StorageLive(_16); + StorageLive(_25); + StorageLive(_27); -+ StorageLive(_30); -+ StorageLive(_31); + StorageLive(_32); + StorageLive(_33); + StorageLive(_34); @@ -151,9 +151,11 @@ + StorageLive(_40); + StorageLive(_41); + StorageLive(_42); -+ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _32 = discriminant((*_33)); -+ switchInt(move _32) -> [0: bb5, 1: bb15, 2: bb14, 3: bb13, otherwise: bb7]; ++ StorageLive(_43); ++ StorageLive(_44); ++ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _34 = discriminant((*_35)); ++ switchInt(move _34) -> [0: bb5, 1: bb15, 2: bb14, 3: bb13, otherwise: bb7]; } - bb3: { @@ -171,6 +173,8 @@ + } + + bb4: { ++ StorageDead(_44); ++ StorageDead(_43); + StorageDead(_42); + StorageDead(_41); + StorageDead(_40); @@ -182,8 +186,6 @@ + StorageDead(_34); + StorageDead(_33); + StorageDead(_32); -+ StorageDead(_31); -+ StorageDead(_30); + StorageDead(_27); + StorageDead(_25); + StorageDead(_16); @@ -204,23 +206,28 @@ - StorageDead(_2); - return; + bb5: { -+ _31 = move _9; -+ _34 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ _33 = move _9; ++ StorageLive(_30); ++ StorageLive(_31); ++ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _31 = move (((*_36) as variant#0).0: ActionPermit<'_, T>); ++ _30 = move _31; ++ StorageDead(_31); ++ _43 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ (((*_43) as variant#3).0: ActionPermit<'_, T>) = move _30; + StorageLive(_12); + StorageLive(_13); + StorageLive(_14); + _14 = (); -+ StorageLive(_43); -+ _43 = Option::<()>::Some(copy _14); -+ _13 = std::future::Ready::<()>(move _43); -+ StorageDead(_43); ++ StorageLive(_45); ++ _45 = Option::<()>::Some(copy _14); ++ _13 = std::future::Ready::<()>(move _45); ++ StorageDead(_45); + StorageDead(_14); + _12 = move _13; + StorageDead(_13); -+ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ (((*_37) as variant#3).1: std::future::Ready<()>) = move _12; + goto -> bb6; } @@ -232,41 +239,41 @@ + StorageLive(_19); + StorageLive(_20); + StorageLive(_21); -+ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _21 = &mut (((*_38) as variant#3).1: std::future::Ready<()>); + _20 = &mut (*_21); + _19 = Pin::<&mut std::future::Ready<()>> { __pointer: copy _20 }; + StorageDead(_20); + StorageLive(_22); + StorageLive(_23); + StorageLive(_24); -+ _24 = copy _31; ++ _24 = copy _33; + _23 = move _24; + _22 = &mut (*_23); + StorageDead(_24); ++ StorageLive(_49); ++ StorageLive(_50); ++ StorageLive(_53); ++ StorageLive(_55); ++ StorageLive(_46); + StorageLive(_47); + StorageLive(_48); ++ _50 = &mut _19; + StorageLive(_51); -+ StorageLive(_53); -+ StorageLive(_44); -+ StorageLive(_45); -+ StorageLive(_46); -+ _48 = &mut _19; -+ StorageLive(_49); -+ StorageLive(_50); -+ _50 = &mut (_19.0: &mut std::future::Ready<()>); -+ _47 = copy (_19.0: &mut std::future::Ready<()>); -+ StorageDead(_50); -+ _49 = Pin::<&mut std::future::Ready<()>> { __pointer: copy _47 }; -+ StorageDead(_49); -+ _46 = &mut ((*_47).0: std::option::Option<()>); -+ _51 = Option::<()>::None; -+ _45 = copy ((*_47).0: std::option::Option<()>); -+ ((*_47).0: std::option::Option<()>) = copy _51; -+ StorageDead(_46); + StorageLive(_52); -+ _52 = discriminant(_45); -+ switchInt(move _52) -> [0: bb16, 1: bb17, otherwise: bb7]; ++ _52 = &mut (_19.0: &mut std::future::Ready<()>); ++ _49 = copy (_19.0: &mut std::future::Ready<()>); ++ StorageDead(_52); ++ _51 = Pin::<&mut std::future::Ready<()>> { __pointer: copy _49 }; ++ StorageDead(_51); ++ _48 = &mut ((*_49).0: std::option::Option<()>); ++ _53 = Option::<()>::None; ++ _47 = copy ((*_49).0: std::option::Option<()>); ++ ((*_49).0: std::option::Option<()>) = copy _53; ++ StorageDead(_48); ++ StorageLive(_54); ++ _54 = discriminant(_47); ++ switchInt(move _54) -> [0: bb16, 1: bb17, otherwise: bb7]; } - bb6 (cleanup): { @@ -288,33 +295,27 @@ + StorageDead(_12); + StorageDead(_28); + StorageDead(_29); -+ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_38)) = 3; ++ StorageDead(_30); ++ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_39)) = 3; + goto -> bb4; + } + + bb9: { + StorageLive(_26); + _26 = copy ((_18 as Ready).0: ()); -+ _30 = copy _26; ++ _32 = copy _26; + StorageDead(_26); + StorageDead(_23); + StorageDead(_21); + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb10, unwind: bb12]; -+ } -+ -+ bb10: { -+ _7 = Poll::<()>::Ready(move _30); + _40 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_40)) = 1; -+ goto -> bb4; ++ drop((((*_40) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb11, unwind: bb12]; + } + -+ bb11 (cleanup): { ++ bb10 (cleanup): { + StorageDead(_22); + StorageDead(_19); + StorageDead(_23); @@ -326,9 +327,17 @@ + drop((((*_41) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb12, unwind terminate(cleanup)]; + } + -+ bb12 (cleanup): { ++ bb11: { ++ StorageDead(_30); ++ _7 = Poll::<()>::Ready(move _32); + _42 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_42)) = 2; ++ discriminant((*_42)) = 1; ++ goto -> bb4; ++ } ++ ++ bb12 (cleanup): { ++ _44 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_44)) = 2; + goto -> bb2; + } + @@ -336,9 +345,10 @@ + StorageLive(_12); + StorageLive(_28); + StorageLive(_29); ++ StorageLive(_30); + _28 = move _9; + StorageDead(_29); -+ _31 = move _28; ++ _33 = move _28; + StorageDead(_28); + _16 = const (); + goto -> bb6; @@ -353,19 +363,19 @@ + } + + bb16: { -+ _53 = option::expect_failed(const "`Ready` polled after completion") -> bb11; ++ _55 = option::expect_failed(const "`Ready` polled after completion") -> bb10; + } + + bb17: { -+ _44 = move ((_45 as Some).0: ()); -+ StorageDead(_52); -+ StorageDead(_45); -+ _18 = Poll::<()>::Ready(move _44); -+ StorageDead(_44); -+ StorageDead(_53); -+ StorageDead(_51); -+ StorageDead(_48); ++ _46 = move ((_47 as Some).0: ()); ++ StorageDead(_54); + StorageDead(_47); ++ _18 = Poll::<()>::Ready(move _46); ++ StorageDead(_46); ++ StorageDead(_55); ++ StorageDead(_53); ++ StorageDead(_50); ++ StorageDead(_49); + StorageDead(_22); + StorageDead(_19); + _25 = discriminant(_18); diff --git a/tests/ui/async-await/async-drop.rs b/tests/ui/async-await/async-drop.rs index b1af81423cea5..6047ac2167a3c 100644 --- a/tests/ui/async-await/async-drop.rs +++ b/tests/ui/async-await/async-drop.rs @@ -11,10 +11,10 @@ //@ edition: 2021 // FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests -use core::future::{async_drop_in_place, AsyncDrop, Future}; +use core::future::{AsyncDrop, Future, async_drop_in_place}; use core::hint::black_box; use core::mem::{self, ManuallyDrop}; -use core::pin::{pin, Pin}; +use core::pin::{Pin, pin}; use core::task::{Context, Poll, Waker}; async fn test_async_drop(x: T, _size: usize) { @@ -26,8 +26,8 @@ async fn test_async_drop(x: T, _size: usize) { // async functions. #[cfg(target_pointer_width = "64")] assert_eq!( - mem::size_of_val(&*dtor), _size, + mem::size_of_val(&*dtor), "sizes did not match for async destructor of type {}", core::any::type_name::(), ); @@ -53,20 +53,18 @@ fn main() { let i = 13; let fut = pin!(async { test_async_drop(Int(0), 0).await; - // FIXME(#63818): niches in coroutines are disabled. - // Some of these sizes should be smaller, as indicated in comments. - test_async_drop(AsyncInt(0), /*104*/ 112).await; - test_async_drop([AsyncInt(1), AsyncInt(2)], /*152*/ 168).await; - test_async_drop((AsyncInt(3), AsyncInt(4)), /*488*/ 528).await; + test_async_drop(AsyncInt(0), 56).await; + test_async_drop([AsyncInt(1), AsyncInt(2)], 96).await; + test_async_drop((AsyncInt(3), AsyncInt(4)), 144).await; test_async_drop(5, 0).await; let j = 42; test_async_drop(&i, 0).await; test_async_drop(&j, 0).await; - test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }, /*1688*/ 1792).await; + test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }, 200).await; test_async_drop(ManuallyDrop::new(AsyncInt(9)), 0).await; let foo = AsyncInt(10); - test_async_drop(AsyncReference { foo: &foo }, /*104*/ 112).await; + test_async_drop(AsyncReference { foo: &foo }, 56).await; let foo = AsyncInt(11); test_async_drop( @@ -75,17 +73,17 @@ fn main() { let foo = AsyncInt(10); foo }, - /*120*/ 136, + 72, ) .await; - test_async_drop(AsyncEnum::A(AsyncInt(12)), /*680*/ 736).await; - test_async_drop(AsyncEnum::B(SyncInt(13)), /*680*/ 736).await; + test_async_drop(AsyncEnum::A(AsyncInt(12)), 168).await; + test_async_drop(AsyncEnum::B(SyncInt(13)), 168).await; test_async_drop(SyncInt(14), /*16*/ 24).await; test_async_drop( SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) }, - /*3064*/ 3296, + 232, ) .await; @@ -101,11 +99,11 @@ fn main() { black_box(core::future::ready(())).await; foo }, - /*120*/ 136, + 72, ) .await; - test_async_drop(AsyncUnion { signed: 21 }, /*32*/ 40).await; + test_async_drop(AsyncUnion { signed: 21 }, 32).await; }); let res = fut.poll(&mut cx); assert_eq!(res, Poll::Ready(())); @@ -149,7 +147,10 @@ struct AsyncReference<'a> { } impl AsyncDrop for AsyncReference<'_> { - type Dropper<'a> = impl Future where Self: 'a; + type Dropper<'a> + = impl Future + where + Self: 'a; fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { async move { @@ -212,11 +213,9 @@ impl AsyncDrop for AsyncUnion { fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { async move { - println!( - "AsyncUnion::Dropper::poll: {}, {}", - unsafe { self.signed }, - unsafe { self.unsigned }, - ); + println!("AsyncUnion::Dropper::poll: {}, {}", unsafe { self.signed }, unsafe { + self.unsigned + },); } } } diff --git a/tests/ui/async-await/awaiting-unsized-param.rs b/tests/ui/async-await/awaiting-unsized-param.rs index 45611eae41f5c..b00b23c1139ee 100644 --- a/tests/ui/async-await/awaiting-unsized-param.rs +++ b/tests/ui/async-await/awaiting-unsized-param.rs @@ -7,6 +7,7 @@ use std::future::Future; async fn bug(mut f: dyn Future + Unpin) -> T { //~^ ERROR the size for values of type `(dyn Future + Unpin + 'static)` cannot be known at compilation time + //~| ERROR the size for values of type `dyn Future + Unpin` cannot be known at compilation time (&mut f).await } diff --git a/tests/ui/async-await/awaiting-unsized-param.stderr b/tests/ui/async-await/awaiting-unsized-param.stderr index 0104736976d5d..4095ca21007b3 100644 --- a/tests/ui/async-await/awaiting-unsized-param.stderr +++ b/tests/ui/async-await/awaiting-unsized-param.stderr @@ -16,6 +16,15 @@ LL | async fn bug(mut f: dyn Future + Unpin) -> T { = help: the trait `Sized` is not implemented for `(dyn Future + Unpin + 'static)` = note: all values captured by value by a closure must have a statically known size -error: aborting due to 1 previous error; 1 warning emitted +error[E0277]: the size for values of type `dyn Future + Unpin` cannot be known at compilation time + --> $DIR/awaiting-unsized-param.rs:8:21 + | +LL | async fn bug(mut f: dyn Future + Unpin) -> T { + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Future + Unpin` + = note: all values live across `await` must have a statically known size + +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout index 642e27b2a57d6..c7342b38cc503 100644 --- a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout +++ b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout @@ -1,39 +1,36 @@ -print-type-size type: `{async fn body of test()}`: 3078 bytes, alignment: 1 bytes +print-type-size type: `{async fn body of test()}`: 2053 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 0 bytes -print-type-size variant `Suspend0`: 3077 bytes -print-type-size local `.__awaitee`: 3077 bytes, type: {async fn body of calls_fut<{async fn body of big_fut()}>()} +print-type-size variant `Suspend0`: 2052 bytes +print-type-size local `.__awaitee`: 2052 bytes, type: {async fn body of calls_fut<{async fn body of big_fut()}>()} print-type-size variant `Returned`: 0 bytes print-type-size variant `Panicked`: 0 bytes -print-type-size type: `std::mem::ManuallyDrop<{async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 3077 bytes, alignment: 1 bytes -print-type-size field `.value`: 3077 bytes -print-type-size type: `std::mem::MaybeUninit<{async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 3077 bytes, alignment: 1 bytes -print-type-size variant `MaybeUninit`: 3077 bytes +print-type-size type: `std::mem::ManuallyDrop<{async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 2052 bytes, alignment: 1 bytes +print-type-size field `.value`: 2052 bytes +print-type-size type: `std::mem::MaybeUninit<{async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 2052 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 2052 bytes print-type-size field `.uninit`: 0 bytes -print-type-size field `.value`: 3077 bytes -print-type-size type: `{async fn body of calls_fut<{async fn body of big_fut()}>()}`: 3077 bytes, alignment: 1 bytes +print-type-size field `.value`: 2052 bytes +print-type-size type: `{async fn body of calls_fut<{async fn body of big_fut()}>()}`: 2052 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes -print-type-size variant `Unresumed`: 1025 bytes -print-type-size upvar `.fut`: 1025 bytes -print-type-size variant `Suspend0`: 2052 bytes -print-type-size upvar `.fut`: 1025 bytes +print-type-size variant `Unresumed`: 2051 bytes +print-type-size upvar `.fut`: 1 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size padding: 1026 bytes +print-type-size local `.fut`: 1025 bytes, alignment: 1 bytes +print-type-size variant `Suspend0`: 1027 bytes print-type-size local `.fut`: 1025 bytes print-type-size local `..coroutine_field4`: 1 bytes, type: bool print-type-size local `.__awaitee`: 1 bytes, type: {async fn body of wait()} -print-type-size variant `Suspend1`: 3076 bytes -print-type-size upvar `.fut`: 1025 bytes +print-type-size variant `Suspend1`: 2051 bytes print-type-size padding: 1025 bytes print-type-size local `..coroutine_field4`: 1 bytes, alignment: 1 bytes, type: bool print-type-size local `.__awaitee`: 1025 bytes, type: {async fn body of big_fut()} -print-type-size variant `Suspend2`: 2052 bytes -print-type-size upvar `.fut`: 1025 bytes +print-type-size variant `Suspend2`: 1027 bytes print-type-size local `.fut`: 1025 bytes print-type-size local `..coroutine_field4`: 1 bytes, type: bool print-type-size local `.__awaitee`: 1 bytes, type: {async fn body of wait()} -print-type-size variant `Returned`: 1025 bytes -print-type-size upvar `.fut`: 1025 bytes -print-type-size variant `Panicked`: 1025 bytes -print-type-size upvar `.fut`: 1025 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body of big_fut()}>`: 1025 bytes, alignment: 1 bytes print-type-size field `.value`: 1025 bytes print-type-size type: `std::mem::MaybeUninit<{async fn body of big_fut()}>`: 1025 bytes, alignment: 1 bytes @@ -43,11 +40,16 @@ print-type-size field `.value`: 1025 bytes print-type-size type: `{async fn body of big_fut()}`: 1025 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 1024 bytes -print-type-size upvar `.arg`: 1024 bytes -print-type-size variant `Returned`: 1024 bytes -print-type-size upvar `.arg`: 1024 bytes -print-type-size variant `Panicked`: 1024 bytes -print-type-size upvar `.arg`: 1024 bytes +print-type-size upvar `.arg`: 1 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size local `.arg`: 1024 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes +print-type-size type: `std::mem::ManuallyDrop<[u8; 1024]>`: 1024 bytes, alignment: 1 bytes +print-type-size field `.value`: 1024 bytes +print-type-size type: `std::mem::MaybeUninit<[u8; 1024]>`: 1024 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 1024 bytes +print-type-size field `.uninit`: 0 bytes +print-type-size field `.value`: 1024 bytes print-type-size type: `std::mem::ManuallyDrop`: 1 bytes, alignment: 1 bytes print-type-size field `.value`: 1 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes diff --git a/tests/ui/async-await/future-sizes/future-as-arg.rs b/tests/ui/async-await/future-sizes/future-as-arg.rs index 317c17b9b3e59..3bbbadc2099df 100644 --- a/tests/ui/async-await/future-sizes/future-as-arg.rs +++ b/tests/ui/async-await/future-sizes/future-as-arg.rs @@ -8,9 +8,10 @@ async fn use_future(fut: impl std::future::Future) { } fn main() { - let actual = std::mem::size_of_val( - &use_future(use_future(use_future(use_future(use_future(test([0; 16]))))))); + let actual = std::mem::size_of_val(&use_future(use_future(use_future(use_future( + use_future(test([0; 16])), + ))))); // Not using an exact number in case it slightly changes over different commits - let expected = 550; - assert!(actual > expected, "expected: >{expected}, actual: {actual}"); + let expected = 21; + assert!(actual >= expected, "expected: >{expected}, actual: {actual}"); } diff --git a/tests/ui/async-await/future-sizes/large-arg.stdout b/tests/ui/async-await/future-sizes/large-arg.stdout index 67168a3d6ef74..8e62f7f1775bf 100644 --- a/tests/ui/async-await/future-sizes/large-arg.stdout +++ b/tests/ui/async-await/future-sizes/large-arg.stdout @@ -1,44 +1,40 @@ -print-type-size type: `{async fn body of test()}`: 3076 bytes, alignment: 1 bytes +print-type-size type: `{async fn body of test()}`: 1028 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 0 bytes -print-type-size variant `Suspend0`: 3075 bytes -print-type-size local `.__awaitee`: 3075 bytes, type: {async fn body of a<[u8; 1024]>()} +print-type-size variant `Suspend0`: 1027 bytes +print-type-size local `.__awaitee`: 1027 bytes, type: {async fn body of a<[u8; 1024]>()} print-type-size variant `Returned`: 0 bytes print-type-size variant `Panicked`: 0 bytes -print-type-size type: `std::mem::ManuallyDrop<{async fn body of a<[u8; 1024]>()}>`: 3075 bytes, alignment: 1 bytes -print-type-size field `.value`: 3075 bytes -print-type-size type: `std::mem::MaybeUninit<{async fn body of a<[u8; 1024]>()}>`: 3075 bytes, alignment: 1 bytes -print-type-size variant `MaybeUninit`: 3075 bytes +print-type-size type: `std::mem::ManuallyDrop<{async fn body of a<[u8; 1024]>()}>`: 1027 bytes, alignment: 1 bytes +print-type-size field `.value`: 1027 bytes +print-type-size type: `std::mem::MaybeUninit<{async fn body of a<[u8; 1024]>()}>`: 1027 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 1027 bytes print-type-size field `.uninit`: 0 bytes -print-type-size field `.value`: 3075 bytes -print-type-size type: `{async fn body of a<[u8; 1024]>()}`: 3075 bytes, alignment: 1 bytes +print-type-size field `.value`: 1027 bytes +print-type-size type: `{async fn body of a<[u8; 1024]>()}`: 1027 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes -print-type-size variant `Suspend0`: 3074 bytes -print-type-size upvar `.t`: 1024 bytes -print-type-size local `.__awaitee`: 2050 bytes, type: {async fn body of b<[u8; 1024]>()} -print-type-size variant `Returned`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes -print-type-size variant `Panicked`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes -print-type-size type: `std::mem::ManuallyDrop<{async fn body of b<[u8; 1024]>()}>`: 2050 bytes, alignment: 1 bytes -print-type-size field `.value`: 2050 bytes -print-type-size type: `std::mem::MaybeUninit<{async fn body of b<[u8; 1024]>()}>`: 2050 bytes, alignment: 1 bytes -print-type-size variant `MaybeUninit`: 2050 bytes +print-type-size upvar `.t`: 1 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size local `.t`: 1024 bytes +print-type-size variant `Suspend0`: 1026 bytes +print-type-size local `.__awaitee`: 1026 bytes, type: {async fn body of b<[u8; 1024]>()} +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes +print-type-size type: `std::mem::ManuallyDrop<{async fn body of b<[u8; 1024]>()}>`: 1026 bytes, alignment: 1 bytes +print-type-size field `.value`: 1026 bytes +print-type-size type: `std::mem::MaybeUninit<{async fn body of b<[u8; 1024]>()}>`: 1026 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 1026 bytes print-type-size field `.uninit`: 0 bytes -print-type-size field `.value`: 2050 bytes -print-type-size type: `{async fn body of b<[u8; 1024]>()}`: 2050 bytes, alignment: 1 bytes +print-type-size field `.value`: 1026 bytes +print-type-size type: `{async fn body of b<[u8; 1024]>()}`: 1026 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes -print-type-size variant `Suspend0`: 2049 bytes -print-type-size upvar `.t`: 1024 bytes +print-type-size upvar `.t`: 1 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size local `.t`: 1024 bytes +print-type-size variant `Suspend0`: 1025 bytes print-type-size local `.__awaitee`: 1025 bytes, type: {async fn body of c<[u8; 1024]>()} -print-type-size variant `Returned`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes -print-type-size variant `Panicked`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body of c<[u8; 1024]>()}>`: 1025 bytes, alignment: 1 bytes print-type-size field `.value`: 1025 bytes print-type-size type: `std::mem::MaybeUninit<{async fn body of c<[u8; 1024]>()}>`: 1025 bytes, alignment: 1 bytes @@ -53,8 +49,13 @@ print-type-size variant `Pending`: 0 bytes print-type-size type: `{async fn body of c<[u8; 1024]>()}`: 1025 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes -print-type-size variant `Returned`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes -print-type-size variant `Panicked`: 1024 bytes -print-type-size upvar `.t`: 1024 bytes +print-type-size upvar `.t`: 1 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size local `.t`: 1024 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes +print-type-size type: `std::mem::ManuallyDrop<[u8; 1024]>`: 1024 bytes, alignment: 1 bytes +print-type-size field `.value`: 1024 bytes +print-type-size type: `std::mem::MaybeUninit<[u8; 1024]>`: 1024 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 1024 bytes +print-type-size field `.uninit`: 0 bytes +print-type-size field `.value`: 1024 bytes diff --git a/tests/ui/async-await/issue-70818.rs b/tests/ui/async-await/issue-70818.rs index 36295a84e7ad7..837d532e067d1 100644 --- a/tests/ui/async-await/issue-70818.rs +++ b/tests/ui/async-await/issue-70818.rs @@ -3,6 +3,7 @@ use std::future::Future; fn foo(ty: T, ty1: U) -> impl Future + Send { //~^ Error future cannot be sent between threads safely + //~| Error future cannot be sent between threads safely async { (ty, ty1) } } diff --git a/tests/ui/async-await/issue-70818.stderr b/tests/ui/async-await/issue-70818.stderr index 8de6a825042bb..d4fe930ce18bf 100644 --- a/tests/ui/async-await/issue-70818.stderr +++ b/tests/ui/async-await/issue-70818.stderr @@ -5,7 +5,7 @@ LL | fn foo(ty: T, ty1: U) -> impl Future + Send { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send` | note: captured value is not `Send` - --> $DIR/issue-70818.rs:6:18 + --> $DIR/issue-70818.rs:7:18 | LL | async { (ty, ty1) } | ^^^ has type `U` which is not `Send` @@ -14,5 +14,21 @@ help: consider restricting type parameter `U` with trait `Send` LL | fn foo(ty: T, ty1: U) -> impl Future + Send { | +++++++++++++++++++ -error: aborting due to 1 previous error +error: future cannot be sent between threads safely + --> $DIR/issue-70818.rs:4:38 + | +LL | fn foo(ty: T, ty1: U) -> impl Future + Send { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send` + | +note: captured value is not `Send` + --> $DIR/issue-70818.rs:4:27 + | +LL | fn foo(ty: T, ty1: U) -> impl Future + Send { + | ^^^ has type `U` which is not `Send` +help: consider restricting type parameter `U` with trait `Send` + | +LL | fn foo(ty: T, ty1: U) -> impl Future + Send { + | +++++++++++++++++++ + +error: aborting due to 2 previous errors diff --git a/tests/ui/async-await/issue-74072-lifetime-name-annotations.rs b/tests/ui/async-await/issue-74072-lifetime-name-annotations.rs index f6c9fdd6d6806..75d303751bc7e 100644 --- a/tests/ui/async-await/issue-74072-lifetime-name-annotations.rs +++ b/tests/ui/async-await/issue-74072-lifetime-name-annotations.rs @@ -9,27 +9,27 @@ pub async fn async_fn(x: &mut i32) -> &i32 { y } -pub fn async_closure(x: &mut i32) -> impl Future { +pub fn async_closure(x: &mut i32) -> impl Future { (async move || { //~^ ERROR lifetime may not live long enough //~| ERROR temporary value dropped while borrowed let y = &*x; - *x += 1; //~ ERROR cannot assign to `*x` because it is borrowed + *x += 1; //~ ERROR cannot assign to value because it is borrowed y })() } -pub fn async_closure_explicit_return_type(x: &mut i32) -> impl Future { +pub fn async_closure_explicit_return_type(x: &mut i32) -> impl Future { (async move || -> &i32 { //~^ ERROR lifetime may not live long enough //~| ERROR temporary value dropped while borrowed let y = &*x; - *x += 1; //~ ERROR cannot assign to `*x` because it is borrowed + *x += 1; //~ ERROR cannot assign to value because it is borrowed y })() } -pub fn async_block(x: &mut i32) -> impl Future { +pub fn async_block(x: &mut i32) -> impl Future { async move { let y = &*x; *x += 1; //~ ERROR cannot assign to `*x` because it is borrowed diff --git a/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr b/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr index e1f268116fc54..52d549d0b2064 100644 --- a/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr +++ b/tests/ui/async-await/issue-74072-lifetime-name-annotations.stderr @@ -10,18 +10,18 @@ LL | *x += 1; LL | y | - returning this value requires that `*x` is borrowed for `'1` -error[E0506]: cannot assign to `*x` because it is borrowed +error[E0506]: cannot assign to value because it is borrowed --> $DIR/issue-74072-lifetime-name-annotations.rs:17:9 | LL | (async move || { | - return type of async closure is &'1 i32 ... LL | let y = &*x; - | --- `*x` is borrowed here + | --- value is borrowed here LL | *x += 1; - | ^^^^^^^ `*x` is assigned to here but it was already borrowed + | ^^^^^^^ value is assigned to here but it was already borrowed LL | y - | - returning this value requires that `*x` is borrowed for `'1` + | - returning this value requires that borrow lasts for `'1` error: lifetime may not live long enough --> $DIR/issue-74072-lifetime-name-annotations.rs:13:20 @@ -44,7 +44,7 @@ LL | | })() error[E0716]: temporary value dropped while borrowed --> $DIR/issue-74072-lifetime-name-annotations.rs:13:5 | -LL | pub fn async_closure(x: &mut i32) -> impl Future { +LL | pub fn async_closure(x: &mut i32) -> impl Future { | - let's call the lifetime of this reference `'1` LL | // (async move || { LL | || @@ -59,18 +59,18 @@ LL | || })() LL | } | - temporary value is freed at the end of this statement -error[E0506]: cannot assign to `*x` because it is borrowed +error[E0506]: cannot assign to value because it is borrowed --> $DIR/issue-74072-lifetime-name-annotations.rs:27:9 | LL | (async move || -> &i32 { | - return type of async closure is &'1 i32 ... LL | let y = &*x; - | --- `*x` is borrowed here + | --- value is borrowed here LL | *x += 1; - | ^^^^^^^ `*x` is assigned to here but it was already borrowed + | ^^^^^^^ value is assigned to here but it was already borrowed LL | y - | - returning this value requires that `*x` is borrowed for `'1` + | - returning this value requires that borrow lasts for `'1` error: lifetime may not live long enough --> $DIR/issue-74072-lifetime-name-annotations.rs:23:28 @@ -93,7 +93,7 @@ LL | | })() error[E0716]: temporary value dropped while borrowed --> $DIR/issue-74072-lifetime-name-annotations.rs:23:5 | -LL | pub fn async_closure_explicit_return_type(x: &mut i32) -> impl Future { +LL | pub fn async_closure_explicit_return_type(x: &mut i32) -> impl Future { | - let's call the lifetime of this reference `'1` LL | // (async move || -> &i32 { LL | || diff --git a/tests/ui/async-await/issue-86507.rs b/tests/ui/async-await/issue-86507.rs index 484122a1ddcfd..e959f2c1166f4 100644 --- a/tests/ui/async-await/issue-86507.rs +++ b/tests/ui/async-await/issue-86507.rs @@ -15,6 +15,7 @@ impl Foo for () { -> Pin + Send + 'async_trait>> where 'me:'async_trait { Box::pin( //~ ERROR future cannot be sent between threads safely + //~^ ERROR future cannot be sent between threads safely async move { let x = x; } diff --git a/tests/ui/async-await/issue-86507.stderr b/tests/ui/async-await/issue-86507.stderr index 6385a8c975e34..83a2c9fe95c01 100644 --- a/tests/ui/async-await/issue-86507.stderr +++ b/tests/ui/async-await/issue-86507.stderr @@ -2,6 +2,7 @@ error: future cannot be sent between threads safely --> $DIR/issue-86507.rs:17:13 | LL | / Box::pin( +LL | | LL | | async move { LL | | let x = x; LL | | } @@ -9,15 +10,37 @@ LL | | ) | |_____________^ future created by async block is not `Send` | note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - --> $DIR/issue-86507.rs:19:29 + --> $DIR/issue-86507.rs:20:29 | LL | let x = x; | ^ has type `&T` which is not `Send`, because `T` is not `Sync` - = note: required for the cast from `Pin>` to `Pin + Send + 'async_trait)>>` + = note: required for the cast from `Pin>` to `Pin + Send + 'async_trait)>>` help: consider further restricting type parameter `T` with trait `Sync` | LL | fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T) | +++++++++++++++++++ -error: aborting due to 1 previous error +error: future cannot be sent between threads safely + --> $DIR/issue-86507.rs:17:13 + | +LL | / Box::pin( +LL | | +LL | | async move { +LL | | let x = x; +LL | | } +LL | | ) + | |_____________^ future created by async block is not `Send` + | +note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + --> $DIR/issue-86507.rs:14:40 + | +LL | fn bar<'me, 'async_trait, T: Send>(x: &'me T) + | ^ has type `&T` which is not `Send`, because `T` is not `Sync` + = note: required for the cast from `Pin>` to `Pin + Send>>` +help: consider further restricting type parameter `T` with trait `Sync` + | +LL | fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T) + | +++++++++++++++++++ + +error: aborting due to 2 previous errors diff --git a/tests/ui/coroutine/auto-trait-regions.rs b/tests/ui/coroutine/auto-trait-regions.rs index 4c239f9ee76cf..c1ad400363af4 100644 --- a/tests/ui/coroutine/auto-trait-regions.rs +++ b/tests/ui/coroutine/auto-trait-regions.rs @@ -30,6 +30,8 @@ fn main() { }; assert_foo(gen); //~^ ERROR implementation of `Foo` is not general enough + //~| ERROR implementation of `Foo` is not general enough + //~| ERROR implementation of `Foo` is not general enough // Allow impls which matches any lifetime let x = &OnlyFooIfRef(No); diff --git a/tests/ui/coroutine/auto-trait-regions.stderr b/tests/ui/coroutine/auto-trait-regions.stderr index a9a0bde2ba019..8ffa6ab6a549a 100644 --- a/tests/ui/coroutine/auto-trait-regions.stderr +++ b/tests/ui/coroutine/auto-trait-regions.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/auto-trait-regions.rs:45:24 + --> $DIR/auto-trait-regions.rs:47:24 | LL | let a = A(&mut true, &mut true, No); | ^^^^ - temporary value is freed at the end of this statement @@ -16,7 +16,7 @@ LL ~ let a = A(&mut binding, &mut true, No); | error[E0716]: temporary value dropped while borrowed - --> $DIR/auto-trait-regions.rs:45:35 + --> $DIR/auto-trait-regions.rs:47:35 | LL | let a = A(&mut true, &mut true, No); | ^^^^ - temporary value is freed at the end of this statement @@ -42,7 +42,27 @@ LL | assert_foo(gen); = note: ...but `Foo` is actually implemented for the type `&'static OnlyFooIfStaticRef` error: implementation of `Foo` is not general enough - --> $DIR/auto-trait-regions.rs:51:5 + --> $DIR/auto-trait-regions.rs:31:5 + | +LL | assert_foo(gen); + | ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough + | + = note: `&'0 OnlyFooIfStaticRef` must implement `Foo`, for any lifetime `'0`... + = note: ...but `Foo` is actually implemented for the type `&'static OnlyFooIfStaticRef` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: implementation of `Foo` is not general enough + --> $DIR/auto-trait-regions.rs:31:5 + | +LL | assert_foo(gen); + | ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough + | + = note: `&'0 OnlyFooIfStaticRef` must implement `Foo`, for any lifetime `'0`... + = note: ...but `Foo` is actually implemented for the type `&'static OnlyFooIfStaticRef` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: implementation of `Foo` is not general enough + --> $DIR/auto-trait-regions.rs:53:5 | LL | assert_foo(gen); | ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough @@ -50,6 +70,6 @@ LL | assert_foo(gen); = note: `Foo` would have to be implemented for the type `A<'0, '1>`, for any two lifetimes `'0` and `'1`... = note: ...but `Foo` is actually implemented for the type `A<'_, '2>`, for some specific lifetime `'2` -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/coroutine/clone-impl.stderr b/tests/ui/coroutine/clone-impl.stderr index ed933fe784edd..f8c1b7ae65bf7 100644 --- a/tests/ui/coroutine/clone-impl.stderr +++ b/tests/ui/coroutine/clone-impl.stderr @@ -7,11 +7,14 @@ LL | move || { LL | check_copy(&gen_clone_0); | ^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:44:5: 44:12}`, the trait `Copy` is not implemented for `Vec` | -note: captured value does not implement `Copy` - --> $DIR/clone-impl.rs:48:14 +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/clone-impl.rs:46:9 | -LL | drop(clonable_0); - | ^^^^^^^^^^ has type `Vec` which does not implement `Copy` +LL | let clonable_0: Vec = Vec::new(); + | ---------- has type `Vec` which does not implement `Copy` +... +LL | yield; + | ^^^^^ yield occurs here, with `clonable_0` maybe used later note: required by a bound in `check_copy` --> $DIR/clone-impl.rs:90:18 | @@ -49,11 +52,14 @@ LL | move || { LL | check_copy(&gen_clone_1); | ^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:59:5: 59:12}`, the trait `Copy` is not implemented for `Vec` | -note: captured value does not implement `Copy` - --> $DIR/clone-impl.rs:69:14 +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/clone-impl.rs:65:9 | -LL | drop(clonable_1); - | ^^^^^^^^^^ has type `Vec` which does not implement `Copy` +LL | let clonable_1: Vec = Vec::new(); + | ---------- has type `Vec` which does not implement `Copy` +... +LL | yield; + | ^^^^^ yield occurs here, with `clonable_1` maybe used later note: required by a bound in `check_copy` --> $DIR/clone-impl.rs:90:18 | @@ -92,11 +98,14 @@ LL | move || { LL | check_copy(&gen_non_clone); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:80:5: 80:12}`, the trait `Copy` is not implemented for `NonClone` | -note: captured value does not implement `Copy` - --> $DIR/clone-impl.rs:82:14 +note: coroutine does not implement `Copy` as this value is used across a yield + --> $DIR/clone-impl.rs:81:9 | -LL | drop(non_clonable); - | ^^^^^^^^^^^^ has type `NonClone` which does not implement `Copy` +LL | let non_clonable: NonClone = NonClone; + | ------------ has type `NonClone` which does not implement `Copy` +... +LL | yield; + | ^^^^^ yield occurs here, with `non_clonable` maybe used later note: required by a bound in `check_copy` --> $DIR/clone-impl.rs:90:18 | @@ -117,11 +126,14 @@ LL | move || { LL | check_clone(&gen_non_clone); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `{coroutine@$DIR/clone-impl.rs:80:5: 80:12}`, the trait `Clone` is not implemented for `NonClone` | -note: captured value does not implement `Clone` - --> $DIR/clone-impl.rs:82:14 +note: coroutine does not implement `Clone` as this value is used across a yield + --> $DIR/clone-impl.rs:81:9 | -LL | drop(non_clonable); - | ^^^^^^^^^^^^ has type `NonClone` which does not implement `Clone` +LL | let non_clonable: NonClone = NonClone; + | ------------ has type `NonClone` which does not implement `Clone` +... +LL | yield; + | ^^^^^ yield occurs here, with `non_clonable` maybe used later note: required by a bound in `check_clone` --> $DIR/clone-impl.rs:91:19 | diff --git a/tests/ui/coroutine/ref-escapes-but-not-over-yield.rs b/tests/ui/coroutine/ref-escapes-but-not-over-yield.rs index 0f9c56786da06..def6fc0600729 100644 --- a/tests/ui/coroutine/ref-escapes-but-not-over-yield.rs +++ b/tests/ui/coroutine/ref-escapes-but-not-over-yield.rs @@ -1,16 +1,13 @@ #![feature(coroutines, stmt_expr_attributes)] fn foo(x: &i32) { - // In this case, a reference to `b` escapes the coroutine, but not - // because of a yield. We see that there is no yield in the scope of - // `b` and give the more generic error message. - let mut a = &3; + let a = &mut &3; let mut b = #[coroutine] move || { yield (); let b = 5; - a = &b; - //~^ ERROR borrowed data escapes outside of coroutine + *a = &b; + //~^ ERROR: borrowed data escapes outside of coroutine }; } diff --git a/tests/ui/coroutine/ref-escapes-but-not-over-yield.stderr b/tests/ui/coroutine/ref-escapes-but-not-over-yield.stderr index 6fa7082c0b8b7..4e4577c9ceb50 100644 --- a/tests/ui/coroutine/ref-escapes-but-not-over-yield.stderr +++ b/tests/ui/coroutine/ref-escapes-but-not-over-yield.stderr @@ -1,13 +1,13 @@ error[E0521]: borrowed data escapes outside of coroutine - --> $DIR/ref-escapes-but-not-over-yield.rs:12:9 + --> $DIR/ref-escapes-but-not-over-yield.rs:9:9 | -LL | let mut a = &3; - | ----- `a` declared here, outside of the coroutine body +LL | let a = &mut &3; + | - `a` declared here, outside of the coroutine body ... -LL | a = &b; - | ^^^^-- - | | | - | | borrow is only valid in the coroutine body +LL | *a = &b; + | ^^^^^-- + | | | + | | borrow is only valid in the coroutine body | reference to `b` escapes the coroutine body here error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/ref-upvar-not-send.rs b/tests/ui/coroutine/ref-upvar-not-send.rs index 89bb5e5495f45..387c21015744d 100644 --- a/tests/ui/coroutine/ref-upvar-not-send.rs +++ b/tests/ui/coroutine/ref-upvar-not-send.rs @@ -8,24 +8,42 @@ fn assert_send(_: T) {} //~| NOTE required by this bound in `assert_send` //~| NOTE required by a bound in `assert_send` //~| NOTE required by this bound in `assert_send` +//~| NOTE required by a bound in `assert_send` +//~| NOTE required by this bound in `assert_send` +//~| NOTE required by a bound in `assert_send` +//~| NOTE required by this bound in `assert_send` fn main() { let x: &*mut () = &std::ptr::null_mut(); + //~^ NOTE has type `&*mut ()` which is not `Send` + //~| NOTE captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` let y: &mut *mut () = &mut std::ptr::null_mut(); - assert_send(#[coroutine] move || { + //~^ NOTE has type `&mut *mut ()` which is not `Send` + //~| NOTE captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` + assert_send( //~^ ERROR coroutine cannot be sent between threads safely //~| NOTE coroutine is not `Send` - yield; - let _x = x; - }); - //~^^ NOTE captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - //~| NOTE has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` - assert_send(#[coroutine] move || { + #[coroutine] + move || { + //~^ ERROR coroutine cannot be sent between threads safely + //~| NOTE coroutine is not `Send` + yield; + let _x = x; + //~^ NOTE captured value is not `Send` + //~| NOTE has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` + }, + ); + assert_send( //~^ ERROR coroutine cannot be sent between threads safely //~| NOTE coroutine is not `Send` - yield; - let _y = y; - }); - //~^^ NOTE captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` - //~| NOTE has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` + #[coroutine] + move || { + //~^ ERROR coroutine cannot be sent between threads safely + //~| NOTE coroutine is not `Send` + yield; + let _y = y; + //~^ NOTE captured value is not `Send` + //~| NOTE has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` + }, + ); } diff --git a/tests/ui/coroutine/ref-upvar-not-send.stderr b/tests/ui/coroutine/ref-upvar-not-send.stderr index 892b5d261c2b8..48c0619172ea9 100644 --- a/tests/ui/coroutine/ref-upvar-not-send.stderr +++ b/tests/ui/coroutine/ref-upvar-not-send.stderr @@ -1,21 +1,67 @@ error: coroutine cannot be sent between threads safely - --> $DIR/ref-upvar-not-send.rs:15:30 + --> $DIR/ref-upvar-not-send.rs:27:9 | -LL | assert_send(#[coroutine] move || { - | ______________________________^ +LL | / move || { LL | | LL | | -LL | | yield; -LL | | let _x = x; -LL | | }); +LL | | yield; +... | +LL | | }, + | |_________^ coroutine is not `Send` + | + = help: the trait `Sync` is not implemented for `*mut ()` +note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + --> $DIR/ref-upvar-not-send.rs:31:22 + | +LL | let _x = x; + | ^ has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` +note: required by a bound in `assert_send` + --> $DIR/ref-upvar-not-send.rs:6:19 + | +LL | fn assert_send(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: coroutine cannot be sent between threads safely + --> $DIR/ref-upvar-not-send.rs:40:9 + | +LL | / move || { +LL | | +LL | | +LL | | yield; +... | +LL | | }, + | |_________^ coroutine is not `Send` + | + = help: within `{coroutine@$DIR/ref-upvar-not-send.rs:40:9: 40:16}`, the trait `Send` is not implemented for `*mut ()` +note: captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` + --> $DIR/ref-upvar-not-send.rs:44:22 + | +LL | let _y = y; + | ^ has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` +note: required by a bound in `assert_send` + --> $DIR/ref-upvar-not-send.rs:6:19 + | +LL | fn assert_send(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: coroutine cannot be sent between threads safely + --> $DIR/ref-upvar-not-send.rs:23:5 + | +LL | / assert_send( +LL | | +LL | | +LL | | #[coroutine] +... | +LL | | }, +LL | | ); | |_____^ coroutine is not `Send` | = help: the trait `Sync` is not implemented for `*mut ()` note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` - --> $DIR/ref-upvar-not-send.rs:19:18 + --> $DIR/ref-upvar-not-send.rs:17:9 | -LL | let _x = x; - | ^ has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` +LL | let x: &*mut () = &std::ptr::null_mut(); + | ^ has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` note: required by a bound in `assert_send` --> $DIR/ref-upvar-not-send.rs:6:19 | @@ -23,28 +69,28 @@ LL | fn assert_send(_: T) {} | ^^^^ required by this bound in `assert_send` error: coroutine cannot be sent between threads safely - --> $DIR/ref-upvar-not-send.rs:23:30 + --> $DIR/ref-upvar-not-send.rs:36:5 | -LL | assert_send(#[coroutine] move || { - | ______________________________^ +LL | / assert_send( LL | | LL | | -LL | | yield; -LL | | let _y = y; -LL | | }); +LL | | #[coroutine] +... | +LL | | }, +LL | | ); | |_____^ coroutine is not `Send` | - = help: within `{coroutine@$DIR/ref-upvar-not-send.rs:23:30: 23:37}`, the trait `Send` is not implemented for `*mut ()` + = help: within `{coroutine@$DIR/ref-upvar-not-send.rs:40:9: 40:16}`, the trait `Send` is not implemented for `*mut ()` note: captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` - --> $DIR/ref-upvar-not-send.rs:27:18 + --> $DIR/ref-upvar-not-send.rs:20:9 | -LL | let _y = y; - | ^ has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` +LL | let y: &mut *mut () = &mut std::ptr::null_mut(); + | ^ has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` note: required by a bound in `assert_send` --> $DIR/ref-upvar-not-send.rs:6:19 | LL | fn assert_send(_: T) {} | ^^^^ required by this bound in `assert_send` -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/coroutine/unsized-capture-across-yield.rs b/tests/ui/coroutine/unsized-capture-across-yield.rs index c86b1823aafff..27b0581e8d7b8 100644 --- a/tests/ui/coroutine/unsized-capture-across-yield.rs +++ b/tests/ui/coroutine/unsized-capture-across-yield.rs @@ -7,6 +7,7 @@ use std::ops::Coroutine; fn capture() -> impl Coroutine { let b: [u8] = *(Box::new([]) as Box<[u8]>); + //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time #[coroutine] move || { println!("{:?}", &b); diff --git a/tests/ui/coroutine/unsized-capture-across-yield.stderr b/tests/ui/coroutine/unsized-capture-across-yield.stderr index 03551f1bbff51..6faa9226f9123 100644 --- a/tests/ui/coroutine/unsized-capture-across-yield.stderr +++ b/tests/ui/coroutine/unsized-capture-across-yield.stderr @@ -8,7 +8,7 @@ LL | #![feature(unsized_locals)] = note: `#[warn(incomplete_features)]` on by default error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/unsized-capture-across-yield.rs:12:27 + --> $DIR/unsized-capture-across-yield.rs:13:27 | LL | move || { | -- this closure captures all values by move @@ -18,6 +18,15 @@ LL | println!("{:?}", &b); = help: the trait `Sized` is not implemented for `[u8]` = note: all values captured by value by a closure must have a statically known size -error: aborting due to 1 previous error; 1 warning emitted +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-capture-across-yield.rs:9:9 + | +LL | let b: [u8] = *(Box::new([]) as Box<[u8]>); + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: all values live across `yield` must have a statically known size + +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 2d2731e4368f5..63d373088eca9 100644 --- a/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/tests/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -92,6 +92,12 @@ error[E0720]: cannot resolve opaque type | LL | fn coroutine_capture() -> impl Sized { | ^^^^^^^^^^ recursive opaque type +LL | +LL | let x = coroutine_capture(); + | - + | | + | coroutine captures itself here + | coroutine captures itself here ... LL | / move || { LL | | yield; diff --git a/tests/ui/mir/lint/storage-live.stderr b/tests/ui/mir/lint/storage-live.stderr index 651b8e2327ee6..1b936ea12692a 100644 --- a/tests/ui/mir/lint/storage-live.stderr +++ b/tests/ui/mir/lint/storage-live.stderr @@ -1,4 +1,4 @@ -error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (after pass CheckForceInline) at bb0[1]: +error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (after pass CheckForceInline 1-1-000) at bb0[1]: StorageLive(_1) which already has storage here --> $DIR/storage-live.rs:23:13 | diff --git a/tests/ui/print_type_sizes/async.stdout b/tests/ui/print_type_sizes/async.stdout index 83a6962e4cd13..6bc30dc71154d 100644 --- a/tests/ui/print_type_sizes/async.stdout +++ b/tests/ui/print_type_sizes/async.stdout @@ -1,15 +1,13 @@ -print-type-size type: `{async fn body of test()}`: 16386 bytes, alignment: 1 bytes +print-type-size type: `{async fn body of test()}`: 8194 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 8192 bytes -print-type-size upvar `.arg`: 8192 bytes -print-type-size variant `Suspend0`: 16385 bytes -print-type-size upvar `.arg`: 8192 bytes +print-type-size upvar `.arg`: 1 bytes, offset: 0 bytes, alignment: 1 bytes print-type-size local `.arg`: 8192 bytes +print-type-size variant `Suspend0`: 8193 bytes print-type-size local `.__awaitee`: 1 bytes, type: {async fn body of wait()} -print-type-size variant `Returned`: 8192 bytes -print-type-size upvar `.arg`: 8192 bytes -print-type-size variant `Panicked`: 8192 bytes -print-type-size upvar `.arg`: 8192 bytes +print-type-size local `.arg`: 8192 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes print-type-size type: `std::mem::ManuallyDrop<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes print-type-size field `.value`: 8192 bytes print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes diff --git a/tests/ui/print_type_sizes/coroutine.stdout b/tests/ui/print_type_sizes/coroutine.stdout index 339bbddfc2a93..6abee21d415b9 100644 --- a/tests/ui/print_type_sizes/coroutine.stdout +++ b/tests/ui/print_type_sizes/coroutine.stdout @@ -1,10 +1,15 @@ print-type-size type: `{coroutine@$DIR/coroutine.rs:11:5: 11:14}`: 8193 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 8192 bytes -print-type-size upvar `.array`: 8192 bytes +print-type-size upvar `.array`: 1 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size local `.array`: 8192 bytes print-type-size variant `Suspend0`: 8192 bytes -print-type-size upvar `.array`: 8192 bytes -print-type-size variant `Returned`: 8192 bytes -print-type-size upvar `.array`: 8192 bytes -print-type-size variant `Panicked`: 8192 bytes -print-type-size upvar `.array`: 8192 bytes +print-type-size local `.array`: 8192 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes +print-type-size type: `std::mem::ManuallyDrop<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes +print-type-size field `.value`: 8192 bytes +print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 8192 bytes +print-type-size field `.uninit`: 0 bytes +print-type-size field `.value`: 8192 bytes diff --git a/tests/ui/stable-mir-print/async-closure.stdout b/tests/ui/stable-mir-print/async-closure.stdout index 21df1fd395403..4167af21cea46 100644 --- a/tests/ui/stable-mir-print/async-closure.stdout +++ b/tests/ui/stable-mir-print/async-closure.stdout @@ -27,13 +27,13 @@ fn foo::{closure#0}(_1: &{async closure@$DIR/async-closure.rs:9:13: 9:21}) -> {a fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}>, _2: &mut Context<'_>) -> Poll<()> { let mut _0: Poll<()>; let _3: i32; - let mut _4: &i32; + let _4: &i32; let mut _5: u32; let mut _6: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _7: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _8: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; debug _task_context => _2; - debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32)); + debug y => (*_4); debug y => _3; bb0: { _6 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); @@ -42,7 +42,7 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo } bb1: { _7 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _4 = CopyForDeref(((*_7).0: &i32)); + _4 = move (((*_7) as variant#0).0: &i32); _3 = (*_4); _0 = std::task::Poll::Ready(()); _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); @@ -59,13 +59,13 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo fn foo::{closure#0}::{closure#1}(_1: Pin<&mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}>, _2: &mut Context<'_>) -> Poll<()> { let mut _0: Poll<()>; let _3: i32; - let mut _4: &i32; + let _4: &i32; let mut _5: u32; let mut _6: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _7: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _8: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; debug _task_context => _2; - debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32)); + debug y => (*_4); debug y => _3; bb0: { _6 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); @@ -74,7 +74,7 @@ fn foo::{closure#0}::{closure#1}(_1: Pin<&mut {async closure body@$DIR/async-clo } bb1: { _7 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _4 = CopyForDeref(((*_7).0: &i32)); + _4 = move (((*_7) as variant#0).0: &i32); _3 = (*_4); _0 = std::task::Poll::Ready(()); _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr index 2ca6a1994485a..c12a270e8a2b9 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr @@ -39,13 +39,13 @@ LL | call(operation).await | ^^^^^^^^^^^^^^^ error[E0792]: expected generic lifetime parameter, found `'any` - --> $DIR/hkl_forbidden4.rs:14:5 + --> $DIR/hkl_forbidden4.rs:14:21 | LL | async fn operation(_: &mut ()) -> () { | - this generic parameter must be used with a generic lifetime parameter LL | LL | call(operation).await - | ^^^^^^^^^^^^^^^ + | ^^^^^ error[E0792]: expected generic lifetime parameter, found `'any` --> $DIR/hkl_forbidden4.rs:23:1 From 9e9e4e0479cc621daf8c87985aac13b22bafbd9b Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Wed, 12 Mar 2025 00:17:59 +0800 Subject: [PATCH 2/9] assign upvars with saved locals for analysis Co-authored-by: Dario Nieuwenhuis --- compiler/rustc_borrowck/src/type_check/mod.rs | 23 +- compiler/rustc_codegen_cranelift/src/base.rs | 3 + compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 3 + compiler/rustc_index/src/vec.rs | 35 ++ compiler/rustc_middle/src/mir/mod.rs | 5 + compiler/rustc_middle/src/mir/query.rs | 10 +- compiler/rustc_middle/src/mir/statement.rs | 5 +- compiler/rustc_middle/src/ty/layout.rs | 5 +- compiler/rustc_middle/src/ty/sty.rs | 7 - .../rustc_mir_build/src/builder/custom/mod.rs | 1 + .../src/coroutine/relocate_upvars.rs | 314 ++++++++++++++++++ compiler/rustc_mir_transform/src/lib.rs | 9 +- compiler/rustc_mir_transform/src/simplify.rs | 15 +- compiler/rustc_mir_transform/src/validate.rs | 13 +- 14 files changed, 410 insertions(+), 38 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/coroutine/relocate_upvars.rs diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 0fda3e31690d1..ea317519cf784 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -25,9 +25,9 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::{ - self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, CoroutineArgsExt, - Dynamic, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, - TypeVisitableExt, UserArgs, UserTypeAnnotationIndex, fold_regions, + self, Binder, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, Dynamic, + GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, TypeVisitableExt, + UserArgs, UserTypeAnnotationIndex, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::ResultsCursor; @@ -2188,14 +2188,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } AggregateKind::Coroutine(_, args) => { - // It doesn't make sense to look at a field beyond the prefix; - // these require a variant index, and are not initialized in - // aggregate rvalues. - match args.as_coroutine().prefix_tys().get(field_index.as_usize()) { - Some(ty) => Ok(*ty), - None => Err(FieldAccessError::OutOfRange { - field_count: args.as_coroutine().prefix_tys().len(), - }), + // It doesn't make sense to look at a field beyond the captured + // upvars. + // Otherwise it require a variant index, and are not initialized + // in aggregate rvalues. + let upvar_tys = &args.as_coroutine().upvar_tys(); + if let Some(ty) = upvar_tys.get(field_index.as_usize()) { + Ok(*ty) + } else { + Err(FieldAccessError::OutOfRange { field_count: upvar_tys.len() }) } } AggregateKind::CoroutineClosure(_, args) => { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 125a9201831ca..2da493516cf35 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -915,6 +915,9 @@ fn codegen_stmt<'tcx>( let variant_dest = lval.downcast_variant(fx, variant_index); (variant_index, variant_dest, active_field_index) } + mir::AggregateKind::Coroutine(_def_id, _args) => { + (FIRST_VARIANT, lval.downcast_variant(fx, FIRST_VARIANT), None) + } _ => (FIRST_VARIANT, lval, None), }; if active_field_index.is_some() { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 96be44ee09ff1..aacf17629e780 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -144,6 +144,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let variant_dest = dest.project_downcast(bx, variant_index); (variant_index, variant_dest, active_field_index) } + mir::AggregateKind::Coroutine(_, _) => { + (FIRST_VARIANT, dest.project_downcast(bx, FIRST_VARIANT), None) + } _ => (FIRST_VARIANT, dest, None), }; if active_field_index.is_some() { diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index 13f0dda180be9..90eb212e69bf5 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -197,6 +197,11 @@ impl IndexVec { pub fn append(&mut self, other: &mut Self) { self.raw.append(&mut other.raw); } + + #[inline] + pub fn debug_map_view(&self) -> IndexSliceMapView<'_, I, T> { + IndexSliceMapView(self.as_slice()) + } } /// `IndexVec` is often used as a map, so it provides some map-like APIs. @@ -220,14 +225,44 @@ impl IndexVec> { pub fn contains(&self, index: I) -> bool { self.get(index).and_then(Option::as_ref).is_some() } + + #[inline] + pub fn debug_map_view_compact(&self) -> IndexSliceMapViewCompact<'_, I, T> { + IndexSliceMapViewCompact(self.as_slice()) + } } +pub struct IndexSliceMapView<'a, I: Idx, T>(&'a IndexSlice); +pub struct IndexSliceMapViewCompact<'a, I: Idx, T>(&'a IndexSlice>); + impl fmt::Debug for IndexVec { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.raw, fmt) } } +impl<'a, I: Idx, T: fmt::Debug> fmt::Debug for IndexSliceMapView<'a, I, T> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut entries = fmt.debug_map(); + for (idx, val) in self.0.iter_enumerated() { + entries.entry(&idx, val); + } + entries.finish() + } +} + +impl<'a, I: Idx, T: fmt::Debug> fmt::Debug for IndexSliceMapViewCompact<'a, I, T> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut entries = fmt.debug_map(); + for (idx, val) in self.0.iter_enumerated() { + if let Some(val) = val { + entries.entry(&idx, val); + } + } + entries.finish() + } +} + impl Deref for IndexVec { type Target = IndexSlice; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 7090e93549e69..31e13d68a917d 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -367,6 +367,9 @@ pub struct Body<'tcx> { #[type_foldable(identity)] #[type_visitable(ignore)] pub function_coverage_info: Option>, + + /// Coroutine local-upvar map + pub local_upvar_map: IndexVec>, } impl<'tcx> Body<'tcx> { @@ -410,6 +413,7 @@ impl<'tcx> Body<'tcx> { tainted_by_errors, coverage_info_hi: None, function_coverage_info: None, + local_upvar_map: IndexVec::new(), }; body.is_polymorphic = body.has_non_region_param(); body @@ -441,6 +445,7 @@ impl<'tcx> Body<'tcx> { tainted_by_errors: None, coverage_info_hi: None, function_coverage_info: None, + local_upvar_map: IndexVec::new(), }; body.is_polymorphic = body.has_non_region_param(); body diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 5a9fe10938ae1..3b1ec30e455e7 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -10,6 +10,7 @@ use rustc_index::bit_set::BitMatrix; use rustc_index::{Idx, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::{Span, Symbol}; +use rustc_type_ir::data_structures::IndexMap; use smallvec::SmallVec; use super::{ConstValue, SourceInfo}; @@ -18,7 +19,7 @@ use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty, TyCtxt, fold_regio rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] - #[debug_format = "_{}"] + #[debug_format = "corsl_{}"] pub struct CoroutineSavedLocal {} } @@ -56,6 +57,13 @@ pub struct CoroutineLayout<'tcx> { #[type_foldable(identity)] #[type_visitable(ignore)] pub storage_conflicts: BitMatrix, + + /// This map `A -> B` allows later MIR passes, error reporters + /// and layout calculator to relate saved locals `A` sourced from upvars + /// and locals `B` that upvars are moved into. + #[type_foldable(identity)] + #[type_visitable(ignore)] + pub relocated_upvars: IndexMap, } impl Debug for CoroutineLayout<'_> { diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index f05a798949b7e..57355febcd040 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -123,11 +123,10 @@ impl<'tcx> PlaceTy<'tcx> { .get(f.index()) .copied() .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")), - // Only prefix fields (upvars and current state) are - // accessible without a variant index. + // Only upvars are accessible without a variant index. ty::Coroutine(_, args) => args .as_coroutine() - .prefix_tys() + .upvar_tys() .get(f.index()) .copied() .unwrap_or_else(|| bug!("field {f:?} out of range: {self:?}")), diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index ebb6a8c08a54c..48e6efc0c0ca5 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -935,9 +935,10 @@ where ), Variants::Multiple { tag, tag_field, .. } => { if i == tag_field { - return TyMaybeWithLayout::TyAndLayout(tag_layout(tag)); + TyMaybeWithLayout::TyAndLayout(tag_layout(tag)) + } else { + TyMaybeWithLayout::Ty(args.as_coroutine().upvar_tys()[i]) } - TyMaybeWithLayout::Ty(args.as_coroutine().prefix_tys()[i]) } }, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 74a94d8278453..9a86cb114b311 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -145,13 +145,6 @@ impl<'tcx> ty::CoroutineArgs> { }) }) } - - /// This is the types of the fields of a coroutine which are not stored in a - /// variant. - #[inline] - fn prefix_tys(self) -> &'tcx List> { - self.upvar_tys() - } } #[derive(Debug, Copy, Clone, HashStable, TypeFoldable, TypeVisitable)] diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index bfc16816e2e5e..2b29434efe54f 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -61,6 +61,7 @@ pub(super) fn build_custom_mir<'tcx>( pass_count: 0, coverage_info_hi: None, function_coverage_info: None, + local_upvar_map: IndexVec::new(), }; body.local_decls.push(LocalDecl::new(return_ty, return_ty_span)); diff --git a/compiler/rustc_mir_transform/src/coroutine/relocate_upvars.rs b/compiler/rustc_mir_transform/src/coroutine/relocate_upvars.rs new file mode 100644 index 0000000000000..495c966a06f5d --- /dev/null +++ b/compiler/rustc_mir_transform/src/coroutine/relocate_upvars.rs @@ -0,0 +1,314 @@ +//! MIR rewrite pass to promote upvars into native locals in the coroutine body +//! +//! # Summary +//! This pass performs the following transformations. +//! 1. It generates a fresh batch of locals for each captured upvars. +//! +//! For each upvar, whether used or not, a fresh local is created with the same type. +//! +//! 2. It replaces the places pointing into those upvars with places pointing into those locals instead +//! +//! Each place that starts with access into the coroutine structure `_1` is replaced with the fresh local as +//! the base. For instance, `(_1.4 as Some).0` is rewritten into `(_34 as Some).0` when `_34` is the fresh local +//! corresponding to the captured upvar stored in `_1.4`. +//! +//! 3. It assembles an prologue to replace the current entry block. +//! +//! This prologue block transfers every captured upvar into its corresponding fresh local, *via scratch locals*. +//! The upvars are first completely moved into the scratch locals in batch, and then moved into the destination +//! locals in batch. +//! The reason is that it is possible that coroutine layout may change and the source memory location of +//! an upvar may not necessarily be mapped exactly to the same place as in the `Unresumed` state. +//! While coroutine layout ensures that the same saved local has stable offsets throughout its lifetime, +//! technically the upvar in `Unresumed` state and their fresh locals are different saved locals. +//! This scratch locals re-estabilish safety so that the correct data permutation can take place. + +use rustc_abi::FieldIdx; +use rustc_index::{IndexSlice, IndexVec}; +use rustc_middle::bug; +use rustc_middle::mir::visit::MutVisitor; +use rustc_middle::mir::{ + self, BasicBlock, BasicBlockData, Body, Local, Place, ProjectionElem, START_BLOCK, SourceInfo, + Statement, StatementKind, Terminator, TerminatorKind, UnwindAction, +}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::Span; + +use crate::pass_manager::MirPass; +use crate::patch::MirPatch; + +pub(crate) struct RelocateUpvars; + +struct UpvarSubstitution<'tcx> { + /// Newly minted local into which the upvar is moved + local: Local, + reloc: Local, + /// Place into the capture structure where this upvar is found + upvar_place: Place<'tcx>, + span: Span, +} + +struct SubstituteUpvarVisitor<'tcx, 'a> { + tcx: TyCtxt<'tcx>, + mappings: &'a IndexSlice>, +} + +impl<'tcx, 'a> MutVisitor<'tcx> for SubstituteUpvarVisitor<'tcx, 'a> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_place( + &mut self, + place: &mut Place<'tcx>, + _context: mir::visit::PlaceContext, + location: mir::Location, + ) { + if let Place { local: ty::CAPTURE_STRUCT_LOCAL, projection } = place + && let [ProjectionElem::Field(field_idx, _ty), rest @ ..] = &***projection + { + let Some(&UpvarSubstitution { local, .. }) = self.mappings.get(*field_idx) else { + bug!( + "SubstituteUpvar: found {field_idx:?} @ {location:?} but there is no upvar for it" + ) + }; + let new_place = Place::from(local); + let new_place = new_place.project_deeper(rest, self.tcx); + *place = new_place; + } + } + + fn visit_terminator( + &mut self, + terminator: &mut mir::Terminator<'tcx>, + location: mir::Location, + ) { + if let TerminatorKind::Drop { place, .. } = &terminator.kind + && let Some(ty::CAPTURE_STRUCT_LOCAL) = place.as_local() + { + // This is a drop on the whole coroutine state, which we will processed later + return; + } + self.super_terminator(terminator, location) + } +} + +impl<'tcx> MirPass<'tcx> for RelocateUpvars { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let Some(coroutine_def_id) = body.source.def_id().as_local() else { + return; + }; + if tcx.coroutine_kind(coroutine_def_id).is_none() { + return; + } + + // The first argument is the coroutine type passed by value + let coroutine_ty = if let Some(decl) = body.local_decls.get(ty::CAPTURE_STRUCT_LOCAL) { + decl.ty + } else { + return; + }; + + // We only care when there is at least one upvar + let (def_id, upvar_tys) = if let ty::Coroutine(def_id, args) = *coroutine_ty.kind() { + let args = args.as_coroutine(); + (def_id, args.upvar_tys()) + } else { + return; + }; + if upvar_tys.is_empty() { + return; + } + + let upvar_infos = tcx.closure_captures(def_id.expect_local()).iter(); + + let mut substitution_mapping = IndexVec::new(); + let mut patch = MirPatch::new(body); + for (field_idx, (upvar_ty, &captured)) in upvar_tys.iter().zip(upvar_infos).enumerate() { + let span = captured.var_ident.span; + + let local = patch.new_local_with_info( + upvar_ty, + span, + mir::LocalInfo::Boring, + matches!(captured.mutability, ty::Mutability::Not), + ); + let reloc = patch.new_local_with_info(upvar_ty, span, mir::LocalInfo::Boring, true); + + let field_idx = FieldIdx::from_usize(field_idx); + let upvar_place = Place::from(ty::CAPTURE_STRUCT_LOCAL) + .project_deeper(&[ProjectionElem::Field(field_idx, upvar_ty)], tcx); + + substitution_mapping.push(UpvarSubstitution { local, reloc, upvar_place, span }); + } + patch.apply(body); + body.local_upvar_map = substitution_mapping.iter().map(|sub| Some(sub.local)).collect(); + SubstituteUpvarVisitor { tcx, mappings: &substitution_mapping }.visit_body(body); + + rewrite_drop_coroutine_struct(body, &substitution_mapping); + insert_substitution_prologue(body, &substitution_mapping); + } + + fn is_required(&self) -> bool { + true + } +} + +fn rewrite_one_drop_coroutine_struct<'tcx>( + patch: &mut MirPatch<'tcx>, + body: &Body<'tcx>, + block: BasicBlock, + substitution_mapping: &IndexSlice>, +) { + let data = &body.basic_blocks[block]; + let source_info = data.terminator().source_info; + let TerminatorKind::Drop { place: _, mut target, mut unwind, replace } = data.terminator().kind + else { + unreachable!() + }; + let mut cleanup = match unwind { + UnwindAction::Cleanup(tgt) => tgt, + UnwindAction::Continue => patch.resume_block(), + UnwindAction::Unreachable => patch.unreachable_cleanup_block(), + UnwindAction::Terminate(reason) => patch.terminate_block(reason), + }; + for &UpvarSubstitution { local, .. } in substitution_mapping { + let place = local.into(); + let mut unwind_one = patch.new_block(BasicBlockData { + statements: vec![Statement { source_info, kind: StatementKind::StorageDead(local) }], + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::Goto { + target: if data.is_cleanup { target } else { cleanup }, + }, + }), + is_cleanup: true, + }); + unwind_one = patch.new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::Drop { + place, + target: unwind_one, + unwind: UnwindAction::Terminate(mir::UnwindTerminateReason::InCleanup), + replace, + }, + }), + is_cleanup: true, + }); + if data.is_cleanup { + unwind = UnwindAction::Cleanup(unwind_one); + cleanup = unwind_one; + target = unwind_one; + } else { + let mut drop_one = patch.new_block(BasicBlockData { + statements: vec![Statement { + source_info, + kind: StatementKind::StorageDead(local), + }], + terminator: Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }), + is_cleanup: false, + }); + drop_one = patch.new_block(BasicBlockData { + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::Drop { place, target: drop_one, unwind, replace }, + }), + statements: vec![], + is_cleanup: false, + }); + target = drop_one; + unwind = UnwindAction::Cleanup(unwind_one); + cleanup = unwind_one; + } + } + patch.patch_terminator(block, TerminatorKind::Goto { target }); +} + +fn rewrite_drop_coroutine_struct<'tcx>( + body: &mut Body<'tcx>, + substitution_mapping: &IndexSlice>, +) { + let mut blocks = vec![]; + for (block, block_data) in body.basic_blocks.iter_enumerated() { + let Terminator { source_info: _, kind: TerminatorKind::Drop { place, .. } } = + block_data.terminator() + else { + continue; + }; + let Some(local) = place.as_local() else { continue }; + if local == ty::CAPTURE_STRUCT_LOCAL { + blocks.push(block) + } + } + let mut patch = MirPatch::new(body); + for block in blocks { + rewrite_one_drop_coroutine_struct(&mut patch, body, block, substitution_mapping); + } + patch.apply(body); +} + +fn insert_substitution_prologue<'tcx>( + body: &mut Body<'tcx>, + substitution_mapping: &IndexSlice>, +) { + let mut patch = MirPatch::new(body); + let mut stmts = Vec::with_capacity(2 * substitution_mapping.len()); + for &UpvarSubstitution { local, reloc, upvar_place, span } in substitution_mapping { + // For each upvar-local _$i + let source_info = SourceInfo::outermost(span); + // StorageLive(_$i) + stmts.push(Statement { source_info, kind: StatementKind::StorageLive(local) }); + // Use a fresh local _$i' here, so as to avoid potential field permutation + // StorageLive(_$i') + stmts.push(Statement { source_info, kind: StatementKind::StorageLive(reloc) }); + // _$i' = move $ + stmts.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + reloc.into(), + mir::Rvalue::Use(mir::Operand::Move(upvar_place)), + ))), + }); + } + for &UpvarSubstitution { local, reloc, upvar_place: _, span } in substitution_mapping { + let source_info = SourceInfo::outermost(span); + // _$i = move $i' + stmts.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + local.into(), + mir::Rvalue::Use(mir::Operand::Move(reloc.into())), + ))), + }); + stmts.push(Statement { source_info, kind: StatementKind::StorageDead(reloc) }); + } + let source_info = SourceInfo::outermost(body.span); + let prologue = patch.new_block(BasicBlockData { + statements: stmts, + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::Drop { + place: Place::from(ty::CAPTURE_STRUCT_LOCAL), + target: START_BLOCK, + unwind: UnwindAction::Continue, + replace: false, + }, + }), + is_cleanup: false, + }); + patch.apply(body); + + // Manually patch so that prologue is the new entry + let preds = body.basic_blocks.predecessors()[START_BLOCK].clone(); + let basic_blocks = body.basic_blocks.as_mut(); + for pred in preds { + for target in &mut basic_blocks[pred].terminator_mut().successors_mut() { + if *target == START_BLOCK { + *target = prologue; + } + } + } + basic_blocks.swap(START_BLOCK, prologue); +} diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 205d388f4fb50..5a8b16554cec0 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -129,7 +129,7 @@ declare_passes! { pub mod cleanup_post_borrowck : CleanupPostBorrowck; mod copy_prop : CopyProp; - mod coroutine : StateTransform; + mod coroutine : RelocateUpvars, StateTransform; mod coverage : InstrumentCoverage; mod ctfe_limit : CtfeLimit; mod dataflow_const_prop : DataflowConstProp; @@ -447,7 +447,12 @@ fn mir_promoted( pm::run_passes( tcx, &mut body, - &[&promote_pass, &simplify::SimplifyCfg::PromoteConsts, &coverage::InstrumentCoverage], + &[ + &coroutine::RelocateUpvars, + &promote_pass, + &simplify::SimplifyCfg::PromoteConsts, + &coverage::InstrumentCoverage, + ], Some(MirPhase::Analysis(AnalysisPhase::Initial)), pm::Optimizations::Allowed, ); diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 84905f4a400f3..01256c71cd6d4 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -403,9 +403,16 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyLocals { // Only bother running the `LocalUpdater` if we actually found locals to remove. if map.iter().any(Option::is_none) { // Update references to all vars and tmps now - let mut updater = LocalUpdater { map, tcx }; + let mut updater = LocalUpdater { map: &map, tcx }; updater.visit_body_preserves_cfg(body); + // Update mapping for local to upvar + for local in &mut body.local_upvar_map { + if let Some(idx) = local { + *local = *map.get(*idx).unwrap_or(&None); + } + } + body.local_decls.shrink_to_fit(); } } @@ -587,12 +594,12 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod } } -struct LocalUpdater<'tcx> { - map: IndexVec>, +struct LocalUpdater<'tcx, 'a> { + map: &'a IndexSlice>, tcx: TyCtxt<'tcx>, } -impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> { +impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx, '_> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 231d7c2ef0225..e48abcd9db539 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -13,7 +13,7 @@ use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{ - self, CoroutineArgsExt, InstanceKind, ScalarInt, Ty, TyCtxt, TypeVisitableExt, Upcast, Variance, + self, InstanceKind, ScalarInt, Ty, TyCtxt, TypeVisitableExt, Upcast, Variance, }; use rustc_middle::{bug, span_bug}; use rustc_trait_selection::traits::ObligationCtxt; @@ -779,14 +779,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }; ty::EarlyBinder::bind(f_ty.ty).instantiate(self.tcx, args) - } else { - let Some(&f_ty) = args.as_coroutine().prefix_tys().get(f.index()) - else { - fail_out_of_bounds(self, location); - return; - }; - + } else if let Some(&f_ty) = args.as_coroutine().upvar_tys().get(f.index()) { f_ty + } else { + fail_out_of_bounds(self, location); + return; }; check_equal(self, location, f_ty); From 0c718d61fdc4e02cdd03f5d86e5cae3f8ec923cb Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Wed, 12 Mar 2025 00:21:28 +0800 Subject: [PATCH 3/9] treat coroutine upvar captures as saved locals as well This allows the liveness analysis to determine which captures are truly saved across a yield point and which are initially used but discarded at first yield points. Co-authored-by: Dario Nieuwenhuis --- compiler/rustc_mir_transform/src/coroutine.rs | 230 ++++++++++++++---- 1 file changed, 177 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 04d96f117072f..5927256c22d7c 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -51,11 +51,15 @@ //! Otherwise it drops all the values in scope at the last suspension point. mod by_move_body; -use std::{iter, ops}; +mod relocate_upvars; + +use std::ops::Deref; pub(super) use by_move_body::coroutine_by_move_body_def_id; +pub(super) use relocate_upvars::RelocateUpvars; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::unord::UnordMap; use rustc_errors::pluralize; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; @@ -65,7 +69,8 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::{ - self, CoroutineArgs, CoroutineArgsExt, GenericArgsRef, InstanceKind, Ty, TyCtxt, TypingMode, + self, CapturedPlace, CoroutineArgs, CoroutineArgsExt, GenericArgsRef, InstanceKind, Ty, TyCtxt, + TypingMode, }; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::impls::{ @@ -92,6 +97,8 @@ struct RenameLocalVisitor<'tcx> { tcx: TyCtxt<'tcx>, } +const VARIANT_UNRESUMED: VariantIdx = VariantIdx::from_usize(CoroutineArgs::UNRESUMED); + impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx @@ -187,8 +194,11 @@ struct TransformVisitor<'tcx> { // A map from a suspension point in a block to the locals which have live storage at that point storage_liveness: IndexVec>>, + // A rev-lookup from basic blocks with yielding terminator to the suspension point index, + suspension_point_at_block: UnordMap, + // A list of suspension points, generated during the transform - suspension_points: Vec>, + suspension_points: IndexVec>>, // The set of locals that have no `StorageLive`/`StorageDead` annotations. always_live_locals: DenseBitSet, @@ -388,6 +398,16 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { // Replace an Local in the remap with a coroutine struct access if let Some(&Some((ty, variant_index, idx))) = self.remap.get(place.local) { replace_base(place, self.make_field(variant_index, idx, ty), self.tcx); + } else if let Place { local: ty::CAPTURE_STRUCT_LOCAL, projection } = *place + && let [first @ ProjectionElem::Field(..), rest @ ..] = &**projection + { + let projections: Vec<_> = [ProjectionElem::Downcast(None, VARIANT_UNRESUMED), *first] + .into_iter() + .chain(rest.iter().copied()) + .collect(); + let new_place = + Place::from(ty::CAPTURE_STRUCT_LOCAL).project_deeper(&projections, self.tcx); + *place = new_place; } } @@ -416,8 +436,9 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { // We must assign the value first in case it gets declared dead below self.make_state(v, source_info, is_return, &mut data.statements); let state = if let Some((resume, mut resume_arg)) = resume { - // Yield - let state = CoroutineArgs::RESERVED_VARIANTS + self.suspension_points.len(); + // This is a `yield` + let suspension_point_idx = *self.suspension_point_at_block.get(&block).unwrap(); + let state = CoroutineArgs::RESERVED_VARIANTS + suspension_point_idx.as_usize(); // The resume arg target location might itself be remapped if its base local is // live across a yield. @@ -439,12 +460,8 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { } } - self.suspension_points.push(SuspensionPoint { - state, - resume, - resume_arg, - drop, - storage_liveness, + self.suspension_points.get_or_insert_with(suspension_point_idx, || { + SuspensionPoint { state, resume, resume_arg, drop, storage_liveness } }); VariantIdx::new(state) @@ -470,19 +487,19 @@ fn make_aggregate_adt<'tcx>( } fn make_coroutine_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let coroutine_ty = body.local_decls.raw[1].ty; + let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; let ref_coroutine_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty); // Replace the by value coroutine argument - body.local_decls.raw[1].ty = ref_coroutine_ty; + body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty = ref_coroutine_ty; // Add a deref to accesses of the coroutine state SelfArgVisitor::new(tcx, ProjectionElem::Deref).visit_body(body); } fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let ref_coroutine_ty = body.local_decls.raw[1].ty; + let ref_coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; let pin_did = tcx.require_lang_item(LangItem::Pin, Some(body.span)); let pin_adt_ref = tcx.adt_def(pin_did); @@ -490,7 +507,7 @@ fn make_coroutine_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body let pin_ref_coroutine_ty = Ty::new_adt(tcx, pin_adt_ref, args); // Replace the by ref coroutine argument - body.local_decls.raw[1].ty = pin_ref_coroutine_ty; + body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty = pin_ref_coroutine_ty; // Add the Pin field access to accesses of the coroutine state SelfArgVisitor::new(tcx, ProjectionElem::Field(FieldIdx::ZERO, ref_coroutine_ty)) @@ -629,15 +646,20 @@ fn transform_gen_context<'tcx>(body: &mut Body<'tcx>) { body.arg_count = 1; } +#[derive(Debug)] struct LivenessInfo { /// Which locals are live across any suspension point. saved_locals: CoroutineSavedLocals, + /// Always live locals + always_live_locals: DenseBitSet, + /// The set of saved locals live at each suspension point. - live_locals_at_suspension_points: Vec>, + live_locals_at_suspension_points: + IndexVec>, /// Parallel vec to the above with SourceInfo for each yield terminator. - source_info_at_suspension_points: Vec, + source_info_at_suspension_points: IndexVec, /// For every saved local, the set of other saved locals that are /// storage-live at the same time as this local. We cannot overlap locals in @@ -647,6 +669,14 @@ struct LivenessInfo { /// For every suspending block, the locals which are storage-live across /// that suspension point. storage_liveness: IndexVec>>, + + /// A rev-lookup of basic blocks to the suspension point index + suspension_point_at_block: UnordMap, +} + +rustc_index::newtype_index! { + #[debug_format = "suspend_{}"] + struct SuspensionPointIdx {} } /// Computes which locals have to be stored in the state-machine for the @@ -660,12 +690,12 @@ struct LivenessInfo { fn locals_live_across_suspend_points<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - always_live_locals: &DenseBitSet, + always_live_locals: DenseBitSet, movable: bool, ) -> LivenessInfo { // Calculate when MIR locals have live storage. This gives us an upper bound of their // lifetimes. - let mut storage_live = MaybeStorageLive::new(std::borrow::Cow::Borrowed(always_live_locals)) + let mut storage_live = MaybeStorageLive::new(std::borrow::Cow::Borrowed(&always_live_locals)) .iterate_to_fixpoint(tcx, body, None) .into_results_cursor(body); @@ -687,11 +717,13 @@ fn locals_live_across_suspend_points<'tcx>( MaybeLiveLocals.iterate_to_fixpoint(tcx, body, Some("coroutine")).into_results_cursor(body); let mut storage_liveness_map = IndexVec::from_elem(None, &body.basic_blocks); - let mut live_locals_at_suspension_points = Vec::new(); - let mut source_info_at_suspension_points = Vec::new(); + let mut live_locals_at_suspension_points = IndexVec::::default(); + let mut source_info_at_suspension_points = IndexVec::default(); let mut live_locals_at_any_suspension_point = DenseBitSet::new_empty(body.local_decls.len()); + let mut suspension_point_at_block = UnordMap::default(); - for (block, data) in body.basic_blocks.iter_enumerated() { + for &block in body.basic_blocks.reverse_postorder() { + let data = &body.basic_blocks[block]; if let TerminatorKind::Yield { .. } = data.terminator().kind { let loc = Location { block, statement_index: data.statements.len() }; @@ -725,7 +757,7 @@ fn locals_live_across_suspend_points<'tcx>( live_locals.intersect(requires_storage_cursor.get()); // The coroutine argument is ignored. - live_locals.remove(SELF_ARG); + live_locals.remove(ty::CAPTURE_STRUCT_LOCAL); debug!("loc = {:?}, live_locals = {:?}", loc, live_locals); @@ -733,8 +765,9 @@ fn locals_live_across_suspend_points<'tcx>( // any suspension points live_locals_at_any_suspension_point.union(&live_locals); - live_locals_at_suspension_points.push(live_locals); + let idx = live_locals_at_suspension_points.push(live_locals); source_info_at_suspension_points.push(data.terminator().source_info); + suspension_point_at_block.insert(block, idx); } } @@ -757,10 +790,12 @@ fn locals_live_across_suspend_points<'tcx>( LivenessInfo { saved_locals, + always_live_locals, live_locals_at_suspension_points, source_info_at_suspension_points, storage_conflicts, storage_liveness: storage_liveness_map, + suspension_point_at_block, } } @@ -769,6 +804,7 @@ fn locals_live_across_suspend_points<'tcx>( /// `CoroutineSavedLocal` is indexed in terms of the elements in this set; /// i.e. `CoroutineSavedLocal::new(1)` corresponds to the second local /// included in this set. +#[derive(Debug)] struct CoroutineSavedLocals(DenseBitSet); impl CoroutineSavedLocals { @@ -801,7 +837,7 @@ impl CoroutineSavedLocals { } } -impl ops::Deref for CoroutineSavedLocals { +impl Deref for CoroutineSavedLocals { type Target = DenseBitSet; fn deref(&self) -> &Self::Target { @@ -919,25 +955,39 @@ impl StorageConflictVisitor<'_, '_> { } } +#[instrument[level = "debug", skip(body), fields(body = ?body.source)]] fn compute_layout<'tcx>( liveness: LivenessInfo, body: &Body<'tcx>, + upvar_tys: &[Ty<'tcx>], + upvar_infos: &[&CapturedPlace<'tcx>], ) -> ( IndexVec, VariantIdx, FieldIdx)>>, CoroutineLayout<'tcx>, IndexVec>>, + UnordMap, ) { let LivenessInfo { saved_locals, + always_live_locals, live_locals_at_suspension_points, source_info_at_suspension_points, storage_conflicts, storage_liveness, + suspension_point_at_block, } = liveness; + // We need to later establish the map between upvars in UNRESUMED and locals in other states. + let local_upvar_map: UnordMap<_, _> = body + .local_upvar_map + .iter_enumerated() + .filter_map(|(field, local)| local.map(|local| (local, field))) + .collect(); + // Gather live local types and their indices. let mut locals = IndexVec::::new(); let mut tys = IndexVec::::new(); + let mut saved_local_upvar_map = UnordMap::default(); for (saved_local, local) in saved_locals.iter_enumerated() { debug!("coroutine saved local {:?} => {:?}", saved_local, local); @@ -965,7 +1015,52 @@ fn compute_layout<'tcx>( debug!(?decl); tys.push(decl); + + if let Some(&field) = local_upvar_map.get(&local) { + saved_local_upvar_map.insert(field, saved_local); + } } + // These are the "saved locals" sourced from the UNRESUMED state. + let upvar_saved_locals: IndexVec = upvar_tys + .iter() + .zip(upvar_infos) + .map(|(&ty, info)| { + tys.push(CoroutineSavedTy { + ty, + source_info: SourceInfo::outermost(info.var_ident.span), + ignore_for_traits: false, + }) + }) + .collect(); + debug!(?upvar_saved_locals); + let storage_conflicts = if let Some(&first) = upvar_saved_locals.raw.first() + && let Some(&last) = upvar_saved_locals.raw.last() + { + let mut enlarged_storage_conflicts = BitMatrix::new(tys.len(), tys.len()); + let mut upvars = DenseBitSet::new_empty(tys.len()); + let mut ineligibles = upvars.clone(); + upvars.insert_range(first..=last); + for (saved_local, local) in saved_locals.iter_enumerated() { + if always_live_locals.contains(local) { + ineligibles.insert(saved_local); + } + } + upvars.union(&ineligibles); + for row in storage_conflicts.rows() { + for column in storage_conflicts.iter(row) { + enlarged_storage_conflicts.insert(row, column); + } + } + for &upvar in &upvar_saved_locals { + enlarged_storage_conflicts.union_row_with(&upvars, upvar); + } + for ineligible in ineligibles.iter() { + enlarged_storage_conflicts.union_row_with(&upvars, ineligible); + } + enlarged_storage_conflicts + } else { + storage_conflicts + }; // Leave empty variants for the UNRESUMED, RETURNED, and POISONED states. // In debuginfo, these will correspond to the beginning (UNRESUMED) or end @@ -982,12 +1077,14 @@ fn compute_layout<'tcx>( // Build the coroutine variant field list. // Create a map from local indices to coroutine struct indices. + let variant_fields: [_; CoroutineArgs::RESERVED_VARIANTS] = + [upvar_saved_locals.clone(), IndexVec::new(), IndexVec::new()]; let mut variant_fields: IndexVec> = - iter::repeat(IndexVec::new()).take(CoroutineArgs::RESERVED_VARIANTS).collect(); + variant_fields.into_iter().collect(); let mut remap = IndexVec::from_elem_n(None, saved_locals.domain_size()); - for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter().enumerate() { + for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter_enumerated() { let variant_index = - VariantIdx::from(CoroutineArgs::RESERVED_VARIANTS + suspension_point_idx); + VariantIdx::from(CoroutineArgs::RESERVED_VARIANTS + suspension_point_idx.as_usize()); let mut fields = IndexVec::new(); for (idx, saved_local) in live_locals.iter().enumerate() { fields.push(saved_local); @@ -1003,29 +1100,42 @@ fn compute_layout<'tcx>( } debug!("coroutine variant_fields = {:?}", variant_fields); debug!("coroutine storage_conflicts = {:#?}", storage_conflicts); + debug!(remap = ?remap.debug_map_view_compact()); + debug!(locals = ?locals.debug_map_view()); let mut field_names = IndexVec::from_elem(None, &tys); for var in &body.var_debug_info { - let VarDebugInfoContents::Place(place) = &var.value else { continue }; - let Some(local) = place.as_local() else { continue }; - let Some(&Some((_, variant, field))) = remap.get(local) else { - continue; - }; - - let saved_local = variant_fields[variant][field]; - field_names.get_or_insert_with(saved_local, || var.name); + debug!(?var); + if let VarDebugInfoContents::Place(place) = &var.value + && let Some(local) = place.local_or_deref_local() + && let Some(&Some((_, variant, field))) = remap.get(local) + { + let saved_local = variant_fields[variant][field]; + field_names.get_or_insert_with(saved_local, || var.name); + } } + for (capture, &saved_local) in upvar_infos.iter().zip(&upvar_saved_locals) { + field_names.get_or_insert_with(saved_local, || capture.var_ident.name); + } + debug!(field_names = ?field_names.debug_map_view()); + let relocated_upvars = upvar_saved_locals + .iter_enumerated() + .filter_map(|(field, &source)| { + saved_local_upvar_map.get(&field).map(|&dest| (source, dest)) + }) + .collect(); let layout = CoroutineLayout { field_tys: tys, field_names, variant_fields, variant_source_info, storage_conflicts, + relocated_upvars, }; debug!(?layout); - (remap, layout, storage_liveness) + (remap, layout, storage_liveness, suspension_point_at_block) } /// Replaces the entry point of `body` with a block that switches on the coroutine discriminant and @@ -1102,7 +1212,8 @@ fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { elaborate_drop( &mut elaborator, *source_info, - Place::from(SELF_ARG), + Place::from(SELF_ARG) + .project_deeper(&[ProjectionElem::Downcast(None, VARIANT_UNRESUMED)], tcx), (), *target, unwind, @@ -1166,7 +1277,7 @@ fn create_coroutine_drop_shim<'tcx>( // Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible // filename. body.source.instance = coroutine_instance; - dump_mir(tcx, false, "coroutine_drop", &0, &body, |_, _| Ok(())); + dump_mir(tcx, false, "coroutine_drop", &0 as _, &body, |_, _| Ok(())); body.source.instance = drop_instance; body @@ -1354,7 +1465,7 @@ fn create_coroutine_resume_function<'tcx>( pm::run_passes_no_validate(tcx, body, &[&abort_unwinding_calls::AbortUnwindingCalls], None); - dump_mir(tcx, false, "coroutine_resume", &0, body, |_, _| Ok(())); + dump_mir(tcx, false, "coroutine_resume", &0 as _, body, |_, _| Ok(())); } fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock { @@ -1403,6 +1514,7 @@ fn create_cases<'tcx>( .suspension_points .iter() .filter_map(|point| { + let Some(point) = point else { bug!("all suspension points must be resolved now") }; // Find the target for this suspension point, if applicable operation.target_block(point).map(|target| { let mut statements = Vec::new(); @@ -1459,8 +1571,12 @@ pub(crate) fn mir_coroutine_witnesses<'tcx>( // The first argument is the coroutine type passed by value let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; - let movable = match *coroutine_ty.kind() { - ty::Coroutine(def_id, _) => tcx.coroutine_movability(def_id) == hir::Movability::Movable, + let (movable, upvar_tys, upvar_infos) = match *coroutine_ty.kind() { + ty::Coroutine(def_id, args) => ( + matches!(tcx.coroutine_movability(def_id), hir::Movability::Movable), + args.as_coroutine().upvar_tys(), + tcx.closure_captures(def_id.expect_local()), + ), ty::Error(_) => return None, _ => span_bug!(body.span, "unexpected coroutine type {}", coroutine_ty), }; @@ -1468,12 +1584,13 @@ pub(crate) fn mir_coroutine_witnesses<'tcx>( // The witness simply contains all locals live across suspend points. let always_live_locals = always_storage_live_locals(body); - let liveness_info = locals_live_across_suspend_points(tcx, body, &always_live_locals, movable); + debug!(?always_live_locals); + let liveness_info = locals_live_across_suspend_points(tcx, body, always_live_locals, movable); // Extract locals which are live across suspension point into `layout` // `remap` gives a mapping from local indices onto coroutine struct indices // `storage_liveness` tells us which locals have live storage at suspension points - let (_, coroutine_layout, _) = compute_layout(liveness_info, body); + let (_, coroutine_layout, _, _) = compute_layout(liveness_info, body, upvar_tys, upvar_infos); check_suspend_tys(tcx, &coroutine_layout, body); check_field_tys_sized(tcx, &coroutine_layout, def_id); @@ -1531,14 +1648,19 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { assert!(body.coroutine_drop().is_none()); // The first argument is the coroutine type passed by value - let coroutine_ty = body.local_decls.raw[1].ty; + let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; let coroutine_kind = body.coroutine_kind().unwrap(); // Get the discriminant type and args which typeck computed - let (discr_ty, movable) = match *coroutine_ty.kind() { - ty::Coroutine(_, args) => { + let (discr_ty, movable, upvar_tys, upvar_infos) = match *coroutine_ty.kind() { + ty::Coroutine(def_id, args) => { let args = args.as_coroutine(); - (args.discr_ty(tcx), coroutine_kind.movability() == hir::Movability::Movable) + ( + args.discr_ty(tcx), + matches!(coroutine_kind.movability(), hir::Movability::Movable), + args.upvar_tys(), + tcx.closure_captures(def_id.expect_local()), + ) } _ => { tcx.dcx().span_bug(body.span, format!("unexpected coroutine type {coroutine_ty}")); @@ -1611,7 +1733,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { let always_live_locals = always_storage_live_locals(body); let liveness_info = - locals_live_across_suspend_points(tcx, body, &always_live_locals, movable); + locals_live_across_suspend_points(tcx, body, always_live_locals.clone(), movable); if tcx.sess.opts.unstable_opts.validate_mir { let mut vis = EnsureCoroutineFieldAssignmentsNeverAlias { @@ -1626,7 +1748,8 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { // Extract locals which are live across suspension point into `layout` // `remap` gives a mapping from local indices onto coroutine struct indices // `storage_liveness` tells us which locals have live storage at suspension points - let (remap, layout, storage_liveness) = compute_layout(liveness_info, body); + let (remap, layout, storage_liveness, suspension_point_at_block) = + compute_layout(liveness_info, body, upvar_tys, upvar_infos); let can_return = can_return(tcx, body, body.typing_env(tcx)); @@ -1641,11 +1764,12 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { remap, storage_liveness, always_live_locals, - suspension_points: Vec::new(), + suspension_points: IndexVec::default(), old_ret_local, discr_ty, old_ret_ty, old_yield_ty, + suspension_point_at_block, }; transform.visit_body(body); @@ -1674,14 +1798,14 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { // This is expanded to a drop ladder in `elaborate_coroutine_drops`. let drop_clean = insert_clean_drop(body); - dump_mir(tcx, false, "coroutine_pre-elab", &0, body, |_, _| Ok(())); + dump_mir(tcx, false, "coroutine_pre-elab", &0 as _, body, |_, _| Ok(())); // Expand `drop(coroutine_struct)` to a drop ladder which destroys upvars. // If any upvars are moved out of, drop elaboration will handle upvar destruction. // However we need to also elaborate the code generated by `insert_clean_drop`. elaborate_coroutine_drops(tcx, body); - dump_mir(tcx, false, "coroutine_post-transform", &0, body, |_, _| Ok(())); + dump_mir(tcx, false, "coroutine_post-transform", &0 as _, body, |_, _| Ok(())); // Create a copy of our MIR and use it to create the drop shim for the coroutine let drop_shim = create_coroutine_drop_shim(tcx, &transform, coroutine_ty, body, drop_clean); From e87d42cc5d31d7ae2d5c951c72ec3e432ace50af Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Wed, 12 Mar 2025 00:25:48 +0800 Subject: [PATCH 4/9] abi: compute coroutine layout to reuse slots of promoted captures In the event that upvar captures are promoted, most certainly because a coroutine suspends at least once, the slots in the promotion prefix shall be reused. This means that the copies emitted in the upvar relocation MIR pass will eventually elided and eliminated in the codegen phase, hence no additional runtime cost is realised. Co-authored-by: Dario Nieuwenhuis --- compiler/rustc_abi/src/layout.rs | 4 +- compiler/rustc_abi/src/layout/coroutine.rs | 83 +++++++++++++--------- 2 files changed, 53 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 7bffeaf4cc9e2..f5555488a2e19 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -215,7 +215,7 @@ impl LayoutCalculator { >( &self, local_layouts: &IndexSlice, - prefix_layouts: IndexVec, + relocated_upvars: &IndexSlice>, variant_fields: &IndexSlice>, storage_conflicts: &BitMatrix, tag_to_layout: impl Fn(Scalar) -> F, @@ -223,7 +223,7 @@ impl LayoutCalculator { coroutine::layout( self, local_layouts, - prefix_layouts, + relocated_upvars, variant_fields, storage_conflicts, tag_to_layout, diff --git a/compiler/rustc_abi/src/layout/coroutine.rs b/compiler/rustc_abi/src/layout/coroutine.rs index 27e704d538c83..352b3a91d2dd7 100644 --- a/compiler/rustc_abi/src/layout/coroutine.rs +++ b/compiler/rustc_abi/src/layout/coroutine.rs @@ -145,7 +145,7 @@ pub(super) fn layout< >( calc: &super::LayoutCalculator, local_layouts: &IndexSlice, - mut prefix_layouts: IndexVec, + relocated_upvars: &IndexSlice>, variant_fields: &IndexSlice>, storage_conflicts: &BitMatrix, tag_to_layout: impl Fn(Scalar) -> F, @@ -155,10 +155,8 @@ pub(super) fn layout< let (ineligible_locals, assignments) = coroutine_saved_local_eligibility(local_layouts.len(), variant_fields, storage_conflicts); - // Build a prefix layout, including "promoting" all ineligible - // locals as part of the prefix. We compute the layout of all of - // these fields at once to get optimal packing. - let tag_index = prefix_layouts.len(); + // Build a prefix layout, consisting of only the state tag. + let tag_index = 0; // `variant_fields` already accounts for the reserved variants, so no need to add them. let max_discr = (variant_fields.len() - 1) as u128; @@ -169,17 +167,17 @@ pub(super) fn layout< }; let promoted_layouts = ineligible_locals.iter().map(|local| local_layouts[local]); - prefix_layouts.push(tag_to_layout(tag)); - prefix_layouts.extend(promoted_layouts); + let prefix_layouts: IndexVec<_, _> = + [tag_to_layout(tag)].into_iter().chain(promoted_layouts).collect(); let prefix = calc.univariant(&prefix_layouts, &ReprOptions::default(), StructKind::AlwaysSized)?; let (prefix_size, prefix_align) = (prefix.size, prefix.align); - // Split the prefix layout into the "outer" fields (upvars and - // discriminant) and the "promoted" fields. Promoted fields will - // get included in each variant that requested them in - // CoroutineLayout. + // Split the prefix layout into the discriminant and + // the "promoted" fields. + // Promoted fields will get included in each variant + // that requested them in CoroutineLayout. debug!("prefix = {:#?}", prefix); let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields { FieldsShape::Arbitrary { mut offsets, memory_index } => { @@ -218,19 +216,45 @@ pub(super) fn layout< let variants = variant_fields .iter_enumerated() .map(|(index, variant_fields)| { + let is_unresumed = index == VariantIdx::new(0); + let mut is_ineligible = IndexVec::from_elem_n(None, variant_fields.len()); + for (field, &local) in variant_fields.iter_enumerated() { + if is_unresumed { + // NOTE(@dingxiangfei2009): rewrite this when let-chain #53667 + // is stabilized + if let Some(inner_local) = relocated_upvars[local] { + if let Ineligible(Some(promoted_field)) = assignments[inner_local] { + is_ineligible.insert(field, promoted_field); + continue; + } + } + } + match assignments[local] { + Assigned(v) if v == index => {} + Ineligible(Some(promoted_field)) => { + is_ineligible.insert(field, promoted_field); + } + Ineligible(None) => { + panic!("an ineligible local should have been promoted into the prefix") + } + Assigned(_) => { + panic!("an eligible local should have been assigned to exactly one variant") + } + Unassigned => { + panic!("each saved local should have been inspected at least once") + } + } + } // Only include overlap-eligible fields when we compute our variant layout. - let variant_only_tys = variant_fields - .iter() - .filter(|local| match assignments[**local] { - Unassigned => unreachable!(), - Assigned(v) if v == index => true, - Assigned(_) => unreachable!("assignment does not match variant"), - Ineligible(_) => false, + let fields: IndexVec<_, _> = variant_fields + .iter_enumerated() + .filter_map(|(field, &local)| { + if is_ineligible.contains(field) { None } else { Some(local_layouts[local]) } }) - .map(|local| local_layouts[*local]); + .collect(); let mut variant = calc.univariant( - &variant_only_tys.collect::>(), + &fields, &ReprOptions::default(), StructKind::Prefixed(prefix_size, prefix_align.abi), )?; @@ -254,19 +278,14 @@ pub(super) fn layout< IndexVec::from_elem_n(FieldIdx::new(invalid_field_idx), invalid_field_idx); let mut offsets_and_memory_index = iter::zip(offsets, memory_index); - let combined_offsets = variant_fields + let combined_offsets = is_ineligible .iter_enumerated() - .map(|(i, local)| { - let (offset, memory_index) = match assignments[*local] { - Unassigned => unreachable!(), - Assigned(_) => { - let (offset, memory_index) = offsets_and_memory_index.next().unwrap(); - (offset, promoted_memory_index.len() as u32 + memory_index) - } - Ineligible(field_idx) => { - let field_idx = field_idx.unwrap(); - (promoted_offsets[field_idx], promoted_memory_index[field_idx]) - } + .map(|(i, &is_ineligible)| { + let (offset, memory_index) = if let Some(field_idx) = is_ineligible { + (promoted_offsets[field_idx], promoted_memory_index[field_idx]) + } else { + let (offset, memory_index) = offsets_and_memory_index.next().unwrap(); + (offset, promoted_memory_index.len() as u32 + memory_index) }; combined_inverse_memory_index[memory_index] = i; offset From 27ca6bb7699418285faebaa5d3740c233b12e1fb Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Wed, 12 Mar 2025 00:31:00 +0800 Subject: [PATCH 5/9] a general improvement in organisation of coroutine capture information Additional MIR dumps are inserted so that it is easier to inspect the bodies of async closures, including those that captures the state by-value. --- .../src/coroutine/by_move_body.rs | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 89a306c610477..3104ccca03b99 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -69,7 +69,6 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::steal::Steal; -use rustc_data_structures::unord::UnordMap; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -77,8 +76,18 @@ use rustc_middle::bug; use rustc_middle::hir::place::{Projection, ProjectionKind}; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::{self, dump_mir}; +use rustc_middle::ty::data_structures::IndexMap; use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, TypeVisitableExt}; +struct CaptureInfo<'tcx> { + /// Field index of the capture in the parent coroutine structure + remapped_idx: FieldIdx, + /// Type of the capture in the parent coroutine structure + remapped_ty: Ty<'tcx>, + peel_deref: bool, + bridging_projections: Vec>, +} + pub(crate) fn coroutine_by_move_body_def_id<'tcx>( tcx: TyCtxt<'tcx>, coroutine_def_id: LocalDefId, @@ -125,23 +134,27 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( .tuple_fields() .len(); - let field_remapping: UnordMap<_, _> = ty::analyze_coroutine_closure_captures( + let field_remapping: IndexMap<_, _> = ty::analyze_coroutine_closure_captures( tcx.closure_captures(parent_def_id).iter().copied(), tcx.closure_captures(coroutine_def_id).iter().skip(num_args).copied(), |(parent_field_idx, parent_capture), (child_field_idx, child_capture)| { // Store this set of additional projections (fields and derefs). // We need to re-apply them later. - let mut child_precise_captures = - child_capture.place.projections[parent_capture.place.projections.len()..].to_vec(); + let child_precise_captures = child_capture.place.projections + [parent_capture.place.projections.len()..] + .iter() + .copied(); // If the parent capture is by-ref, then we need to apply an additional // deref before applying any further projections to this place. - if parent_capture.is_by_ref() { - child_precise_captures.insert( - 0, - Projection { ty: parent_capture.place.ty(), kind: ProjectionKind::Deref }, - ); - } + let bridging_projections = if parent_capture.is_by_ref() { + [Projection { ty: parent_capture.place.ty(), kind: ProjectionKind::Deref }] + .into_iter() + .chain(child_precise_captures) + .collect() + } else { + child_precise_captures.collect() + }; // If the child capture is by-ref, then we need to apply a "ref" // projection (i.e. `&`) at the end. But wait! We don't have that // as a projection kind. So instead, we can apply its dual and @@ -167,8 +180,8 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( // Finally, store the type of the parent's captured place. We need // this when building the field projection in the MIR body later on. - let mut parent_capture_ty = parent_capture.place.ty(); - parent_capture_ty = match parent_capture.info.capture_kind { + let parent_capture_ty = parent_capture.place.ty(); + let remapped_ty = match parent_capture.info.capture_kind { ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => parent_capture_ty, ty::UpvarCapture::ByRef(kind) => Ty::new_ref( tcx, @@ -180,19 +193,19 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( Some(( FieldIdx::from_usize(child_field_idx + num_args), - ( - FieldIdx::from_usize(parent_field_idx + num_args), - parent_capture_ty, + CaptureInfo { + remapped_idx: FieldIdx::from_usize(parent_field_idx + num_args), + remapped_ty, peel_deref, - child_precise_captures, - ), + bridging_projections, + }, )) }, ) .flatten() .collect(); - if coroutine_kind == ty::ClosureKind::FnOnce { + if matches!(coroutine_kind, ty::ClosureKind::FnOnce) { assert_eq!(field_remapping.len(), tcx.closure_captures(parent_def_id).len()); // The by-move body is just the body :) return coroutine_def_id.to_def_id(); @@ -211,6 +224,7 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( ); let mut by_move_body = body.clone(); + dump_mir(tcx, false, "built", &"before", &by_move_body, |_, _| Ok(())); MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body); // This will always be `{closure#1}`, since the original coroutine is `{closure#0}`. @@ -241,7 +255,7 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( struct MakeByMoveBody<'tcx> { tcx: TyCtxt<'tcx>, - field_remapping: UnordMap, bool, Vec>)>, + field_remapping: IndexMap>, by_move_coroutine_ty: Ty<'tcx>, } @@ -262,8 +276,12 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> { if place.local == ty::CAPTURE_STRUCT_LOCAL && let Some((&mir::ProjectionElem::Field(idx, _), projection)) = place.projection.split_first() - && let Some(&(remapped_idx, remapped_ty, peel_deref, ref bridging_projections)) = - self.field_remapping.get(&idx) + && let Some(&CaptureInfo { + remapped_idx, + remapped_ty, + peel_deref, + ref bridging_projections, + }) = self.field_remapping.get(&idx) { // As noted before, if the parent closure captures a field by value, and // the child captures a field by ref, then for the by-move body we're @@ -340,7 +358,7 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> { local: ty::CAPTURE_STRUCT_LOCAL, projection: [mir::ProjectionElem::Field(idx, _)], } = place.as_ref() - && let Some(&(_, _, true, _)) = self.field_remapping.get(&idx) + && let Some(CaptureInfo { peel_deref: true, .. }) = self.field_remapping.get(idx) { statement.kind = mir::StatementKind::Nop; } From 661274fc8a9b5ef93accaddb82c4f85431db59b7 Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Wed, 12 Mar 2025 00:33:25 +0800 Subject: [PATCH 6/9] update the layout calculator to use relocated upvar info --- compiler/rustc_ty_utils/src/layout.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 7334beb52c9d5..e2885f056b8d0 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -9,6 +9,7 @@ use rustc_abi::{ use rustc_hashes::Hash64; use rustc_index::IndexVec; use rustc_middle::bug; +use rustc_middle::mir::CoroutineSavedLocal; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, @@ -410,7 +411,7 @@ fn layout_of_uncached<'tcx>( return Err(error(cx, LayoutError::Unknown(ty))); }; - let local_layouts = info + let local_layouts: IndexVec<_, _> = info .field_tys .iter() .map(|local| { @@ -418,20 +419,17 @@ fn layout_of_uncached<'tcx>( let uninit_ty = Ty::new_maybe_uninit(tcx, field_ty.instantiate(tcx, args)); cx.spanned_layout_of(uninit_ty, local.source_info.span) }) - .try_collect::>()?; - - let prefix_layouts = args - .as_coroutine() - .prefix_tys() - .iter() - .map(|ty| cx.layout_of(ty)) - .try_collect::>()?; + .try_collect()?; + let relocated_upvars = IndexVec::from_fn_n( + |local: CoroutineSavedLocal| info.relocated_upvars.get(&local).copied(), + info.field_tys.len(), + ); let layout = cx .calc .coroutine( &local_layouts, - prefix_layouts, + &relocated_upvars, &info.variant_fields, &info.storage_conflicts, |tag| TyAndLayout { @@ -826,7 +824,11 @@ fn variant_info_for_coroutine<'tcx>( .then(|| Symbol::intern(&field_layout.ty.to_string())), } }) - .chain(upvar_fields.iter().copied()) + .chain( + if variant_idx == FIRST_VARIANT { &upvar_fields[..] } else { &[] } + .iter() + .copied(), + ) .collect(); // If the variant has no state-specific fields, then it's the size of the upvars. From 9e04ab9d104c4fb6706b1a83939a230e6b693fe9 Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Wed, 12 Mar 2025 00:35:39 +0800 Subject: [PATCH 7/9] interpret direct field access to coroutine state as upvar access --- compiler/rustc_codegen_ssa/src/mir/operand.rs | 9 +++------ compiler/rustc_const_eval/src/interpret/step.rs | 3 +++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 7e355b6406aed..beb7fcc689c22 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -701,13 +701,12 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + #[instrument(level = "debug", skip(self, bx), ret)] fn maybe_codegen_consume_direct( &mut self, bx: &mut Bx, place_ref: mir::PlaceRef<'tcx>, ) -> Option> { - debug!("maybe_codegen_consume_direct(place_ref={:?})", place_ref); - match self.locals[place_ref.local] { LocalRef::Operand(mut o) => { // Moves out of scalar and scalar pair fields are trivial. @@ -750,13 +749,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + #[instrument(level = "debug", skip(self, bx), ret)] pub fn codegen_consume( &mut self, bx: &mut Bx, place_ref: mir::PlaceRef<'tcx>, ) -> OperandRef<'tcx, Bx::Value> { - debug!("codegen_consume(place_ref={:?})", place_ref); - let ty = self.monomorphized_place_ty(place_ref); let layout = bx.cx().layout_of(ty); @@ -775,13 +773,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.load_operand(place) } + #[instrument(level = "debug", skip(self, bx), ret)] pub fn codegen_operand( &mut self, bx: &mut Bx, operand: &mir::Operand<'tcx>, ) -> OperandRef<'tcx, Bx::Value> { - debug!("codegen_operand(operand={:?})", operand); - match *operand { mir::Operand::Copy(ref place) | mir::Operand::Move(ref place) => { self.codegen_consume(bx, place.as_ref()) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index ddf2d65914f6c..e917c353e3304 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -305,6 +305,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let variant_dest = self.project_downcast(dest, variant_index)?; (variant_index, variant_dest, active_field_index) } + mir::AggregateKind::Coroutine(_def_id, _args) => { + (FIRST_VARIANT, self.project_downcast(dest, FIRST_VARIANT)?, None) + } mir::AggregateKind::RawPtr(..) => { // Pointers don't have "fields" in the normal sense, so the // projection-based code below would either fail in projection From 241761663773a03e726f97532b22c4eba64ace1c Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Wed, 12 Mar 2025 00:37:21 +0800 Subject: [PATCH 8/9] update debug information to point it to the correct location for upvars --- .../src/debuginfo/metadata.rs | 6 ++-- .../src/debuginfo/metadata/enums/cpp_like.rs | 2 -- .../src/debuginfo/metadata/enums/mod.rs | 34 ++----------------- .../src/debuginfo/metadata/enums/native.rs | 4 --- 4 files changed, 4 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 98d59f5a8ae06..c3c0769b32427 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -14,9 +14,7 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{ HasTypingEnv, LayoutOf, TyAndLayout, WIDE_PTR_ADDR, WIDE_PTR_EXTRA, }; -use rustc_middle::ty::{ - self, AdtKind, CoroutineArgsExt, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility, -}; +use rustc_middle::ty::{self, AdtKind, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility}; use rustc_session::config::{self, DebugInfo, Lto}; use rustc_span::{DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, Symbol, hygiene}; use rustc_symbol_mangling::typeid_for_trait_ref; @@ -1101,7 +1099,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>( closure_or_coroutine_di_node: &'ll DIType, ) -> SmallVec<&'ll DIType> { let (&def_id, up_var_tys) = match closure_or_coroutine_ty.kind() { - ty::Coroutine(def_id, args) => (def_id, args.as_coroutine().prefix_tys()), + ty::Coroutine(def_id, args) => (def_id, args.as_coroutine().upvar_tys()), ty::Closure(def_id, args) => (def_id, args.as_closure().upvar_tys()), ty::CoroutineClosure(def_id, args) => (def_id, args.as_coroutine_closure().upvar_tys()), _ => { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index a72e205c9b249..5d82a2b00670e 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -724,7 +724,6 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>( let coroutine_layout = cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args.kind_ty()).unwrap(); - let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id); let variant_range = coroutine_args.variant_range(coroutine_def_id, cx.tcx); let variant_count = (variant_range.start.as_u32()..variant_range.end.as_u32()).len(); @@ -764,7 +763,6 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>( coroutine_type_and_layout, coroutine_type_di_node, coroutine_layout, - common_upvar_names, ); let span = coroutine_layout.variant_source_info[variant_index].span; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index 9f6a5cc89e023..ed3561faaf2fc 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -5,12 +5,10 @@ use rustc_codegen_ssa::debuginfo::type_names::{compute_debuginfo_type_name, cpp_ use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo}; use rustc_codegen_ssa::traits::MiscCodegenMethods; use rustc_hir::def::CtorKind; -use rustc_index::IndexSlice; use rustc_middle::bug; use rustc_middle::mir::CoroutineLayout; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty, VariantDef}; -use rustc_span::Symbol; use super::type_map::{DINodeCreationResult, UniqueTypeId}; use super::{SmallVec, size_and_align_of}; @@ -286,7 +284,6 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( coroutine_type_and_layout: TyAndLayout<'tcx>, coroutine_type_di_node: &'ll DIType, coroutine_layout: &CoroutineLayout<'tcx>, - common_upvar_names: &IndexSlice, ) -> &'ll DIType { let variant_name = CoroutineArgs::variant_name(variant_index); let unique_type_id = UniqueTypeId::for_enum_variant_struct_type( @@ -297,11 +294,6 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( let variant_layout = coroutine_type_and_layout.for_variant(cx, variant_index); - let coroutine_args = match coroutine_type_and_layout.ty.kind() { - ty::Coroutine(_, args) => args.as_coroutine(), - _ => unreachable!(), - }; - type_map::build_type_with_children( cx, type_map::stub( @@ -316,7 +308,7 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( ), |cx, variant_struct_type_di_node| { // Fields that just belong to this variant/state - let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count()) + (0..variant_layout.fields.count()) .map(|field_index| { let coroutine_saved_local = coroutine_layout.variant_fields[variant_index] [FieldIdx::from_usize(field_index)]; @@ -339,29 +331,7 @@ fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( None, ) }) - .collect(); - - // Fields that are common to all states - let common_fields: SmallVec<_> = coroutine_args - .prefix_tys() - .iter() - .zip(common_upvar_names) - .enumerate() - .map(|(index, (upvar_ty, upvar_name))| { - build_field_di_node( - cx, - variant_struct_type_di_node, - upvar_name.as_str(), - cx.size_and_align_of(upvar_ty), - coroutine_type_and_layout.fields.offset(index), - DIFlags::FlagZero, - type_di_node(cx, upvar_ty), - None, - ) - }) - .collect(); - - state_specific_fields.into_iter().chain(common_fields).collect() + .collect() }, |cx| build_generic_type_param_di_nodes(cx, coroutine_type_and_layout.ty), ) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 187d97c54c873..69489480ced38 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -188,9 +188,6 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( ) }; - let common_upvar_names = - cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id); - // Build variant struct types let variant_struct_type_di_nodes: SmallVec<_> = variants .indices() @@ -218,7 +215,6 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( coroutine_type_and_layout, coroutine_type_di_node, coroutine_layout, - common_upvar_names, ), source_info, } From 815be82bdd0e0c496c36578ca6902454e962adb8 Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Wed, 12 Mar 2025 00:38:46 +0800 Subject: [PATCH 9/9] improve diagnostics so that the message respect the locals storing upvars ... and treat them as such --- .../src/diagnostics/mutability_errors.rs | 105 +++++++++++------- compiler/rustc_borrowck/src/lib.rs | 27 ++++- .../src/deref_separator.rs | 1 + .../rustc_mir_transform/src/pass_manager.rs | 13 ++- compiler/rustc_mir_transform/src/patch.rs | 4 + .../src/error_reporting/traits/suggestions.rs | 24 ++-- 6 files changed, 118 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 3951b467961a5..90edf53f1305a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -393,49 +393,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); - let captured_place = self.upvars[upvar_index.index()]; - - err.span_label(span, format!("cannot {act}")); - - let upvar_hir_id = captured_place.get_root_variable(); - - if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id) - && let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) = - pat.kind - { - if upvar_ident.name == kw::SelfLower { - for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) { - if let Some(fn_decl) = node.fn_decl() { - if !matches!( - fn_decl.implicit_self, - hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut - ) { - err.span_suggestion_verbose( - upvar_ident.span.shrink_to_lo(), - "consider changing this to be mutable", - "mut ", - Applicability::MachineApplicable, - ); - break; - } - } - } - } else { - err.span_suggestion_verbose( - upvar_ident.span.shrink_to_lo(), - "consider changing this to be mutable", - "mut ", - Applicability::MachineApplicable, - ); - } - } + self.suggest_mutable_upvar(*upvar_index, the_place_err, &mut err, span, act); + } - let tcx = self.infcx.tcx; - if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind() - && let ty::Closure(id, _) = *ty.kind() - { - self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err); - } + PlaceRef { local, projection: [] } + if let Some(upvar_index) = self + .body + .local_upvar_map + .iter_enumerated() + .filter_map(|(field, &local)| local.map(|local| (field, local))) + .find_map(|(field, relocated)| (relocated == local).then_some(field)) => + { + self.suggest_mutable_upvar(upvar_index, the_place_err, &mut err, span, act); } // complete hack to approximate old AST-borrowck @@ -542,6 +511,58 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } + fn suggest_mutable_upvar( + &self, + upvar_index: FieldIdx, + the_place_err: PlaceRef<'tcx>, + err: &mut Diag<'infcx>, + span: Span, + act: &str, + ) { + let captured_place = self.upvars[upvar_index.index()]; + + err.span_label(span, format!("cannot {act}")); + + let upvar_hir_id = captured_place.get_root_variable(); + + if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id) + && let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) = pat.kind + { + if upvar_ident.name == kw::SelfLower { + for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) { + if let Some(fn_decl) = node.fn_decl() { + if !matches!( + fn_decl.implicit_self, + hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut + ) { + err.span_suggestion_verbose( + upvar_ident.span.shrink_to_lo(), + "consider changing this to be mutable", + "mut ", + Applicability::MachineApplicable, + ); + break; + } + } + } + } else { + err.span_suggestion_verbose( + upvar_ident.span.shrink_to_lo(), + "consider changing this to be mutable", + "mut ", + Applicability::MachineApplicable, + ); + } + } + + let tcx = self.infcx.tcx; + if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind() + && let ty::Closure(id, _) = *ty.kind() + { + self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, err); + } + } + /// Suggest `map[k] = v` => `map.insert(k, v)` and the like. fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'infcx>, span: Span) { let Some(adt) = ty.ty_adt_def() else { return }; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 07b3f3477a849..c27bb6b6b595c 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -24,6 +24,7 @@ use std::ops::{ControlFlow, Deref}; use rustc_abi::FieldIdx; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::dominators::Dominators; +use rustc_data_structures::unord::UnordMap; use rustc_errors::LintDiagnostic; use rustc_hir as hir; use rustc_hir::CRATE_HIR_ID; @@ -261,6 +262,7 @@ fn do_mir_borrowck<'tcx>( regioncx: ®ioncx, used_mut: Default::default(), used_mut_upvars: SmallVec::new(), + local_from_upvars: UnordMap::default(), borrow_set: &borrow_set, upvars: &[], local_names: IndexVec::from_elem(None, &promoted_body.local_decls), @@ -286,6 +288,11 @@ fn do_mir_borrowck<'tcx>( promoted_mbcx.report_move_errors(); } + let mut local_from_upvars = UnordMap::default(); + for (field, &local) in body.local_upvar_map.iter_enumerated() { + let Some(local) = local else { continue }; + local_from_upvars.insert(local, field); + } let mut mbcx = MirBorrowckCtxt { infcx: &infcx, body, @@ -300,6 +307,7 @@ fn do_mir_borrowck<'tcx>( regioncx: ®ioncx, used_mut: Default::default(), used_mut_upvars: SmallVec::new(), + local_from_upvars, borrow_set: &borrow_set, upvars: tcx.closure_captures(def), local_names, @@ -555,6 +563,9 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { /// If the function we're checking is a closure, then we'll need to report back the list of /// mutable upvars that have been used. This field keeps track of them. used_mut_upvars: SmallVec<[FieldIdx; 8]>, + /// Since upvars are moved to real locals, we need to map mutations to the locals back to + /// the upvars, so that used_mut_upvars is up-to-date. + local_from_upvars: UnordMap, /// Region inference context. This contains the results from region inference and lets us e.g. /// find out which CFG points are contained in each borrow region. regioncx: &'a RegionInferenceContext<'tcx>, @@ -2265,7 +2276,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // at this point, we have set up the error reporting state. if let Some(init_index) = previously_initialized { - if let (AccessKind::Mutate, Some(_)) = (error_access, place.as_local()) { + if let (AccessKind::Mutate, Some(local)) = (error_access, place.as_local()) + && self.body.local_upvar_map.iter().flatten().all(|upvar| upvar != &local) + { // If this is a mutate access to an immutable local variable with no projections // report the error as an illegal reassignment let init = &self.move_data.inits[init_index]; @@ -2293,10 +2306,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // If the local may have been initialized, and it is now currently being // mutated, then it is justified to be annotated with the `mut` // keyword, since the mutation may be a possible reassignment. - if is_local_mutation_allowed != LocalMutationIsAllowed::Yes - && self.is_local_ever_initialized(local, state).is_some() - { - self.used_mut.insert(local); + if !matches!(is_local_mutation_allowed, LocalMutationIsAllowed::Yes) { + if self.is_local_ever_initialized(local, state).is_some() { + self.used_mut.insert(local); + } else if let Some(&field) = self.local_from_upvars.get(&local) { + self.used_mut_upvars.push(field); + } } } RootPlace { @@ -2314,6 +2329,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { projection: place_projection, }) { self.used_mut_upvars.push(field); + } else if let Some(&field) = self.local_from_upvars.get(&place_local) { + self.used_mut_upvars.push(field); } } } diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs index bc914ea656415..00d81cc3b3ccd 100644 --- a/compiler/rustc_mir_transform/src/deref_separator.rs +++ b/compiler/rustc_mir_transform/src/deref_separator.rs @@ -40,6 +40,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for DerefChecker<'a, 'tcx> { ty, self.local_decls[p_ref.local].source_info.span, LocalInfo::DerefTemp, + false, ); // We are adding current p_ref's projections to our diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 7a8d3ba1ff1fa..e5da2c36b2407 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -294,11 +294,20 @@ fn run_passes_inner<'tcx>( if dump_enabled { dump_mir_for_pass(tcx, body, name, true); } + let (dialect, phase_num) = body.phase.index(); if validate { - validate_body(tcx, body, format!("after pass {name}")); + validate_body( + tcx, + body, + format!("after pass {name} {dialect}-{phase_num}-{:03}", body.pass_count), + ); } if lint { - lint_body(tcx, body, format!("after pass {name}")); + lint_body( + tcx, + body, + format!("after pass {name} {dialect}-{phase_num}-{:03}", body.pass_count), + ); } body.pass_count += 1; diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index 6a177faeac81f..e3a9883dbeff9 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -154,10 +154,14 @@ impl<'tcx> MirPatch<'tcx> { ty: Ty<'tcx>, span: Span, local_info: LocalInfo<'tcx>, + immutable: bool, ) -> Local { let index = self.next_local; self.next_local += 1; let mut new_decl = LocalDecl::new(ty, span); + if immutable { + new_decl = new_decl.immutable(); + } **new_decl.local_info.as_mut().unwrap_crate_local() = local_info; self.new_locals.push(new_decl); Local::new(index) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 9383b82ff3cde..4374c1347cf52 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; use std::iter; use itertools::{EitherOrBoth, Itertools}; -use rustc_abi::ExternAbi; +use rustc_abi::{ExternAbi, FIRST_VARIANT}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::codes::*; @@ -2361,18 +2361,28 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let Some(coroutine_info) = self.tcx.mir_coroutine_witnesses(coroutine_did) { debug!(?coroutine_info); - 'find_source: for (variant, source_info) in - coroutine_info.variant_fields.iter().zip(&coroutine_info.variant_source_info) + // Variants are scanned "in reverse" because suspension points + // tend to contain more diagnostic information than the unresumed state. + 'find_source: for ((variant_idx, variant), source_info) in coroutine_info + .variant_fields + .iter_enumerated() + .zip(&coroutine_info.variant_source_info) + .rev() { debug!(?variant); for &local in variant { let decl = &coroutine_info.field_tys[local]; debug!(?decl); if ty_matches(ty::Binder::dummy(decl.ty)) && !decl.ignore_for_traits { - interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior( - decl.source_info.span, - Some((source_info.span, from_awaited_ty)), - )); + let span = decl.source_info.span; + if variant_idx == FIRST_VARIANT { + interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Upvar(span)); + } else { + interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior( + span, + Some((source_info.span, from_awaited_ty)), + )); + } break 'find_source; } }