diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 470405babb379..8d3c70b05734f 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -33,7 +33,6 @@ use super::FnCtxt; use crate::expr_use_visitor as euv; -use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -72,7 +71,7 @@ enum PlaceAncestryRelation { /// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo` /// during capture analysis. Information in this map feeds into the minimum capture /// analysis pass. -type InferredCaptureInformation<'tcx> = FxIndexMap, ty::CaptureInfo>; +type InferredCaptureInformation<'tcx> = Vec<(Place<'tcx>, ty::CaptureInfo)>; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { @@ -258,7 +257,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_kind, }; - capture_information.insert(place, fake_info); + capture_information.push((place, fake_info)); } } @@ -384,76 +383,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_clause: hir::CaptureBy, capture_information: InferredCaptureInformation<'tcx>, ) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'tcx>)>) { - let mut processed: InferredCaptureInformation<'tcx> = Default::default(); - let mut closure_kind = ty::ClosureKind::LATTICE_BOTTOM; let mut origin: Option<(Span, Place<'tcx>)> = None; - for (place, mut capture_info) in capture_information { - // Apply rules for safety before inferring closure kind - let (place, capture_kind) = - restrict_capture_precision(place, capture_info.capture_kind); - capture_info.capture_kind = capture_kind; + let processed = capture_information + .into_iter() + .map(|(place, mut capture_info)| { + // Apply rules for safety before inferring closure kind + let (place, capture_kind) = + restrict_capture_precision(place, capture_info.capture_kind); - let (place, capture_kind) = - truncate_capture_for_optimization(place, capture_info.capture_kind); - capture_info.capture_kind = capture_kind; + let (place, capture_kind) = truncate_capture_for_optimization(place, capture_kind); - let usage_span = if let Some(usage_expr) = capture_info.path_expr_id { - self.tcx.hir().span(usage_expr) - } else { - unreachable!() - }; + let usage_span = if let Some(usage_expr) = capture_info.path_expr_id { + self.tcx.hir().span(usage_expr) + } else { + unreachable!() + }; - let updated = match capture_info.capture_kind { - ty::UpvarCapture::ByValue => match closure_kind { - ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { - (ty::ClosureKind::FnOnce, Some((usage_span, place.clone()))) - } - // If closure is already FnOnce, don't update - ty::ClosureKind::FnOnce => (closure_kind, origin), - }, + let updated = match capture_kind { + ty::UpvarCapture::ByValue => match closure_kind { + ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { + (ty::ClosureKind::FnOnce, Some((usage_span, place.clone()))) + } + // If closure is already FnOnce, don't update + ty::ClosureKind::FnOnce => (closure_kind, origin.take()), + }, - ty::UpvarCapture::ByRef( - ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, - ) => { - match closure_kind { - ty::ClosureKind::Fn => { - (ty::ClosureKind::FnMut, Some((usage_span, place.clone()))) + ty::UpvarCapture::ByRef( + ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, + ) => { + match closure_kind { + ty::ClosureKind::Fn => { + (ty::ClosureKind::FnMut, Some((usage_span, place.clone()))) + } + // Don't update the origin + ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce => { + (closure_kind, origin.take()) + } } - // Don't update the origin - ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce => (closure_kind, origin), } - } - - _ => (closure_kind, origin), - }; - closure_kind = updated.0; - origin = updated.1; + _ => (closure_kind, origin.take()), + }; - let (place, capture_kind) = match capture_clause { - hir::CaptureBy::Value => adjust_for_move_closure(place, capture_info.capture_kind), - hir::CaptureBy::Ref => { - adjust_for_non_move_closure(place, capture_info.capture_kind) - } - }; + closure_kind = updated.0; + origin = updated.1; - // This restriction needs to be applied after we have handled adjustments for `move` - // closures. We want to make sure any adjustment that might make us move the place into - // the closure gets handled. - let (place, capture_kind) = - restrict_precision_for_drop_types(self, place, capture_kind, usage_span); + let (place, capture_kind) = match capture_clause { + hir::CaptureBy::Value => adjust_for_move_closure(place, capture_kind), + hir::CaptureBy::Ref => adjust_for_non_move_closure(place, capture_kind), + }; - capture_info.capture_kind = capture_kind; + // This restriction needs to be applied after we have handled adjustments for `move` + // closures. We want to make sure any adjustment that might make us move the place into + // the closure gets handled. + let (place, capture_kind) = + restrict_precision_for_drop_types(self, place, capture_kind, usage_span); - let capture_info = if let Some(existing) = processed.get(&place) { - determine_capture_info(*existing, capture_info) - } else { - capture_info - }; - processed.insert(place, capture_info); - } + capture_info.capture_kind = capture_kind; + (place, capture_info) + }) + .collect(); (processed, closure_kind, origin) } @@ -609,8 +600,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !descendant_found { for possible_ancestor in min_cap_list.iter_mut() { match determine_place_ancestry_relation(&place, &possible_ancestor.place) { + PlaceAncestryRelation::SamePlace => { + ancestor_found = true; + possible_ancestor.info = determine_capture_info( + possible_ancestor.info, + updated_capture_info, + ); + + // Only one related place will be in the list. + break; + } // current place is descendant of possible_ancestor - PlaceAncestryRelation::Descendant | PlaceAncestryRelation::SamePlace => { + PlaceAncestryRelation::Descendant => { ancestor_found = true; let backup_path_expr_id = possible_ancestor.info.path_expr_id; @@ -630,7 +631,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we need to keep the ancestor's `path_expr_id` possible_ancestor.info.path_expr_id = backup_path_expr_id; - // Only one ancestor of the current place will be in the list. + // Only one related place will be in the list. break; } _ => {} @@ -1532,7 +1533,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn log_capture_analysis_first_pass( &self, closure_def_id: rustc_hir::def_id::DefId, - capture_information: &FxIndexMap, ty::CaptureInfo>, + capture_information: &InferredCaptureInformation<'tcx>, closure_span: Span, ) { if self.should_log_capture_analysis(closure_def_id) { @@ -1759,20 +1760,6 @@ struct InferBorrowKind<'a, 'tcx> { fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>, } -impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { - fn adjust_capture_info(&mut self, place: Place<'tcx>, capture_info: ty::CaptureInfo) { - match self.capture_information.get_mut(&place) { - Some(curr_info) => { - *curr_info = determine_capture_info(*curr_info, capture_info); - } - None => { - debug!("Capturing new place {:?}, capture_info={:?}", place, capture_info); - self.capture_information.insert(place, capture_info); - } - } - } -} - impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId) { let PlaceBase::Upvar(_) = place.base else { return }; @@ -1797,14 +1784,14 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); - self.adjust_capture_info( + self.capture_information.push(( place_with_id.place.clone(), ty::CaptureInfo { capture_kind_expr_id: Some(diag_expr_id), path_expr_id: Some(diag_expr_id), capture_kind: ty::UpvarCapture::ByValue, }, - ); + )); } #[instrument(skip(self), level = "debug")] @@ -1835,14 +1822,14 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow); } - self.adjust_capture_info( + self.capture_information.push(( place, ty::CaptureInfo { capture_kind_expr_id: Some(diag_expr_id), path_expr_id: Some(diag_expr_id), capture_kind, }, - ); + )); } #[instrument(skip(self), level = "debug")] diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs index 7a4b21f022365..2bcbd792e3a83 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -15,6 +15,7 @@ fn main() { //~^ NOTE: Capturing m[] -> MutBorrow //~| NOTE: Min Capture m[] -> MutBorrow m[1] += 40; + //~^ NOTE: Capturing m[] -> MutBorrow }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr index 69ec53447b8a6..129b26456ce1d 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr @@ -15,7 +15,7 @@ LL | | LL | | LL | | m[0] += 10; ... | -LL | | m[1] += 40; +LL | | LL | | }; | |_____^ | @@ -24,6 +24,11 @@ note: Capturing m[] -> MutBorrow | LL | m[0] += 10; | ^ +note: Capturing m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:17:9 + | +LL | m[1] += 40; + | ^ error: Min Capture analysis includes: --> $DIR/arrays-completely-captured.rs:11:5 @@ -33,7 +38,7 @@ LL | | LL | | LL | | m[0] += 10; ... | -LL | | m[1] += 40; +LL | | LL | | }; | |_____^ | diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs index 9918802334ecc..6c65a7bf87b96 100644 --- a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs @@ -15,6 +15,8 @@ fn arrays() { //~| ERROR: Min Capture analysis includes: let [a, b, .., e] = arr; //~^ NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Capturing arr[Index] -> ByValue //~| NOTE: Min Capture arr[] -> ByValue assert_eq!(a, "A"); assert_eq!(b, "B"); diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr index b53adb5248161..44fbe6d8158f2 100644 --- a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr @@ -8,7 +8,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/destructure_patterns.rs:36:13 + --> $DIR/destructure_patterns.rs:38:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/destructure_patterns.rs:56:13 + --> $DIR/destructure_patterns.rs:58:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -42,6 +42,16 @@ note: Capturing arr[Index] -> ByValue | LL | let [a, b, .., e] = arr; | ^^^ +note: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:16:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ +note: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:16:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ error: Min Capture analysis includes: --> $DIR/destructure_patterns.rs:13:5 @@ -62,7 +72,7 @@ LL | let [a, b, .., e] = arr; | ^^^ error: First Pass analysis includes: - --> $DIR/destructure_patterns.rs:39:5 + --> $DIR/destructure_patterns.rs:41:5 | LL | / || { LL | | @@ -74,18 +84,18 @@ LL | | }; | |_____^ | note: Capturing p[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:42:58 + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ note: Capturing p[(2, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:42:58 + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ error: Min Capture analysis includes: - --> $DIR/destructure_patterns.rs:39:5 + --> $DIR/destructure_patterns.rs:41:5 | LL | / || { LL | | @@ -97,18 +107,18 @@ LL | | }; | |_____^ | note: Min Capture p[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:42:58 + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ note: Min Capture p[(2, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:42:58 + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ error: First Pass analysis includes: - --> $DIR/destructure_patterns.rs:59:5 + --> $DIR/destructure_patterns.rs:61:5 | LL | / || { LL | | @@ -120,23 +130,23 @@ LL | | }; | |_____^ | note: Capturing t[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ note: Capturing t[(1, 0)] -> ImmBorrow - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ note: Capturing t[(2, 0),(0, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ error: Min Capture analysis includes: - --> $DIR/destructure_patterns.rs:59:5 + --> $DIR/destructure_patterns.rs:61:5 | LL | / || { LL | | @@ -148,17 +158,17 @@ LL | | }; | |_____^ | note: Min Capture t[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ note: Min Capture t[(1, 0)] -> ImmBorrow - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ note: Min Capture t[(2, 0),(0, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:62:54 + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs new file mode 100644 index 0000000000000..46b54846e32eb --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.rs @@ -0,0 +1,25 @@ +// edition:2021 + +// Test that we point to the correct location that results a union being captured. +// Union is special because it can't be disjointly captured. + +union A { + y: u32, + x: (), +} + +fn main() { + let mut a = A { y: 1 }; + let mut c = || { + //~^ borrow of `a.y` occurs here + let _ = unsafe { &a.y }; + let _ = &mut a; + //~^ borrow occurs due to use in closure + let _ = unsafe { &mut a.y }; + }; + a.y = 1; + //~^ cannot assign to `a.y` because it is borrowed [E0506] + //~| assignment to borrowed `a.y` occurs here + c(); + //~^ borrow later used here +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr new file mode 100644 index 0000000000000..7c34e2336c867 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/union.stderr @@ -0,0 +1,18 @@ +error[E0506]: cannot assign to `a.y` because it is borrowed + --> $DIR/union.rs:20:5 + | +LL | let mut c = || { + | -- borrow of `a.y` occurs here +... +LL | let _ = &mut a; + | - borrow occurs due to use in closure +... +LL | a.y = 1; + | ^^^^^^^ assignment to borrowed `a.y` occurs here +... +LL | c(); + | - borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs index f6775b3a3a5ac..22eae744b8080 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -40,6 +40,7 @@ fn main() { //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow c2(); println!("{}", p.y); + //~^ NOTE: Capturing p[(1, 0)] -> ImmBorrow }; c1(); diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr index 013bc74e67e1e..a50d0c6a182bc 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -58,7 +58,7 @@ LL | | LL | | LL | | println!("{}", p.x); ... | -LL | | println!("{}", p.y); +LL | | LL | | }; | |_____^ | @@ -72,6 +72,11 @@ note: Capturing p[(1, 0)] -> MutBorrow | LL | || p.y += incr; | ^^^ +note: Capturing p[(1, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:42:24 + | +LL | println!("{}", p.y); + | ^^^ error: Min Capture analysis includes: --> $DIR/nested-closure.rs:22:5 @@ -81,7 +86,7 @@ LL | | LL | | LL | | println!("{}", p.x); ... | -LL | | println!("{}", p.y); +LL | | LL | | }; | |_____^ | diff --git a/src/test/ui/closures/2229_closure_analysis/repr_packed.rs b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs index 7d472ad020f29..3ed780f51c73b 100644 --- a/src/test/ui/closures/2229_closure_analysis/repr_packed.rs +++ b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs @@ -48,6 +48,7 @@ fn test_alignment_affected() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: let z1: &String = &foo.x; + //~^ NOTE: Capturing foo[] -> ImmBorrow let z2: &mut u16 = &mut foo.y; //~^ NOTE: Capturing foo[] -> MutBorrow //~| NOTE: Min Capture foo[] -> MutBorrow diff --git a/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr b/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr index 405f66210aa55..580061ebc6ed9 100644 --- a/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr +++ b/src/test/ui/closures/2229_closure_analysis/repr_packed.stderr @@ -17,7 +17,7 @@ LL | let mut c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/repr_packed.rs:78:13 + --> $DIR/repr_packed.rs:79:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,8 +83,13 @@ LL | | println!("({}, {})", z1, z2); LL | | }; | |_____^ | +note: Capturing foo[] -> ImmBorrow + --> $DIR/repr_packed.rs:50:28 + | +LL | let z1: &String = &foo.x; + | ^^^^^ note: Capturing foo[] -> MutBorrow - --> $DIR/repr_packed.rs:51:33 + --> $DIR/repr_packed.rs:52:33 | LL | let z2: &mut u16 = &mut foo.y; | ^^^^^ @@ -102,13 +107,13 @@ LL | | }; | |_____^ | note: Min Capture foo[] -> MutBorrow - --> $DIR/repr_packed.rs:51:33 + --> $DIR/repr_packed.rs:52:33 | LL | let z2: &mut u16 = &mut foo.y; | ^^^^^ error: First Pass analysis includes: - --> $DIR/repr_packed.rs:81:5 + --> $DIR/repr_packed.rs:82:5 | LL | / || { LL | | @@ -120,18 +125,18 @@ LL | | }; | |_____^ | note: Capturing foo[] -> ImmBorrow - --> $DIR/repr_packed.rs:84:24 + --> $DIR/repr_packed.rs:85:24 | LL | println!("{}", foo.x); | ^^^^^ note: Capturing foo[(0, 0)] -> ByValue - --> $DIR/repr_packed.rs:88:18 + --> $DIR/repr_packed.rs:89:18 | LL | let _z = foo.x; | ^^^^^ error: Min Capture analysis includes: - --> $DIR/repr_packed.rs:81:5 + --> $DIR/repr_packed.rs:82:5 | LL | / || { LL | | @@ -143,7 +148,7 @@ LL | | }; | |_____^ | note: Min Capture foo[] -> ByValue - --> $DIR/repr_packed.rs:84:24 + --> $DIR/repr_packed.rs:85:24 | LL | println!("{}", foo.x); | ^^^^^ foo[] used here