Skip to content

Commit 939eaeb

Browse files
authored
Unrolled build for #153759
Rollup merge of #153759 - Zalathar:chunk-tests, r=wesleywiser Add a suite of ChunkedBitSet union/subtract/intersect test scenarios While working on #153754, I found that these code paths are under-tested.
2 parents f964de4 + 2a87040 commit 939eaeb

1 file changed

Lines changed: 110 additions & 0 deletions

File tree

  • compiler/rustc_index/src/bit_set

compiler/rustc_index/src/bit_set/tests.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,116 @@ fn chunked_bitset() {
362362
);
363363
}
364364

365+
/// Additional helper methods for testing.
366+
impl ChunkedBitSet<usize> {
367+
/// Creates a new `ChunkedBitSet` containing all `i` for which `fill_fn(i)` is true.
368+
fn fill_with(domain_size: usize, fill_fn: impl Fn(usize) -> bool) -> Self {
369+
let mut this = ChunkedBitSet::new_empty(domain_size);
370+
for i in 0..domain_size {
371+
if fill_fn(i) {
372+
this.insert(i);
373+
}
374+
}
375+
this
376+
}
377+
378+
/// Asserts that for each `i` in `0..self.domain_size()`, `self.contains(i) == expected_fn(i)`.
379+
#[track_caller]
380+
fn assert_filled_with(&self, expected_fn: impl Fn(usize) -> bool) {
381+
for i in 0..self.domain_size() {
382+
let expected = expected_fn(i);
383+
assert_eq!(self.contains(i), expected, "i = {i}");
384+
}
385+
}
386+
}
387+
388+
#[test]
389+
fn chunked_bulk_ops() {
390+
struct ChunkedBulkOp {
391+
name: &'static str,
392+
op_fn: fn(&mut ChunkedBitSet<usize>, &ChunkedBitSet<usize>) -> bool,
393+
spec_fn: fn(fn(usize) -> bool, fn(usize) -> bool, usize) -> bool,
394+
}
395+
let ops = &[
396+
ChunkedBulkOp {
397+
name: "union",
398+
op_fn: ChunkedBitSet::union,
399+
spec_fn: |fizz, buzz, i| fizz(i) || buzz(i),
400+
},
401+
ChunkedBulkOp {
402+
name: "subtract",
403+
op_fn: ChunkedBitSet::subtract,
404+
spec_fn: |fizz, buzz, i| fizz(i) && !buzz(i),
405+
},
406+
ChunkedBulkOp {
407+
name: "intersect",
408+
op_fn: ChunkedBitSet::intersect,
409+
spec_fn: |fizz, buzz, i| fizz(i) && buzz(i),
410+
},
411+
];
412+
413+
let domain_sizes = [
414+
CHUNK_BITS / 7, // Smaller than a full chunk.
415+
CHUNK_BITS,
416+
(CHUNK_BITS + CHUNK_BITS / 7), // Larger than a full chunk.
417+
];
418+
419+
for ChunkedBulkOp { name, op_fn, spec_fn } in ops {
420+
for domain_size in domain_sizes {
421+
// If false, use different values for LHS and RHS, to test "fizz op buzz".
422+
// If true, use identical values, to test "fizz op fizz".
423+
for identical in [false, true] {
424+
// If false, make a clone of LHS before doing the op.
425+
// This covers optimizations that depend on whether chunk words are shared or not.
426+
for unique in [false, true] {
427+
// Print the current test case, so that we can see which one failed.
428+
println!(
429+
"Testing op={name}, domain_size={domain_size}, identical={identical}, unique={unique} ..."
430+
);
431+
432+
let fizz_fn = |i| i % 3 == 0;
433+
let buzz_fn = if identical { fizz_fn } else { |i| i % 5 == 0 };
434+
435+
// Check that `fizz op buzz` gives the expected results.
436+
chunked_bulk_ops_test_inner(
437+
domain_size,
438+
unique,
439+
fizz_fn,
440+
buzz_fn,
441+
op_fn,
442+
|i| spec_fn(fizz_fn, buzz_fn, i),
443+
);
444+
}
445+
}
446+
}
447+
}
448+
}
449+
450+
fn chunked_bulk_ops_test_inner(
451+
domain_size: usize,
452+
unique: bool,
453+
fizz_fn: impl Fn(usize) -> bool + Copy,
454+
buzz_fn: impl Fn(usize) -> bool + Copy,
455+
op_fn: impl Fn(&mut ChunkedBitSet<usize>, &ChunkedBitSet<usize>) -> bool,
456+
expected_fn: impl Fn(usize) -> bool + Copy,
457+
) {
458+
// Create two bitsets, "fizz" (LHS) and "buzz" (RHS).
459+
let mut fizz = ChunkedBitSet::fill_with(domain_size, fizz_fn);
460+
let buzz = ChunkedBitSet::fill_with(domain_size, buzz_fn);
461+
462+
// If requested, clone `fizz` so that its word Rcs are not uniquely-owned.
463+
let _cloned = (!unique).then(|| fizz.clone());
464+
465+
// Perform the op (e.g. union/subtract/intersect), and verify that the
466+
// mutated LHS contains exactly the expected values.
467+
let changed = op_fn(&mut fizz, &buzz);
468+
fizz.assert_filled_with(expected_fn);
469+
470+
// Verify that the "changed" return value is correct.
471+
let should_change = (0..domain_size).any(|i| fizz_fn(i) != expected_fn(i));
472+
assert_eq!(changed, should_change);
473+
}
474+
365475
fn with_elements_chunked(elements: &[usize], domain_size: usize) -> ChunkedBitSet<usize> {
366476
let mut s = ChunkedBitSet::new_empty(domain_size);
367477
for &e in elements {

0 commit comments

Comments
 (0)