Track row intersections
This commit is contained in:
parent
d73bd3fb3b
commit
89d01babe6
1 changed files with 36 additions and 18 deletions
|
@ -712,6 +712,7 @@
|
||||||
//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
|
//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
|
||||||
//! reason not to, for example if they crucially depend on a particular feature like `or_patterns`.
|
//! reason not to, for example if they crucially depend on a particular feature like `or_patterns`.
|
||||||
|
|
||||||
|
use rustc_index::bit_set::BitSet;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
@ -911,6 +912,11 @@ struct MatrixRow<'p, Cx: TypeCx> {
|
||||||
/// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful.
|
/// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful.
|
||||||
/// This is reset to `false` when specializing.
|
/// This is reset to `false` when specializing.
|
||||||
useful: bool,
|
useful: bool,
|
||||||
|
/// Tracks which rows above this one have an intersection with this one, i.e. such that there is
|
||||||
|
/// a value that matches both rows.
|
||||||
|
/// Note: Because of relevancy we may miss some intersections. The intersections we do find are
|
||||||
|
/// correct.
|
||||||
|
intersects: BitSet<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
|
impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
|
||||||
|
@ -938,6 +944,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
|
||||||
parent_row: self.parent_row,
|
parent_row: self.parent_row,
|
||||||
is_under_guard: self.is_under_guard,
|
is_under_guard: self.is_under_guard,
|
||||||
useful: false,
|
useful: false,
|
||||||
|
intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`.
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -955,6 +962,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
|
||||||
parent_row,
|
parent_row,
|
||||||
is_under_guard: self.is_under_guard,
|
is_under_guard: self.is_under_guard,
|
||||||
useful: false,
|
useful: false,
|
||||||
|
intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -993,13 +1001,15 @@ struct Matrix<'p, Cx: TypeCx> {
|
||||||
impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
|
impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
|
||||||
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
|
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
|
||||||
/// expands it. Internal method, prefer [`Matrix::new`].
|
/// expands it. Internal method, prefer [`Matrix::new`].
|
||||||
fn expand_and_push(&mut self, row: MatrixRow<'p, Cx>) {
|
fn expand_and_push(&mut self, mut row: MatrixRow<'p, Cx>) {
|
||||||
if !row.is_empty() && row.head().is_or_pat() {
|
if !row.is_empty() && row.head().is_or_pat() {
|
||||||
// Expand nested or-patterns.
|
// Expand nested or-patterns.
|
||||||
for new_row in row.expand_or_pat() {
|
for mut new_row in row.expand_or_pat() {
|
||||||
|
new_row.intersects = BitSet::new_empty(self.rows.len());
|
||||||
self.rows.push(new_row);
|
self.rows.push(new_row);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
row.intersects = BitSet::new_empty(self.rows.len());
|
||||||
self.rows.push(row);
|
self.rows.push(row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1019,9 +1029,10 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
|
||||||
for (row_id, arm) in arms.iter().enumerate() {
|
for (row_id, arm) in arms.iter().enumerate() {
|
||||||
let v = MatrixRow {
|
let v = MatrixRow {
|
||||||
pats: PatStack::from_pattern(arm.pat),
|
pats: PatStack::from_pattern(arm.pat),
|
||||||
parent_row: row_id, // dummy, we won't read it
|
parent_row: row_id, // dummy, we don't read it
|
||||||
is_under_guard: arm.has_guard,
|
is_under_guard: arm.has_guard,
|
||||||
useful: false,
|
useful: false,
|
||||||
|
intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`.
|
||||||
};
|
};
|
||||||
matrix.expand_and_push(v);
|
matrix.expand_and_push(v);
|
||||||
}
|
}
|
||||||
|
@ -1349,21 +1360,19 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
|
||||||
let Some(ty) = matrix.head_ty() else {
|
let Some(ty) = matrix.head_ty() else {
|
||||||
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
|
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
|
||||||
// A row is useful iff it has no (unguarded) rows above it.
|
// A row is useful iff it has no (unguarded) rows above it.
|
||||||
for row in matrix.rows_mut() {
|
let mut useful = true; // Whether the next row is useful.
|
||||||
// All rows are useful until they're not.
|
for (i, row) in matrix.rows_mut().enumerate() {
|
||||||
row.useful = true;
|
row.useful = useful;
|
||||||
// When there's an unguarded row, the match is exhaustive and any subsequent row is not
|
row.intersects.insert_range(0..i);
|
||||||
// useful.
|
// The next rows stays useful if this one is under a guard.
|
||||||
if !row.is_under_guard {
|
useful &= row.is_under_guard;
|
||||||
return Ok(WitnessMatrix::empty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// No (unguarded) rows, so the match is not exhaustive. We return a new witness unless
|
return if useful && matrix.wildcard_row_is_relevant {
|
||||||
// irrelevant.
|
// The wildcard row is useful; the match is non-exhaustive.
|
||||||
return if matrix.wildcard_row_is_relevant {
|
|
||||||
Ok(WitnessMatrix::unit_witness())
|
Ok(WitnessMatrix::unit_witness())
|
||||||
} else {
|
} else {
|
||||||
// We choose to not report anything here; see at the top for details.
|
// Either the match is exhaustive, or we choose not to report anything because of
|
||||||
|
// relevancy. See at the top for details.
|
||||||
Ok(WitnessMatrix::empty())
|
Ok(WitnessMatrix::empty())
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1424,10 +1433,19 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
|
||||||
// Accumulate the found witnesses.
|
// Accumulate the found witnesses.
|
||||||
ret.extend(witnesses);
|
ret.extend(witnesses);
|
||||||
|
|
||||||
// A parent row is useful if any of its children is.
|
|
||||||
for child_row in spec_matrix.rows() {
|
for child_row in spec_matrix.rows() {
|
||||||
let parent_row = &mut matrix.rows[child_row.parent_row];
|
let parent_row_id = child_row.parent_row;
|
||||||
parent_row.useful = parent_row.useful || child_row.useful;
|
let parent_row = &mut matrix.rows[parent_row_id];
|
||||||
|
// A parent row is useful if any of its children is.
|
||||||
|
parent_row.useful |= child_row.useful;
|
||||||
|
for child_intersection in child_row.intersects.iter() {
|
||||||
|
// Convert the intersecting ids into ids for the parent matrix.
|
||||||
|
let parent_intersection = spec_matrix.rows[child_intersection].parent_row;
|
||||||
|
// Note: self-intersection can happen with or-patterns.
|
||||||
|
if parent_intersection != parent_row_id {
|
||||||
|
parent_row.intersects.insert(parent_intersection);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue