From 23c0334fd3549c1e3a701c2669babbe272504251 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sun, 27 Jun 2021 21:19:39 -0400 Subject: [PATCH 01/28] 2229: Reduce the size of closures with `capture_disjoint_fields` One key observation while going over the closure size profile of rustc was that we are disjointly capturing one or more fields starting at an immutable reference. Disjoint capture over immutable reference doesn't add too much value because the fields can either be borrowed immutably or copied. One possible edge case of the optimization is when a fields of a struct have a longer lifetime than the structure, therefore we can't completely get rid of all the accesses on top of sharef refs, only the rightmost one. Here is a possible example: ```rust struct MyStruct<'a> { a: &'static A, b: B, c: C<'a>, } fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { let c = || drop(&*m.a.field_of_a); // Here we really do want to capture `*m.a` because that outlives `'static` // If we capture `m`, then the closure no longer outlives `'static' // it is constrained to `'a` } ``` --- compiler/rustc_typeck/src/check/upvar.rs | 50 ++++++++++++++++++- .../diagnostics/cant-mutate-imm-borrow.rs | 4 +- .../diagnostics/cant-mutate-imm-borrow.stderr | 4 +- .../2229_closure_analysis/move_closure.rs | 6 +-- .../2229_closure_analysis/move_closure.stderr | 6 +-- .../optimization/edge_case.rs | 37 ++++++++++++++ .../optimization/edge_case.stderr | 45 +++++++++++++++++ .../optimization/edge_case_run_pass.rs | 31 ++++++++++++ .../optimization/edge_case_run_pass.stderr | 11 ++++ 9 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.stderr diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 7b5b14ae6c831..8418626b8e6e0 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -1609,11 +1609,17 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { "consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})", place_with_id, diag_expr_id, mode ); + + let place_with_id = PlaceWithHirId { + place: truncate_capture_for_optimization(&place_with_id.place), + ..*place_with_id + }; + if !self.capture_information.contains_key(&place_with_id.place) { - self.init_capture_info_for_place(place_with_id, diag_expr_id); + self.init_capture_info_for_place(&place_with_id, diag_expr_id); } - self.adjust_upvar_borrow_kind_for_consume(place_with_id, diag_expr_id, mode); + self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id, mode); } fn borrow( @@ -1636,6 +1642,8 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { &place_with_id.place, ); + let place = truncate_capture_for_optimization(&place); + let place_with_id = PlaceWithHirId { place, ..*place_with_id }; if !self.capture_information.contains_key(&place_with_id.place) { @@ -1967,6 +1975,44 @@ fn determine_place_ancestry_relation( } } +/// Reduces the precision of the captured place when the precision doesn't yeild any benefit from +/// borrow checking prespective, allowing us to save us on the size of the capture. +/// +/// +/// Fields that are read through a shared reference will always be read via a shared ref or a copy, +/// and therefore capturing precise paths yields no benefit. This optimization truncates the +/// rightmost deref of the capture if the deref is applied to a shared ref. +/// +/// Reason we only drop the last deref is because of the following edge case: +/// +/// ```rust +/// struct MyStruct<'a> { +/// a: &'static A, +/// b: B, +/// c: C<'a>, +/// } +/// +/// fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { +/// let c = || drop(&*m.a.field_of_a); +/// // Here we really do want to capture `*m.a` because that outlives `'static` +/// +/// // If we capture `m`, then the closure no longer outlives `'static' +/// // it is constrained to `'a` +/// } +/// ``` +fn truncate_capture_for_optimization<'tcx>(place: &Place<'tcx>) -> Place<'tcx> { + let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)); + + let idx = place.projections.iter().rposition(|proj| ProjectionKind::Deref == proj.kind); + + match idx { + Some(idx) if is_shared_ref(place.ty_before_projection(idx)) => { + Place { projections: place.projections[0..=idx].to_vec(), ..place.clone() } + } + None | Some(_) => place.clone(), + } +} + /// Precise capture is enabled if the feature gate `capture_disjoint_fields` is enabled or if /// user is using Rust Edition 2021 or higher. /// diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs index a5b4a19d8c3ff..77effcb006588 100644 --- a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs @@ -8,10 +8,10 @@ fn main() { let mut y = (&x, "Y"); let z = (&mut y, "Z"); - // `x.0` is mutable but we access `x` via `z.0.0`, which is an immutable reference and + // `x.0` is mutable but we access `x` via `*z.0.0`, which is an immutable reference and // therefore can't be mutated. let mut c = || { - //~^ ERROR: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference + //~^ ERROR: cannot borrow `*z.0.0` as mutable, as it is behind a `&` reference z.0.0.0 = format!("X1"); }; diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr index cfe531e17d3d7..38c530b809a62 100644 --- a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr @@ -1,11 +1,11 @@ -error[E0596]: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference +error[E0596]: cannot borrow `*z.0.0` as mutable, as it is behind a `&` reference --> $DIR/cant-mutate-imm-borrow.rs:13:17 | LL | let mut c = || { | ^^ cannot borrow as mutable LL | LL | z.0.0.0 = format!("X1"); - | ------- mutable borrow occurs due to use of `z.0.0.0` in closure + | ------- mutable borrow occurs due to use of `*z.0.0` in closure error: aborting due to previous error diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/move_closure.rs index 06db19974eb04..76874e03dc02a 100644 --- a/src/test/ui/closures/2229_closure_analysis/move_closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/move_closure.rs @@ -78,8 +78,8 @@ fn struct_contains_ref_to_another_struct_2() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: let _t = t.0.0; - //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow - //~| NOTE: Min Capture t[(0, 0),Deref,(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing t[(0, 0),Deref] -> ImmBorrow + //~| NOTE: Min Capture t[(0, 0),Deref] -> ImmBorrow }; c(); @@ -100,7 +100,7 @@ fn struct_contains_ref_to_another_struct_3() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: let _t = t.0.0; - //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing t[(0, 0),Deref] -> ImmBorrow //~| NOTE: Capturing t[(0, 0)] -> ByValue //~| NOTE: Min Capture t[(0, 0)] -> ByValue }; diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.stderr b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr index 013cacfb9f2a5..b35aadfcbd419 100644 --- a/src/test/ui/closures/2229_closure_analysis/move_closure.stderr +++ b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr @@ -190,7 +190,7 @@ LL | | LL | | }; | |_____^ | -note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow +note: Capturing t[(0, 0),Deref] -> ImmBorrow --> $DIR/move_closure.rs:80:18 | LL | let _t = t.0.0; @@ -208,7 +208,7 @@ LL | | LL | | }; | |_____^ | -note: Min Capture t[(0, 0),Deref,(0, 0)] -> ImmBorrow +note: Min Capture t[(0, 0),Deref] -> ImmBorrow --> $DIR/move_closure.rs:80:18 | LL | let _t = t.0.0; @@ -226,7 +226,7 @@ LL | | LL | | }; | |_____^ | -note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow +note: Capturing t[(0, 0),Deref] -> ImmBorrow --> $DIR/move_closure.rs:102:18 | LL | let _t = t.0.0; diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs new file mode 100644 index 0000000000000..960c61ccaef49 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs @@ -0,0 +1,37 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 + +#![feature(rustc_attrs)] +#![allow(unused)] +#![allow(dead_code)] + +struct Int(i32); +struct B<'a>(&'a i32); + +const I : Int = Int(0); +const REF_I : &'static Int = &I; + + +struct MyStruct<'a> { + a: &'static Int, + b: B<'a>, +} + +fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { + let c = #[rustc_capture_analysis] || drop(&m.a.0); + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + //~| ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + //~| NOTE: Capturing m[Deref,(0, 0),Deref] -> ImmBorrow + //~| NOTE: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow + c +} + +fn main() { + let t = 0; + let s = MyStruct { a: REF_I, b: B(&t) }; + let _ = foo(&s); +} diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr new file mode 100644 index 0000000000000..c9f9386b9aa1f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr @@ -0,0 +1,45 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/edge_case.rs:23:13 + | +LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/edge_case.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/edge_case.rs:23:39 + | +LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); + | ^^^^^^^^^^^^^^^ + | +note: Capturing m[Deref,(0, 0),Deref] -> ImmBorrow + --> $DIR/edge_case.rs:23:48 + | +LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/edge_case.rs:23:39 + | +LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); + | ^^^^^^^^^^^^^^^ + | +note: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow + --> $DIR/edge_case.rs:23:48 + | +LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); + | ^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs new file mode 100644 index 0000000000000..2c4ba0c91a6d1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs @@ -0,0 +1,31 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 + +// run-pass + +#![allow(unused)] +#![allow(dead_code)] + +struct Int(i32); +struct B<'a>(&'a i32); + +const I : Int = Int(0); +const REF_I : &'static Int = &I; + +struct MyStruct<'a> { + a: &'static Int, + b: B<'a>, +} + +fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { + let c = || drop(&m.a.0); + c +} + +fn main() { + let t = 0; + let s = MyStruct { a: REF_I, b: B(&t) }; + let _ = foo(&s); +} diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.stderr b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.stderr new file mode 100644 index 0000000000000..36906d1685ce3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/edge_case_run_pass.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + From 32fa96486a5a9969d6151cf436dca4a31f8da292 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Tue, 29 Jun 2021 03:33:24 -0400 Subject: [PATCH 02/28] Edition 2021 for tests --- .../optimization/edge_case.rs | 5 +---- .../optimization/edge_case.stderr | 21 ++++++------------- .../optimization/edge_case_run_pass.rs | 6 +----- .../optimization/edge_case_run_pass.stderr | 11 ---------- 4 files changed, 8 insertions(+), 35 deletions(-) delete mode 100644 src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.stderr diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs index 960c61ccaef49..37a2a97d44279 100644 --- a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs @@ -1,7 +1,4 @@ -#![feature(capture_disjoint_fields)] -//~^ WARNING: the feature `capture_disjoint_fields` is incomplete -//~| NOTE: `#[warn(incomplete_features)]` on by default -//~| NOTE: see issue #53488 +// edition:2021 #![feature(rustc_attrs)] #![allow(unused)] diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr index c9f9386b9aa1f..b727c06d9528f 100644 --- a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/edge_case.rs:23:13 + --> $DIR/edge_case.rs:20:13 | LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,39 +7,30 @@ LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); = note: see issue #15701 for more information = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable -warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/edge_case.rs:1:12 - | -LL | #![feature(capture_disjoint_fields)] - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #53488 for more information - error: First Pass analysis includes: - --> $DIR/edge_case.rs:23:39 + --> $DIR/edge_case.rs:20:39 | LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); | ^^^^^^^^^^^^^^^ | note: Capturing m[Deref,(0, 0),Deref] -> ImmBorrow - --> $DIR/edge_case.rs:23:48 + --> $DIR/edge_case.rs:20:48 | LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); | ^^^^^ error: Min Capture analysis includes: - --> $DIR/edge_case.rs:23:39 + --> $DIR/edge_case.rs:20:39 | LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); | ^^^^^^^^^^^^^^^ | note: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow - --> $DIR/edge_case.rs:23:48 + --> $DIR/edge_case.rs:20:48 | LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); | ^^^^^ -error: aborting due to 3 previous errors; 1 warning emitted +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs index 2c4ba0c91a6d1..033fd6f17753a 100644 --- a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs @@ -1,8 +1,4 @@ -#![feature(capture_disjoint_fields)] -//~^ WARNING: the feature `capture_disjoint_fields` is incomplete -//~| NOTE: `#[warn(incomplete_features)]` on by default -//~| NOTE: see issue #53488 - +// edition:2021 // run-pass #![allow(unused)] diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.stderr b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.stderr deleted file mode 100644 index 36906d1685ce3..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/edge_case_run_pass.rs:1:12 - | -LL | #![feature(capture_disjoint_fields)] - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #53488 for more information - -warning: 1 warning emitted - From 38dcae2cda85916d91d727ffe08a944e69ee3162 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 6 Jul 2021 20:52:53 -0400 Subject: [PATCH 03/28] Apply suggestions from code review --- compiler/rustc_typeck/src/check/upvar.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 8418626b8e6e0..86d978718dd28 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -2003,9 +2003,13 @@ fn determine_place_ancestry_relation( fn truncate_capture_for_optimization<'tcx>(place: &Place<'tcx>) -> Place<'tcx> { let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)); + // Find the right-most deref (if any). All the projections that come after this + // are fields or other "in-place pointer adjustments"; these refer therefore to + // data owned by whatever pointer is being dereferenced here. let idx = place.projections.iter().rposition(|proj| ProjectionKind::Deref == proj.kind); match idx { + // If that pointer is a shared reference, then we don't need those fields. Some(idx) if is_shared_ref(place.ty_before_projection(idx)) => { Place { projections: place.projections[0..=idx].to_vec(), ..place.clone() } } From b0f98c60a6ce6d71e244b355e1a602d6994fa8ed Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Wed, 7 Jul 2021 13:13:58 +0200 Subject: [PATCH 04/28] test integer log10 values close to all powers of 10 --- library/core/tests/num/int_log.rs | 54 +++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/library/core/tests/num/int_log.rs b/library/core/tests/num/int_log.rs index 99a9b17ab1183..51122c11ce1e2 100644 --- a/library/core/tests/num/int_log.rs +++ b/library/core/tests/num/int_log.rs @@ -97,3 +97,57 @@ fn checked_log10() { assert_eq!(i.checked_log10(), Some((i as f32).log10() as u16)); } } + +macro_rules! log10_loop { + ($T:ty, $log10_max:expr) => { + assert_eq!(<$T>::MAX.log10(), $log10_max); + for i in 0..=$log10_max { + let p = (10 as $T).pow(i as u32); + if p >= 10 { + assert_eq!((p - 9).log10(), i - 1); + assert_eq!((p - 1).log10(), i - 1); + } + assert_eq!(p.log10(), i); + assert_eq!((p + 1).log10(), i); + if p >= 10 { + assert_eq!((p + 9).log10(), i); + } + + // also check `x.log(10)` + if p >= 10 { + assert_eq!((p - 9).log(10), i - 1); + assert_eq!((p - 1).log(10), i - 1); + } + assert_eq!(p.log(10), i); + assert_eq!((p + 1).log(10), i); + if p >= 10 { + assert_eq!((p + 9).log(10), i); + } + } + }; +} + +#[test] +fn log10_u8() { + log10_loop! { u8, 2 } +} + +#[test] +fn log10_u16() { + log10_loop! { u16, 4 } +} + +#[test] +fn log10_u32() { + log10_loop! { u32, 9 } +} + +#[test] +fn log10_u64() { + log10_loop! { u64, 19 } +} + +#[test] +fn log10_u128() { + log10_loop! { u128, 38 } +} From ed76c11202c66154a5779f45a9ac70eeccdc9d8b Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Wed, 7 Jul 2021 14:10:05 +0200 Subject: [PATCH 05/28] special case for integer log10 --- library/core/src/num/int_log10.rs | 134 ++++++++++++++++++++++++++++ library/core/src/num/int_macros.rs | 5 +- library/core/src/num/mod.rs | 1 + library/core/src/num/uint_macros.rs | 7 +- 4 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 library/core/src/num/int_log10.rs diff --git a/library/core/src/num/int_log10.rs b/library/core/src/num/int_log10.rs new file mode 100644 index 0000000000000..a23ca51ef87d0 --- /dev/null +++ b/library/core/src/num/int_log10.rs @@ -0,0 +1,134 @@ +mod unchecked { + // 0 < val <= u8::MAX + pub const fn u8(val: u8) -> u32 { + if val >= 100 { + 2 + } else if val >= 10 { + 1 + } else { + 0 + } + } + + // 0 < val <= u16::MAX + pub const fn u16(val: u16) -> u32 { + if val >= 10_000 { + 4 + } else if val >= 1000 { + 3 + } else if val >= 100 { + 2 + } else if val >= 10 { + 1 + } else { + 0 + } + } + + // 0 < val < 100_000_000 + const fn less_than_8(mut val: u32) -> u32 { + let mut log = 0; + if val >= 10_000 { + val /= 10_000; + log += 4; + } + log + if val >= 1000 { + 3 + } else if val >= 100 { + 2 + } else if val >= 10 { + 1 + } else { + 0 + } + } + + // 0 < val <= u32::MAX + pub const fn u32(mut val: u32) -> u32 { + let mut log = 0; + if val >= 100_000_000 { + val /= 100_000_000; + log += 8; + } + log + less_than_8(val) + } + + // 0 < val < 10_000_000_000_000_000 + const fn less_than_16(mut val: u64) -> u32 { + let mut log = 0; + if val >= 100_000_000 { + val /= 100_000_000; + log += 8; + } + log + less_than_8(val as u32) + } + + // 0 < val <= u64::MAX + pub const fn u64(mut val: u64) -> u32 { + let mut log = 0; + if val >= 10_000_000_000_000_000 { + val /= 10_000_000_000_000_000; + log += 16; + } + log + less_than_16(val) + } + + // 0 < val <= u128::MAX + pub const fn u128(mut val: u128) -> u32 { + let mut log = 0; + if val >= 100_000_000_000_000_000_000_000_000_000_000 { + val /= 100_000_000_000_000_000_000_000_000_000_000; + log += 32; + return log + less_than_8(val as u32); + } + if val >= 10_000_000_000_000_000 { + val /= 10_000_000_000_000_000; + log += 16; + } + log + less_than_16(val as u64) + } + + // 0 < val <= i8::MAX + pub const fn i8(val: i8) -> u32 { + u8(val as u8) + } + + // 0 < val <= i16::MAX + pub const fn i16(val: i16) -> u32 { + u16(val as u16) + } + + // 0 < val <= i32::MAX + pub const fn i32(val: i32) -> u32 { + u32(val as u32) + } + + // 0 < val <= i64::MAX + pub const fn i64(val: i64) -> u32 { + u64(val as u64) + } + + // 0 < val <= i128::MAX + pub const fn i128(val: i128) -> u32 { + u128(val as u128) + } +} + +macro_rules! impl_checked { + ($T:ident) => { + pub const fn $T(val: $T) -> Option<$T> { + if val > 0 { Some(unchecked::$T(val) as $T) } else { None } + } + }; +} + +impl_checked! { u8 } +impl_checked! { u16 } +impl_checked! { u32 } +impl_checked! { u64 } +impl_checked! { u128 } +impl_checked! { i8 } +impl_checked! { i16 } +impl_checked! { i32 } +impl_checked! { i64 } +impl_checked! { i128 } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index a9461649d4aa1..982729388c87d 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1929,7 +1929,10 @@ macro_rules! int_impl { without modifying the original"] #[inline] pub const fn checked_log10(self) -> Option { - self.checked_log(10) + match int_log10::$ActualT(self as $ActualT) { + Some(s) => Some(s as Self), + None => None, + } } /// Computes the absolute value of `self`. diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 81d00c281ade8..26d84a60702c5 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -41,6 +41,7 @@ mod int_macros; // import int_impl! mod uint_macros; // import uint_impl! mod error; +mod int_log10; mod nonzero; mod wrapping; diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index bf4d2e7433e2f..ca1b05fdfbe48 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1,5 +1,5 @@ macro_rules! uint_impl { - ($SelfT:ty, $ActualT:ty, $BITS:expr, $MaxV:expr, + ($SelfT:ty, $ActualT:ident, $BITS:expr, $MaxV:expr, $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, $reversed:expr, $le_bytes:expr, $be_bytes:expr, $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { @@ -819,7 +819,10 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn checked_log10(self) -> Option { - self.checked_log(10) + match int_log10::$ActualT(self as $ActualT) { + Some(s) => Some(s as Self), + None => None, + } } /// Checked negation. Computes `-self`, returning `None` unless `self == From 60535441c8c8f9190ae0fbf62f6f1de75e414b9d Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 6 Jul 2021 13:41:22 +0000 Subject: [PATCH 06/28] Check FromIterator trait impl in prelude collision check. --- compiler/rustc_span/src/symbol.rs | 1 + .../src/check/method/prelude2021.rs | 21 +++++++++++++++++++ library/core/src/iter/traits/collect.rs | 1 + 3 files changed, 23 insertions(+) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index be4f12c6d1cb8..085e3811ac1a3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -155,6 +155,7 @@ symbols! { FormatSpec, Formatter, From, + FromIterator, Future, FxHashMap, FxHashSet, diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs index 6ca0b3ed66b39..f853c0fd9ccdb 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_typeck/src/check/method/prelude2021.rs @@ -4,11 +4,13 @@ use hir::ItemKind; use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{Ref, Ty}; use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS; use rustc_span::symbol::kw::Underscore; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; +use rustc_trait_selection::infer::InferCtxtExt; use crate::check::{ method::probe::{self, Pick}, @@ -206,6 +208,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } + // For from_iter, check if the type actualy implements FromIterator. + // If we know it does not, we don't need to warn. + if method_name.name == sym::from_iter { + if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) { + if !self + .infcx + .type_implements_trait( + trait_def_id, + self_ty, + InternalSubsts::empty(), + self.param_env, + ) + .may_apply() + { + return; + } + } + } + // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`, // since such methods take precedence over trait methods. if matches!(pick.kind, probe::PickKind::InherentImplPick) { diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 348e1f7e3f23d..7f87ead6feed6 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -89,6 +89,7 @@ over elements of type `{A}`", label = "value of type `{Self}` cannot be built from `std::iter::Iterator`" )] +#[rustc_diagnostic_item = "FromIterator"] pub trait FromIterator: Sized { /// Creates a value from an iterator. /// From f77dd5ac9371897d30dd3a8c43023e14f86dd0bf Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 6 Jul 2021 13:51:25 +0000 Subject: [PATCH 07/28] Add test for trait check in prelude collision lint. --- .../future-prelude-collision-unneeded.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs b/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs index 4be82056ad59b..8b3b7e74a7f95 100644 --- a/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs +++ b/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs @@ -11,8 +11,23 @@ impl S { } } -// See https://github.com/rust-lang/rust/issues/86633 +struct X; + +trait Hey { + fn from_iter(_: i32) -> Self; +} + +impl Hey for X { + fn from_iter(_: i32) -> Self { + X + } +} + fn main() { + // See https://github.com/rust-lang/rust/issues/86633 let s = S; let s2 = s.try_into(); + + // See https://github.com/rust-lang/rust/issues/86902 + X::from_iter(1); } From 10d6b34d32e377798868586d2d8e25d972cbeb0d Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 7 Jul 2021 13:25:48 +0000 Subject: [PATCH 08/28] Add generic types to prelude collision lint test. --- .../future-prelude-collision-unneeded.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs b/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs index 8b3b7e74a7f95..6e20767998664 100644 --- a/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs +++ b/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs @@ -23,6 +23,28 @@ impl Hey for X { } } +struct Y(T); + +impl Hey for Y { + fn from_iter(_: i32) -> Self { + Y(0) + } +} + +struct Z(T); + +impl Hey for Z { + fn from_iter(_: i32) -> Self { + Z(0) + } +} + +impl std::iter::FromIterator for Z { + fn from_iter>(_: T) -> Self { + todo!() + } +} + fn main() { // See https://github.com/rust-lang/rust/issues/86633 let s = S; @@ -30,4 +52,7 @@ fn main() { // See https://github.com/rust-lang/rust/issues/86902 X::from_iter(1); + Y::from_iter(1); + Y::::from_iter(1); + Z::::from_iter(1); } From 99b5d2a88ffae4923a0076bc2c669cc3cc0affed Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 8 Jul 2021 11:33:33 +0200 Subject: [PATCH 09/28] Fix typo in comment. --- compiler/rustc_typeck/src/check/method/prelude2021.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs index f853c0fd9ccdb..f13e23914f7ab 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_typeck/src/check/method/prelude2021.rs @@ -208,7 +208,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - // For from_iter, check if the type actualy implements FromIterator. + // For from_iter, check if the type actually implements FromIterator. // If we know it does not, we don't need to warn. if method_name.name == sym::from_iter { if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) { From 2083207536e710071475dc489f155c574951e3fd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 8 Jul 2021 12:31:56 -0400 Subject: [PATCH 10/28] Update src/test/ui/rust-2021/future-prelude-collision-unneeded.rs --- src/test/ui/rust-2021/future-prelude-collision-unneeded.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs b/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs index 6e20767998664..247d5884b868a 100644 --- a/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs +++ b/src/test/ui/rust-2021/future-prelude-collision-unneeded.rs @@ -50,6 +50,8 @@ fn main() { let s = S; let s2 = s.try_into(); + // Check that we do not issue suggestions for types that do not implement `FromIter`. + // // See https://github.com/rust-lang/rust/issues/86902 X::from_iter(1); Y::from_iter(1); From d05eafae2fcc05bd64ab094a1352a5c16df3106e Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 30 May 2021 10:23:50 -0700 Subject: [PATCH 11/28] Move the `PartialEq` and `Eq` impls for arrays to a separate file --- library/core/src/array/equality.rs | 111 ++++++++++++++++++++++++++++ library/core/src/array/mod.rs | 113 +---------------------------- 2 files changed, 112 insertions(+), 112 deletions(-) create mode 100644 library/core/src/array/equality.rs diff --git a/library/core/src/array/equality.rs b/library/core/src/array/equality.rs new file mode 100644 index 0000000000000..dcd78e7a245d4 --- /dev/null +++ b/library/core/src/array/equality.rs @@ -0,0 +1,111 @@ +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq<[B; N]> for [A; N] +where + A: PartialEq, +{ + #[inline] + fn eq(&self, other: &[B; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[B; N]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq<[B]> for [A; N] +where + A: PartialEq, +{ + #[inline] + fn eq(&self, other: &[B]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[B]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq<[A; N]> for [B] +where + B: PartialEq, +{ + #[inline] + fn eq(&self, other: &[A; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[A; N]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq<&[B]> for [A; N] +where + A: PartialEq, +{ + #[inline] + fn eq(&self, other: &&[B]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &&[B]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq<[A; N]> for &[B] +where + B: PartialEq, +{ + #[inline] + fn eq(&self, other: &[A; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[A; N]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq<&mut [B]> for [A; N] +where + A: PartialEq, +{ + #[inline] + fn eq(&self, other: &&mut [B]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &&mut [B]) -> bool { + self[..] != other[..] + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq<[A; N]> for &mut [B] +where + B: PartialEq, +{ + #[inline] + fn eq(&self, other: &[A; N]) -> bool { + self[..] == other[..] + } + #[inline] + fn ne(&self, other: &[A; N]) -> bool { + self[..] != other[..] + } +} + +// NOTE: some less important impls are omitted to reduce code bloat +// __impl_slice_eq2! { [A; $N], &'b [B; $N] } +// __impl_slice_eq2! { [A; $N], &'b mut [B; $N] } + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for [T; N] {} diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 030b42a53d05d..32d344010aafd 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -14,6 +14,7 @@ use crate::mem::{self, MaybeUninit}; use crate::ops::{Index, IndexMut}; use crate::slice::{Iter, IterMut}; +mod equality; mod iter; #[stable(feature = "array_value_iter", since = "1.51.0")] @@ -230,118 +231,6 @@ where } } -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[B; N]> for [A; N] -where - A: PartialEq, -{ - #[inline] - fn eq(&self, other: &[B; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[B; N]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[B]> for [A; N] -where - A: PartialEq, -{ - #[inline] - fn eq(&self, other: &[B]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[B]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[A; N]> for [B] -where - B: PartialEq, -{ - #[inline] - fn eq(&self, other: &[A; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[A; N]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<&[B]> for [A; N] -where - A: PartialEq, -{ - #[inline] - fn eq(&self, other: &&[B]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &&[B]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[A; N]> for &[B] -where - B: PartialEq, -{ - #[inline] - fn eq(&self, other: &[A; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[A; N]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<&mut [B]> for [A; N] -where - A: PartialEq, -{ - #[inline] - fn eq(&self, other: &&mut [B]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &&mut [B]) -> bool { - self[..] != other[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[A; N]> for &mut [B] -where - B: PartialEq, -{ - #[inline] - fn eq(&self, other: &[A; N]) -> bool { - self[..] == other[..] - } - #[inline] - fn ne(&self, other: &[A; N]) -> bool { - self[..] != other[..] - } -} - -// NOTE: some less important impls are omitted to reduce code bloat -// __impl_slice_eq2! { [A; $N], &'b [B; $N] } -// __impl_slice_eq2! { [A; $N], &'b mut [B; $N] } - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for [T; N] {} - #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for [T; N] { #[inline] From 2456495a260827217d3c612d6c577c2f165c61eb Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 30 May 2021 10:25:41 -0700 Subject: [PATCH 12/28] Stop generating `alloca`s+`memcmp` for simple array equality --- compiler/rustc_codegen_llvm/src/context.rs | 5 ++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 26 +++++++++ .../rustc_mir/src/interpret/intrinsics.rs | 18 +++++++ compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_typeck/src/check/intrinsic.rs | 8 +++ library/core/src/array/equality.rs | 53 ++++++++++++++++++- library/core/src/intrinsics.rs | 16 ++++++ src/test/codegen/array-equality.rs | 36 +++++++++++++ src/test/codegen/slice-ref-equality.rs | 19 ++++++- .../intrinsic-raw_eq-const-padding.rs | 12 +++++ .../intrinsic-raw_eq-const-padding.stderr | 21 ++++++++ .../ui/intrinsics/intrinsic-raw_eq-const.rs | 27 ++++++++++ 12 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 src/test/codegen/array-equality.rs create mode 100644 src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.rs create mode 100644 src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr create mode 100644 src/test/ui/intrinsics/intrinsic-raw_eq-const.rs diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index f662887abf820..d1aecd32e2f2d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -500,6 +500,7 @@ impl CodegenCx<'b, 'tcx> { let t_i32 = self.type_i32(); let t_i64 = self.type_i64(); let t_i128 = self.type_i128(); + let t_isize = self.type_isize(); let t_f32 = self.type_f32(); let t_f64 = self.type_f64(); @@ -712,6 +713,10 @@ impl CodegenCx<'b, 'tcx> { ifn!("llvm.assume", fn(i1) -> void); ifn!("llvm.prefetch", fn(i8p, t_i32, t_i32, t_i32) -> void); + // This isn't an "LLVM intrinsic", but LLVM's optimization passes + // recognize it like one and we assume it exists in `core::slice::cmp` + ifn!("memcmp", fn(i8p, i8p, t_isize) -> t_i32); + // variadic intrinsics ifn!("llvm.va_start", fn(i8p) -> void); ifn!("llvm.va_end", fn(i8p) -> void); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 1fb201eda6bb0..615295e96e116 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -296,6 +296,32 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } + sym::raw_eq => { + let tp_ty = substs.type_at(0); + let (size, align) = self.size_and_align_of(tp_ty); + let a = args[0].immediate(); + let b = args[1].immediate(); + if size.bytes() == 0 { + self.const_bool(true) + } else if size > self.data_layout().pointer_size * 4 { + let i8p_ty = self.type_i8p(); + let a_ptr = self.bitcast(a, i8p_ty); + let b_ptr = self.bitcast(b, i8p_ty); + let n = self.const_usize(size.bytes()); + let llfn = self.get_intrinsic("memcmp"); + let cmp = self.call(llfn, &[a_ptr, b_ptr, n], None); + self.icmp(IntPredicate::IntEQ, cmp, self.const_i32(0)) + } else { + let integer_ty = self.type_ix(size.bits()); + let ptr_ty = self.type_ptr_to(integer_ty); + let a_ptr = self.bitcast(a, ptr_ty); + let a_val = self.load(a_ptr, align); + let b_ptr = self.bitcast(b, ptr_ty); + let b_val = self.load(b_ptr, align); + self.icmp(IntPredicate::IntEQ, a_val, b_val) + } + } + _ if name_str.starts_with("simd_") => { match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) { Ok(llval) => llval, diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs index 4e4166dad50e2..5dd679b8912ce 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -472,6 +472,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { throw_ub_format!("`assume` intrinsic called with `false`"); } } + sym::raw_eq => { + let result = self.raw_eq_intrinsic(&args[0], &args[1])?; + self.write_scalar(result, dest)?; + } _ => return Ok(false), } @@ -559,4 +563,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.memory.copy(src, align, dst, align, size, nonoverlapping) } + + pub(crate) fn raw_eq_intrinsic( + &mut self, + lhs: &OpTy<'tcx, >::PointerTag>, + rhs: &OpTy<'tcx, >::PointerTag>, + ) -> InterpResult<'tcx, Scalar> { + let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap().ty)?; + + let lhs = self.read_scalar(lhs)?.check_init()?; + let rhs = self.read_scalar(rhs)?.check_init()?; + let lhs_bytes = self.memory.read_bytes(lhs, layout.size)?; + let rhs_bytes = self.memory.read_bytes(rhs, layout.size)?; + Ok(Scalar::Int((lhs_bytes == rhs_bytes).into())) + } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index edb97d70517b0..3ab32fe418db1 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -933,6 +933,7 @@ symbols! { quote, range_inclusive_new, raw_dylib, + raw_eq, raw_identifiers, raw_ref_op, re_rebalance_coherence, diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 882d5d54b7c9f..18ccaf79d32c8 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -380,6 +380,14 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), + sym::raw_eq => { + let param_count = if intrinsic_name == sym::raw_eq { 2 } else { 1 }; + let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }; + let param_ty = + tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)), param(0)); + (1, vec![param_ty; param_count], tcx.types.bool) + } + other => { tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other }); return; diff --git a/library/core/src/array/equality.rs b/library/core/src/array/equality.rs index dcd78e7a245d4..6d66b9e2f2780 100644 --- a/library/core/src/array/equality.rs +++ b/library/core/src/array/equality.rs @@ -5,11 +5,11 @@ where { #[inline] fn eq(&self, other: &[B; N]) -> bool { - self[..] == other[..] + SpecArrayEq::spec_eq(self, other) } #[inline] fn ne(&self, other: &[B; N]) -> bool { - self[..] != other[..] + SpecArrayEq::spec_ne(self, other) } } @@ -109,3 +109,52 @@ where #[stable(feature = "rust1", since = "1.0.0")] impl Eq for [T; N] {} + +trait SpecArrayEq: Sized { + fn spec_eq(a: &[Self; N], b: &[Other; N]) -> bool; + fn spec_ne(a: &[Self; N], b: &[Other; N]) -> bool; +} + +impl, Other, const N: usize> SpecArrayEq for T { + default fn spec_eq(a: &[Self; N], b: &[Other; N]) -> bool { + a[..] == b[..] + } + default fn spec_ne(a: &[Self; N], b: &[Other; N]) -> bool { + a[..] != b[..] + } +} + +impl + IsRawEqComparable, U, const N: usize> SpecArrayEq for T { + #[cfg(bootstrap)] + fn spec_eq(a: &[T; N], b: &[U; N]) -> bool { + a[..] == b[..] + } + #[cfg(not(bootstrap))] + fn spec_eq(a: &[T; N], b: &[U; N]) -> bool { + // SAFETY: This is why `IsRawEqComparable` is an `unsafe trait`. + unsafe { + let b = &*b.as_ptr().cast::<[T; N]>(); + crate::intrinsics::raw_eq(a, b) + } + } + fn spec_ne(a: &[T; N], b: &[U; N]) -> bool { + !Self::spec_eq(a, b) + } +} + +/// `U` exists on here mostly because `min_specialization` didn't let me +/// repeat the `T` type parameter in the above specialization, so instead +/// the `T == U` constraint comes from the impls on this. +/// # Safety +/// - Neither `Self` nor `U` has any padding. +/// - `Self` and `U` have the same layout. +/// - `Self: PartialEq` is byte-wise (this means no floats, among other things) +#[rustc_specialization_trait] +unsafe trait IsRawEqComparable {} + +macro_rules! is_raw_comparable { + ($($t:ty),+) => {$( + unsafe impl IsRawEqComparable<$t> for $t {} + )+}; +} +is_raw_comparable!(bool, char, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index c5a4bbd320804..7d2c278aa0523 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1913,6 +1913,22 @@ extern "rust-intrinsic" { /// Allocate at compile time. Should not be called at runtime. #[rustc_const_unstable(feature = "const_heap", issue = "79597")] pub fn const_allocate(size: usize, align: usize) -> *mut u8; + + /// Determines whether the raw bytes of the two values are equal. + /// + /// The is particularly handy for arrays, since it allows things like just + /// comparing `i96`s instead of forcing `alloca`s for `[6 x i16]`. + /// + /// Above some backend-decided threshold this will emit calls to `memcmp`, + /// like slice equality does, instead of causing massive code size. + /// + /// # Safety + /// + /// This doesn't take into account padding, so if `T` has padding + /// the result will be `undef`, which cannot be exposed to safe code. + #[cfg(not(bootstrap))] + #[rustc_const_unstable(feature = "const_intrinsic_raw_eq", issue = "none")] + pub fn raw_eq(a: &T, b: &T) -> bool; } // Some functions are defined here because they accidentally got made diff --git a/src/test/codegen/array-equality.rs b/src/test/codegen/array-equality.rs new file mode 100644 index 0000000000000..6a9fb5c8f864d --- /dev/null +++ b/src/test/codegen/array-equality.rs @@ -0,0 +1,36 @@ +// compile-flags: -O +// only-x86_64 + +#![crate_type = "lib"] + +// CHECK-LABEL: @array_eq_value +#[no_mangle] +pub fn array_eq_value(a: [u16; 6], b: [u16; 6]) -> bool { + // CHECK-NEXT: start: + // CHECK-NEXT: %2 = icmp eq i96 %0, %1 + // CHECK-NEXT: ret i1 %2 + a == b +} + +// CHECK-LABEL: @array_eq_ref +#[no_mangle] +pub fn array_eq_ref(a: &[u16; 6], b: &[u16; 6]) -> bool { + // CHECK: start: + // CHECK: load i96, i96* %{{.+}}, align 2 + // CHECK: load i96, i96* %{{.+}}, align 2 + // CHECK: icmp eq i96 + // CHECK-NEXT: ret + a == b +} + +// CHECK-LABEL: @array_eq_long +#[no_mangle] +pub fn array_eq_long(a: &[u16; 1234], b: &[u16; 1234]) -> bool { + // CHECK-NEXT: start: + // CHECK-NEXT: bitcast + // CHECK-NEXT: bitcast + // CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* nonnull dereferenceable(2468) %{{.+}}, i8* nonnull dereferenceable(2468) %{{.+}}, i64 2468) + // CHECK-NEXT: %[[EQ:.+]] = icmp eq i32 %[[CMP]], 0 + // CHECK-NEXT: ret i1 %[[EQ]] + a == b +} diff --git a/src/test/codegen/slice-ref-equality.rs b/src/test/codegen/slice-ref-equality.rs index acc7879e7b189..1f99ac7342b39 100644 --- a/src/test/codegen/slice-ref-equality.rs +++ b/src/test/codegen/slice-ref-equality.rs @@ -2,15 +2,30 @@ #![crate_type = "lib"] -// #71602: check that slice equality just generates a single bcmp +// #71602 reported a simple array comparison just generating a loop. +// This was originally fixed by ensuring it generates a single bcmp, +// but we now generate it as a load instead. `is_zero_slice` was +// tweaked to still test the case of comparison against a slice, +// and `is_zero_array` tests the new array-specific behaviour. // CHECK-LABEL: @is_zero_slice #[no_mangle] pub fn is_zero_slice(data: &[u8; 4]) -> bool { - // CHECK: start: + // CHECK: : // CHECK-NEXT: %{{.+}} = getelementptr {{.+}} // CHECK-NEXT: %[[BCMP:.+]] = tail call i32 @{{bcmp|memcmp}}({{.+}}) // CHECK-NEXT: %[[EQ:.+]] = icmp eq i32 %[[BCMP]], 0 // CHECK-NEXT: ret i1 %[[EQ]] + &data[..] == [0; 4] +} + +// CHECK-LABEL: @is_zero_array +#[no_mangle] +pub fn is_zero_array(data: &[u8; 4]) -> bool { + // CHECK: start: + // CHECK-NEXT: %[[PTR:.+]] = bitcast [4 x i8]* {{.+}} to i32* + // CHECK-NEXT: %[[LOAD:.+]] = load i32, i32* %[[PTR]], align 1 + // CHECK-NEXT: %[[EQ:.+]] = icmp eq i32 %[[LOAD]], 0 + // CHECK-NEXT: ret i1 %[[EQ]] *data == [0; 4] } diff --git a/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.rs b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.rs new file mode 100644 index 0000000000000..ec1c47cfaea9f --- /dev/null +++ b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.rs @@ -0,0 +1,12 @@ +#![feature(core_intrinsics)] +#![feature(const_intrinsic_raw_eq)] +#![deny(const_err)] + +const BAD_RAW_EQ_CALL: bool = unsafe { + std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) +//~^ ERROR any use of this value will cause an error +//~| WARNING this was previously accepted by the compiler but is being phased out +}; + +pub fn main() { +} diff --git a/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr new file mode 100644 index 0000000000000..74df99a69d1fa --- /dev/null +++ b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr @@ -0,0 +1,21 @@ +error: any use of this value will cause an error + --> $DIR/intrinsic-raw_eq-const-padding.rs:6:5 + | +LL | / const BAD_RAW_EQ_CALL: bool = unsafe { +LL | | std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading 4 bytes of memory starting at alloc2, but 1 byte is uninitialized starting at alloc2+0x1, and this operation requires initialized memory +LL | | +LL | | +LL | | }; + | |__- + | +note: the lint level is defined here + --> $DIR/intrinsic-raw_eq-const-padding.rs:3:9 + | +LL | #![deny(const_err)] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 + +error: aborting due to previous error + diff --git a/src/test/ui/intrinsics/intrinsic-raw_eq-const.rs b/src/test/ui/intrinsics/intrinsic-raw_eq-const.rs new file mode 100644 index 0000000000000..8ea954673020e --- /dev/null +++ b/src/test/ui/intrinsics/intrinsic-raw_eq-const.rs @@ -0,0 +1,27 @@ +// run-pass + +#![feature(core_intrinsics)] +#![feature(const_intrinsic_raw_eq)] +#![deny(const_err)] + +pub fn main() { + use std::intrinsics::raw_eq; + + const RAW_EQ_I32_TRUE: bool = unsafe { raw_eq(&42_i32, &42) }; + assert!(RAW_EQ_I32_TRUE); + + const RAW_EQ_I32_FALSE: bool = unsafe { raw_eq(&4_i32, &2) }; + assert!(!RAW_EQ_I32_FALSE); + + const RAW_EQ_CHAR_TRUE: bool = unsafe { raw_eq(&'a', &'a') }; + assert!(RAW_EQ_CHAR_TRUE); + + const RAW_EQ_CHAR_FALSE: bool = unsafe { raw_eq(&'a', &'A') }; + assert!(!RAW_EQ_CHAR_FALSE); + + const RAW_EQ_ARRAY_TRUE: bool = unsafe { raw_eq(&[13_u8, 42], &[13, 42]) }; + assert!(RAW_EQ_ARRAY_TRUE); + + const RAW_EQ_ARRAY_FALSE: bool = unsafe { raw_eq(&[13_u8, 42], &[42, 13]) }; + assert!(!RAW_EQ_ARRAY_FALSE); +} From b63b2f1e426016b24a9e552faf17f20f421f5034 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 30 May 2021 11:31:56 -0700 Subject: [PATCH 13/28] PR feedback - Add `:Sized` assertion in interpreter impl - Use `Scalar::from_bool` instead of `ScalarInt: From` - Remove unneeded comparison in intrinsic typeck - Make this UB to call with undef, not just return undef in that case --- compiler/rustc_mir/src/interpret/intrinsics.rs | 3 ++- compiler/rustc_typeck/src/check/intrinsic.rs | 3 +-- library/core/src/intrinsics.rs | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs index 5dd679b8912ce..ad9cf3e7d2fe9 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -570,11 +570,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { rhs: &OpTy<'tcx, >::PointerTag>, ) -> InterpResult<'tcx, Scalar> { let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap().ty)?; + assert!(!layout.is_unsized()); let lhs = self.read_scalar(lhs)?.check_init()?; let rhs = self.read_scalar(rhs)?.check_init()?; let lhs_bytes = self.memory.read_bytes(lhs, layout.size)?; let rhs_bytes = self.memory.read_bytes(rhs, layout.size)?; - Ok(Scalar::Int((lhs_bytes == rhs_bytes).into())) + Ok(Scalar::from_bool(lhs_bytes == rhs_bytes)) } } diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 18ccaf79d32c8..6661df21ed952 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -381,11 +381,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), sym::raw_eq => { - let param_count = if intrinsic_name == sym::raw_eq { 2 } else { 1 }; let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }; let param_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)), param(0)); - (1, vec![param_ty; param_count], tcx.types.bool) + (1, vec![param_ty; 2], tcx.types.bool) } other => { diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 7d2c278aa0523..238f00e41b3af 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1924,8 +1924,12 @@ extern "rust-intrinsic" { /// /// # Safety /// - /// This doesn't take into account padding, so if `T` has padding - /// the result will be `undef`, which cannot be exposed to safe code. + /// It's UB to call this if any of the *bytes* in `*a` or `*b` are uninitialized. + /// Note that this is a stricter criterion than just the *values* being + /// fully-initialized: if `T` has padding, it's UB to call this intrinsic. + /// + /// (The implementation is allowed to branch on the results of comparisons, + /// which is UB if any of their inputs are `undef`.) #[cfg(not(bootstrap))] #[rustc_const_unstable(feature = "const_intrinsic_raw_eq", issue = "none")] pub fn raw_eq(a: &T, b: &T) -> bool; From 12163534a94b24a8a754fffe6e601904c2c6f31f Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 30 May 2021 18:04:07 -0700 Subject: [PATCH 14/28] Implement the raw_eq intrinsic in codegen_cranelift --- .../src/intrinsics/mod.rs | 39 +++++++++++++++++++ .../src/value_and_place.rs | 6 +++ 2 files changed, 45 insertions(+) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 52896fc7127e8..3e658cb121138 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1115,6 +1115,45 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( ); ret.write_cvalue(fx, CValue::by_val(res, ret.layout())); }; + + raw_eq, (v lhs_ref, v rhs_ref) { + fn type_by_size(size: Size) -> Option { + Some(match size.bits() { + 8 => types::I8, + 16 => types::I16, + 32 => types::I32, + 64 => types::I64, + 128 => types::I128, + _ => return None, + }) + } + + let size = fx.layout_of(T).layout.size; + let is_eq_value = + if size == Size::ZERO { + // No bytes means they're trivially equal + fx.bcx.ins().bconst(types::B1, true) + } else if let Some(clty) = type_by_size(size) { + // Can't use `trusted` for these loads; they could be unaligned. + let mut flags = MemFlags::new(); + flags.set_notrap(); + let lhs_val = fx.bcx.ins().load(clty, flags, lhs_ref, 0); + let rhs_val = fx.bcx.ins().load(clty, flags, rhs_ref, 0); + fx.bcx.ins().icmp(IntCC::Equal, lhs_val, rhs_val) + } else { + // Just call `memcmp` (like slices do in core) when the + // size is too large or it's not a power-of-two. + let ptr_ty = pointer_ty(fx.tcx); + let signed_bytes = i64::try_from(size.bytes()).unwrap(); + let bytes_val = fx.bcx.ins().iconst(ptr_ty, signed_bytes); + let params = vec![AbiParam::new(ptr_ty); 3]; + let returns = vec![AbiParam::new(types::I32)]; + let args = &[lhs_ref, rhs_ref, bytes_val]; + let cmp = fx.lib_call("memcmp", params, returns, args)[0]; + fx.bcx.ins().icmp_imm(IntCC::Equal, cmp, 0) + }; + ret.write_cvalue(fx, CValue::by_val(is_eq_value, ret.layout())); + }; } if let Some((_, dest)) = destination { diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 171f39805f896..b6f5f5707fbc5 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -437,6 +437,12 @@ impl<'tcx> CPlace<'tcx> { | (types::F32, types::I32) | (types::I64, types::F64) | (types::F64, types::I64) => fx.bcx.ins().bitcast(dst_ty, data), + + // Widen an abstract SSA boolean to something that can be stored in memory + (types::B1, types::I8 | types::I16 | types::I32 | types::I64 | types::I128) => { + fx.bcx.ins().bint(dst_ty, data) + } + _ if src_ty.is_vector() && dst_ty.is_vector() => { fx.bcx.ins().raw_bitcast(dst_ty, data) } From 039a3bafecb42b51c2cc5f1bc1e0b0109873b729 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 30 May 2021 21:27:29 -0700 Subject: [PATCH 15/28] Add another codegen test, array_eq_zero Showing that this avoids an alloca and private constant. --- src/test/codegen/array-equality.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/codegen/array-equality.rs b/src/test/codegen/array-equality.rs index 6a9fb5c8f864d..aa56e32e26ce8 100644 --- a/src/test/codegen/array-equality.rs +++ b/src/test/codegen/array-equality.rs @@ -34,3 +34,12 @@ pub fn array_eq_long(a: &[u16; 1234], b: &[u16; 1234]) -> bool { // CHECK-NEXT: ret i1 %[[EQ]] a == b } + +// CHECK-LABEL: @array_eq_zero(i128 %0) +#[no_mangle] +pub fn array_eq_zero(x: [u16; 8]) -> bool { + // CHECK-NEXT: start: + // CHECK-NEXT: %[[EQ:.+]] = icmp eq i128 %0, 0 + // CHECK-NEXT: ret i1 %[[EQ]] + x == [0; 8] +} From 3d2869c6ff6ca7bd28383db6831f68b2d0349b0a Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Mon, 31 May 2021 10:26:08 -0700 Subject: [PATCH 16/28] PR Feedback: Don't put SSA-only types in `CValue`s --- compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs | 8 +++++--- .../rustc_codegen_cranelift/src/value_and_place.rs | 10 ++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 3e658cb121138..31f7e0d4e37b1 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1132,14 +1132,15 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( let is_eq_value = if size == Size::ZERO { // No bytes means they're trivially equal - fx.bcx.ins().bconst(types::B1, true) + fx.bcx.ins().iconst(types::I8, 1) } else if let Some(clty) = type_by_size(size) { // Can't use `trusted` for these loads; they could be unaligned. let mut flags = MemFlags::new(); flags.set_notrap(); let lhs_val = fx.bcx.ins().load(clty, flags, lhs_ref, 0); let rhs_val = fx.bcx.ins().load(clty, flags, rhs_ref, 0); - fx.bcx.ins().icmp(IntCC::Equal, lhs_val, rhs_val) + let eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_val, rhs_val); + fx.bcx.ins().bint(types::I8, eq) } else { // Just call `memcmp` (like slices do in core) when the // size is too large or it's not a power-of-two. @@ -1150,7 +1151,8 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( let returns = vec![AbiParam::new(types::I32)]; let args = &[lhs_ref, rhs_ref, bytes_val]; let cmp = fx.lib_call("memcmp", params, returns, args)[0]; - fx.bcx.ins().icmp_imm(IntCC::Equal, cmp, 0) + let eq = fx.bcx.ins().icmp_imm(IntCC::Equal, cmp, 0); + fx.bcx.ins().bint(types::I8, eq) }; ret.write_cvalue(fx, CValue::by_val(is_eq_value, ret.layout())); }; diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index b6f5f5707fbc5..ae8ccc626b470 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -437,12 +437,6 @@ impl<'tcx> CPlace<'tcx> { | (types::F32, types::I32) | (types::I64, types::F64) | (types::F64, types::I64) => fx.bcx.ins().bitcast(dst_ty, data), - - // Widen an abstract SSA boolean to something that can be stored in memory - (types::B1, types::I8 | types::I16 | types::I32 | types::I64 | types::I128) => { - fx.bcx.ins().bint(dst_ty, data) - } - _ if src_ty.is_vector() && dst_ty.is_vector() => { fx.bcx.ins().raw_bitcast(dst_ty, data) } @@ -459,6 +453,10 @@ impl<'tcx> CPlace<'tcx> { ptr.store(fx, data, MemFlags::trusted()); ptr.load(fx, dst_ty, MemFlags::trusted()) } + + // `CValue`s should never contain SSA-only types, so if you ended + // up here having seen an error like `B1 -> I8`, then before + // calling `write_cvalue` you need to add a `bint` instruction. _ => unreachable!("write_cvalue_transmute: {:?} -> {:?}", src_ty, dst_ty), }; //fx.bcx.set_val_label(data, cranelift_codegen::ir::ValueLabel::new(var.index())); From 6444f24a29d1b9868e5dba647daf8209499757f6 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 1 Jun 2021 06:19:49 -0700 Subject: [PATCH 17/28] Use cranelift's `Type::int` instead of doing the match myself --- compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 31f7e0d4e37b1..3979886e10cfc 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1118,14 +1118,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( raw_eq, (v lhs_ref, v rhs_ref) { fn type_by_size(size: Size) -> Option { - Some(match size.bits() { - 8 => types::I8, - 16 => types::I16, - 32 => types::I32, - 64 => types::I64, - 128 => types::I128, - _ => return None, - }) + Type::int(size.bits().try_into().ok()?) } let size = fx.layout_of(T).layout.size; From 07fb5ee78f4f251637c5c4414982a8c6e32e186d Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 2 Jun 2021 23:35:30 -0700 Subject: [PATCH 18/28] Adjust the threshold to look at the ABI, not just the size --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 36 +++++++++++++------- src/test/codegen/array-equality.rs | 12 +++++++ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 615295e96e116..9a968659e2fe8 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -297,28 +297,40 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { } sym::raw_eq => { + use abi::Abi::*; let tp_ty = substs.type_at(0); - let (size, align) = self.size_and_align_of(tp_ty); + let layout = self.layout_of(tp_ty).layout; + let use_integer_compare = match layout.abi { + Scalar(_) | ScalarPair(_, _) => true, + Uninhabited | Vector { .. } => false, + Aggregate { .. } => { + // For rusty ABIs, small aggregates are actually passed + // as `RegKind::Integer` (see `FnAbi::adjust_for_abi`), + // so we re-use that same threshold here. + layout.size <= self.data_layout().pointer_size * 2 + } + }; + let a = args[0].immediate(); let b = args[1].immediate(); - if size.bytes() == 0 { + if layout.size.bytes() == 0 { self.const_bool(true) - } else if size > self.data_layout().pointer_size * 4 { + } else if use_integer_compare { + let integer_ty = self.type_ix(layout.size.bits()); + let ptr_ty = self.type_ptr_to(integer_ty); + let a_ptr = self.bitcast(a, ptr_ty); + let a_val = self.load(a_ptr, layout.align.abi); + let b_ptr = self.bitcast(b, ptr_ty); + let b_val = self.load(b_ptr, layout.align.abi); + self.icmp(IntPredicate::IntEQ, a_val, b_val) + } else { let i8p_ty = self.type_i8p(); let a_ptr = self.bitcast(a, i8p_ty); let b_ptr = self.bitcast(b, i8p_ty); - let n = self.const_usize(size.bytes()); + let n = self.const_usize(layout.size.bytes()); let llfn = self.get_intrinsic("memcmp"); let cmp = self.call(llfn, &[a_ptr, b_ptr, n], None); self.icmp(IntPredicate::IntEQ, cmp, self.const_i32(0)) - } else { - let integer_ty = self.type_ix(size.bits()); - let ptr_ty = self.type_ptr_to(integer_ty); - let a_ptr = self.bitcast(a, ptr_ty); - let a_val = self.load(a_ptr, align); - let b_ptr = self.bitcast(b, ptr_ty); - let b_val = self.load(b_ptr, align); - self.icmp(IntPredicate::IntEQ, a_val, b_val) } } diff --git a/src/test/codegen/array-equality.rs b/src/test/codegen/array-equality.rs index aa56e32e26ce8..4b60fa4b0bffa 100644 --- a/src/test/codegen/array-equality.rs +++ b/src/test/codegen/array-equality.rs @@ -23,6 +23,18 @@ pub fn array_eq_ref(a: &[u16; 6], b: &[u16; 6]) -> bool { a == b } +// CHECK-LABEL: @array_eq_value_still_passed_by_pointer +#[no_mangle] +pub fn array_eq_value_still_passed_by_pointer(a: [u16; 9], b: [u16; 9]) -> bool { + // CHECK-NEXT: start: + // CHECK-NEXT: bitcast + // CHECK-NEXT: bitcast + // CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* nonnull dereferenceable(18) %{{.+}}, i8* nonnull dereferenceable(18) %{{.+}}, i64 18) + // CHECK-NEXT: %[[EQ:.+]] = icmp eq i32 %[[CMP]], 0 + // CHECK-NEXT: ret i1 %[[EQ]] + a == b +} + // CHECK-LABEL: @array_eq_long #[no_mangle] pub fn array_eq_long(a: &[u16; 1234], b: &[u16; 1234]) -> bool { From d0644947a3b093bfc09452ecd14291cea5dd8f14 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 8 Jul 2021 15:16:37 -0700 Subject: [PATCH 19/28] Bless a UI test --- .../intrinsic-raw_eq-const-padding.rs | 3 +-- .../intrinsic-raw_eq-const-padding.stderr | 20 ++++--------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.rs b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.rs index ec1c47cfaea9f..a205a8730a0b8 100644 --- a/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.rs +++ b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.rs @@ -4,8 +4,7 @@ const BAD_RAW_EQ_CALL: bool = unsafe { std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) -//~^ ERROR any use of this value will cause an error -//~| WARNING this was previously accepted by the compiler but is being phased out +//~^ ERROR evaluation of constant value failed }; pub fn main() { diff --git a/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr index 74df99a69d1fa..3a1b090012770 100644 --- a/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr +++ b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr @@ -1,21 +1,9 @@ -error: any use of this value will cause an error +error[E0080]: evaluation of constant value failed --> $DIR/intrinsic-raw_eq-const-padding.rs:6:5 | -LL | / const BAD_RAW_EQ_CALL: bool = unsafe { -LL | | std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) - | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading 4 bytes of memory starting at alloc2, but 1 byte is uninitialized starting at alloc2+0x1, and this operation requires initialized memory -LL | | -LL | | -LL | | }; - | |__- - | -note: the lint level is defined here - --> $DIR/intrinsic-raw_eq-const-padding.rs:3:9 - | -LL | #![deny(const_err)] - | ^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 +LL | std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading 4 bytes of memory starting at alloc2, but 1 byte is uninitialized starting at alloc2+0x1, and this operation requires initialized memory error: aborting due to previous error +For more information about this error, try `rustc --explain E0080`. From 0b7ff9660f95daf752281220f48722061db60def Mon Sep 17 00:00:00 2001 From: Roxane Date: Tue, 6 Jul 2021 16:48:49 -0400 Subject: [PATCH 20/28] Add note on why the variable is not fully captured --- compiler/rustc_typeck/src/check/upvar.rs | 92 ++++++++++++------- .../migrations/auto_traits.stderr | 3 + .../migrations/insignificant_drop.fixed | 11 +++ .../migrations/insignificant_drop.rs | 11 +++ .../migrations/insignificant_drop.stderr | 58 ++++++++---- .../insignificant_drop_attr_migrations.fixed | 2 + .../insignificant_drop_attr_migrations.rs | 2 + .../insignificant_drop_attr_migrations.stderr | 14 ++- .../migrations/migrations_rustfix.fixed | 3 + .../migrations/migrations_rustfix.rs | 3 + .../migrations/migrations_rustfix.stderr | 13 ++- .../migrations/mir_calls_to_shims.stderr | 1 + .../migrations/precise.stderr | 4 + .../migrations/significant_drop.fixed | 11 +++ .../migrations/significant_drop.rs | 11 +++ .../migrations/significant_drop.stderr | 63 +++++++++---- 16 files changed, 227 insertions(+), 75 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 4d6d1da194fc6..106fea2b2f73d 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -510,6 +510,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .as_str(), ); + for (var_hir_id, diagnostics_info) in need_migrations.iter() { + for (captured_hir_id, captured_name) in diagnostics_info.iter() { + if let Some(captured_hir_id) = captured_hir_id { + let cause_span = self.tcx.hir().span(*captured_hir_id); + diagnostics_builder.span_label(cause_span, format!("in Rust 2018, closure captures all of `{}`, but in Rust 2021, it only captures `{}`", + self.tcx.hir().name(*var_hir_id), + captured_name, + )); + } + } + } diagnostics_builder.note("for more information, see "); let closure_body_span = self.tcx.hir().span(body_id.hir_id); let (sugg, app) = @@ -579,13 +590,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { var_hir_id: hir::HirId, check_trait: Option, closure_clause: hir::CaptureBy, - ) -> bool { + ) -> Option<(Option, String)> { let root_var_min_capture_list = if let Some(root_var_min_capture_list) = min_captures.and_then(|m| m.get(&var_hir_id)) { root_var_min_capture_list } else { - return false; + return None; }; let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); @@ -639,10 +650,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or(false); if !obligation_holds_for_capture && obligation_should_hold { - return true; + return Some((capture.info.path_expr_id, capture.to_string(self.tcx))); } } - false + None } /// Figures out the list of root variables (and their types) that aren't completely @@ -660,68 +671,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, var_hir_id: hir::HirId, closure_clause: hir::CaptureBy, - ) -> Option> { + ) -> Option<(FxHashSet<&str>, FxHashSet<(Option, String)>)> { let tcx = self.infcx.tcx; // Check whether catpured fields also implement the trait let mut auto_trait_reasons = FxHashSet::default(); + let mut diagnostics_info = FxHashSet::default(); - if self.need_2229_migrations_for_trait( + if let Some(info) = self.need_2229_migrations_for_trait( min_captures, var_hir_id, tcx.lang_items().clone_trait(), closure_clause, ) { auto_trait_reasons.insert("`Clone`"); + diagnostics_info.insert(info); } - if self.need_2229_migrations_for_trait( + if let Some(info) = self.need_2229_migrations_for_trait( min_captures, var_hir_id, tcx.lang_items().sync_trait(), closure_clause, ) { auto_trait_reasons.insert("`Sync`"); + diagnostics_info.insert(info); } - if self.need_2229_migrations_for_trait( + if let Some(info) = self.need_2229_migrations_for_trait( min_captures, var_hir_id, tcx.get_diagnostic_item(sym::send_trait), closure_clause, ) { auto_trait_reasons.insert("`Send`"); + diagnostics_info.insert(info); } - if self.need_2229_migrations_for_trait( + if let Some(info) = self.need_2229_migrations_for_trait( min_captures, var_hir_id, tcx.lang_items().unpin_trait(), closure_clause, ) { auto_trait_reasons.insert("`Unpin`"); + diagnostics_info.insert(info); } - if self.need_2229_migrations_for_trait( + if let Some(info) = self.need_2229_migrations_for_trait( min_captures, var_hir_id, tcx.get_diagnostic_item(sym::unwind_safe_trait), closure_clause, ) { auto_trait_reasons.insert("`UnwindSafe`"); + diagnostics_info.insert(info); } - if self.need_2229_migrations_for_trait( + if let Some(info) = self.need_2229_migrations_for_trait( min_captures, var_hir_id, tcx.get_diagnostic_item(sym::ref_unwind_safe_trait), closure_clause, ) { auto_trait_reasons.insert("`RefUnwindSafe`"); + diagnostics_info.insert(info); } if auto_trait_reasons.len() > 0 { - return Some(auto_trait_reasons); + return Some((auto_trait_reasons, diagnostics_info)); } return None; @@ -746,11 +764,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, closure_clause: hir::CaptureBy, var_hir_id: hir::HirId, - ) -> bool { + ) -> Option, String)>> { let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); if !ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) { - return false; + return None; } let root_var_min_capture_list = if let Some(root_var_min_capture_list) = @@ -763,21 +781,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match closure_clause { // Only migrate if closure is a move closure - hir::CaptureBy::Value => return true, + hir::CaptureBy::Value => return Some(FxHashSet::default()), hir::CaptureBy::Ref => {} } - return false; + return None; }; - let projections_list = root_var_min_capture_list - .iter() - .filter_map(|captured_place| match captured_place.info.capture_kind { + let mut projections_list = Vec::new(); + let mut diagnostics_info = FxHashSet::default(); + + for captured_place in root_var_min_capture_list.iter() { + match captured_place.info.capture_kind { // Only care about captures that are moved into the closure - ty::UpvarCapture::ByValue(..) => Some(captured_place.place.projections.as_slice()), - ty::UpvarCapture::ByRef(..) => None, - }) - .collect::>(); + ty::UpvarCapture::ByValue(..) => { + projections_list.push(captured_place.place.projections.as_slice()); + diagnostics_info.insert(( + captured_place.info.path_expr_id, + captured_place.to_string(self.tcx), + )); + } + ty::UpvarCapture::ByRef(..) => {} + } + } let is_moved = !projections_list.is_empty(); @@ -793,10 +819,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { projections_list, ) { - return true; + return Some(diagnostics_info); } - return false; + return None; } /// Figures out the list of root variables (and their types) that aren't completely @@ -820,7 +846,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_span: Span, closure_clause: hir::CaptureBy, min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, - ) -> (Vec, String) { + ) -> (Vec<(hir::HirId, FxHashSet<(Option, String)>)>, String) { let upvars = if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { upvars } else { @@ -834,14 +860,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Perform auto-trait analysis for (&var_hir_id, _) in upvars.iter() { let mut need_migration = false; - if let Some(trait_migration_cause) = + let mut responsible_captured_hir_ids = FxHashSet::default(); + + if let Some((trait_migration_cause, diagnostics_info)) = self.compute_2229_migrations_for_trait(min_captures, var_hir_id, closure_clause) { need_migration = true; auto_trait_reasons.extend(trait_migration_cause); + responsible_captured_hir_ids.extend(diagnostics_info); } - if self.compute_2229_migrations_for_drop( + if let Some(diagnostics_info) = self.compute_2229_migrations_for_drop( closure_def_id, closure_span, min_captures, @@ -850,10 +879,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { need_migration = true; drop_reorder_reason = true; + responsible_captured_hir_ids.extend(diagnostics_info); } if need_migration { - need_migrations.push(var_hir_id); + need_migrations.push((var_hir_id, responsible_captured_hir_ids)); } } @@ -1877,10 +1907,10 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis( /// - s2: Comma separated names of the variables being migrated. fn migration_suggestion_for_2229( tcx: TyCtxt<'_>, - need_migrations: &Vec, + need_migrations: &Vec<(hir::HirId, FxHashSet<(Option, String)>)>, ) -> (String, String) { let need_migrations_variables = - need_migrations.iter().map(|v| var_name(tcx, *v)).collect::>(); + need_migrations.iter().map(|(v, _)| var_name(tcx, *v)).collect::>(); let migration_ref_concat = need_migrations_variables.iter().map(|v| format!("&{}", v)).collect::>().join(", "); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr index 3d3dde15412bf..39b9dc2e22fa4 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr @@ -6,6 +6,7 @@ LL | thread::spawn(move || unsafe { LL | | LL | | LL | | *fptr.0 = 20; + | | ------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0` LL | | }); | |_____^ | @@ -32,6 +33,7 @@ LL | thread::spawn(move || unsafe { LL | | LL | | LL | | *fptr.0.0 = 20; + | | --------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0` LL | | }); | |_____^ | @@ -53,6 +55,7 @@ LL | let c = || { LL | | LL | | LL | | let f_1 = f.1; + | | --- in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1` LL | | println!("{:?}", f_1.0); LL | | }; | |_____^ diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed index 51d9c4881af3f..71fa99997d388 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed @@ -18,8 +18,11 @@ fn test1_all_need_migration() { //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t1 = t1.0; + //~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` let _t2 = t2.0; + //~^ NOTE: in Rust 2018, closure captures all of `t2`, but in Rust 2021, it only captures `t2.0` }; c(); @@ -37,7 +40,9 @@ fn test2_only_precise_paths_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t1 = t1.0; + //~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` let _t2 = t2; }; @@ -54,6 +59,7 @@ fn test3_only_by_value_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` println!("{}", t1.1); }; @@ -73,6 +79,7 @@ fn test4_only_non_copy_types_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t1 = t1.0; }; @@ -92,6 +99,7 @@ fn test5_only_drop_types_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _s = s.0; }; @@ -108,6 +116,8 @@ fn test6_move_closures_non_copy_types_might_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured println!("{} {}", t1.1, t.1); + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` + //~| NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.1` }; c(); @@ -124,6 +134,7 @@ fn test7_drop_non_drop_aggregate_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs index c732cbb4fa51e..a80aa92aa80f4 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs @@ -18,8 +18,11 @@ fn test1_all_need_migration() { //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t1 = t1.0; + //~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` let _t2 = t2.0; + //~^ NOTE: in Rust 2018, closure captures all of `t2`, but in Rust 2021, it only captures `t2.0` }; c(); @@ -37,7 +40,9 @@ fn test2_only_precise_paths_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t1 = t1.0; + //~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` let _t2 = t2; }; @@ -54,6 +59,7 @@ fn test3_only_by_value_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` println!("{}", t1.1); }; @@ -73,6 +79,7 @@ fn test4_only_non_copy_types_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t1 = t1.0; }; @@ -92,6 +99,7 @@ fn test5_only_drop_types_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _s = s.0; }; @@ -108,6 +116,8 @@ fn test6_move_closures_non_copy_types_might_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured println!("{} {}", t1.1, t.1); + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` + //~| NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.1` }; c(); @@ -124,6 +134,7 @@ fn test7_drop_non_drop_aggregate_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr index 89a2b0eb95366..64708027383db 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr @@ -6,8 +6,16 @@ LL | let c = || { LL | | LL | | LL | | -... | +LL | | +LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | +LL | | let _t1 = t1.0; + | | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` +LL | | LL | | let _t2 = t2.0; + | | ---- in Rust 2018, closure captures all of `t2`, but in Rust 2021, it only captures `t2.0` +LL | | LL | | }; | |_____^ | @@ -28,14 +36,19 @@ LL | let _t = t.0; ... error: drop order will change in Rust 2021 - --> $DIR/insignificant_drop.rs:35:13 + --> $DIR/insignificant_drop.rs:38:13 | LL | let c = || { | _____________^ LL | | LL | | LL | | -... | +LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | +LL | | let _t1 = t1.0; + | | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` +LL | | LL | | let _t2 = t2; LL | | }; | |_____^ @@ -48,11 +61,11 @@ LL | LL | LL | LL | let _t = t.0; -LL | let _t1 = t1.0; +LL | ... error: drop order will change in Rust 2021 - --> $DIR/insignificant_drop.rs:52:13 + --> $DIR/insignificant_drop.rs:57:13 | LL | let c = || { | _____________^ @@ -60,6 +73,8 @@ LL | | LL | | LL | | LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | LL | | println!("{}", t1.1); LL | | }; | |_____^ @@ -72,11 +87,11 @@ LL | LL | LL | LL | let _t = t.0; -LL | println!("{}", t1.1); +LL | ... error: drop order will change in Rust 2021 - --> $DIR/insignificant_drop.rs:71:13 + --> $DIR/insignificant_drop.rs:77:13 | LL | let c = || { | _____________^ @@ -84,6 +99,8 @@ LL | | LL | | LL | | LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | LL | | let _t1 = t1.0; LL | | }; | |_____^ @@ -96,11 +113,11 @@ LL | LL | LL | LL | let _t = t.0; -LL | let _t1 = t1.0; +LL | ... error: drop order will change in Rust 2021 - --> $DIR/insignificant_drop.rs:90:13 + --> $DIR/insignificant_drop.rs:97:13 | LL | let c = || { | _____________^ @@ -108,6 +125,8 @@ LL | | LL | | LL | | LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | LL | | let _s = s.0; LL | | }; | |_____^ @@ -120,11 +139,11 @@ LL | LL | LL | LL | let _t = t.0; -LL | let _s = s.0; +LL | ... error: drop order will change in Rust 2021 - --> $DIR/insignificant_drop.rs:106:13 + --> $DIR/insignificant_drop.rs:114:13 | LL | let c = move || { | _____________^ @@ -132,6 +151,11 @@ LL | | LL | | LL | | LL | | println!("{} {}", t1.1, t.1); + | | ---- --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` + | | | + | | in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.1` +LL | | +LL | | LL | | }; | |_____^ | @@ -143,11 +167,11 @@ LL | LL | LL | LL | println!("{} {}", t1.1, t.1); -LL | }; - | +LL | + ... error: drop order will change in Rust 2021 - --> $DIR/insignificant_drop.rs:122:13 + --> $DIR/insignificant_drop.rs:132:13 | LL | let c = || { | _____________^ @@ -155,6 +179,8 @@ LL | | LL | | LL | | LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | LL | | }; | |_____^ | @@ -166,8 +192,8 @@ LL | LL | LL | LL | let _t = t.0; -LL | }; - | +LL | + ... error: aborting due to 7 previous errors diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed index 8c85cd990d308..f150bd8cc9cbb 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed @@ -39,6 +39,7 @@ fn significant_drop_needs_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` }; c(); @@ -57,6 +58,7 @@ fn generic_struct_with_significant_drop_needs_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.1; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs index 17cee28e31117..03c0ab959b25c 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs @@ -39,6 +39,7 @@ fn significant_drop_needs_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` }; c(); @@ -57,6 +58,7 @@ fn generic_struct_with_significant_drop_needs_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.1; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr index 1d3bda03d0e16..478b331f7fa5b 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr @@ -7,6 +7,8 @@ LL | | LL | | LL | | LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | LL | | }; | |_____^ | @@ -23,11 +25,11 @@ LL | LL | LL | LL | let _t = t.0; -LL | }; - | +LL | + ... error: drop order will change in Rust 2021 - --> $DIR/insignificant_drop_attr_migrations.rs:55:13 + --> $DIR/insignificant_drop_attr_migrations.rs:56:13 | LL | let c = move || { | _____________^ @@ -35,6 +37,8 @@ LL | | LL | | LL | | LL | | let _t = t.1; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` +LL | | LL | | }; | |_____^ | @@ -46,8 +50,8 @@ LL | LL | LL | LL | let _t = t.1; -LL | }; - | +LL | + ... error: aborting due to 2 previous errors diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed index c974299c1536b..a4a38d47500df 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed @@ -21,6 +21,8 @@ fn closure_contains_block() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` + }; c(); @@ -30,6 +32,7 @@ fn closure_doesnt_contain_block() { let t = (Foo(0), Foo(0)); let c = || { let _ = &t; t.0 }; //~^ ERROR: drop order + //~| NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs index dd9556aa56784..01a47dffd85a8 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs @@ -21,6 +21,8 @@ fn closure_contains_block() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` + }; c(); @@ -30,6 +32,7 @@ fn closure_doesnt_contain_block() { let t = (Foo(0), Foo(0)); let c = || t.0; //~^ ERROR: drop order + //~| NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr index 2d5e5e5e55c14..58109128c2c07 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr @@ -7,6 +7,9 @@ LL | | LL | | LL | | LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | +LL | | LL | | }; | |_____^ | @@ -23,14 +26,16 @@ LL | LL | LL | LL | let _t = t.0; -LL | }; - | +LL | + ... error: drop order will change in Rust 2021 - --> $DIR/migrations_rustfix.rs:31:13 + --> $DIR/migrations_rustfix.rs:33:13 | LL | let c = || t.0; - | ^^^^^^ + | ^^^--- + | | + | in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` | = note: for more information, see help: add a dummy let to cause `t` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr index dca5c454b83be..2513ceecc6998 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr @@ -6,6 +6,7 @@ LL | let result = panic::catch_unwind(move || { LL | | LL | | LL | | f.0() + | | --- in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.0` LL | | }); | |_____^ | diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr index 2788207296f11..09829123db3f4 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr @@ -6,6 +6,7 @@ LL | let c = || { LL | | LL | | LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` LL | | let _t = &t.1; LL | | }; | |_____^ @@ -34,8 +35,11 @@ LL | let c = || { LL | | LL | | LL | | let _x = u.0.0; + | | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.0` LL | | let _x = u.0.1; + | | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.1` LL | | let _x = u.1.0; + | | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.1.0` LL | | }; | |_____^ | diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed index 58ed2de26b3a7..5bdefb6762e4f 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed @@ -27,8 +27,11 @@ fn test1_all_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t1 = t1.0; + //~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` let _t2 = t2.0; + //~^ NOTE: in Rust 2018, closure captures all of `t2`, but in Rust 2021, it only captures `t2.0` }; c(); @@ -46,7 +49,9 @@ fn test2_only_precise_paths_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t1 = t1.0; + //~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` let _t2 = t2; }; @@ -63,6 +68,7 @@ fn test3_only_by_value_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` println!("{:?}", t1.1); }; @@ -81,6 +87,7 @@ fn test4_type_contains_drop_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` }; c(); @@ -97,6 +104,7 @@ fn test5_drop_non_drop_aggregate_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` }; c(); @@ -111,6 +119,7 @@ fn test6_significant_insignificant_drop_aggregate_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.1; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` }; c(); @@ -127,6 +136,8 @@ fn test7_move_closures_non_copy_types_might_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured println!("{:?} {:?}", t1.1, t.1); + //~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.1` + //~| NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs index 0890fc1c21256..44119a76e14d0 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs @@ -27,8 +27,11 @@ fn test1_all_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t1 = t1.0; + //~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` let _t2 = t2.0; + //~^ NOTE: in Rust 2018, closure captures all of `t2`, but in Rust 2021, it only captures `t2.0` }; c(); @@ -46,7 +49,9 @@ fn test2_only_precise_paths_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t1 = t1.0; + //~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` let _t2 = t2; }; @@ -63,6 +68,7 @@ fn test3_only_by_value_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` println!("{:?}", t1.1); }; @@ -81,6 +87,7 @@ fn test4_type_contains_drop_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` }; c(); @@ -97,6 +104,7 @@ fn test5_drop_non_drop_aggregate_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` }; c(); @@ -111,6 +119,7 @@ fn test6_significant_insignificant_drop_aggregate_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.1; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` }; c(); @@ -127,6 +136,8 @@ fn test7_move_closures_non_copy_types_might_need_migration() { //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured println!("{:?} {:?}", t1.1, t.1); + //~^ NOTE: in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.1` + //~| NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr index ebf9f169fd400..fa4903b76badf 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr @@ -6,8 +6,15 @@ LL | let c = || { LL | | LL | | LL | | -... | +LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | +LL | | let _t1 = t1.0; + | | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` +LL | | LL | | let _t2 = t2.0; + | | ---- in Rust 2018, closure captures all of `t2`, but in Rust 2021, it only captures `t2.0` +LL | | LL | | }; | |_____^ | @@ -24,18 +31,23 @@ LL | LL | LL | LL | let _t = t.0; -LL | let _t1 = t1.0; +LL | ... error: drop order will change in Rust 2021 - --> $DIR/significant_drop.rs:44:13 + --> $DIR/significant_drop.rs:47:13 | LL | let c = || { | _____________^ LL | | LL | | LL | | -... | +LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | +LL | | let _t1 = t1.0; + | | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` +LL | | LL | | let _t2 = t2; LL | | }; | |_____^ @@ -48,11 +60,11 @@ LL | LL | LL | LL | let _t = t.0; -LL | let _t1 = t1.0; +LL | ... error: drop order will change in Rust 2021 - --> $DIR/significant_drop.rs:61:13 + --> $DIR/significant_drop.rs:66:13 | LL | let c = || { | _____________^ @@ -60,6 +72,8 @@ LL | | LL | | LL | | LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | LL | | println!("{:?}", t1.1); LL | | }; | |_____^ @@ -72,11 +86,11 @@ LL | LL | LL | LL | let _t = t.0; -LL | println!("{:?}", t1.1); +LL | ... error: drop order will change in Rust 2021 - --> $DIR/significant_drop.rs:79:13 + --> $DIR/significant_drop.rs:85:13 | LL | let c = || { | _____________^ @@ -84,6 +98,8 @@ LL | | LL | | LL | | LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | LL | | }; | |_____^ | @@ -95,11 +111,11 @@ LL | LL | LL | LL | let _t = t.0; -LL | }; - | +LL | + ... error: drop order will change in Rust 2021 - --> $DIR/significant_drop.rs:95:13 + --> $DIR/significant_drop.rs:102:13 | LL | let c = || { | _____________^ @@ -107,6 +123,8 @@ LL | | LL | | LL | | LL | | let _t = t.0; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | | LL | | }; | |_____^ | @@ -118,11 +136,11 @@ LL | LL | LL | LL | let _t = t.0; -LL | }; - | +LL | + ... error: drop order will change in Rust 2021 - --> $DIR/significant_drop.rs:109:13 + --> $DIR/significant_drop.rs:117:13 | LL | let c = || { | _____________^ @@ -130,6 +148,8 @@ LL | | LL | | LL | | LL | | let _t = t.1; + | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` +LL | | LL | | }; | |_____^ | @@ -141,11 +161,11 @@ LL | LL | LL | LL | let _t = t.1; -LL | }; - | +LL | + ... error: drop order will change in Rust 2021 - --> $DIR/significant_drop.rs:125:13 + --> $DIR/significant_drop.rs:134:13 | LL | let c = move || { | _____________^ @@ -153,6 +173,11 @@ LL | | LL | | LL | | LL | | println!("{:?} {:?}", t1.1, t.1); + | | ---- --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` + | | | + | | in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.1` +LL | | +LL | | LL | | }; | |_____^ | @@ -164,8 +189,8 @@ LL | LL | LL | LL | println!("{:?} {:?}", t1.1, t.1); -LL | }; - | +LL | + ... error: aborting due to 7 previous errors From 0e8e89daa6c53b32e866205fd00ba385c66b90de Mon Sep 17 00:00:00 2001 From: Roxane Date: Tue, 6 Jul 2021 16:49:07 -0400 Subject: [PATCH 21/28] Update error message --- compiler/rustc_typeck/src/check/upvar.rs | 4 ++-- .../migrations/auto_traits.fixed | 6 +++--- .../migrations/auto_traits.rs | 6 +++--- .../migrations/auto_traits.stderr | 6 +++--- .../migrations/insignificant_drop.stderr | 14 +++++++------- .../insignificant_drop_attr_migrations.stderr | 4 ++-- .../migrations/migrations_rustfix.stderr | 4 ++-- .../migrations/mir_calls_to_shims.fixed | 2 +- .../migrations/mir_calls_to_shims.rs | 2 +- .../migrations/mir_calls_to_shims.stderr | 2 +- .../migrations/precise.stderr | 4 ++-- .../migrations/significant_drop.stderr | 14 +++++++------- 12 files changed, 34 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 106fea2b2f73d..3f042dbd4a56a 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -505,7 +505,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |lint| { let mut diagnostics_builder = lint.build( format!( - "{} will change in Rust 2021", + "changes to closure capture in Rust 2021 will affect {}", reasons ) .as_str(), @@ -567,7 +567,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if auto_trait_reasons.len() > 0 { reasons = format!( - "{} trait implementation", + "{} closure trait implementation", auto_trait_reasons.clone().into_iter().collect::>().join(", ") ); } diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed index 134d07c400b3a..7c7387ff14ee8 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed @@ -11,7 +11,7 @@ fn test_send_trait() { let mut f = 10; let fptr = SendPointer(&mut f as *mut i32); thread::spawn(move || { let _ = &fptr; unsafe { - //~^ ERROR: `Send` trait implementation + //~^ ERROR: `Send` closure trait implementation //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0 = 20; } }); @@ -28,7 +28,7 @@ fn test_sync_trait() { let f = CustomInt(&mut f as *mut i32); let fptr = SyncPointer(f); thread::spawn(move || { let _ = &fptr; unsafe { - //~^ ERROR: `Sync`, `Send` trait implementation + //~^ ERROR: `Sync`, `Send` closure trait implementation //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0.0 = 20; } }); @@ -49,7 +49,7 @@ impl Clone for U { fn test_clone_trait() { let f = U(S(String::from("Hello World")), T(0)); let c = || { let _ = &f; - //~^ ERROR: `Clone` trait implementation, and drop order + //~^ ERROR: `Clone` closure trait implementation, and drop order //~| HELP: add a dummy let to cause `f` to be fully captured let f_1 = f.1; println!("{:?}", f_1.0); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs index b48a724f052f0..e606a206b3b78 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs @@ -11,7 +11,7 @@ fn test_send_trait() { let mut f = 10; let fptr = SendPointer(&mut f as *mut i32); thread::spawn(move || unsafe { - //~^ ERROR: `Send` trait implementation + //~^ ERROR: `Send` closure trait implementation //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0 = 20; }); @@ -28,7 +28,7 @@ fn test_sync_trait() { let f = CustomInt(&mut f as *mut i32); let fptr = SyncPointer(f); thread::spawn(move || unsafe { - //~^ ERROR: `Sync`, `Send` trait implementation + //~^ ERROR: `Sync`, `Send` closure trait implementation //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0.0 = 20; }); @@ -49,7 +49,7 @@ impl Clone for U { fn test_clone_trait() { let f = U(S(String::from("Hello World")), T(0)); let c = || { - //~^ ERROR: `Clone` trait implementation, and drop order + //~^ ERROR: `Clone` closure trait implementation, and drop order //~| HELP: add a dummy let to cause `f` to be fully captured let f_1 = f.1; println!("{:?}", f_1.0); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr index 39b9dc2e22fa4..b628c5c3fad9f 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr @@ -1,4 +1,4 @@ -error: `Send` trait implementation will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect `Send` closure trait implementation --> $DIR/auto_traits.rs:13:19 | LL | thread::spawn(move || unsafe { @@ -25,7 +25,7 @@ LL | *fptr.0 = 20; LL | } }); | -error: `Sync`, `Send` trait implementation will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect `Sync`, `Send` closure trait implementation --> $DIR/auto_traits.rs:30:19 | LL | thread::spawn(move || unsafe { @@ -47,7 +47,7 @@ LL | *fptr.0.0 = 20; LL | } }); | -error: `Clone` trait implementation, and drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect `Clone` closure trait implementation, and drop order --> $DIR/auto_traits.rs:51:13 | LL | let c = || { diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr index 64708027383db..11b90a700551a 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr @@ -1,4 +1,4 @@ -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:15:13 | LL | let c = || { @@ -35,7 +35,7 @@ LL | LL | let _t = t.0; ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:38:13 | LL | let c = || { @@ -64,7 +64,7 @@ LL | let _t = t.0; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:57:13 | LL | let c = || { @@ -90,7 +90,7 @@ LL | let _t = t.0; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:77:13 | LL | let c = || { @@ -116,7 +116,7 @@ LL | let _t = t.0; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:97:13 | LL | let c = || { @@ -142,7 +142,7 @@ LL | let _t = t.0; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:114:13 | LL | let c = move || { @@ -170,7 +170,7 @@ LL | println!("{} {}", t1.1, t.1); LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:132:13 | LL | let c = || { diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr index 478b331f7fa5b..b328abfeb52af 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr @@ -1,4 +1,4 @@ -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop_attr_migrations.rs:37:13 | LL | let c = || { @@ -28,7 +28,7 @@ LL | let _t = t.0; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop_attr_migrations.rs:56:13 | LL | let c = move || { diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr index 58109128c2c07..a4426c79a166c 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr @@ -1,4 +1,4 @@ -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/migrations_rustfix.rs:19:13 | LL | let c = || { @@ -29,7 +29,7 @@ LL | let _t = t.0; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/migrations_rustfix.rs:33:13 | LL | let c = || t.0; diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed index 7f49b460ef63a..309a3c4a70bfb 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed @@ -17,7 +17,7 @@ where { let f = panic::AssertUnwindSafe(f); let result = panic::catch_unwind(move || { let _ = &f; - //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` trait implementation + //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` closure trait implementation //~| HELP: add a dummy let to cause `f` to be fully captured f.0() }); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs index 3c654bec52605..690e509b318b4 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs @@ -17,7 +17,7 @@ where { let f = panic::AssertUnwindSafe(f); let result = panic::catch_unwind(move || { - //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` trait implementation + //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` closure trait implementation //~| HELP: add a dummy let to cause `f` to be fully captured f.0() }); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr index 2513ceecc6998..3925a0fdb980e 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr @@ -1,4 +1,4 @@ -error: `UnwindSafe`, `RefUnwindSafe` trait implementation will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect `UnwindSafe`, `RefUnwindSafe` closure trait implementation --> $DIR/mir_calls_to_shims.rs:19:38 | LL | let result = panic::catch_unwind(move || { diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr index 09829123db3f4..00ee8b1f0543f 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr @@ -1,4 +1,4 @@ -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/precise.rs:19:13 | LL | let c = || { @@ -27,7 +27,7 @@ LL | let _t = &t.1; LL | }; | -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/precise.rs:41:13 | LL | let c = || { diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr index fa4903b76badf..f715ea9a48f7d 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr @@ -1,4 +1,4 @@ -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:25:13 | LL | let c = || { @@ -34,7 +34,7 @@ LL | let _t = t.0; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:47:13 | LL | let c = || { @@ -63,7 +63,7 @@ LL | let _t = t.0; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:66:13 | LL | let c = || { @@ -89,7 +89,7 @@ LL | let _t = t.0; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:85:13 | LL | let c = || { @@ -114,7 +114,7 @@ LL | let _t = t.0; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:102:13 | LL | let c = || { @@ -139,7 +139,7 @@ LL | let _t = t.0; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:117:13 | LL | let c = || { @@ -164,7 +164,7 @@ LL | let _t = t.1; LL | ... -error: drop order will change in Rust 2021 +error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:134:13 | LL | let c = move || { From 2900c1a5e82b9c43917ba4e3e4ca347289b3d21a Mon Sep 17 00:00:00 2001 From: Roxane Date: Wed, 7 Jul 2021 10:29:06 -0400 Subject: [PATCH 22/28] Add note pointing to where a closure and it's captured variables are dropped --- compiler/rustc_typeck/src/check/upvar.rs | 40 +++++++ .../migrations/auto_traits.fixed | 8 ++ .../migrations/auto_traits.rs | 8 ++ .../migrations/auto_traits.stderr | 31 ++++-- .../migrations/insignificant_drop.fixed | 11 ++ .../migrations/insignificant_drop.rs | 11 ++ .../migrations/insignificant_drop.stderr | 43 ++++++-- .../insignificant_drop_attr_migrations.fixed | 2 + .../insignificant_drop_attr_migrations.rs | 2 + .../insignificant_drop_attr_migrations.stderr | 8 +- .../migrations/migrations_rustfix.fixed | 3 +- .../migrations/migrations_rustfix.rs | 3 +- .../migrations/migrations_rustfix.stderr | 7 +- .../migrations/mir_calls_to_shims.fixed | 3 + .../migrations/mir_calls_to_shims.rs | 3 + .../migrations/mir_calls_to_shims.stderr | 9 +- .../migrations/precise.stderr | 6 ++ .../migrations/significant_drop.fixed | 52 +++++++++ .../migrations/significant_drop.rs | 52 +++++++++ .../migrations/significant_drop.stderr | 101 ++++++++++++++++-- 20 files changed, 373 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 3f042dbd4a56a..a5c4e161771a1 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -511,6 +511,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .as_str(), ); for (var_hir_id, diagnostics_info) in need_migrations.iter() { + let mut captured_names = format!(""); for (captured_hir_id, captured_name) in diagnostics_info.iter() { if let Some(captured_hir_id) = captured_hir_id { let cause_span = self.tcx.hir().span(*captured_hir_id); @@ -518,8 +519,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.hir().name(*var_hir_id), captured_name, )); + if captured_names == "" { + captured_names = format!("`{}`", captured_name); + } else { + captured_names = format!("{}, `{}`", captured_names, captured_name); + } } } + + if reasons.contains("drop order") { + let drop_location_span = drop_location_span(self.tcx, &closure_hir_id); + + diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` would be dropped here, but in Rust 2021, only {} would be dropped here alongside the closure", + self.tcx.hir().name(*var_hir_id), + captured_names, + )); + } } diagnostics_builder.note("for more information, see "); let closure_body_span = self.tcx.hir().span(body_id.hir_id); @@ -1350,6 +1365,31 @@ fn apply_capture_kind_on_capture_ty( } } +/// Returns the Span of where the value with the provided HirId would be dropped +fn drop_location_span(tcx: TyCtxt<'tcx>, hir_id: &hir::HirId) -> Span { + let owner_id = tcx.hir().get_enclosing_scope(*hir_id).unwrap(); + + let owner_node = tcx.hir().get(owner_id); + match owner_node { + hir::Node::Item(item) => match item.kind { + hir::ItemKind::Fn(_, _, owner_id) => { + let owner_span = tcx.hir().span(owner_id.hir_id); + tcx.sess.source_map().end_point(owner_span) + } + _ => { + bug!("Drop location span error: need to handle more ItemKind {:?}", item.kind); + } + }, + hir::Node::Block(block) => { + let owner_span = tcx.hir().span(block.hir_id); + tcx.sess.source_map().end_point(owner_span) + } + _ => { + bug!("Drop location span error: need to handle more Node {:?}", owner_node); + } + } +} + struct InferBorrowKind<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed index 7c7387ff14ee8..ca55152cc2a83 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed @@ -1,5 +1,6 @@ // run-rustfix #![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here use std::thread; @@ -12,8 +13,10 @@ fn test_send_trait() { let fptr = SendPointer(&mut f as *mut i32); thread::spawn(move || { let _ = &fptr; unsafe { //~^ ERROR: `Send` closure trait implementation + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0 = 20; + //~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0` } }); } @@ -29,8 +32,10 @@ fn test_sync_trait() { let fptr = SyncPointer(f); thread::spawn(move || { let _ = &fptr; unsafe { //~^ ERROR: `Sync`, `Send` closure trait implementation + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0.0 = 20; + //~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0` } }); } @@ -50,8 +55,10 @@ fn test_clone_trait() { let f = U(S(String::from("Hello World")), T(0)); let c = || { let _ = &f; //~^ ERROR: `Clone` closure trait implementation, and drop order + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured let f_1 = f.1; + //~^ NOTE: in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1` println!("{:?}", f_1.0); }; @@ -59,6 +66,7 @@ fn test_clone_trait() { c_clone(); } +//~^ NOTE: in Rust 2018, `f` would be dropped here, but in Rust 2021, only `f.1` would be dropped here alongside the closure fn main() { test_send_trait(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs index e606a206b3b78..66c43cd865be2 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs @@ -1,5 +1,6 @@ // run-rustfix #![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here use std::thread; @@ -12,8 +13,10 @@ fn test_send_trait() { let fptr = SendPointer(&mut f as *mut i32); thread::spawn(move || unsafe { //~^ ERROR: `Send` closure trait implementation + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0 = 20; + //~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0` }); } @@ -29,8 +32,10 @@ fn test_sync_trait() { let fptr = SyncPointer(f); thread::spawn(move || unsafe { //~^ ERROR: `Sync`, `Send` closure trait implementation + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0.0 = 20; + //~^ NOTE: in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0` }); } @@ -50,8 +55,10 @@ fn test_clone_trait() { let f = U(S(String::from("Hello World")), T(0)); let c = || { //~^ ERROR: `Clone` closure trait implementation, and drop order + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured let f_1 = f.1; + //~^ NOTE: in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1` println!("{:?}", f_1.0); }; @@ -59,6 +66,7 @@ fn test_clone_trait() { c_clone(); } +//~^ NOTE: in Rust 2018, `f` would be dropped here, but in Rust 2021, only `f.1` would be dropped here alongside the closure fn main() { test_send_trait(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr index b628c5c3fad9f..38f8976058d3d 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr @@ -1,12 +1,14 @@ error: changes to closure capture in Rust 2021 will affect `Send` closure trait implementation - --> $DIR/auto_traits.rs:13:19 + --> $DIR/auto_traits.rs:14:19 | LL | thread::spawn(move || unsafe { | ___________________^ LL | | LL | | +LL | | LL | | *fptr.0 = 20; | | ------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0` +LL | | LL | | }); | |_____^ | @@ -21,19 +23,22 @@ help: add a dummy let to cause `fptr` to be fully captured LL | thread::spawn(move || { let _ = &fptr; unsafe { LL | LL | +LL | LL | *fptr.0 = 20; -LL | } }); - | +LL | + ... error: changes to closure capture in Rust 2021 will affect `Sync`, `Send` closure trait implementation - --> $DIR/auto_traits.rs:30:19 + --> $DIR/auto_traits.rs:33:19 | LL | thread::spawn(move || unsafe { | ___________________^ LL | | LL | | +LL | | LL | | *fptr.0.0 = 20; | | --------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0` +LL | | LL | | }); | |_____^ | @@ -43,22 +48,28 @@ help: add a dummy let to cause `fptr` to be fully captured LL | thread::spawn(move || { let _ = &fptr; unsafe { LL | LL | +LL | LL | *fptr.0.0 = 20; -LL | } }); - | +LL | + ... error: changes to closure capture in Rust 2021 will affect `Clone` closure trait implementation, and drop order - --> $DIR/auto_traits.rs:51:13 + --> $DIR/auto_traits.rs:56:13 | LL | let c = || { | _____________^ LL | | LL | | +LL | | LL | | let f_1 = f.1; | | --- in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1` +LL | | LL | | println!("{:?}", f_1.0); LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `f` would be dropped here, but in Rust 2021, only `f.1` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `f` to be fully captured @@ -66,10 +77,10 @@ help: add a dummy let to cause `f` to be fully captured LL | let c = || { let _ = &f; LL | LL | +LL | LL | let f_1 = f.1; -LL | println!("{:?}", f_1.0); -LL | }; - | +LL | + ... error: aborting due to 3 previous errors diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed index 71fa99997d388..e836f27cd7af1 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed @@ -27,6 +27,9 @@ fn test1_all_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure +//~| in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure // String implements drop and therefore should be migrated. // But in this test cases, `t2` is completely captured and when it is dropped won't be affected @@ -48,6 +51,8 @@ fn test2_only_precise_paths_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure // If a variable would've not been captured by value then it would've not been // dropped with the closure and therefore doesn't need migration. @@ -65,6 +70,7 @@ fn test3_only_by_value_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // Copy types get copied into the closure instead of move. Therefore we don't need to // migrate then as their drop order isn't tied to the closure. @@ -85,6 +91,7 @@ fn test4_only_non_copy_types_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure fn test5_only_drop_types_need_migration() { struct S(i32, i32); @@ -105,6 +112,7 @@ fn test5_only_drop_types_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // Since we are using a move closure here, both `t` and `t1` get moved // even though they are being used by ref inside the closure. @@ -122,6 +130,8 @@ fn test6_move_closures_non_copy_types_might_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure +//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure // Test migration analysis in case of Drop + Non Drop aggregates. // Note we need migration here only because the non-copy (because Drop type) is captured, @@ -139,6 +149,7 @@ fn test7_drop_non_drop_aggregate_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure fn main() { test1_all_need_migration(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs index a80aa92aa80f4..fbf9b983f07a2 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs @@ -27,6 +27,9 @@ fn test1_all_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure +//~| in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure // String implements drop and therefore should be migrated. // But in this test cases, `t2` is completely captured and when it is dropped won't be affected @@ -48,6 +51,8 @@ fn test2_only_precise_paths_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure // If a variable would've not been captured by value then it would've not been // dropped with the closure and therefore doesn't need migration. @@ -65,6 +70,7 @@ fn test3_only_by_value_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // Copy types get copied into the closure instead of move. Therefore we don't need to // migrate then as their drop order isn't tied to the closure. @@ -85,6 +91,7 @@ fn test4_only_non_copy_types_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure fn test5_only_drop_types_need_migration() { struct S(i32, i32); @@ -105,6 +112,7 @@ fn test5_only_drop_types_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // Since we are using a move closure here, both `t` and `t1` get moved // even though they are being used by ref inside the closure. @@ -122,6 +130,8 @@ fn test6_move_closures_non_copy_types_might_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure +//~| in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure // Test migration analysis in case of Drop + Non Drop aggregates. // Note we need migration here only because the non-copy (because Drop type) is captured, @@ -139,6 +149,7 @@ fn test7_drop_non_drop_aggregate_need_migration() { c(); } +//~^ in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure fn main() { test1_all_need_migration(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr index 11b90a700551a..e7b2cd1c55347 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr @@ -18,6 +18,13 @@ LL | | let _t2 = t2.0; LL | | LL | | }; | |_____^ +... +LL | } + | - + | | + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure + | in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure | note: the lint level is defined here --> $DIR/insignificant_drop.rs:3:9 @@ -36,7 +43,7 @@ LL | let _t = t.0; ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/insignificant_drop.rs:38:13 + --> $DIR/insignificant_drop.rs:41:13 | LL | let c = || { | _____________^ @@ -52,6 +59,12 @@ LL | | LL | | let _t2 = t2; LL | | }; | |_____^ +... +LL | } + | - + | | + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t`, `t1` to be fully captured @@ -65,7 +78,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/insignificant_drop.rs:57:13 + --> $DIR/insignificant_drop.rs:62:13 | LL | let c = || { | _____________^ @@ -78,6 +91,9 @@ LL | | LL | | println!("{}", t1.1); LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -91,7 +107,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/insignificant_drop.rs:77:13 + --> $DIR/insignificant_drop.rs:83:13 | LL | let c = || { | _____________^ @@ -104,6 +120,9 @@ LL | | LL | | let _t1 = t1.0; LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -117,7 +136,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/insignificant_drop.rs:97:13 + --> $DIR/insignificant_drop.rs:104:13 | LL | let c = || { | _____________^ @@ -130,6 +149,9 @@ LL | | LL | | let _s = s.0; LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -143,7 +165,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/insignificant_drop.rs:114:13 + --> $DIR/insignificant_drop.rs:122:13 | LL | let c = move || { | _____________^ @@ -158,6 +180,12 @@ LL | | LL | | LL | | }; | |_____^ +... +LL | } + | - + | | + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t1`, `t` to be fully captured @@ -171,7 +199,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/insignificant_drop.rs:132:13 + --> $DIR/insignificant_drop.rs:142:13 | LL | let c = || { | _____________^ @@ -183,6 +211,9 @@ LL | | let _t = t.0; LL | | LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed index f150bd8cc9cbb..4626c04e9ba6e 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed @@ -44,6 +44,7 @@ fn significant_drop_needs_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // Even if a type implements an insignificant drop, if it's // elements have a significant drop then the overall type is @@ -63,6 +64,7 @@ fn generic_struct_with_significant_drop_needs_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure fn main() { significant_drop_needs_migration(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs index 03c0ab959b25c..ebcf1551bacf6 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs @@ -44,6 +44,7 @@ fn significant_drop_needs_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // Even if a type implements an insignificant drop, if it's // elements have a significant drop then the overall type is @@ -63,6 +64,7 @@ fn generic_struct_with_significant_drop_needs_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure fn main() { significant_drop_needs_migration(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr index b328abfeb52af..97ff0409d0a63 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr @@ -11,6 +11,9 @@ LL | | let _t = t.0; LL | | LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | note: the lint level is defined here --> $DIR/insignificant_drop_attr_migrations.rs:3:9 @@ -29,7 +32,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/insignificant_drop_attr_migrations.rs:56:13 + --> $DIR/insignificant_drop_attr_migrations.rs:57:13 | LL | let c = move || { | _____________^ @@ -41,6 +44,9 @@ LL | | let _t = t.1; LL | | LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed index a4a38d47500df..f3c15a2e6b676 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed @@ -22,11 +22,11 @@ fn closure_contains_block() { //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` - }; c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure fn closure_doesnt_contain_block() { let t = (Foo(0), Foo(0)); @@ -38,6 +38,7 @@ fn closure_doesnt_contain_block() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure fn main() { closure_contains_block(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs index 01a47dffd85a8..50936d15302b7 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs @@ -22,11 +22,11 @@ fn closure_contains_block() { //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` - }; c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure fn closure_doesnt_contain_block() { let t = (Foo(0), Foo(0)); @@ -38,6 +38,7 @@ fn closure_doesnt_contain_block() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure fn main() { closure_contains_block(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr index a4426c79a166c..09bae6d3f1344 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr @@ -9,9 +9,11 @@ LL | | LL | | let _t = t.0; | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` LL | | -LL | | LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | note: the lint level is defined here --> $DIR/migrations_rustfix.rs:2:9 @@ -36,6 +38,9 @@ LL | let c = || t.0; | ^^^--- | | | in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed index 309a3c4a70bfb..cb3148214b4b8 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed @@ -1,6 +1,7 @@ // run-rustfix #![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here // ignore-wasm32-bare compiled with panic=abort by default #![feature(fn_traits)] #![feature(never_type)] @@ -18,8 +19,10 @@ where let f = panic::AssertUnwindSafe(f); let result = panic::catch_unwind(move || { let _ = &f; //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` closure trait implementation + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured f.0() + //~^ NOTE: in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.0` }); if let Ok(..) = result { panic!("diverging function returned"); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs index 690e509b318b4..f6f8ad2c52078 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs @@ -1,6 +1,7 @@ // run-rustfix #![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here // ignore-wasm32-bare compiled with panic=abort by default #![feature(fn_traits)] #![feature(never_type)] @@ -18,8 +19,10 @@ where let f = panic::AssertUnwindSafe(f); let result = panic::catch_unwind(move || { //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` closure trait implementation + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured f.0() + //~^ NOTE: in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.0` }); if let Ok(..) = result { panic!("diverging function returned"); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr index 3925a0fdb980e..9a45e276fcdfc 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr @@ -1,12 +1,14 @@ error: changes to closure capture in Rust 2021 will affect `UnwindSafe`, `RefUnwindSafe` closure trait implementation - --> $DIR/mir_calls_to_shims.rs:19:38 + --> $DIR/mir_calls_to_shims.rs:20:38 | LL | let result = panic::catch_unwind(move || { | ______________________________________^ LL | | LL | | +LL | | LL | | f.0() | | --- in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.0` +LL | | LL | | }); | |_____^ | @@ -21,9 +23,10 @@ help: add a dummy let to cause `f` to be fully captured LL | let result = panic::catch_unwind(move || { let _ = &f; LL | LL | +LL | LL | f.0() -LL | }); - | +LL | + ... error: aborting due to previous error diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr index 00ee8b1f0543f..7bec8ef06fce3 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr @@ -10,6 +10,9 @@ LL | | let _t = t.0; LL | | let _t = &t.1; LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | note: the lint level is defined here --> $DIR/precise.rs:3:9 @@ -42,6 +45,9 @@ LL | | let _x = u.1.0; | | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.1.0` LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.1`, `u.0.0`, `u.1.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `u` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed index 5bdefb6762e4f..236fdb9e26ea2 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed @@ -36,6 +36,9 @@ fn test1_all_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure // String implements drop and therefore should be migrated. // But in this test cases, `t2` is completely captured and when it is dropped won't be affected @@ -57,6 +60,8 @@ fn test2_only_precise_paths_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure // If a variable would've not been captured by value then it would've not been // dropped with the closure and therefore doesn't need migration. @@ -74,6 +79,7 @@ fn test3_only_by_value_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // The root variable might not implement drop themselves but some path starting // at the root variable might implement Drop. @@ -92,6 +98,7 @@ fn test4_type_contains_drop_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // Test migration analysis in case of Drop + Non Drop aggregates. // Note we need migration here only because the non-copy (because Drop type) is captured, @@ -109,6 +116,7 @@ fn test5_drop_non_drop_aggregate_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // Test migration analysis in case of Significant and Insignificant Drop aggregates. fn test6_significant_insignificant_drop_aggregate_need_migration() { @@ -124,6 +132,7 @@ fn test6_significant_insignificant_drop_aggregate_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure // Since we are using a move closure here, both `t` and `t1` get moved // even though they are being used by ref inside the closure. @@ -142,6 +151,47 @@ fn test7_move_closures_non_copy_types_might_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure + + +fn test8_drop_order_and_blocks() { + { + let tuple = + (String::from("foo"), String::from("bar")); + { + let c = || { let _ = &tuple; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `tuple` to be fully captured + tuple.0; + //~^ NOTE: in Rust 2018, closure captures all of `tuple`, but in Rust 2021, it only captures `tuple.0` + }; + + c(); + } + //~^ NOTE: in Rust 2018, `tuple` would be dropped here, but in Rust 2021, only `tuple.0` would be dropped here alongside the closure + } +} + +fn test9_drop_order_and_nested_closures() { + let tuple = + (String::from("foo"), String::from("bar")); + let b = || { + let c = || { let _ = &tuple; + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `tuple` to be fully captured + tuple.0; + //~^ NOTE: in Rust 2018, closure captures all of `tuple`, but in Rust 2021, it only captures `tuple.0` + }; + + c(); + }; + //~^ NOTE: in Rust 2018, `tuple` would be dropped here, but in Rust 2021, only `tuple.0` would be dropped here alongside the closure + + b(); +} fn main() { test1_all_need_migration(); @@ -151,4 +201,6 @@ fn main() { test5_drop_non_drop_aggregate_need_migration(); test6_significant_insignificant_drop_aggregate_need_migration(); test7_move_closures_non_copy_types_might_need_migration(); + test8_drop_order_and_blocks(); + test9_drop_order_and_nested_closures(); } diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs index 44119a76e14d0..a57f7aa565e12 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs @@ -36,6 +36,9 @@ fn test1_all_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure // String implements drop and therefore should be migrated. // But in this test cases, `t2` is completely captured and when it is dropped won't be affected @@ -57,6 +60,8 @@ fn test2_only_precise_paths_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure // If a variable would've not been captured by value then it would've not been // dropped with the closure and therefore doesn't need migration. @@ -74,6 +79,7 @@ fn test3_only_by_value_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // The root variable might not implement drop themselves but some path starting // at the root variable might implement Drop. @@ -92,6 +98,7 @@ fn test4_type_contains_drop_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // Test migration analysis in case of Drop + Non Drop aggregates. // Note we need migration here only because the non-copy (because Drop type) is captured, @@ -109,6 +116,7 @@ fn test5_drop_non_drop_aggregate_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure // Test migration analysis in case of Significant and Insignificant Drop aggregates. fn test6_significant_insignificant_drop_aggregate_need_migration() { @@ -124,6 +132,7 @@ fn test6_significant_insignificant_drop_aggregate_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure // Since we are using a move closure here, both `t` and `t1` get moved // even though they are being used by ref inside the closure. @@ -142,6 +151,47 @@ fn test7_move_closures_non_copy_types_might_need_migration() { c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure + + +fn test8_drop_order_and_blocks() { + { + let tuple = + (String::from("foo"), String::from("bar")); + { + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `tuple` to be fully captured + tuple.0; + //~^ NOTE: in Rust 2018, closure captures all of `tuple`, but in Rust 2021, it only captures `tuple.0` + }; + + c(); + } + //~^ NOTE: in Rust 2018, `tuple` would be dropped here, but in Rust 2021, only `tuple.0` would be dropped here alongside the closure + } +} + +fn test9_drop_order_and_nested_closures() { + let tuple = + (String::from("foo"), String::from("bar")); + let b = || { + let c = || { + //~^ ERROR: drop order + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `tuple` to be fully captured + tuple.0; + //~^ NOTE: in Rust 2018, closure captures all of `tuple`, but in Rust 2021, it only captures `tuple.0` + }; + + c(); + }; + //~^ NOTE: in Rust 2018, `tuple` would be dropped here, but in Rust 2021, only `tuple.0` would be dropped here alongside the closure + + b(); +} fn main() { test1_all_need_migration(); @@ -151,4 +201,6 @@ fn main() { test5_drop_non_drop_aggregate_need_migration(); test6_significant_insignificant_drop_aggregate_need_migration(); test7_move_closures_non_copy_types_might_need_migration(); + test8_drop_order_and_blocks(); + test9_drop_order_and_nested_closures(); } diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr index f715ea9a48f7d..13eac4943a469 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr @@ -17,6 +17,13 @@ LL | | let _t2 = t2.0; LL | | LL | | }; | |_____^ +... +LL | } + | - + | | + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure + | in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure | note: the lint level is defined here --> $DIR/significant_drop.rs:2:9 @@ -35,7 +42,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/significant_drop.rs:47:13 + --> $DIR/significant_drop.rs:50:13 | LL | let c = || { | _____________^ @@ -51,6 +58,12 @@ LL | | LL | | let _t2 = t2; LL | | }; | |_____^ +... +LL | } + | - + | | + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t`, `t1` to be fully captured @@ -64,7 +77,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/significant_drop.rs:66:13 + --> $DIR/significant_drop.rs:71:13 | LL | let c = || { | _____________^ @@ -77,6 +90,9 @@ LL | | LL | | println!("{:?}", t1.1); LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -90,7 +106,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/significant_drop.rs:85:13 + --> $DIR/significant_drop.rs:91:13 | LL | let c = || { | _____________^ @@ -102,6 +118,9 @@ LL | | let _t = t.0; LL | | LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -115,7 +134,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/significant_drop.rs:102:13 + --> $DIR/significant_drop.rs:109:13 | LL | let c = || { | _____________^ @@ -127,6 +146,9 @@ LL | | let _t = t.0; LL | | LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -140,7 +162,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/significant_drop.rs:117:13 + --> $DIR/significant_drop.rs:125:13 | LL | let c = || { | _____________^ @@ -152,6 +174,9 @@ LL | | let _t = t.1; LL | | LL | | }; | |_____^ +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -165,7 +190,7 @@ LL | ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/significant_drop.rs:134:13 + --> $DIR/significant_drop.rs:143:13 | LL | let c = move || { | _____________^ @@ -180,6 +205,12 @@ LL | | LL | | LL | | }; | |_____^ +... +LL | } + | - + | | + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t1`, `t` to be fully captured @@ -192,5 +223,61 @@ LL | println!("{:?} {:?}", t1.1, t.1); LL | ... -error: aborting due to 7 previous errors +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/significant_drop.rs:163:21 + | +LL | let c = || { + | _____________________^ +LL | | +LL | | +LL | | +LL | | tuple.0; + | | ------- in Rust 2018, closure captures all of `tuple`, but in Rust 2021, it only captures `tuple.0` +LL | | +LL | | }; + | |_____________^ +... +LL | } + | - in Rust 2018, `tuple` would be dropped here, but in Rust 2021, only `tuple.0` would be dropped here alongside the closure + | + = note: for more information, see +help: add a dummy let to cause `tuple` to be fully captured + | +LL | let c = || { let _ = &tuple; +LL | +LL | +LL | +LL | tuple.0; +LL | + ... + +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/significant_drop.rs:181:17 + | +LL | let c = || { + | _________________^ +LL | | +LL | | +LL | | +LL | | tuple.0; + | | ------- in Rust 2018, closure captures all of `tuple`, but in Rust 2021, it only captures `tuple.0` +LL | | +LL | | }; + | |_________^ +... +LL | }; + | - in Rust 2018, `tuple` would be dropped here, but in Rust 2021, only `tuple.0` would be dropped here alongside the closure + | + = note: for more information, see +help: add a dummy let to cause `tuple` to be fully captured + | +LL | let c = || { let _ = &tuple; +LL | +LL | +LL | +LL | tuple.0; +LL | + ... + +error: aborting due to 9 previous errors From 36eb5442bd79b199b506e7912f72aade202a1ee1 Mon Sep 17 00:00:00 2001 From: Roxane Date: Wed, 7 Jul 2021 11:04:28 -0400 Subject: [PATCH 23/28] Add note clarifying why a closure no longer implements a trait --- compiler/rustc_typeck/src/check/upvar.rs | 16 ++++++++++++ .../migrations/auto_traits.fixed | 3 +++ .../migrations/auto_traits.rs | 3 +++ .../migrations/auto_traits.stderr | 25 +++++++++++++------ .../migrations/mir_calls_to_shims.fixed | 1 + .../migrations/mir_calls_to_shims.rs | 1 + .../migrations/mir_calls_to_shims.stderr | 7 ++++-- 7 files changed, 46 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index a5c4e161771a1..9806fd95c8f5c 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -535,6 +535,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { captured_names, )); } + + if reasons.contains("closure trait implementation") { + let closure_body_span = self.tcx.hir().span(body_id.hir_id); + let closure_ending_span = self.tcx.sess.source_map().guess_head_span(closure_body_span).shrink_to_lo(); + + let missing_trait = &reasons[..reasons.find("closure trait implementation").unwrap() - 1]; + + diagnostics_builder.span_label(closure_ending_span, format!("in Rust 2018, this closure would implement {} as `{}` implements {}, but in Rust 2021, this closure will no longer implement {} as {} does not implement {}", + missing_trait, + self.tcx.hir().name(*var_hir_id), + missing_trait, + missing_trait, + captured_names, + missing_trait, + )); + } } diagnostics_builder.note("for more information, see "); let closure_body_span = self.tcx.hir().span(body_id.hir_id); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed index ca55152cc2a83..4cc8a2b54be8e 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed @@ -13,6 +13,7 @@ fn test_send_trait() { let fptr = SendPointer(&mut f as *mut i32); thread::spawn(move || { let _ = &fptr; unsafe { //~^ ERROR: `Send` closure trait implementation + //~| NOTE: in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr.0` does not implement `Send` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0 = 20; @@ -32,6 +33,7 @@ fn test_sync_trait() { let fptr = SyncPointer(f); thread::spawn(move || { let _ = &fptr; unsafe { //~^ ERROR: `Sync`, `Send` closure trait implementation + //~| NOTE: in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0.0 = 20; @@ -55,6 +57,7 @@ fn test_clone_trait() { let f = U(S(String::from("Hello World")), T(0)); let c = || { let _ = &f; //~^ ERROR: `Clone` closure trait implementation, and drop order + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f.1` does not implement `Clone` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured let f_1 = f.1; diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs index 66c43cd865be2..a75c0aa527985 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs @@ -13,6 +13,7 @@ fn test_send_trait() { let fptr = SendPointer(&mut f as *mut i32); thread::spawn(move || unsafe { //~^ ERROR: `Send` closure trait implementation + //~| NOTE: in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr.0` does not implement `Send` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0 = 20; @@ -32,6 +33,7 @@ fn test_sync_trait() { let fptr = SyncPointer(f); thread::spawn(move || unsafe { //~^ ERROR: `Sync`, `Send` closure trait implementation + //~| NOTE: in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0.0 = 20; @@ -55,6 +57,7 @@ fn test_clone_trait() { let f = U(S(String::from("Hello World")), T(0)); let c = || { //~^ ERROR: `Clone` closure trait implementation, and drop order + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f.1` does not implement `Clone` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured let f_1 = f.1; diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr index 38f8976058d3d..c8867e1ac92a0 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr @@ -2,7 +2,10 @@ error: changes to closure capture in Rust 2021 will affect `Send` closure trait --> $DIR/auto_traits.rs:14:19 | LL | thread::spawn(move || unsafe { - | ___________________^ + | ^ - in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr.0` does not implement `Send` + | ___________________| + | | +LL | | LL | | LL | | LL | | @@ -24,15 +27,18 @@ LL | thread::spawn(move || { let _ = &fptr; unsafe { LL | LL | LL | -LL | *fptr.0 = 20; LL | +LL | *fptr.0 = 20; ... error: changes to closure capture in Rust 2021 will affect `Sync`, `Send` closure trait implementation - --> $DIR/auto_traits.rs:33:19 + --> $DIR/auto_traits.rs:34:19 | LL | thread::spawn(move || unsafe { - | ___________________^ + | ^ - in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send` + | ___________________| + | | +LL | | LL | | LL | | LL | | @@ -49,15 +55,18 @@ LL | thread::spawn(move || { let _ = &fptr; unsafe { LL | LL | LL | -LL | *fptr.0.0 = 20; LL | +LL | *fptr.0.0 = 20; ... error: changes to closure capture in Rust 2021 will affect `Clone` closure trait implementation, and drop order - --> $DIR/auto_traits.rs:56:13 + --> $DIR/auto_traits.rs:58:13 | LL | let c = || { - | _____________^ + | ^ - in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f.1` does not implement `Clone` + | _____________| + | | +LL | | LL | | LL | | LL | | @@ -78,8 +87,8 @@ LL | let c = || { let _ = &f; LL | LL | LL | -LL | let f_1 = f.1; LL | +LL | let f_1 = f.1; ... error: aborting due to 3 previous errors diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed index cb3148214b4b8..f6e4f159ad08d 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed @@ -19,6 +19,7 @@ where let f = panic::AssertUnwindSafe(f); let result = panic::catch_unwind(move || { let _ = &f; //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` closure trait implementation + //~| NOTE: in Rust 2018, this closure would implement `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured f.0() diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs index f6f8ad2c52078..f6481d51fcd3f 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs @@ -19,6 +19,7 @@ where let f = panic::AssertUnwindSafe(f); let result = panic::catch_unwind(move || { //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` closure trait implementation + //~| NOTE: in Rust 2018, this closure would implement `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured f.0() diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr index 9a45e276fcdfc..bc8580b23b2d5 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr @@ -2,7 +2,10 @@ error: changes to closure capture in Rust 2021 will affect `UnwindSafe`, `RefUnw --> $DIR/mir_calls_to_shims.rs:20:38 | LL | let result = panic::catch_unwind(move || { - | ______________________________________^ + | ^ - in Rust 2018, this closure would implement `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe` + | ______________________________________| + | | +LL | | LL | | LL | | LL | | @@ -24,8 +27,8 @@ LL | let result = panic::catch_unwind(move || { let _ = &f; LL | LL | LL | -LL | f.0() LL | +LL | f.0() ... error: aborting due to previous error From 81b062ae889c6f0b156cb8eac1a0dcb97d1ed4bc Mon Sep 17 00:00:00 2001 From: Roxane Date: Wed, 7 Jul 2021 18:47:32 -0400 Subject: [PATCH 24/28] Fix wording --- compiler/rustc_typeck/src/check/upvar.rs | 33 ++- .../migrations/auto_traits.fixed | 12 +- .../migrations/auto_traits.rs | 12 +- .../migrations/auto_traits.stderr | 65 ++---- .../migrations/insignificant_drop.stderr | 181 ++++++--------- .../insignificant_drop_attr_migrations.stderr | 38 ++- .../migrations/migrations_rustfix.stderr | 19 +- .../migrations/mir_calls_to_shims.fixed | 4 +- .../migrations/mir_calls_to_shims.rs | 4 +- .../migrations/mir_calls_to_shims.stderr | 20 +- .../migrations/precise.stderr | 43 ++-- .../migrations/significant_drop.stderr | 216 +++++++----------- 12 files changed, 250 insertions(+), 397 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 9806fd95c8f5c..635b411f0e4f0 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -498,10 +498,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); + let closure_span = self.tcx.hir().span(closure_hir_id); + let closure_head_span = self.tcx.sess.source_map().guess_head_span(closure_span); self.tcx.struct_span_lint_hir( lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_hir_id, - span, + closure_head_span, |lint| { let mut diagnostics_builder = lint.build( format!( @@ -512,6 +514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); for (var_hir_id, diagnostics_info) in need_migrations.iter() { let mut captured_names = format!(""); + // Label every Span which are responsible for the captured values for (captured_hir_id, captured_name) in diagnostics_info.iter() { if let Some(captured_hir_id) = captured_hir_id { let cause_span = self.tcx.hir().span(*captured_hir_id); @@ -527,6 +530,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + // Add a label pointing to where a closure and it's captured variables affected by drop order are dropped if reasons.contains("drop order") { let drop_location_span = drop_location_span(self.tcx, &closure_hir_id); @@ -536,13 +540,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); } - if reasons.contains("closure trait implementation") { - let closure_body_span = self.tcx.hir().span(body_id.hir_id); - let closure_ending_span = self.tcx.sess.source_map().guess_head_span(closure_body_span).shrink_to_lo(); + // Add a label explaining why a closure no longer implements a trait + if reasons.contains("trait implementation") { + let missing_trait = &reasons[..reasons.find("trait implementation").unwrap() - 1]; - let missing_trait = &reasons[..reasons.find("closure trait implementation").unwrap() - 1]; - - diagnostics_builder.span_label(closure_ending_span, format!("in Rust 2018, this closure would implement {} as `{}` implements {}, but in Rust 2021, this closure will no longer implement {} as {} does not implement {}", + diagnostics_builder.span_label(closure_head_span, format!("in Rust 2018, this closure would implement {} as `{}` implements {}, but in Rust 2021, this closure would no longer implement {} as {} does not implement {}", missing_trait, self.tcx.hir().name(*var_hir_id), missing_trait, @@ -598,7 +600,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if auto_trait_reasons.len() > 0 { reasons = format!( - "{} closure trait implementation", + "{} trait implementation for closure", auto_trait_reasons.clone().into_iter().collect::>().join(", ") ); } @@ -1386,24 +1388,19 @@ fn drop_location_span(tcx: TyCtxt<'tcx>, hir_id: &hir::HirId) -> Span { let owner_id = tcx.hir().get_enclosing_scope(*hir_id).unwrap(); let owner_node = tcx.hir().get(owner_id); - match owner_node { + let owner_span = match owner_node { hir::Node::Item(item) => match item.kind { - hir::ItemKind::Fn(_, _, owner_id) => { - let owner_span = tcx.hir().span(owner_id.hir_id); - tcx.sess.source_map().end_point(owner_span) - } + hir::ItemKind::Fn(_, _, owner_id) => tcx.hir().span(owner_id.hir_id), _ => { bug!("Drop location span error: need to handle more ItemKind {:?}", item.kind); } }, - hir::Node::Block(block) => { - let owner_span = tcx.hir().span(block.hir_id); - tcx.sess.source_map().end_point(owner_span) - } + hir::Node::Block(block) => tcx.hir().span(block.hir_id), _ => { bug!("Drop location span error: need to handle more Node {:?}", owner_node); } - } + }; + tcx.sess.source_map().end_point(owner_span) } struct InferBorrowKind<'a, 'tcx> { diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed index 4cc8a2b54be8e..42863b440fc1d 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed @@ -12,8 +12,8 @@ fn test_send_trait() { let mut f = 10; let fptr = SendPointer(&mut f as *mut i32); thread::spawn(move || { let _ = &fptr; unsafe { - //~^ ERROR: `Send` closure trait implementation - //~| NOTE: in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr.0` does not implement `Send` + //~^ ERROR: `Send` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure would no longer implement `Send` as `fptr.0` does not implement `Send` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0 = 20; @@ -32,8 +32,8 @@ fn test_sync_trait() { let f = CustomInt(&mut f as *mut i32); let fptr = SyncPointer(f); thread::spawn(move || { let _ = &fptr; unsafe { - //~^ ERROR: `Sync`, `Send` closure trait implementation - //~| NOTE: in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send` + //~^ ERROR: `Sync`, `Send` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure would no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0.0 = 20; @@ -56,8 +56,8 @@ impl Clone for U { fn test_clone_trait() { let f = U(S(String::from("Hello World")), T(0)); let c = || { let _ = &f; - //~^ ERROR: `Clone` closure trait implementation, and drop order - //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f.1` does not implement `Clone` + //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f.1` does not implement `Clone` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured let f_1 = f.1; diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs index a75c0aa527985..ae987f526d044 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs @@ -12,8 +12,8 @@ fn test_send_trait() { let mut f = 10; let fptr = SendPointer(&mut f as *mut i32); thread::spawn(move || unsafe { - //~^ ERROR: `Send` closure trait implementation - //~| NOTE: in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr.0` does not implement `Send` + //~^ ERROR: `Send` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure would no longer implement `Send` as `fptr.0` does not implement `Send` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0 = 20; @@ -32,8 +32,8 @@ fn test_sync_trait() { let f = CustomInt(&mut f as *mut i32); let fptr = SyncPointer(f); thread::spawn(move || unsafe { - //~^ ERROR: `Sync`, `Send` closure trait implementation - //~| NOTE: in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send` + //~^ ERROR: `Sync`, `Send` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure would no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `fptr` to be fully captured *fptr.0.0 = 20; @@ -56,8 +56,8 @@ impl Clone for U { fn test_clone_trait() { let f = U(S(String::from("Hello World")), T(0)); let c = || { - //~^ ERROR: `Clone` closure trait implementation, and drop order - //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f.1` does not implement `Clone` + //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f.1` does not implement `Clone` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured let f_1 = f.1; diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr index c8867e1ac92a0..2af85852c01a0 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr @@ -1,19 +1,11 @@ -error: changes to closure capture in Rust 2021 will affect `Send` closure trait implementation +error: changes to closure capture in Rust 2021 will affect `Send` trait implementation for closure --> $DIR/auto_traits.rs:14:19 | -LL | thread::spawn(move || unsafe { - | ^ - in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` as `fptr.0` does not implement `Send` - | ___________________| - | | -LL | | -LL | | -LL | | -LL | | -LL | | *fptr.0 = 20; - | | ------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0` -LL | | -LL | | }); - | |_____^ +LL | thread::spawn(move || unsafe { + | ^^^^^^^^^^^^^^ in Rust 2018, this closure would implement `Send` as `fptr` implements `Send`, but in Rust 2021, this closure would no longer implement `Send` as `fptr.0` does not implement `Send` +... +LL | *fptr.0 = 20; + | ------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0` | note: the lint level is defined here --> $DIR/auto_traits.rs:2:9 @@ -31,22 +23,14 @@ LL | LL | *fptr.0 = 20; ... -error: changes to closure capture in Rust 2021 will affect `Sync`, `Send` closure trait implementation +error: changes to closure capture in Rust 2021 will affect `Sync`, `Send` trait implementation for closure --> $DIR/auto_traits.rs:34:19 | -LL | thread::spawn(move || unsafe { - | ^ - in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure will no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send` - | ___________________| - | | -LL | | -LL | | -LL | | -LL | | -LL | | *fptr.0.0 = 20; - | | --------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0` -LL | | -LL | | }); - | |_____^ +LL | thread::spawn(move || unsafe { + | ^^^^^^^^^^^^^^ in Rust 2018, this closure would implement `Sync`, `Send` as `fptr` implements `Sync`, `Send`, but in Rust 2021, this closure would no longer implement `Sync`, `Send` as `fptr.0.0` does not implement `Sync`, `Send` +... +LL | *fptr.0.0 = 20; + | --------- in Rust 2018, closure captures all of `fptr`, but in Rust 2021, it only captures `fptr.0.0` | = note: for more information, see help: add a dummy let to cause `fptr` to be fully captured @@ -59,26 +43,17 @@ LL | LL | *fptr.0.0 = 20; ... -error: changes to closure capture in Rust 2021 will affect `Clone` closure trait implementation, and drop order +error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure, and drop order --> $DIR/auto_traits.rs:58:13 | -LL | let c = || { - | ^ - in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` as `f.1` does not implement `Clone` - | _____________| - | | -LL | | -LL | | -LL | | -LL | | -LL | | let f_1 = f.1; - | | --- in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1` -LL | | -LL | | println!("{:?}", f_1.0); -LL | | }; - | |_____^ +LL | let c = || { + | ^^ in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f.1` does not implement `Clone` +... +LL | let f_1 = f.1; + | --- in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.1` ... -LL | } - | - in Rust 2018, `f` would be dropped here, but in Rust 2021, only `f.1` would be dropped here alongside the closure +LL | } + | - in Rust 2018, `f` would be dropped here, but in Rust 2021, only `f.1` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `f` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr index e7b2cd1c55347..e9e4794cff5f7 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr @@ -1,30 +1,24 @@ error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:15:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | let _t1 = t1.0; - | | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` -LL | | -LL | | let _t2 = t2.0; - | | ---- in Rust 2018, closure captures all of `t2`, but in Rust 2021, it only captures `t2.0` -LL | | -LL | | }; - | |_____^ +LL | let c = || { + | ^^ ... -LL | } - | - - | | - | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure - | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure - | in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | +LL | let _t1 = t1.0; + | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` +LL | +LL | let _t2 = t2.0; + | ---- in Rust 2018, closure captures all of `t2`, but in Rust 2021, it only captures `t2.0` +... +LL | } + | - + | | + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure + | in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure | note: the lint level is defined here --> $DIR/insignificant_drop.rs:3:9 @@ -45,26 +39,20 @@ LL | let _t = t.0; error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:41:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | let _t1 = t1.0; - | | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` -LL | | -LL | | let _t2 = t2; -LL | | }; - | |_____^ +LL | let c = || { + | ^^ ... -LL | } - | - - | | - | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure - | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | +LL | let _t1 = t1.0; + | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` +... +LL | } + | - + | | + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t`, `t1` to be fully captured @@ -80,20 +68,14 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:62:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | println!("{}", t1.1); -LL | | }; - | |_____^ +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -109,20 +91,14 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:83:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | let _t1 = t1.0; -LL | | }; - | |_____^ +LL | let c = || { + | ^^ ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -138,20 +114,14 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:104:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | let _s = s.0; -LL | | }; - | |_____^ +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -167,25 +137,19 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:122:13 | -LL | let c = move || { - | _____________^ -LL | | -LL | | -LL | | -LL | | println!("{} {}", t1.1, t.1); - | | ---- --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` - | | | - | | in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.1` -LL | | -LL | | -LL | | }; - | |_____^ +LL | let c = move || { + | ^^^^^^^ ... -LL | } - | - - | | - | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure - | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure +LL | println!("{} {}", t1.1, t.1); + | ---- --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` + | | + | in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.1` +... +LL | } + | - + | | + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t1`, `t` to be fully captured @@ -201,19 +165,14 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop.rs:142:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | }; - | |_____^ +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr index 97ff0409d0a63..1e97ca34d162e 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr @@ -1,19 +1,14 @@ error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop_attr_migrations.rs:37:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | }; - | |_____^ +LL | let c = || { + | ^^ ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | note: the lint level is defined here --> $DIR/insignificant_drop_attr_migrations.rs:3:9 @@ -34,19 +29,14 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/insignificant_drop_attr_migrations.rs:57:13 | -LL | let c = move || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.1; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` -LL | | -LL | | }; - | |_____^ +LL | let c = move || { + | ^^^^^^^ +... +LL | let _t = t.1; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr index 09bae6d3f1344..f8f72d1580ca9 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr @@ -1,19 +1,14 @@ error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/migrations_rustfix.rs:19:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | }; - | |_____^ +LL | let c = || { + | ^^ ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | note: the lint level is defined here --> $DIR/migrations_rustfix.rs:2:9 diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed index f6e4f159ad08d..f24804018cd85 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed @@ -18,8 +18,8 @@ where { let f = panic::AssertUnwindSafe(f); let result = panic::catch_unwind(move || { let _ = &f; - //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` closure trait implementation - //~| NOTE: in Rust 2018, this closure would implement `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe` + //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure would no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured f.0() diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs index f6481d51fcd3f..6a6a51c51322d 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs @@ -18,8 +18,8 @@ where { let f = panic::AssertUnwindSafe(f); let result = panic::catch_unwind(move || { - //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` closure trait implementation - //~| NOTE: in Rust 2018, this closure would implement `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe` + //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure would no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured f.0() diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr index bc8580b23b2d5..6ee0d0d252aba 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr @@ -1,19 +1,11 @@ -error: changes to closure capture in Rust 2021 will affect `UnwindSafe`, `RefUnwindSafe` closure trait implementation +error: changes to closure capture in Rust 2021 will affect `UnwindSafe`, `RefUnwindSafe` trait implementation for closure --> $DIR/mir_calls_to_shims.rs:20:38 | -LL | let result = panic::catch_unwind(move || { - | ^ - in Rust 2018, this closure would implement `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe` - | ______________________________________| - | | -LL | | -LL | | -LL | | -LL | | -LL | | f.0() - | | --- in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.0` -LL | | -LL | | }); - | |_____^ +LL | let result = panic::catch_unwind(move || { + | ^^^^^^^ in Rust 2018, this closure would implement `UnwindSafe`, `RefUnwindSafe` as `f` implements `UnwindSafe`, `RefUnwindSafe`, but in Rust 2021, this closure would no longer implement `UnwindSafe`, `RefUnwindSafe` as `f.0` does not implement `UnwindSafe`, `RefUnwindSafe` +... +LL | f.0() + | --- in Rust 2018, closure captures all of `f`, but in Rust 2021, it only captures `f.0` | note: the lint level is defined here --> $DIR/mir_calls_to_shims.rs:3:9 diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr index 7bec8ef06fce3..153c0d6b68671 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr @@ -1,18 +1,14 @@ error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/precise.rs:19:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | let _t = &t.1; -LL | | }; - | |_____^ +LL | let c = || { + | ^^ ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | note: the lint level is defined here --> $DIR/precise.rs:3:9 @@ -33,21 +29,18 @@ LL | }; error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/precise.rs:41:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | let _x = u.0.0; - | | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.0` -LL | | let _x = u.0.1; - | | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.1` -LL | | let _x = u.1.0; - | | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.1.0` -LL | | }; - | |_____^ +LL | let c = || { + | ^^ +... +LL | let _x = u.0.0; + | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.0` +LL | let _x = u.0.1; + | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.1` +LL | let _x = u.1.0; + | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.1.0` ... -LL | } - | - in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.1`, `u.0.0`, `u.1.0` would be dropped here alongside the closure +LL | } + | - in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.1`, `u.0.0`, `u.1.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `u` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr index 13eac4943a469..b2b9ae8fd12f5 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr @@ -1,29 +1,24 @@ error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:25:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | let _t1 = t1.0; - | | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` -LL | | -LL | | let _t2 = t2.0; - | | ---- in Rust 2018, closure captures all of `t2`, but in Rust 2021, it only captures `t2.0` -LL | | -LL | | }; - | |_____^ +LL | let c = || { + | ^^ ... -LL | } - | - - | | - | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure - | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure - | in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | +LL | let _t1 = t1.0; + | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` +LL | +LL | let _t2 = t2.0; + | ---- in Rust 2018, closure captures all of `t2`, but in Rust 2021, it only captures `t2.0` +... +LL | } + | - + | | + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure + | in Rust 2018, `t2` would be dropped here, but in Rust 2021, only `t2.0` would be dropped here alongside the closure | note: the lint level is defined here --> $DIR/significant_drop.rs:2:9 @@ -44,26 +39,20 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:50:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | let _t1 = t1.0; - | | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` -LL | | -LL | | let _t2 = t2; -LL | | }; - | |_____^ +LL | let c = || { + | ^^ ... -LL | } - | - - | | - | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure - | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +LL | +LL | let _t1 = t1.0; + | ---- in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.0` +... +LL | } + | - + | | + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t`, `t1` to be fully captured @@ -79,20 +68,14 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:71:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | println!("{:?}", t1.1); -LL | | }; - | |_____^ +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -108,19 +91,14 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:91:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | }; - | |_____^ +LL | let c = || { + | ^^ +... +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -136,19 +114,14 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:109:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.0; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` -LL | | -LL | | }; - | |_____^ +LL | let c = || { + | ^^ ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure +LL | let _t = t.0; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -164,19 +137,14 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:125:13 | -LL | let c = || { - | _____________^ -LL | | -LL | | -LL | | -LL | | let _t = t.1; - | | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` -LL | | -LL | | }; - | |_____^ +LL | let c = || { + | ^^ ... -LL | } - | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure +LL | let _t = t.1; + | --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` +... +LL | } + | - in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t` to be fully captured @@ -192,25 +160,19 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:143:13 | -LL | let c = move || { - | _____________^ -LL | | -LL | | -LL | | -LL | | println!("{:?} {:?}", t1.1, t.1); - | | ---- --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` - | | | - | | in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.1` -LL | | -LL | | -LL | | }; - | |_____^ +LL | let c = move || { + | ^^^^^^^ +... +LL | println!("{:?} {:?}", t1.1, t.1); + | ---- --- in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.1` + | | + | in Rust 2018, closure captures all of `t1`, but in Rust 2021, it only captures `t1.1` ... -LL | } - | - - | | - | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure - | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure +LL | } + | - + | | + | in Rust 2018, `t1` would be dropped here, but in Rust 2021, only `t1.1` would be dropped here alongside the closure + | in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.1` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `t1`, `t` to be fully captured @@ -226,19 +188,14 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:163:21 | -LL | let c = || { - | _____________________^ -LL | | -LL | | -LL | | -LL | | tuple.0; - | | ------- in Rust 2018, closure captures all of `tuple`, but in Rust 2021, it only captures `tuple.0` -LL | | -LL | | }; - | |_____________^ +LL | let c = || { + | ^^ +... +LL | tuple.0; + | ------- in Rust 2018, closure captures all of `tuple`, but in Rust 2021, it only captures `tuple.0` ... -LL | } - | - in Rust 2018, `tuple` would be dropped here, but in Rust 2021, only `tuple.0` would be dropped here alongside the closure +LL | } + | - in Rust 2018, `tuple` would be dropped here, but in Rust 2021, only `tuple.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `tuple` to be fully captured @@ -254,19 +211,14 @@ LL | error: changes to closure capture in Rust 2021 will affect drop order --> $DIR/significant_drop.rs:181:17 | -LL | let c = || { - | _________________^ -LL | | -LL | | -LL | | -LL | | tuple.0; - | | ------- in Rust 2018, closure captures all of `tuple`, but in Rust 2021, it only captures `tuple.0` -LL | | -LL | | }; - | |_________^ +LL | let c = || { + | ^^ +... +LL | tuple.0; + | ------- in Rust 2018, closure captures all of `tuple`, but in Rust 2021, it only captures `tuple.0` ... -LL | }; - | - in Rust 2018, `tuple` would be dropped here, but in Rust 2021, only `tuple.0` would be dropped here alongside the closure +LL | }; + | - in Rust 2018, `tuple` would be dropped here, but in Rust 2021, only `tuple.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `tuple` to be fully captured From 59f634bc2d9188375ef51944b16e0450c638afdb Mon Sep 17 00:00:00 2001 From: Roxane Date: Wed, 7 Jul 2021 19:08:04 -0400 Subject: [PATCH 25/28] Update comments --- compiler/rustc_typeck/src/check/upvar.rs | 26 ++++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 635b411f0e4f0..11638720e8d2b 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -616,7 +616,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { reasons } - /// Returns true if migration is needed for trait for the provided var_hir_id + /// Returns a tuple that contains the hir_id pointing to the use that resulted in the + /// corresponding place being captured and a String which contains the captured value's name + /// (i.e: a.b.c) if migration is needed for trait for the provided var_hir_id, otherwise returns None fn need_2229_migrations_for_trait( &self, min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, @@ -693,12 +695,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// captured by the closure when `capture_disjoint_fields` is enabled and auto-traits /// differ between the root variable and the captured paths. /// - /// The output list would include a root variable if: - /// - It would have been captured into the closure when `capture_disjoint_fields` wasn't - /// enabled, **and** - /// - It wasn't completely captured by the closure, **and** - /// - One of the paths captured does not implement all the auto-traits its root variable - /// implements. + /// Returns a tuple containing a HashSet of traits that not implemented by the captured fields + /// of a root variables that has the provided var_hir_id and a HashSet of tuples that contains + /// the hir_id pointing to the use that resulted in the corresponding place being captured and + /// a String which contains the captured value's name (i.e: a.b.c) if migration is needed for + /// trait for the provided var_hir_id, otherwise returns None fn compute_2229_migrations_for_trait( &self, min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, @@ -788,8 +789,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// - It wasn't completely captured by the closure, **and** /// - One of the paths starting at this root variable, that is not captured needs Drop. /// - /// This function only returns true for significant drops. A type is considerent to have a - /// significant drop if it's Drop implementation is not annotated by `rustc_insignificant_dtor`. + /// This function only returns a HashSet of tuples for significant drops. The returned HashSet + /// of tuples contains the hir_id pointing to the use that resulted in the corresponding place + /// being captured anda String which contains the captured value's name (i.e: a.b.c) fn compute_2229_migrations_for_drop( &self, closure_def_id: DefId, @@ -871,8 +873,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// - One of the paths captured does not implement all the auto-traits its root variable /// implements. /// - /// Returns a tuple containing a vector of HirIds as well as a String containing the reason - /// why root variables whose HirId is contained in the vector should be fully captured. + /// Returns a tuple containing a vector of tuples of HirIds and a HashSet of tuples that contains + /// the hir_id pointing to the use that resulted in the corresponding place being captured and + /// a String which contains the captured value's name (i.e: a.b.c), as well as a String + /// containing the reason why root variables whose HirId is contained in the vector should fn compute_2229_migrations( &self, closure_def_id: DefId, From ca443729575da9c81e47da80adcbf1b14fa1e09b Mon Sep 17 00:00:00 2001 From: Roxane Date: Thu, 8 Jul 2021 17:04:58 -0400 Subject: [PATCH 26/28] Handle multi diagnostics --- compiler/rustc_typeck/src/check/upvar.rs | 352 +++++++++--------- .../migrations/multi_diagnostics.fixed | 138 +++++++ .../migrations/multi_diagnostics.rs | 138 +++++++ .../migrations/multi_diagnostics.stderr | 134 +++++++ .../migrations/precise.fixed | 12 + .../migrations/precise.rs | 12 + .../migrations/precise.stderr | 22 +- 7 files changed, 624 insertions(+), 184 deletions(-) create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 11638720e8d2b..2b82b4dff2e5a 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -50,6 +50,7 @@ use rustc_span::sym; use rustc_span::{MultiSpan, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_data_structures::stable_map::FxHashMap; use rustc_data_structures::stable_set::FxHashSet; use rustc_index::vec::Idx; use rustc_target::abi::VariantIdx; @@ -81,6 +82,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } +/// Intermediate format to store the hir_id pointing to the use that resulted in the +/// corresponding place being captured and a String which contains the captured value's +/// name (i.e: a.b.c) +type CapturesInfo = (Option, String); + +/// Intermediate format to store information needed to generate migration lint. The tuple +/// contains the hir_id pointing to the use that resulted in the +/// corresponding place being captured, a String which contains the captured value's +/// name (i.e: a.b.c) and a String which contains the reason why migration is needed for that +/// capture +type MigrationNeededForCapture = (Option, String, String); + +/// Intermediate format to store the hir id of the root variable and a HashSet containing +/// information on why the root variable should be fully captured +type MigrationDiagnosticInfo = (hir::HirId, FxHashSet); + struct InferBorrowKindVisitor<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, } @@ -513,45 +530,41 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .as_str(), ); for (var_hir_id, diagnostics_info) in need_migrations.iter() { - let mut captured_names = format!(""); - // Label every Span which are responsible for the captured values - for (captured_hir_id, captured_name) in diagnostics_info.iter() { + // Labels all the usage of the captured variable and why they are responsible + // for migration being needed + for (captured_hir_id, captured_name, reasons) in diagnostics_info.iter() { if let Some(captured_hir_id) = captured_hir_id { let cause_span = self.tcx.hir().span(*captured_hir_id); diagnostics_builder.span_label(cause_span, format!("in Rust 2018, closure captures all of `{}`, but in Rust 2021, it only captures `{}`", self.tcx.hir().name(*var_hir_id), captured_name, )); - if captured_names == "" { - captured_names = format!("`{}`", captured_name); - } else { - captured_names = format!("{}, `{}`", captured_names, captured_name); - } } - } - // Add a label pointing to where a closure and it's captured variables affected by drop order are dropped - if reasons.contains("drop order") { - let drop_location_span = drop_location_span(self.tcx, &closure_hir_id); + // Add a label pointing to where a captured variable affected by drop order + // is dropped + if reasons.contains("drop order") { + let drop_location_span = drop_location_span(self.tcx, &closure_hir_id); - diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` would be dropped here, but in Rust 2021, only {} would be dropped here alongside the closure", - self.tcx.hir().name(*var_hir_id), - captured_names, - )); - } + diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` would be dropped here, but in Rust 2021, only `{}` would be dropped here alongside the closure", + self.tcx.hir().name(*var_hir_id), + captured_name, + )); + } + + // Add a label explaining why a closure no longer implements a trait + if reasons.contains("trait implementation") { + let missing_trait = &reasons[..reasons.find("trait implementation").unwrap() - 1]; - // Add a label explaining why a closure no longer implements a trait - if reasons.contains("trait implementation") { - let missing_trait = &reasons[..reasons.find("trait implementation").unwrap() - 1]; - - diagnostics_builder.span_label(closure_head_span, format!("in Rust 2018, this closure would implement {} as `{}` implements {}, but in Rust 2021, this closure would no longer implement {} as {} does not implement {}", - missing_trait, - self.tcx.hir().name(*var_hir_id), - missing_trait, - missing_trait, - captured_names, - missing_trait, - )); + diagnostics_builder.span_label(closure_head_span, format!("in Rust 2018, this closure would implement {} as `{}` implements {}, but in Rust 2021, this closure would no longer implement {} as `{}` does not implement {}", + missing_trait, + self.tcx.hir().name(*var_hir_id), + missing_trait, + missing_trait, + captured_name, + missing_trait, + )); + } } } diagnostics_builder.note("for more information, see "); @@ -616,16 +629,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { reasons } - /// Returns a tuple that contains the hir_id pointing to the use that resulted in the - /// corresponding place being captured and a String which contains the captured value's name - /// (i.e: a.b.c) if migration is needed for trait for the provided var_hir_id, otherwise returns None - fn need_2229_migrations_for_trait( + /// Figures out the list of root variables (and their types) that aren't completely + /// captured by the closure when `capture_disjoint_fields` is enabled and auto-traits + /// differ between the root variable and the captured paths. + /// + /// Returns a tuple containing a HashMap of CapturesInfo that maps to a HashSet of trait names + /// if migration is needed for traits for the provided var_hir_id, otherwise returns None + fn compute_2229_migrations_for_trait( &self, min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, var_hir_id: hir::HirId, - check_trait: Option, closure_clause: hir::CaptureBy, - ) -> Option<(Option, String)> { + ) -> Option>> { + let auto_traits_def_id = vec![ + self.tcx.lang_items().clone_trait(), + self.tcx.lang_items().sync_trait(), + self.tcx.get_diagnostic_item(sym::send_trait), + self.tcx.lang_items().unpin_trait(), + self.tcx.get_diagnostic_item(sym::unwind_safe_trait), + self.tcx.get_diagnostic_item(sym::ref_unwind_safe_trait), + ]; + let auto_traits = + vec!["`Clone`", "`Sync`", "`Send`", "`Unpin`", "`UnwindSafe`", "`RefUnwindSafe`"]; + let root_var_min_capture_list = if let Some(root_var_min_capture_list) = min_captures.and_then(|m| m.get(&var_hir_id)) { @@ -650,19 +676,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - let obligation_should_hold = check_trait - .map(|check_trait| { - self.infcx - .type_implements_trait( - check_trait, - ty, - self.tcx.mk_substs_trait(ty, &[]), - self.param_env, - ) - .must_apply_modulo_regions() - }) - .unwrap_or(false); + let mut obligations_should_hold = Vec::new(); + // Checks if a root variable implements any of the auto traits + for check_trait in auto_traits_def_id.iter() { + obligations_should_hold.push( + check_trait + .map(|check_trait| { + self.infcx + .type_implements_trait( + check_trait, + ty, + self.tcx.mk_substs_trait(ty, &[]), + self.param_env, + ) + .must_apply_modulo_regions() + }) + .unwrap_or(false), + ); + } + let mut problematic_captures = FxHashMap::default(); // Check whether captured fields also implement the trait for capture in root_var_min_capture_list.iter() { let ty = apply_capture_kind_on_capture_ty( @@ -671,112 +704,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture.info.capture_kind, ); - let obligation_holds_for_capture = check_trait - .map(|check_trait| { - self.infcx - .type_implements_trait( - check_trait, - ty, - self.tcx.mk_substs_trait(ty, &[]), - self.param_env, - ) - .must_apply_modulo_regions() - }) - .unwrap_or(false); - - if !obligation_holds_for_capture && obligation_should_hold { - return Some((capture.info.path_expr_id, capture.to_string(self.tcx))); + // Checks if a capture implements any of the auto traits + let mut obligations_holds_for_capture = Vec::new(); + for check_trait in auto_traits_def_id.iter() { + obligations_holds_for_capture.push( + check_trait + .map(|check_trait| { + self.infcx + .type_implements_trait( + check_trait, + ty, + self.tcx.mk_substs_trait(ty, &[]), + self.param_env, + ) + .must_apply_modulo_regions() + }) + .unwrap_or(false), + ); } - } - None - } - - /// Figures out the list of root variables (and their types) that aren't completely - /// captured by the closure when `capture_disjoint_fields` is enabled and auto-traits - /// differ between the root variable and the captured paths. - /// - /// Returns a tuple containing a HashSet of traits that not implemented by the captured fields - /// of a root variables that has the provided var_hir_id and a HashSet of tuples that contains - /// the hir_id pointing to the use that resulted in the corresponding place being captured and - /// a String which contains the captured value's name (i.e: a.b.c) if migration is needed for - /// trait for the provided var_hir_id, otherwise returns None - fn compute_2229_migrations_for_trait( - &self, - min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, - var_hir_id: hir::HirId, - closure_clause: hir::CaptureBy, - ) -> Option<(FxHashSet<&str>, FxHashSet<(Option, String)>)> { - let tcx = self.infcx.tcx; - // Check whether catpured fields also implement the trait - let mut auto_trait_reasons = FxHashSet::default(); - let mut diagnostics_info = FxHashSet::default(); - - if let Some(info) = self.need_2229_migrations_for_trait( - min_captures, - var_hir_id, - tcx.lang_items().clone_trait(), - closure_clause, - ) { - auto_trait_reasons.insert("`Clone`"); - diagnostics_info.insert(info); - } + let mut capture_problems = FxHashSet::default(); - if let Some(info) = self.need_2229_migrations_for_trait( - min_captures, - var_hir_id, - tcx.lang_items().sync_trait(), - closure_clause, - ) { - auto_trait_reasons.insert("`Sync`"); - diagnostics_info.insert(info); - } - - if let Some(info) = self.need_2229_migrations_for_trait( - min_captures, - var_hir_id, - tcx.get_diagnostic_item(sym::send_trait), - closure_clause, - ) { - auto_trait_reasons.insert("`Send`"); - diagnostics_info.insert(info); - } - - if let Some(info) = self.need_2229_migrations_for_trait( - min_captures, - var_hir_id, - tcx.lang_items().unpin_trait(), - closure_clause, - ) { - auto_trait_reasons.insert("`Unpin`"); - diagnostics_info.insert(info); - } - - if let Some(info) = self.need_2229_migrations_for_trait( - min_captures, - var_hir_id, - tcx.get_diagnostic_item(sym::unwind_safe_trait), - closure_clause, - ) { - auto_trait_reasons.insert("`UnwindSafe`"); - diagnostics_info.insert(info); - } + // Checks if for any of the auto traits, one or more trait is implemented + // by the root variable but not by the capture + for (idx, _) in obligations_should_hold.iter().enumerate() { + if !obligations_holds_for_capture[idx] && obligations_should_hold[idx] { + capture_problems.insert(auto_traits[idx]); + } + } - if let Some(info) = self.need_2229_migrations_for_trait( - min_captures, - var_hir_id, - tcx.get_diagnostic_item(sym::ref_unwind_safe_trait), - closure_clause, - ) { - auto_trait_reasons.insert("`RefUnwindSafe`"); - diagnostics_info.insert(info); + if capture_problems.len() > 0 { + problematic_captures.insert( + (capture.info.path_expr_id, capture.to_string(self.tcx)), + capture_problems, + ); + } } - - if auto_trait_reasons.len() > 0 { - return Some((auto_trait_reasons, diagnostics_info)); + if problematic_captures.len() > 0 { + return Some(problematic_captures); } - - return None; + None } /// Figures out the list of root variables (and their types) that aren't completely @@ -789,9 +756,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// - It wasn't completely captured by the closure, **and** /// - One of the paths starting at this root variable, that is not captured needs Drop. /// - /// This function only returns a HashSet of tuples for significant drops. The returned HashSet - /// of tuples contains the hir_id pointing to the use that resulted in the corresponding place - /// being captured anda String which contains the captured value's name (i.e: a.b.c) + /// This function only returns a HashSet of CapturesInfo for significant drops. If there + /// are no significant drops than None is returned fn compute_2229_migrations_for_drop( &self, closure_def_id: DefId, @@ -799,7 +765,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, closure_clause: hir::CaptureBy, var_hir_id: hir::HirId, - ) -> Option, String)>> { + ) -> Option> { let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); if !ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) { @@ -873,17 +839,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// - One of the paths captured does not implement all the auto-traits its root variable /// implements. /// - /// Returns a tuple containing a vector of tuples of HirIds and a HashSet of tuples that contains - /// the hir_id pointing to the use that resulted in the corresponding place being captured and - /// a String which contains the captured value's name (i.e: a.b.c), as well as a String + /// Returns a tuple containing a vector of MigrationDiagnosticInfo, as well as a String /// containing the reason why root variables whose HirId is contained in the vector should + /// be captured fn compute_2229_migrations( &self, closure_def_id: DefId, closure_span: Span, closure_clause: hir::CaptureBy, min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, - ) -> (Vec<(hir::HirId, FxHashSet<(Option, String)>)>, String) { + ) -> (Vec, String) { let upvars = if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { upvars } else { @@ -891,42 +856,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut need_migrations = Vec::new(); - let mut auto_trait_reasons = FxHashSet::default(); - let mut drop_reorder_reason = false; + let mut auto_trait_migration_reasons = FxHashSet::default(); + let mut drop_migration_needed = false; // Perform auto-trait analysis for (&var_hir_id, _) in upvars.iter() { - let mut need_migration = false; let mut responsible_captured_hir_ids = FxHashSet::default(); - if let Some((trait_migration_cause, diagnostics_info)) = + let auto_trait_diagnostic = if let Some(diagnostics_info) = self.compute_2229_migrations_for_trait(min_captures, var_hir_id, closure_clause) { - need_migration = true; - auto_trait_reasons.extend(trait_migration_cause); - responsible_captured_hir_ids.extend(diagnostics_info); + diagnostics_info + } else { + FxHashMap::default() + }; + + let drop_reorder_diagnostic = if let Some(diagnostics_info) = self + .compute_2229_migrations_for_drop( + closure_def_id, + closure_span, + min_captures, + closure_clause, + var_hir_id, + ) { + drop_migration_needed = true; + diagnostics_info + } else { + FxHashSet::default() + }; + + // Combine all the captures responsible for needing migrations into one HashSet + let mut capture_disagnostic = drop_reorder_diagnostic.clone(); + for key in auto_trait_diagnostic.keys() { + capture_disagnostic.insert(key.clone()); } - if let Some(diagnostics_info) = self.compute_2229_migrations_for_drop( - closure_def_id, - closure_span, - min_captures, - closure_clause, - var_hir_id, - ) { - need_migration = true; - drop_reorder_reason = true; - responsible_captured_hir_ids.extend(diagnostics_info); + for captured_info in capture_disagnostic.iter() { + // Get the auto trait reasons of why migration is needed because of that capture, if there are any + let capture_trait_reasons = + if let Some(reasons) = auto_trait_diagnostic.get(captured_info) { + reasons.clone() + } else { + FxHashSet::default() + }; + + // Check if migration is needed because of drop reorder as a result of that capture + let capture_drop_reorder_reason = drop_reorder_diagnostic.contains(captured_info); + + // Combine all the reasons of why the root variable should be captured as a result of + // auto trait implementation issues + auto_trait_migration_reasons.extend(capture_trait_reasons.clone()); + + responsible_captured_hir_ids.insert(( + captured_info.0, + captured_info.1.clone(), + self.compute_2229_migrations_reasons( + capture_trait_reasons, + capture_drop_reorder_reason, + ), + )); } - if need_migration { + if capture_disagnostic.len() > 0 { need_migrations.push((var_hir_id, responsible_captured_hir_ids)); } } - ( need_migrations, - self.compute_2229_migrations_reasons(auto_trait_reasons, drop_reorder_reason), + self.compute_2229_migrations_reasons( + auto_trait_migration_reasons, + drop_migration_needed, + ), ) } @@ -1964,7 +1964,7 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis( /// - s2: Comma separated names of the variables being migrated. fn migration_suggestion_for_2229( tcx: TyCtxt<'_>, - need_migrations: &Vec<(hir::HirId, FxHashSet<(Option, String)>)>, + need_migrations: &Vec, ) -> (String, String) { let need_migrations_variables = need_migrations.iter().map(|(v, _)| var_name(tcx, *v)).collect::>(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed new file mode 100644 index 0000000000000..e5102fee58877 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed @@ -0,0 +1,138 @@ +// run-rustfix +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +use std::thread; + +struct S(String); + +#[derive(Clone)] +struct T(i32); + +struct U(S, T); + +impl Clone for U { + fn clone(&self) -> Self { + U(S(String::from("Hello World")), T(0)) + } +} + +fn test_multi_issues() { + let f1 = U(S(String::from("foo")), T(0)); + let f2 = U(S(String::from("bar")), T(0)); + let c = || { let _ = (&f1, &f2); + //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured + let _f_1 = f1.0; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` + let _f_2 = f2.1; + //~^ NOTE: in Rust 2018, closure captures all of `f2`, but in Rust 2021, it only captures `f2.1` + }; + + let c_clone = c.clone(); + + c_clone(); +} +//~^ NOTE: in Rust 2018, `f2` would be dropped here, but in Rust 2021, only `f2.1` would be dropped here alongside the closure + +fn test_capturing_all_disjoint_fields_individually() { + let f1 = U(S(String::from("foo")), T(0)); + let c = || { let _ = &f1; + //~^ ERROR: `Clone` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_1 = f1.0; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` + let _f_2 = f1.1; + }; + + let c_clone = c.clone(); + + c_clone(); +} + +struct U1(S, T, S); + +impl Clone for U1 { + fn clone(&self) -> Self { + U1(S(String::from("foo")), T(0), S(String::from("bar"))) + } +} + +fn test_capturing_several_disjoint_fields_individually_1() { + let f1 = U1(S(String::from("foo")), T(0), S(String::from("bar"))); + let c = || { let _ = &f1; + //~^ ERROR: `Clone` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.2` does not implement `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_0 = f1.0; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` + let _f_2 = f1.2; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.2` + }; + + let c_clone = c.clone(); + + c_clone(); +} + +fn test_capturing_several_disjoint_fields_individually_2() { + let f1 = U1(S(String::from("foo")), T(0), S(String::from("bar"))); + let c = || { let _ = &f1; + //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_0 = f1.0; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` + let _f_1 = f1.1; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.1` + }; + + let c_clone = c.clone(); + + c_clone(); +} +//~^ NOTE: in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.1` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.0` would be dropped here alongside the closure + +struct SendPointer(*mut i32); +unsafe impl Send for SendPointer {} + +struct CustomInt(*mut i32); +struct SyncPointer(CustomInt); +unsafe impl Sync for SyncPointer {} +unsafe impl Send for CustomInt {} + +fn test_multi_traits_issues() { + let mut f1 = 10; + let f1 = CustomInt(&mut f1 as *mut i32); + let fptr1 = SyncPointer(f1); + + let mut f2 = 10; + let fptr2 = SendPointer(&mut f2 as *mut i32); + thread::spawn(move || { let _ = (&fptr1, &fptr2); unsafe { + //~^ ERROR: `Sync`, `Send` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `Sync`, `Send` as `fptr1` implements `Sync`, `Send`, but in Rust 2021, this closure would no longer implement `Sync`, `Send` as `fptr1.0.0` does not implement `Sync`, `Send` + //~| NOTE: in Rust 2018, this closure would implement `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure would no longer implement `Send` as `fptr2.0` does not implement `Send` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `fptr1`, `fptr2` to be fully captured + *fptr1.0.0 = 20; + //~^ NOTE: in Rust 2018, closure captures all of `fptr1`, but in Rust 2021, it only captures `fptr1.0.0` + *fptr2.0 = 20; + //~^ NOTE: in Rust 2018, closure captures all of `fptr2`, but in Rust 2021, it only captures `fptr2.0` + } }); +} + +fn main() { + test_multi_issues(); + test_capturing_all_disjoint_fields_individually(); + test_capturing_several_disjoint_fields_individually_1(); + test_capturing_several_disjoint_fields_individually_2(); + test_multi_traits_issues(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs new file mode 100644 index 0000000000000..d05c0bc1bbce9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs @@ -0,0 +1,138 @@ +// run-rustfix +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here + +use std::thread; + +struct S(String); + +#[derive(Clone)] +struct T(i32); + +struct U(S, T); + +impl Clone for U { + fn clone(&self) -> Self { + U(S(String::from("Hello World")), T(0)) + } +} + +fn test_multi_issues() { + let f1 = U(S(String::from("foo")), T(0)); + let f2 = U(S(String::from("bar")), T(0)); + let c = || { + //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured + let _f_1 = f1.0; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` + let _f_2 = f2.1; + //~^ NOTE: in Rust 2018, closure captures all of `f2`, but in Rust 2021, it only captures `f2.1` + }; + + let c_clone = c.clone(); + + c_clone(); +} +//~^ NOTE: in Rust 2018, `f2` would be dropped here, but in Rust 2021, only `f2.1` would be dropped here alongside the closure + +fn test_capturing_all_disjoint_fields_individually() { + let f1 = U(S(String::from("foo")), T(0)); + let c = || { + //~^ ERROR: `Clone` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_1 = f1.0; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` + let _f_2 = f1.1; + }; + + let c_clone = c.clone(); + + c_clone(); +} + +struct U1(S, T, S); + +impl Clone for U1 { + fn clone(&self) -> Self { + U1(S(String::from("foo")), T(0), S(String::from("bar"))) + } +} + +fn test_capturing_several_disjoint_fields_individually_1() { + let f1 = U1(S(String::from("foo")), T(0), S(String::from("bar"))); + let c = || { + //~^ ERROR: `Clone` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.2` does not implement `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_0 = f1.0; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` + let _f_2 = f1.2; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.2` + }; + + let c_clone = c.clone(); + + c_clone(); +} + +fn test_capturing_several_disjoint_fields_individually_2() { + let f1 = U1(S(String::from("foo")), T(0), S(String::from("bar"))); + let c = || { + //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `f1` to be fully captured + let _f_0 = f1.0; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` + let _f_1 = f1.1; + //~^ NOTE: in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.1` + }; + + let c_clone = c.clone(); + + c_clone(); +} +//~^ NOTE: in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.1` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.0` would be dropped here alongside the closure + +struct SendPointer(*mut i32); +unsafe impl Send for SendPointer {} + +struct CustomInt(*mut i32); +struct SyncPointer(CustomInt); +unsafe impl Sync for SyncPointer {} +unsafe impl Send for CustomInt {} + +fn test_multi_traits_issues() { + let mut f1 = 10; + let f1 = CustomInt(&mut f1 as *mut i32); + let fptr1 = SyncPointer(f1); + + let mut f2 = 10; + let fptr2 = SendPointer(&mut f2 as *mut i32); + thread::spawn(move || unsafe { + //~^ ERROR: `Sync`, `Send` trait implementation for closure + //~| NOTE: in Rust 2018, this closure would implement `Sync`, `Send` as `fptr1` implements `Sync`, `Send`, but in Rust 2021, this closure would no longer implement `Sync`, `Send` as `fptr1.0.0` does not implement `Sync`, `Send` + //~| NOTE: in Rust 2018, this closure would implement `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure would no longer implement `Send` as `fptr2.0` does not implement `Send` + //~| NOTE: for more information, see + //~| HELP: add a dummy let to cause `fptr1`, `fptr2` to be fully captured + *fptr1.0.0 = 20; + //~^ NOTE: in Rust 2018, closure captures all of `fptr1`, but in Rust 2021, it only captures `fptr1.0.0` + *fptr2.0 = 20; + //~^ NOTE: in Rust 2018, closure captures all of `fptr2`, but in Rust 2021, it only captures `fptr2.0` + }); +} + +fn main() { + test_multi_issues(); + test_capturing_all_disjoint_fields_individually(); + test_capturing_several_disjoint_fields_individually_1(); + test_capturing_several_disjoint_fields_individually_2(); + test_multi_traits_issues(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr new file mode 100644 index 0000000000000..5f09230d45ca0 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr @@ -0,0 +1,134 @@ +error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure, and drop order + --> $DIR/multi_diagnostics.rs:23:13 + | +LL | let c = || { + | ^^ in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` +... +LL | let _f_1 = f1.0; + | ---- in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` +LL | +LL | let _f_2 = f2.1; + | ---- in Rust 2018, closure captures all of `f2`, but in Rust 2021, it only captures `f2.1` +... +LL | } + | - in Rust 2018, `f2` would be dropped here, but in Rust 2021, only `f2.1` would be dropped here alongside the closure + | +note: the lint level is defined here + --> $DIR/multi_diagnostics.rs:2:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see +help: add a dummy let to cause `f1`, `f2` to be fully captured + | +LL | let c = || { let _ = (&f1, &f2); +LL | +LL | +LL | +LL | +LL | let _f_1 = f1.0; + ... + +error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure + --> $DIR/multi_diagnostics.rs:42:13 + | +LL | let c = || { + | ^^ in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` +... +LL | let _f_1 = f1.0; + | ---- in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` + | + = note: for more information, see +help: add a dummy let to cause `f1` to be fully captured + | +LL | let c = || { let _ = &f1; +LL | +LL | +LL | +LL | +LL | let _f_1 = f1.0; + ... + +error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure + --> $DIR/multi_diagnostics.rs:67:13 + | +LL | let c = || { + | ^^ + | | + | in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` + | in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.2` does not implement `Clone` +... +LL | let _f_0 = f1.0; + | ---- in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` +LL | +LL | let _f_2 = f1.2; + | ---- in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.2` + | + = note: for more information, see +help: add a dummy let to cause `f1` to be fully captured + | +LL | let c = || { let _ = &f1; +LL | +LL | +LL | +LL | +LL | + ... + +error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure, and drop order + --> $DIR/multi_diagnostics.rs:86:13 + | +LL | let c = || { + | ^^ in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` +... +LL | let _f_0 = f1.0; + | ---- in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.0` +LL | +LL | let _f_1 = f1.1; + | ---- in Rust 2018, closure captures all of `f1`, but in Rust 2021, it only captures `f1.1` +... +LL | } + | - + | | + | in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.0` would be dropped here alongside the closure + | in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.1` would be dropped here alongside the closure + | + = note: for more information, see +help: add a dummy let to cause `f1` to be fully captured + | +LL | let c = || { let _ = &f1; +LL | +LL | +LL | +LL | +LL | let _f_0 = f1.0; + ... + +error: changes to closure capture in Rust 2021 will affect `Sync`, `Send` trait implementation for closure + --> $DIR/multi_diagnostics.rs:119:19 + | +LL | thread::spawn(move || unsafe { + | ^^^^^^^^^^^^^^ + | | + | in Rust 2018, this closure would implement `Sync`, `Send` as `fptr1` implements `Sync`, `Send`, but in Rust 2021, this closure would no longer implement `Sync`, `Send` as `fptr1.0.0` does not implement `Sync`, `Send` + | in Rust 2018, this closure would implement `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure would no longer implement `Send` as `fptr2.0` does not implement `Send` +... +LL | *fptr1.0.0 = 20; + | ---------- in Rust 2018, closure captures all of `fptr1`, but in Rust 2021, it only captures `fptr1.0.0` +LL | +LL | *fptr2.0 = 20; + | -------- in Rust 2018, closure captures all of `fptr2`, but in Rust 2021, it only captures `fptr2.0` + | + = note: for more information, see +help: add a dummy let to cause `fptr1`, `fptr2` to be fully captured + | +LL | thread::spawn(move || { let _ = (&fptr1, &fptr2); unsafe { +LL | +LL | +LL | +LL | +LL | + ... + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed index ba5e5b573f1d6..226172fb93eb1 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed @@ -1,6 +1,7 @@ // run-rustfix #![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here #[derive(Debug)] struct Foo(i32); @@ -18,13 +19,16 @@ fn test_precise_analysis_drop_paths_not_captured_by_move() { let c = || { let _ = &t; //~^ ERROR: drop order + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t = &t.1; }; c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure struct S; impl Drop for S { @@ -40,14 +44,22 @@ fn test_precise_analysis_long_path_missing() { let c = || { let _ = &u; //~^ ERROR: drop order + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `u` to be fully captured let _x = u.0.0; + //~^ NOTE: in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.0` let _x = u.0.1; + //~^ NOTE: in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.1` let _x = u.1.0; + //~^ NOTE: in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.1.0` }; c(); } +//~^ NOTE: in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.0` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.1` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.1.0` would be dropped here alongside the closure + fn main() { test_precise_analysis_drop_paths_not_captured_by_move(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs b/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs index 92b6f25c80dad..7035abe6de0a2 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs @@ -1,6 +1,7 @@ // run-rustfix #![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE: the lint level is defined here #[derive(Debug)] struct Foo(i32); @@ -18,13 +19,16 @@ fn test_precise_analysis_drop_paths_not_captured_by_move() { let c = || { //~^ ERROR: drop order + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `t` to be fully captured let _t = t.0; + //~^ NOTE: in Rust 2018, closure captures all of `t`, but in Rust 2021, it only captures `t.0` let _t = &t.1; }; c(); } +//~^ NOTE: in Rust 2018, `t` would be dropped here, but in Rust 2021, only `t.0` would be dropped here alongside the closure struct S; impl Drop for S { @@ -40,14 +44,22 @@ fn test_precise_analysis_long_path_missing() { let c = || { //~^ ERROR: drop order + //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `u` to be fully captured let _x = u.0.0; + //~^ NOTE: in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.0` let _x = u.0.1; + //~^ NOTE: in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.1` let _x = u.1.0; + //~^ NOTE: in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.1.0` }; c(); } +//~^ NOTE: in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.0` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.1` would be dropped here alongside the closure +//~| NOTE: in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.1.0` would be dropped here alongside the closure + fn main() { test_precise_analysis_drop_paths_not_captured_by_move(); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr index 153c0d6b68671..ee2cae0ecb2a1 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr @@ -1,5 +1,5 @@ error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/precise.rs:19:13 + --> $DIR/precise.rs:20:13 | LL | let c = || { | ^^ @@ -21,26 +21,32 @@ help: add a dummy let to cause `t` to be fully captured LL | let c = || { let _ = &t; LL | LL | +LL | LL | let _t = t.0; -LL | let _t = &t.1; -LL | }; - | +LL | + ... error: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/precise.rs:41:13 + --> $DIR/precise.rs:45:13 | LL | let c = || { | ^^ ... LL | let _x = u.0.0; | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.0` +LL | LL | let _x = u.0.1; | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.0.1` +LL | LL | let _x = u.1.0; | ----- in Rust 2018, closure captures all of `u`, but in Rust 2021, it only captures `u.1.0` ... LL | } - | - in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.1`, `u.0.0`, `u.1.0` would be dropped here alongside the closure + | - + | | + | in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.1` would be dropped here alongside the closure + | in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.0` would be dropped here alongside the closure + | in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.1.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `u` to be fully captured @@ -48,9 +54,9 @@ help: add a dummy let to cause `u` to be fully captured LL | let c = || { let _ = &u; LL | LL | +LL | LL | let _x = u.0.0; -LL | let _x = u.0.1; -LL | let _x = u.1.0; +LL | ... error: aborting due to 2 previous errors From 8cbeaf738267c8ddb5ab205dae4404e2f25eeeeb Mon Sep 17 00:00:00 2001 From: Roxane Date: Fri, 9 Jul 2021 10:18:55 -0400 Subject: [PATCH 27/28] Address comments --- compiler/rustc_typeck/src/check/upvar.rs | 10 +++++----- .../2229_closure_analysis/migrations/auto_traits.fixed | 2 +- .../2229_closure_analysis/migrations/auto_traits.rs | 2 +- .../migrations/auto_traits.stderr | 2 +- .../migrations/multi_diagnostics.fixed | 4 ++-- .../migrations/multi_diagnostics.rs | 4 ++-- .../migrations/multi_diagnostics.stderr | 6 +++--- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 2b82b4dff2e5a..b6d1468b7a617 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -619,7 +619,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if auto_trait_reasons.len() > 0 && drop_reason { - reasons = format!("{}, and ", reasons); + reasons = format!("{} and ", reasons); } if drop_reason { @@ -886,12 +886,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Combine all the captures responsible for needing migrations into one HashSet - let mut capture_disagnostic = drop_reorder_diagnostic.clone(); + let mut capture_diagnostic = drop_reorder_diagnostic.clone(); for key in auto_trait_diagnostic.keys() { - capture_disagnostic.insert(key.clone()); + capture_diagnostic.insert(key.clone()); } - for captured_info in capture_disagnostic.iter() { + for captured_info in capture_diagnostic.iter() { // Get the auto trait reasons of why migration is needed because of that capture, if there are any let capture_trait_reasons = if let Some(reasons) = auto_trait_diagnostic.get(captured_info) { @@ -917,7 +917,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); } - if capture_disagnostic.len() > 0 { + if capture_diagnostic.len() > 0 { need_migrations.push((var_hir_id, responsible_captured_hir_ids)); } } diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed index 42863b440fc1d..e2b7b8f027520 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed @@ -56,7 +56,7 @@ impl Clone for U { fn test_clone_trait() { let f = U(S(String::from("Hello World")), T(0)); let c = || { let _ = &f; - //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~^ ERROR: `Clone` trait implementation for closure and drop order //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f.1` does not implement `Clone` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs index ae987f526d044..6c56ca27475a3 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs @@ -56,7 +56,7 @@ impl Clone for U { fn test_clone_trait() { let f = U(S(String::from("Hello World")), T(0)); let c = || { - //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~^ ERROR: `Clone` trait implementation for closure and drop order //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f.1` does not implement `Clone` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr index 2af85852c01a0..9c954b1465d83 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr @@ -43,7 +43,7 @@ LL | LL | *fptr.0.0 = 20; ... -error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure, and drop order +error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure and drop order --> $DIR/auto_traits.rs:58:13 | LL | let c = || { diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed index e5102fee58877..98f578abc44dc 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed @@ -21,7 +21,7 @@ fn test_multi_issues() { let f1 = U(S(String::from("foo")), T(0)); let f2 = U(S(String::from("bar")), T(0)); let c = || { let _ = (&f1, &f2); - //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~^ ERROR: `Clone` trait implementation for closure and drop order //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured @@ -84,7 +84,7 @@ fn test_capturing_several_disjoint_fields_individually_1() { fn test_capturing_several_disjoint_fields_individually_2() { let f1 = U1(S(String::from("foo")), T(0), S(String::from("bar"))); let c = || { let _ = &f1; - //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~^ ERROR: `Clone` trait implementation for closure and drop order //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f1` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs index d05c0bc1bbce9..1577b91c96018 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs @@ -21,7 +21,7 @@ fn test_multi_issues() { let f1 = U(S(String::from("foo")), T(0)); let f2 = U(S(String::from("bar")), T(0)); let c = || { - //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~^ ERROR: `Clone` trait implementation for closure and drop order //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured @@ -84,7 +84,7 @@ fn test_capturing_several_disjoint_fields_individually_1() { fn test_capturing_several_disjoint_fields_individually_2() { let f1 = U1(S(String::from("foo")), T(0), S(String::from("bar"))); let c = || { - //~^ ERROR: `Clone` trait implementation for closure, and drop order + //~^ ERROR: `Clone` trait implementation for closure and drop order //~| NOTE: in Rust 2018, this closure would implement `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure would no longer implement `Clone` as `f1.0` does not implement `Clone` //~| NOTE: for more information, see //~| HELP: add a dummy let to cause `f1` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr index 5f09230d45ca0..856fbdf5f9fd0 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr @@ -1,4 +1,4 @@ -error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure, and drop order +error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure and drop order --> $DIR/multi_diagnostics.rs:23:13 | LL | let c = || { @@ -75,7 +75,7 @@ LL | LL | ... -error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure, and drop order +error: changes to closure capture in Rust 2021 will affect `Clone` trait implementation for closure and drop order --> $DIR/multi_diagnostics.rs:86:13 | LL | let c = || { @@ -90,8 +90,8 @@ LL | let _f_1 = f1.1; LL | } | - | | - | in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.0` would be dropped here alongside the closure | in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.1` would be dropped here alongside the closure + | in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.0` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `f1` to be fully captured From 08c616741c6eff64443cc50d2ec8db9663ff61d8 Mon Sep 17 00:00:00 2001 From: Roxane Date: Fri, 9 Jul 2021 13:32:30 -0400 Subject: [PATCH 28/28] Ensure deterministic ordering for diagnostics --- compiler/rustc_typeck/src/check/upvar.rs | 8 +++++--- .../migrations/multi_diagnostics.stderr | 2 +- .../2229_closure_analysis/migrations/precise.stderr | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index b6d1468b7a617..c42ca936e9758 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -96,7 +96,7 @@ type MigrationNeededForCapture = (Option, String, String); /// Intermediate format to store the hir id of the root variable and a HashSet containing /// information on why the root variable should be fully captured -type MigrationDiagnosticInfo = (hir::HirId, FxHashSet); +type MigrationDiagnosticInfo = (hir::HirId, Vec); struct InferBorrowKindVisitor<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, @@ -861,7 +861,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Perform auto-trait analysis for (&var_hir_id, _) in upvars.iter() { - let mut responsible_captured_hir_ids = FxHashSet::default(); + let mut responsible_captured_hir_ids = Vec::new(); let auto_trait_diagnostic = if let Some(diagnostics_info) = self.compute_2229_migrations_for_trait(min_captures, var_hir_id, closure_clause) @@ -891,6 +891,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_diagnostic.insert(key.clone()); } + let mut capture_diagnostic = capture_diagnostic.into_iter().collect::>(); + capture_diagnostic.sort(); for captured_info in capture_diagnostic.iter() { // Get the auto trait reasons of why migration is needed because of that capture, if there are any let capture_trait_reasons = @@ -907,7 +909,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // auto trait implementation issues auto_trait_migration_reasons.extend(capture_trait_reasons.clone()); - responsible_captured_hir_ids.insert(( + responsible_captured_hir_ids.push(( captured_info.0, captured_info.1.clone(), self.compute_2229_migrations_reasons( diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr index 856fbdf5f9fd0..8a42683c1df9f 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr @@ -90,8 +90,8 @@ LL | let _f_1 = f1.1; LL | } | - | | - | in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.1` would be dropped here alongside the closure | in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.0` would be dropped here alongside the closure + | in Rust 2018, `f1` would be dropped here, but in Rust 2021, only `f1.1` would be dropped here alongside the closure | = note: for more information, see help: add a dummy let to cause `f1` to be fully captured diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr index ee2cae0ecb2a1..5bf73ccc55400 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr @@ -44,8 +44,8 @@ LL | let _x = u.1.0; LL | } | - | | - | in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.1` would be dropped here alongside the closure | in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.0` would be dropped here alongside the closure + | in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.0.1` would be dropped here alongside the closure | in Rust 2018, `u` would be dropped here, but in Rust 2021, only `u.1.0` would be dropped here alongside the closure | = note: for more information, see