From ca5edfa724fbf2cc7518f2201d67a364dcb0c6f6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 28 Feb 2024 00:06:02 +0100 Subject: [PATCH] Collect fake borrows ahead of time --- .../rustc_mir_build/src/build/matches/mod.rs | 41 +--------- .../rustc_mir_build/src/build/matches/util.rs | 82 ++++++++++++++++++- 2 files changed, 83 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 7474b4b37d7..3b90856db2c 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -315,7 +315,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // The set of places that we are creating fake borrows of. If there are // no match guards then we don't need any fake borrows, so don't track // them. - let mut fake_borrows = match_has_guard.then(FxIndexSet::default); + let mut fake_borrows = match_has_guard + .then(|| util::FakeBorrowCollector::collect_fake_borrows(self, candidates)); let otherwise_block = self.cfg.start_new_block(); @@ -1373,37 +1374,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { assert!(candidate.pre_binding_block.is_none()); assert!(candidate.subcandidates.is_empty()); - if let Some(fake_borrows) = fake_borrows { - // Insert a borrows of prefixes of places that are bound and are - // behind a dereference projection. - // - // These borrows are taken to avoid situations like the following: - // - // match x[10] { - // _ if { x = &[0]; false } => (), - // y => (), // Out of bounds array access! - // } - // - // match *x { - // // y is bound by reference in the guard and then by copy in the - // // arm, so y is 2 in the arm! - // y if { y == 1 && (x = &2) == () } => y, - // _ => 3, - // } - for Binding { source, .. } in &candidate.bindings { - if let Some(i) = - source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref) - { - let proj_base = &source.projection[..i]; - - fake_borrows.insert(Place { - local: source.local, - projection: self.tcx.mk_place_elems(proj_base), - }); - } - } - } - candidate.pre_binding_block = Some(start_block); let otherwise_block = self.cfg.start_new_block(); if candidate.has_guard { @@ -1656,13 +1626,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => {} } - // Insert a Shallow borrow of any places that is switched on. - if let Some(fb) = fake_borrows - && let Some(resolved_place) = match_place.try_to_place(self) - { - fb.insert(resolved_place); - } - (match_place, test) } diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 637cef29e18..2351f69a914 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -1,6 +1,7 @@ use crate::build::expr::as_place::{PlaceBase, PlaceBuilder}; -use crate::build::matches::{FlatPat, MatchPair, TestCase}; +use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase}; use crate::build::Builder; +use rustc_data_structures::fx::FxIndexSet; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; @@ -260,3 +261,82 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { MatchPair { place, test_case, subpairs, pattern } } } + +pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> { + cx: &'a mut Builder<'b, 'tcx>, + fake_borrows: FxIndexSet>, +} + +impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { + pub(super) fn collect_fake_borrows( + cx: &'a mut Builder<'b, 'tcx>, + candidates: &[&mut Candidate<'_, 'tcx>], + ) -> FxIndexSet> { + let mut collector = Self { cx, fake_borrows: FxIndexSet::default() }; + for candidate in candidates.iter() { + collector.visit_candidate(candidate); + } + collector.fake_borrows + } + + fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) { + for binding in &candidate.bindings { + self.visit_binding(binding); + } + for match_pair in &candidate.match_pairs { + self.visit_match_pair(match_pair); + } + } + + fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'_, 'tcx>) { + for binding in &flat_pat.bindings { + self.visit_binding(binding); + } + for match_pair in &flat_pat.match_pairs { + self.visit_match_pair(match_pair); + } + } + + fn visit_match_pair(&mut self, match_pair: &MatchPair<'_, 'tcx>) { + if let TestCase::Or { pats, .. } = &match_pair.test_case { + for flat_pat in pats.iter() { + self.visit_flat_pat(flat_pat) + } + } else { + // Insert a Shallow borrow of any place that is switched on. + if let Some(resolved_place) = match_pair.place.try_to_place(self.cx) { + self.fake_borrows.insert(resolved_place); + } + + for subpair in &match_pair.subpairs { + self.visit_match_pair(subpair); + } + } + } + + fn visit_binding(&mut self, Binding { source, .. }: &Binding<'tcx>) { + // Insert a borrows of prefixes of places that are bound and are + // behind a dereference projection. + // + // These borrows are taken to avoid situations like the following: + // + // match x[10] { + // _ if { x = &[0]; false } => (), + // y => (), // Out of bounds array access! + // } + // + // match *x { + // // y is bound by reference in the guard and then by copy in the + // // arm, so y is 2 in the arm! + // y if { y == 1 && (x = &2) == () } => y, + // _ => 3, + // } + if let Some(i) = source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref) { + let proj_base = &source.projection[..i]; + self.fake_borrows.insert(Place { + local: source.local, + projection: self.cx.tcx.mk_place_elems(proj_base), + }); + } + } +}