Skip to content

Commit 56fa0c9

Browse files
committed
Track row intersections
1 parent d8b44d2 commit 56fa0c9

File tree

1 file changed

+36
-18
lines changed

1 file changed

+36
-18
lines changed

compiler/rustc_pattern_analysis/src/usefulness.rs

+36-18
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,7 @@
712712
//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
713713
//! reason not to, for example if they crucially depend on a particular feature like `or_patterns`.
714714
715+
use rustc_index::bit_set::BitSet;
715716
use smallvec::{smallvec, SmallVec};
716717
use std::fmt;
717718

@@ -915,6 +916,11 @@ struct MatrixRow<'p, Cx: TypeCx> {
915916
/// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful.
916917
/// This is reset to `false` when specializing.
917918
useful: bool,
919+
/// Tracks which rows above this one have an intersection with this one, i.e. such that there is
920+
/// a value that matches both rows.
921+
/// Note: Because of relevancy we may miss some intersections. The intersections we do find are
922+
/// correct.
923+
intersects: BitSet<usize>,
918924
}
919925

920926
impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
@@ -942,6 +948,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
942948
parent_row: self.parent_row,
943949
is_under_guard: self.is_under_guard,
944950
useful: false,
951+
intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`.
945952
})
946953
}
947954

@@ -960,6 +967,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
960967
parent_row,
961968
is_under_guard: self.is_under_guard,
962969
useful: false,
970+
intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`.
963971
}
964972
}
965973
}
@@ -998,13 +1006,15 @@ struct Matrix<'p, Cx: TypeCx> {
9981006
impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
9991007
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
10001008
/// expands it. Internal method, prefer [`Matrix::new`].
1001-
fn expand_and_push(&mut self, row: MatrixRow<'p, Cx>) {
1009+
fn expand_and_push(&mut self, mut row: MatrixRow<'p, Cx>) {
10021010
if !row.is_empty() && row.head().is_or_pat() {
10031011
// Expand nested or-patterns.
1004-
for new_row in row.expand_or_pat() {
1012+
for mut new_row in row.expand_or_pat() {
1013+
new_row.intersects = BitSet::new_empty(self.rows.len());
10051014
self.rows.push(new_row);
10061015
}
10071016
} else {
1017+
row.intersects = BitSet::new_empty(self.rows.len());
10081018
self.rows.push(row);
10091019
}
10101020
}
@@ -1024,9 +1034,10 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
10241034
for (row_id, arm) in arms.iter().enumerate() {
10251035
let v = MatrixRow {
10261036
pats: PatStack::from_pattern(arm.pat),
1027-
parent_row: row_id, // dummy, we won't read it
1037+
parent_row: row_id, // dummy, we don't read it
10281038
is_under_guard: arm.has_guard,
10291039
useful: false,
1040+
intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`.
10301041
};
10311042
matrix.expand_and_push(v);
10321043
}
@@ -1354,21 +1365,19 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
13541365
let Some(ty) = matrix.head_ty() else {
13551366
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
13561367
// A row is useful iff it has no (unguarded) rows above it.
1357-
for row in matrix.rows_mut() {
1358-
// All rows are useful until they're not.
1359-
row.useful = true;
1360-
// When there's an unguarded row, the match is exhaustive and any subsequent row is not
1361-
// useful.
1362-
if !row.is_under_guard {
1363-
return WitnessMatrix::empty();
1364-
}
1368+
let mut useful = true; // Whether the next row is useful.
1369+
for (i, row) in matrix.rows_mut().enumerate() {
1370+
row.useful = useful;
1371+
row.intersects.insert_range(0..i);
1372+
// The next rows stays useful if this one is under a guard.
1373+
useful &= row.is_under_guard;
13651374
}
1366-
// No (unguarded) rows, so the match is not exhaustive. We return a new witness unless
1367-
// irrelevant.
1368-
return if matrix.wildcard_row_is_relevant {
1375+
return if useful && matrix.wildcard_row_is_relevant {
1376+
// The wildcard row is useful; the match is non-exhaustive.
13691377
WitnessMatrix::unit_witness()
13701378
} else {
1371-
// We choose to not report anything here; see at the top for details.
1379+
// Either the match is exhaustive, or we choose not to report anything because of
1380+
// relevancy. See at the top for details.
13721381
WitnessMatrix::empty()
13731382
};
13741383
};
@@ -1429,10 +1438,19 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
14291438
// Accumulate the found witnesses.
14301439
ret.extend(witnesses);
14311440

1432-
// A parent row is useful if any of its children is.
14331441
for child_row in spec_matrix.rows() {
1434-
let parent_row = &mut matrix.rows[child_row.parent_row];
1435-
parent_row.useful = parent_row.useful || child_row.useful;
1442+
let parent_row_id = child_row.parent_row;
1443+
let parent_row = &mut matrix.rows[parent_row_id];
1444+
// A parent row is useful if any of its children is.
1445+
parent_row.useful |= child_row.useful;
1446+
for child_intersection in child_row.intersects.iter() {
1447+
// Convert the intersecting ids into ids for the parent matrix.
1448+
let parent_intersection = spec_matrix.rows[child_intersection].parent_row;
1449+
// Note: self-intersection can happen with or-patterns.
1450+
if parent_intersection != parent_row_id {
1451+
parent_row.intersects.insert(parent_intersection);
1452+
}
1453+
}
14361454
}
14371455
}
14381456

0 commit comments

Comments
 (0)