diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index ef618ff51edc0..a141fdd7b88da 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -90,10 +90,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { enum CapturesInfo { /// We previously captured all of `x`, but now we capture some sub-path. CapturingLess { source_expr: Option, var_name: String }, - //CapturingNothing { - // // where the variable appears in the closure (but is not captured) - // use_span: Span, - //}, + CapturingNothing { + // where the variable appears in the closure (but is not captured) + use_span: Span, + }, } /// Reasons that we might issue a migration warning. @@ -758,6 +758,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { captured_name, )); } + CapturesInfo::CapturingNothing { use_span } => { + diagnostics_builder.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", + self.tcx.hir().name(*var_hir_id), + )); + } + _ => { } } @@ -773,6 +779,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { captured_name, )); } + CapturesInfo::CapturingNothing { use_span: _ } => { + diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", + v = self.tcx.hir().name(*var_hir_id), + )); + } } } @@ -787,6 +798,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { p = captured_name, )); } + + // Cannot happen: if we don't capture a variable, we impl strictly more traits + CapturesInfo::CapturingNothing { use_span } => span_bug!(*use_span, "missing trait from not capturing something"), } } } @@ -1051,10 +1065,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match closure_clause { // Only migrate if closure is a move closure hir::CaptureBy::Value => { - let diagnostics_info = FxHashSet::default(); - //diagnostics_info.insert(CapturesInfo::CapturingNothing); - //let upvars = self.tcx.upvars_mentioned(closure_def_id).expect("must be an upvar"); - //let _span = upvars[&var_hir_id]; + let mut diagnostics_info = FxHashSet::default(); + let upvars = self.tcx.upvars_mentioned(closure_def_id).expect("must be an upvar"); + let upvar = upvars[&var_hir_id]; + diagnostics_info.insert(CapturesInfo::CapturingNothing { use_span: upvar.span }); return Some(diagnostics_info); } hir::CaptureBy::Ref => {} diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed b/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed new file mode 100644 index 0000000000000..4e0b18e72338a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE lint level is defined here + +fn main() { + struct Foo(u32); + impl Drop for Foo { + fn drop(&mut self) { + println!("dropped {}", self.0); + } + } + + let f0 = Foo(0); + let f1 = Foo(1); + + let c0 = move || { + let _ = &f0; + //~^ ERROR changes to closure capture in Rust 2021 will affect drop order + //~| NOTE for more information + let _ = f0; + //~^ NOTE in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect + }; + + let c1 = move || { + let _ = &f1; + }; + + println!("dropping 0"); + drop(c0); + println!("dropping 1"); + drop(c1); + println!("dropped all"); +} +//~^ NOTE in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.rs b/src/test/ui/closures/2229_closure_analysis/issue-90465.rs new file mode 100644 index 0000000000000..466e6dbabc502 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.rs @@ -0,0 +1,34 @@ +// run-rustfix + +#![deny(rust_2021_incompatible_closure_captures)] +//~^ NOTE lint level is defined here + +fn main() { + struct Foo(u32); + impl Drop for Foo { + fn drop(&mut self) { + println!("dropped {}", self.0); + } + } + + let f0 = Foo(0); + let f1 = Foo(1); + + let c0 = move || { + //~^ ERROR changes to closure capture in Rust 2021 will affect drop order + //~| NOTE for more information + let _ = f0; + //~^ NOTE in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect + }; + + let c1 = move || { + let _ = &f1; + }; + + println!("dropping 0"); + drop(c0); + println!("dropping 1"); + drop(c1); + println!("dropped all"); +} +//~^ NOTE in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure diff --git a/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr b/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr new file mode 100644 index 0000000000000..3e921dc0f8a66 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-90465.stderr @@ -0,0 +1,26 @@ +error: changes to closure capture in Rust 2021 will affect drop order + --> $DIR/issue-90465.rs:17:14 + | +LL | let c0 = move || { + | ^^^^^^^ +... +LL | let _ = f0; + | -- in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect +... +LL | } + | - in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure + | +note: the lint level is defined here + --> $DIR/issue-90465.rs:3:9 + | +LL | #![deny(rust_2021_incompatible_closure_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: for more information, see +help: add a dummy let to cause `f0` to be fully captured + | +LL ~ let c0 = move || { +LL + let _ = &f0; + | + +error: aborting due to previous error +