Factor or-pattern expansion

This commit is contained in:
Nadrieril 2021-01-01 22:14:22 +00:00
parent 293af41790
commit 0162d603b3
3 changed files with 58 additions and 37 deletions

View File

@ -385,6 +385,27 @@ impl<'tcx> Pat<'tcx> {
pub(super) fn is_wildcard(&self) -> bool {
matches!(*self.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild)
}
fn is_or_pat(&self) -> bool {
matches!(*self.kind, PatKind::Or { .. })
}
/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
fn expand_or_pat(&self) -> Vec<&Self> {
fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
if let PatKind::Or { pats } = pat.kind.as_ref() {
for pat in pats {
expand(pat, vec);
}
} else {
vec.push(pat)
}
}
let mut pats = Vec::new();
expand(self, &mut pats);
pats
}
}
/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
@ -425,23 +446,14 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
self.pats.iter().copied()
}
// If the first pattern is an or-pattern, expand this pattern. Otherwise, return `None`.
fn expand_or_pat(&self) -> Option<Vec<Self>> {
if self.is_empty() {
None
} else if let PatKind::Or { pats } = &*self.head().kind {
Some(
pats.iter()
.map(|pat| {
let mut new_patstack = PatStack::from_pattern(pat);
new_patstack.pats.extend_from_slice(&self.pats[1..]);
new_patstack
})
.collect(),
)
} else {
None
}
// Recursively expand the first pattern into its subpatterns. Only useful if the pattern is an
// or-pattern. Panics if `self` is empty.
fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = PatStack<'p, 'tcx>> + Captures<'a> {
self.head().expand_or_pat().into_iter().map(move |pat| {
let mut new_patstack = PatStack::from_pattern(pat);
new_patstack.pats.extend_from_slice(&self.pats[1..]);
new_patstack
})
}
/// This computes `S(self.head_ctor(), self)`. See top of the file for explanations.
@ -508,13 +520,12 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
self.patterns.get(0).map(|r| r.len())
}
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it.
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
/// expands it.
fn push(&mut self, row: PatStack<'p, 'tcx>) {
if let Some(rows) = row.expand_or_pat() {
for row in rows {
// We recursively expand the or-patterns of the new rows.
// This is necessary as we might have `0 | (1 | 2)` or e.g., `x @ 0 | x @ (1 | 2)`.
self.push(row)
if !row.is_empty() && row.head().is_or_pat() {
for row in row.expand_or_pat() {
self.patterns.push(row);
}
} else {
self.patterns.push(row);
@ -968,8 +979,9 @@ fn is_useful<'p, 'tcx>(
let pcx = PatCtxt { cx, ty, span: v.head().span, is_top_level };
// If the first pattern is an or-pattern, expand it.
let ret = if let Some(vs) = v.expand_or_pat() {
let ret = if v.head().is_or_pat() {
debug!("expanding or-pattern");
let vs: Vec<_> = v.expand_or_pat().collect();
let subspans: Vec<_> = vs.iter().map(|v| v.head().span).collect();
// We expand the or pattern, trying each of its branches in turn and keeping careful track
// of possible unreachable sub-branches.

View File

@ -53,8 +53,11 @@ fn main() {
_ => {}
}
match 0 {
// We get two errors because recursive or-pattern expansion means we don't notice the two
// errors span a whole pattern. This could be better but doesn't matter much
0 | (0 | 0) => {}
//~^ ERROR unreachable
//~| ERROR unreachable
_ => {}
}
match None {

View File

@ -83,71 +83,77 @@ LL | (0 | 1) | 1 => {}
| ^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:56:14
--> $DIR/exhaustiveness-unreachable-pattern.rs:58:14
|
LL | 0 | (0 | 0) => {}
| ^^^^^
| ^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:63:13
--> $DIR/exhaustiveness-unreachable-pattern.rs:58:18
|
LL | 0 | (0 | 0) => {}
| ^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:66:13
|
LL | / Some(
LL | | 0 | 0) => {}
| |______________________^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:69:15
--> $DIR/exhaustiveness-unreachable-pattern.rs:72:15
|
LL | | 0
| ^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:71:15
--> $DIR/exhaustiveness-unreachable-pattern.rs:74:15
|
LL | | 0] => {}
| ^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:79:10
--> $DIR/exhaustiveness-unreachable-pattern.rs:82:10
|
LL | [1
| ^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:91:10
--> $DIR/exhaustiveness-unreachable-pattern.rs:94:10
|
LL | [true
| ^^^^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:98:36
--> $DIR/exhaustiveness-unreachable-pattern.rs:101:36
|
LL | (true | false, None | Some(true
| ^^^^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:114:14
--> $DIR/exhaustiveness-unreachable-pattern.rs:117:14
|
LL | Some(0
| ^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:133:19
--> $DIR/exhaustiveness-unreachable-pattern.rs:136:19
|
LL | | false) => {}
| ^^^^^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:141:15
--> $DIR/exhaustiveness-unreachable-pattern.rs:144:15
|
LL | | true) => {}
| ^^^^
error: unreachable pattern
--> $DIR/exhaustiveness-unreachable-pattern.rs:147:15
--> $DIR/exhaustiveness-unreachable-pattern.rs:150:15
|
LL | | true,
| ^^^^
error: aborting due to 24 previous errors
error: aborting due to 25 previous errors