diff options
| author | bors <bors@rust-lang.org> | 2021-09-01 06:13:15 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2021-09-01 06:13:15 +0000 |
| commit | 608b5e1c209ffb4d6d0cf83817c823b12bbb7659 (patch) | |
| tree | ca7f8941b59b8cec5b0ff28af68f7b581ea8cdd7 | |
| parent | c4f26b15e37101c54829efab456922a53e3103ad (diff) | |
| parent | e340a0e249c2efc069235c9db47e47c3e136570c (diff) | |
| download | rust-608b5e1c209ffb4d6d0cf83817c823b12bbb7659.tar.gz rust-608b5e1c209ffb4d6d0cf83817c823b12bbb7659.zip | |
Auto merge of #88272 - willcrichton:mutable-sparse-matrix, r=ecstatic-morse
Add bit removal methods to SparseBitMatrix and factor *BitSet relational methods into more extensible trait I need the ability to clear the bits out of a row from `SparseBitMatrix`. Currently, all the mutating methods only allow insertion of bits, and there is no way to get access to the underlying data. One approach is simply to make `ensure_row` public, since it grants `&mut` access to the underlying `HybridBitSet`. This PR adds the `pub` modifier. However, presumably this method was private for a reason, so I'm open to other designs. I would prefer general mutable access to the rows, because that way I can add many mutating operations (`clear`, `intersect`, etc.) without filing a PR each time :-) r? `@ecstatic-morse`
| -rw-r--r-- | compiler/rustc_index/src/bit_set.rs | 413 | ||||
| -rw-r--r-- | compiler/rustc_index/src/bit_set/tests.rs | 94 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/borrow_check/region_infer/values.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_mir/src/transform/generator.rs | 4 |
4 files changed, 386 insertions, 129 deletions
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index df777502c44..aeb3f9970ab 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -16,6 +16,43 @@ pub type Word = u64; pub const WORD_BYTES: usize = mem::size_of::<Word>(); pub const WORD_BITS: usize = WORD_BYTES * 8; +pub trait BitRelations<Rhs> { + fn union(&mut self, other: &Rhs) -> bool; + fn subtract(&mut self, other: &Rhs) -> bool; + fn intersect(&mut self, other: &Rhs) -> bool; +} + +macro_rules! bit_relations_inherent_impls { + () => { + /// Sets `self = self | other` and returns `true` if `self` changed + /// (i.e., if new bits were added). + pub fn union<Rhs>(&mut self, other: &Rhs) -> bool + where + Self: BitRelations<Rhs>, + { + <Self as BitRelations<Rhs>>::union(self, other) + } + + /// Sets `self = self - other` and returns `true` if `self` changed. + /// (i.e., if any bits were removed). + pub fn subtract<Rhs>(&mut self, other: &Rhs) -> bool + where + Self: BitRelations<Rhs>, + { + <Self as BitRelations<Rhs>>::subtract(self, other) + } + + /// Sets `self = self & other` and return `true` if `self` changed. + /// (i.e., if any bits were removed). + pub fn intersect<Rhs>(&mut self, other: &Rhs) -> bool + where + Self: BitRelations<Rhs>, + { + <Self as BitRelations<Rhs>>::intersect(self, other) + } + }; +} + /// A fixed-size bitset type with a dense representation. /// /// NOTE: Use [`GrowableBitSet`] if you need support for resizing after creation. @@ -134,25 +171,6 @@ impl<T: Idx> BitSet<T> { new_word != word } - /// Sets `self = self | other` and returns `true` if `self` changed - /// (i.e., if new bits were added). - pub fn union(&mut self, other: &impl UnionIntoBitSet<T>) -> bool { - other.union_into(self) - } - - /// Sets `self = self - other` and returns `true` if `self` changed. - /// (i.e., if any bits were removed). - pub fn subtract(&mut self, other: &impl SubtractFromBitSet<T>) -> bool { - other.subtract_from(self) - } - - /// Sets `self = self & other` and return `true` if `self` changed. - /// (i.e., if any bits were removed). - pub fn intersect(&mut self, other: &BitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut self.words, &other.words, |a, b| a & b) - } - /// Gets a slice of the underlying words. pub fn words(&self) -> &[Word] { &self.words @@ -208,33 +226,208 @@ impl<T: Idx> BitSet<T> { not_already } + + bit_relations_inherent_impls! {} } -/// This is implemented by all the bitsets so that BitSet::union() can be -/// passed any type of bitset. -pub trait UnionIntoBitSet<T: Idx> { - // Performs `other = other | self`. - fn union_into(&self, other: &mut BitSet<T>) -> bool; +// dense REL dense +impl<T: Idx> BitRelations<BitSet<T>> for BitSet<T> { + fn union(&mut self, other: &BitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size); + bitwise(&mut self.words, &other.words, |a, b| a | b) + } + + fn subtract(&mut self, other: &BitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size); + bitwise(&mut self.words, &other.words, |a, b| a & !b) + } + + fn intersect(&mut self, other: &BitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size); + bitwise(&mut self.words, &other.words, |a, b| a & b) + } } -/// This is implemented by all the bitsets so that BitSet::subtract() can be -/// passed any type of bitset. -pub trait SubtractFromBitSet<T: Idx> { - // Performs `other = other - self`. - fn subtract_from(&self, other: &mut BitSet<T>) -> bool; +// Applies a function to mutate a bitset, and returns true if any +// of the applications return true +fn sequential_update<T: Idx>( + mut self_update: impl FnMut(T) -> bool, + it: impl Iterator<Item = T>, +) -> bool { + let mut changed = false; + for elem in it { + changed |= self_update(elem); + } + changed } -impl<T: Idx> UnionIntoBitSet<T> for BitSet<T> { - fn union_into(&self, other: &mut BitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut other.words, &self.words, |a, b| a | b) +// Optimization of intersection for SparseBitSet that's generic +// over the RHS +fn sparse_intersect<T: Idx>( + set: &mut SparseBitSet<T>, + other_contains: impl Fn(&T) -> bool, +) -> bool { + let size = set.elems.len(); + set.elems.retain(|elem| other_contains(elem)); + set.elems.len() != size +} + +// Optimization of dense/sparse intersection. The resulting set is +// guaranteed to be at most the size of the sparse set, and hence can be +// represented as a sparse set. Therefore the sparse set is copied and filtered, +// then returned as the new set. +fn dense_sparse_intersect<T: Idx>( + dense: &BitSet<T>, + sparse: &SparseBitSet<T>, +) -> (SparseBitSet<T>, bool) { + let mut sparse_copy = sparse.clone(); + sparse_intersect(&mut sparse_copy, |el| dense.contains(*el)); + let n = sparse_copy.len(); + (sparse_copy, n != dense.count()) +} + +// hybrid REL dense +impl<T: Idx> BitRelations<BitSet<T>> for HybridBitSet<T> { + fn union(&mut self, other: &BitSet<T>) -> bool { + assert_eq!(self.domain_size(), other.domain_size); + match self { + HybridBitSet::Sparse(sparse) => { + // `self` is sparse and `other` is dense. To + // merge them, we have two available strategies: + // * Densify `self` then merge other + // * Clone other then integrate bits from `self` + // The second strategy requires dedicated method + // since the usual `union` returns the wrong + // result. In the dedicated case the computation + // is slightly faster if the bits of the sparse + // bitset map to only few words of the dense + // representation, i.e. indices are near each + // other. + // + // Benchmarking seems to suggest that the second + // option is worth it. + let mut new_dense = other.clone(); + let changed = new_dense.reverse_union_sparse(sparse); + *self = HybridBitSet::Dense(new_dense); + changed + } + + HybridBitSet::Dense(dense) => dense.union(other), + } + } + + fn subtract(&mut self, other: &BitSet<T>) -> bool { + assert_eq!(self.domain_size(), other.domain_size); + match self { + HybridBitSet::Sparse(sparse) => { + sequential_update(|elem| sparse.remove(elem), other.iter()) + } + HybridBitSet::Dense(dense) => dense.subtract(other), + } + } + + fn intersect(&mut self, other: &BitSet<T>) -> bool { + assert_eq!(self.domain_size(), other.domain_size); + match self { + HybridBitSet::Sparse(sparse) => sparse_intersect(sparse, |elem| other.contains(*elem)), + HybridBitSet::Dense(dense) => dense.intersect(other), + } } } -impl<T: Idx> SubtractFromBitSet<T> for BitSet<T> { - fn subtract_from(&self, other: &mut BitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut other.words, &self.words, |a, b| a & !b) +// dense REL hybrid +impl<T: Idx> BitRelations<HybridBitSet<T>> for BitSet<T> { + fn union(&mut self, other: &HybridBitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size()); + match other { + HybridBitSet::Sparse(sparse) => { + sequential_update(|elem| self.insert(elem), sparse.iter().cloned()) + } + HybridBitSet::Dense(dense) => self.union(dense), + } + } + + fn subtract(&mut self, other: &HybridBitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size()); + match other { + HybridBitSet::Sparse(sparse) => { + sequential_update(|elem| self.remove(elem), sparse.iter().cloned()) + } + HybridBitSet::Dense(dense) => self.subtract(dense), + } + } + + fn intersect(&mut self, other: &HybridBitSet<T>) -> bool { + assert_eq!(self.domain_size, other.domain_size()); + match other { + HybridBitSet::Sparse(sparse) => { + let (updated, changed) = dense_sparse_intersect(self, sparse); + + // We can't directly assign the SparseBitSet to the BitSet, and + // doing `*self = updated.to_dense()` would cause a drop / reallocation. Instead, + // the BitSet is cleared and `updated` is copied into `self`. + self.clear(); + for elem in updated.iter() { + self.insert(*elem); + } + changed + } + HybridBitSet::Dense(dense) => self.intersect(dense), + } + } +} + +// hybrid REL hybrid +impl<T: Idx> BitRelations<HybridBitSet<T>> for HybridBitSet<T> { + fn union(&mut self, other: &HybridBitSet<T>) -> bool { + assert_eq!(self.domain_size(), other.domain_size()); + match self { + HybridBitSet::Sparse(_) => { + match other { + HybridBitSet::Sparse(other_sparse) => { + // Both sets are sparse. Add the elements in + // `other_sparse` to `self` one at a time. This + // may or may not cause `self` to be densified. + let mut changed = false; + for elem in other_sparse.iter() { + changed |= self.insert(*elem); + } + changed + } + + HybridBitSet::Dense(other_dense) => self.union(other_dense), + } + } + + HybridBitSet::Dense(self_dense) => self_dense.union(other), + } + } + + fn subtract(&mut self, other: &HybridBitSet<T>) -> bool { + assert_eq!(self.domain_size(), other.domain_size()); + match self { + HybridBitSet::Sparse(self_sparse) => { + sequential_update(|elem| self_sparse.remove(elem), other.iter()) + } + HybridBitSet::Dense(self_dense) => self_dense.subtract(other), + } + } + + fn intersect(&mut self, other: &HybridBitSet<T>) -> bool { + assert_eq!(self.domain_size(), other.domain_size()); + match self { + HybridBitSet::Sparse(self_sparse) => { + sparse_intersect(self_sparse, |elem| other.contains(*elem)) + } + HybridBitSet::Dense(self_dense) => match other { + HybridBitSet::Sparse(other_sparse) => { + let (updated, changed) = dense_sparse_intersect(self_dense, other_sparse); + *self = HybridBitSet::Sparse(updated); + changed + } + HybridBitSet::Dense(other_dense) => self_dense.intersect(other_dense), + }, + } } } @@ -441,28 +634,8 @@ impl<T: Idx> SparseBitSet<T> { fn iter(&self) -> slice::Iter<'_, T> { self.elems.iter() } -} -impl<T: Idx> UnionIntoBitSet<T> for SparseBitSet<T> { - fn union_into(&self, other: &mut BitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size); - let mut changed = false; - for elem in self.iter() { - changed |= other.insert(*elem); - } - changed - } -} - -impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> { - fn subtract_from(&self, other: &mut BitSet<T>) -> bool { - assert_eq!(self.domain_size, other.domain_size); - let mut changed = false; - for elem in self.iter() { - changed |= other.remove(*elem); - } - changed - } + bit_relations_inherent_impls! {} } /// A fixed-size bitset type with a hybrid representation: sparse when there @@ -579,48 +752,6 @@ impl<T: Idx> HybridBitSet<T> { } } - pub fn union(&mut self, other: &HybridBitSet<T>) -> bool { - match self { - HybridBitSet::Sparse(self_sparse) => { - match other { - HybridBitSet::Sparse(other_sparse) => { - // Both sets are sparse. Add the elements in - // `other_sparse` to `self` one at a time. This - // may or may not cause `self` to be densified. - assert_eq!(self.domain_size(), other.domain_size()); - let mut changed = false; - for elem in other_sparse.iter() { - changed |= self.insert(*elem); - } - changed - } - HybridBitSet::Dense(other_dense) => { - // `self` is sparse and `other` is dense. To - // merge them, we have two available strategies: - // * Densify `self` then merge other - // * Clone other then integrate bits from `self` - // The second strategy requires dedicated method - // since the usual `union` returns the wrong - // result. In the dedicated case the computation - // is slightly faster if the bits of the sparse - // bitset map to only few words of the dense - // representation, i.e. indices are near each - // other. - // - // Benchmarking seems to suggest that the second - // option is worth it. - let mut new_dense = other_dense.clone(); - let changed = new_dense.reverse_union_sparse(self_sparse); - *self = HybridBitSet::Dense(new_dense); - changed - } - } - } - - HybridBitSet::Dense(self_dense) => self_dense.union(other), - } - } - /// Converts to a dense set, consuming itself in the process. pub fn to_dense(self) -> BitSet<T> { match self { @@ -635,24 +766,8 @@ impl<T: Idx> HybridBitSet<T> { HybridBitSet::Dense(dense) => HybridIter::Dense(dense.iter()), } } -} - -impl<T: Idx> UnionIntoBitSet<T> for HybridBitSet<T> { - fn union_into(&self, other: &mut BitSet<T>) -> bool { - match self { - HybridBitSet::Sparse(sparse) => sparse.union_into(other), - HybridBitSet::Dense(dense) => dense.union_into(other), - } - } -} -impl<T: Idx> SubtractFromBitSet<T> for HybridBitSet<T> { - fn subtract_from(&self, other: &mut BitSet<T>) -> bool { - match self { - HybridBitSet::Sparse(sparse) => sparse.subtract_from(other), - HybridBitSet::Dense(dense) => dense.subtract_from(other), - } - } + bit_relations_inherent_impls! {} } pub enum HybridIter<'a, T: Idx> { @@ -974,6 +1089,26 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { self.ensure_row(row).insert(column) } + /// Sets the cell at `(row, column)` to false. Put another way, delete + /// `column` from the bitset for `row`. Has no effect if `row` does not + /// exist. + /// + /// Returns `true` if this changed the matrix. + pub fn remove(&mut self, row: R, column: C) -> bool { + match self.rows.get_mut(row) { + Some(Some(row)) => row.remove(column), + _ => false, + } + } + + /// Sets all columns at `row` to false. Has no effect if `row` does + /// not exist. + pub fn clear(&mut self, row: R) { + if let Some(Some(row)) = self.rows.get_mut(row) { + row.clear(); + } + } + /// Do the bits from `row` contain `column`? Put another way, is /// the matrix cell at `(row, column)` true? Put yet another way, /// if the matrix represents (transitive) reachability, can @@ -1002,11 +1137,6 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { } } - /// Union a row, `from`, into the `into` row. - pub fn union_into_row(&mut self, into: R, from: &HybridBitSet<C>) -> bool { - self.ensure_row(into).union(from) - } - /// Insert all bits in the given row. pub fn insert_all_into_row(&mut self, row: R) { self.ensure_row(row).insert_all(); @@ -1025,6 +1155,45 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { pub fn row(&self, row: R) -> Option<&HybridBitSet<C>> { if let Some(Some(row)) = self.rows.get(row) { Some(row) } else { None } } + + /// Interescts `row` with `set`. `set` can be either `BitSet` or + /// `HybridBitSet`. Has no effect if `row` does not exist. + /// + /// Returns true if the row was changed. + pub fn intersect_row<Set>(&mut self, row: R, set: &Set) -> bool + where + HybridBitSet<C>: BitRelations<Set>, + { + match self.rows.get_mut(row) { + Some(Some(row)) => row.intersect(set), + _ => false, + } + } + + /// Subtracts `set from `row`. `set` can be either `BitSet` or + /// `HybridBitSet`. Has no effect if `row` does not exist. + /// + /// Returns true if the row was changed. + pub fn subtract_row<Set>(&mut self, row: R, set: &Set) -> bool + where + HybridBitSet<C>: BitRelations<Set>, + { + match self.rows.get_mut(row) { + Some(Some(row)) => row.subtract(set), + _ => false, + } + } + + /// Unions `row` with `set`. `set` can be either `BitSet` or + /// `HybridBitSet`. + /// + /// Returns true if the row was changed. + pub fn union_row<Set>(&mut self, row: R, set: &Set) -> bool + where + HybridBitSet<C>: BitRelations<Set>, + { + self.ensure_row(row).union(set) + } } #[inline] diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs index c11b98e77aa..aebc6d0ddd8 100644 --- a/compiler/rustc_index/src/bit_set/tests.rs +++ b/compiler/rustc_index/src/bit_set/tests.rs @@ -104,18 +104,40 @@ fn hybrid_bitset() { assert!(dense10.superset(&dense10)); // dense + dense (self) assert!(dense256.superset(&dense10)); // dense + dense - let mut hybrid = sparse038; + let mut hybrid = sparse038.clone(); assert!(!sparse01358.union(&hybrid)); // no change assert!(hybrid.union(&sparse01358)); assert!(hybrid.superset(&sparse01358) && sparse01358.superset(&hybrid)); - assert!(!dense10.union(&sparse01358)); assert!(!dense256.union(&dense10)); - let mut dense = dense10; + + // dense / sparse where dense superset sparse + assert!(!dense10.clone().union(&sparse01358)); + assert!(sparse01358.clone().union(&dense10)); + assert!(dense10.clone().intersect(&sparse01358)); + assert!(!sparse01358.clone().intersect(&dense10)); + assert!(dense10.clone().subtract(&sparse01358)); + assert!(sparse01358.clone().subtract(&dense10)); + + // dense / sparse where sparse superset dense + let dense038 = sparse038.to_dense(); + assert!(!sparse01358.clone().union(&dense038)); + assert!(dense038.clone().union(&sparse01358)); + assert!(sparse01358.clone().intersect(&dense038)); + assert!(!dense038.clone().intersect(&sparse01358)); + assert!(sparse01358.clone().subtract(&dense038)); + assert!(dense038.clone().subtract(&sparse01358)); + + let mut dense = dense10.clone(); assert!(dense.union(&dense256)); assert!(dense.superset(&dense256) && dense256.superset(&dense)); assert!(hybrid.union(&dense256)); assert!(hybrid.superset(&dense256) && dense256.superset(&hybrid)); + assert!(!dense10.clone().intersect(&dense256)); + assert!(dense256.clone().intersect(&dense10)); + assert!(dense10.clone().subtract(&dense256)); + assert!(dense256.clone().subtract(&dense10)); + assert_eq!(dense256.iter().count(), 256); let mut dense0 = dense256; for i in 0..256 { @@ -282,6 +304,72 @@ fn sparse_matrix_iter() { assert!(iter.next().is_none()); } +#[test] +fn sparse_matrix_operations() { + let mut matrix: SparseBitMatrix<usize, usize> = SparseBitMatrix::new(100); + matrix.insert(3, 22); + matrix.insert(3, 75); + matrix.insert(2, 99); + matrix.insert(4, 0); + + let mut disjoint: HybridBitSet<usize> = HybridBitSet::new_empty(100); + disjoint.insert(33); + + let mut superset = HybridBitSet::new_empty(100); + superset.insert(22); + superset.insert(75); + superset.insert(33); + + let mut subset = HybridBitSet::new_empty(100); + subset.insert(22); + + // SparseBitMatrix::remove + { + let mut matrix = matrix.clone(); + matrix.remove(3, 22); + assert!(!matrix.row(3).unwrap().contains(22)); + matrix.remove(0, 0); + assert!(matrix.row(0).is_none()); + } + + // SparseBitMatrix::clear + { + let mut matrix = matrix.clone(); + matrix.clear(3); + assert!(!matrix.row(3).unwrap().contains(75)); + matrix.clear(0); + assert!(matrix.row(0).is_none()); + } + + // SparseBitMatrix::intersect_row + { + let mut matrix = matrix.clone(); + assert!(!matrix.intersect_row(3, &superset)); + assert!(matrix.intersect_row(3, &subset)); + matrix.intersect_row(0, &disjoint); + assert!(matrix.row(0).is_none()); + } + + // SparseBitMatrix::subtract_row + { + let mut matrix = matrix.clone(); + assert!(!matrix.subtract_row(3, &disjoint)); + assert!(matrix.subtract_row(3, &subset)); + assert!(matrix.subtract_row(3, &superset)); + matrix.intersect_row(0, &disjoint); + assert!(matrix.row(0).is_none()); + } + + // SparseBitMatrix::union_row + { + let mut matrix = matrix.clone(); + assert!(!matrix.union_row(3, &subset)); + assert!(matrix.union_row(3, &disjoint)); + matrix.union_row(0, &disjoint); + assert!(matrix.row(0).is_some()); + } +} + /// Merge dense hybrid set into empty sparse hybrid set. #[bench] fn union_hybrid_sparse_empty_to_dense(b: &mut Bencher) { diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/values.rs b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs index f247d07e1f0..2864abde002 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/values.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/values.rs @@ -160,7 +160,7 @@ impl<N: Idx> LivenessValues<N> { /// region. Returns whether any of them are newly added. crate fn add_elements(&mut self, row: N, locations: &HybridBitSet<PointIndex>) -> bool { debug!("LivenessValues::add_elements(row={:?}, locations={:?})", row, locations); - self.points.union_into_row(row, locations) + self.points.union_row(row, locations) } /// Adds all the control-flow points to the values for `r`. @@ -294,7 +294,7 @@ impl<N: Idx> RegionValues<N> { /// the region `to` in `self`. crate fn merge_liveness<M: Idx>(&mut self, to: N, from: M, values: &LivenessValues<M>) { if let Some(set) = values.points.row(from) { - self.points.union_into_row(to, set); + self.points.union_row(to, set); } } diff --git a/compiler/rustc_mir/src/transform/generator.rs b/compiler/rustc_mir/src/transform/generator.rs index 963f93a1ace..acdaa5b4568 100644 --- a/compiler/rustc_mir/src/transform/generator.rs +++ b/compiler/rustc_mir/src/transform/generator.rs @@ -626,7 +626,7 @@ fn compute_storage_conflicts( // Locals that are always live or ones that need to be stored across // suspension points are not eligible for overlap. let mut ineligible_locals = always_live_locals.into_inner(); - ineligible_locals.intersect(saved_locals); + ineligible_locals.intersect(&**saved_locals); // Compute the storage conflicts for all eligible locals. let mut visitor = StorageConflictVisitor { @@ -701,7 +701,7 @@ impl<'body, 'tcx, 's> StorageConflictVisitor<'body, 'tcx, 's> { } let mut eligible_storage_live = flow_state.clone(); - eligible_storage_live.intersect(&self.saved_locals); + eligible_storage_live.intersect(&**self.saved_locals); for local in eligible_storage_live.iter() { self.local_conflicts.union_row_with(&eligible_storage_live, local); |
