about summary refs log tree commit diff
path: root/compiler/rustc_index
diff options
context:
space:
mode:
authorZalathar <Zalathar@users.noreply.github.com>2025-01-12 12:44:28 +1100
committerZalathar <Zalathar@users.noreply.github.com>2025-01-14 23:49:10 +1100
commite70112caf86dac4a01739538d3e6f3d163e47642 (patch)
treee8284cc108e7e36555f21e9e64b387612e3ecec8 /compiler/rustc_index
parent1a23a6fd8b27943ffdbac2d5e4eb088cdecb06ff (diff)
downloadrust-e70112caf86dac4a01739538d3e6f3d163e47642.tar.gz
rust-e70112caf86dac4a01739538d3e6f3d163e47642.zip
Add `DenseBitSet::union_not`
This is similar to the existing `union`, except that bits in the RHS are
negated before being incorporated into the LHS.

Currently only `DenseBitSet` is supported. Supporting other bitset types is
possible, but non-trivial, and currently isn't needed.
Diffstat (limited to 'compiler/rustc_index')
-rw-r--r--compiler/rustc_index/src/bit_set.rs30
-rw-r--r--compiler/rustc_index/src/bit_set/tests.rs26
2 files changed, 56 insertions, 0 deletions
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index d93707b745d..f12df831cb5 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -281,6 +281,24 @@ impl<T: Idx> DenseBitSet<T> {
     }
 
     bit_relations_inherent_impls! {}
+
+    /// Sets `self = self | !other`.
+    ///
+    /// FIXME: Incorporate this into [`BitRelations`] and fill out
+    /// implementations for other bitset types, if needed.
+    pub fn union_not(&mut self, other: &DenseBitSet<T>) {
+        assert_eq!(self.domain_size, other.domain_size);
+
+        // FIXME(Zalathar): If we were to forcibly _set_ all excess bits before
+        // the bitwise update, and then clear them again afterwards, we could
+        // quickly and accurately detect whether the update changed anything.
+        // But that's only worth doing if there's an actual use-case.
+
+        bitwise(&mut self.words, &other.words, |a, b| a | !b);
+        // The bitwise update `a | !b` can result in the last word containing
+        // out-of-domain bits, so we need to clear them.
+        self.clear_excess_bits();
+    }
 }
 
 // dense REL dense
@@ -1087,6 +1105,18 @@ impl<T: Idx> fmt::Debug for ChunkedBitSet<T> {
     }
 }
 
+/// Sets `out_vec[i] = op(out_vec[i], in_vec[i])` for each index `i` in both
+/// slices. The slices must have the same length.
+///
+/// Returns true if at least one bit in `out_vec` was changed.
+///
+/// ## Warning
+/// Some bitwise operations (e.g. union-not, xor) can set output bits that were
+/// unset in in both inputs. If this happens in the last word/chunk of a bitset,
+/// it can cause the bitset to contain out-of-domain values, which need to
+/// be cleared with `clear_excess_bits_in_final_word`. This also makes the
+/// "changed" return value unreliable, because the change might have only
+/// affected excess bits.
 #[inline]
 fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool
 where
diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs
index 0350740aa81..eaa4aafe721 100644
--- a/compiler/rustc_index/src/bit_set/tests.rs
+++ b/compiler/rustc_index/src/bit_set/tests.rs
@@ -76,6 +76,32 @@ fn union_two_sets() {
 }
 
 #[test]
+fn union_not() {
+    let mut a = DenseBitSet::<usize>::new_empty(100);
+    let mut b = DenseBitSet::<usize>::new_empty(100);
+
+    a.insert(3);
+    a.insert(5);
+    a.insert(80);
+    a.insert(81);
+
+    b.insert(5); // Already in `a`.
+    b.insert(7);
+    b.insert(63);
+    b.insert(81); // Already in `a`.
+    b.insert(90);
+
+    a.union_not(&b);
+
+    // After union-not, `a` should contain all values in the domain, except for
+    // the ones that are in `b` and were _not_ already in `a`.
+    assert_eq!(
+        a.iter().collect::<Vec<_>>(),
+        (0usize..100).filter(|&x| !matches!(x, 7 | 63 | 90)).collect::<Vec<_>>(),
+    );
+}
+
+#[test]
 fn chunked_bitset() {
     let mut b0 = ChunkedBitSet::<usize>::new_empty(0);
     let b0b = b0.clone();