Skip to content

Commit 30bc24e

Browse files
committed
Keep rows with guards in the matrix
1 parent 48094e2 commit 30bc24e

File tree

6 files changed

+52
-60
lines changed

6 files changed

+52
-60
lines changed

compiler/rustc_mir_build/src/thir/pattern/usefulness.rs

Lines changed: 38 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -383,16 +383,17 @@ impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
383383
/// works well.
384384
#[derive(Clone)]
385385
pub(crate) struct PatStack<'p, 'tcx> {
386-
pub(crate) pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>,
386+
pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>,
387+
is_under_guard: bool,
387388
}
388389

389390
impl<'p, 'tcx> PatStack<'p, 'tcx> {
390-
fn from_pattern(pat: &'p DeconstructedPat<'p, 'tcx>) -> Self {
391-
Self::from_vec(smallvec![pat])
391+
fn from_pattern(pat: &'p DeconstructedPat<'p, 'tcx>, is_under_guard: bool) -> Self {
392+
PatStack { pats: smallvec![pat], is_under_guard }
392393
}
393394

394-
fn from_vec(vec: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>) -> Self {
395-
PatStack { pats: vec }
395+
fn from_vec(vec: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>, is_under_guard: bool) -> Self {
396+
PatStack { pats: vec, is_under_guard }
396397
}
397398

398399
fn is_empty(&self) -> bool {
@@ -415,7 +416,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
415416
// or-pattern. Panics if `self` is empty.
416417
fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = PatStack<'p, 'tcx>> + Captures<'a> {
417418
self.head().iter_fields().map(move |pat| {
418-
let mut new_patstack = PatStack::from_pattern(pat);
419+
let mut new_patstack = PatStack::from_pattern(pat, self.is_under_guard);
419420
new_patstack.pats.extend_from_slice(&self.pats[1..]);
420421
new_patstack
421422
})
@@ -425,7 +426,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
425426
fn expand_and_extend<'a>(&'a self, matrix: &mut Matrix<'p, 'tcx>) {
426427
if !self.is_empty() && self.head().is_or_pat() {
427428
for pat in self.head().iter_fields() {
428-
let mut new_patstack = PatStack::from_pattern(pat);
429+
let mut new_patstack = PatStack::from_pattern(pat, self.is_under_guard);
429430
new_patstack.pats.extend_from_slice(&self.pats[1..]);
430431
if !new_patstack.is_empty() && new_patstack.head().is_or_pat() {
431432
new_patstack.expand_and_extend(matrix);
@@ -451,7 +452,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
451452
// `self.head()`.
452453
let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(pcx, ctor);
453454
new_fields.extend_from_slice(&self.pats[1..]);
454-
PatStack::from_vec(new_fields)
455+
PatStack::from_vec(new_fields, self.is_under_guard)
455456
}
456457
}
457458

@@ -469,12 +470,12 @@ impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> {
469470
/// A 2D matrix.
470471
#[derive(Clone)]
471472
pub(super) struct Matrix<'p, 'tcx> {
472-
pub patterns: Vec<PatStack<'p, 'tcx>>,
473+
pub rows: Vec<PatStack<'p, 'tcx>>,
473474
}
474475

475476
impl<'p, 'tcx> Matrix<'p, 'tcx> {
476477
fn empty() -> Self {
477-
Matrix { patterns: vec![] }
478+
Matrix { rows: vec![] }
478479
}
479480

480481
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
@@ -483,15 +484,22 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
483484
if !row.is_empty() && row.head().is_or_pat() {
484485
row.expand_and_extend(self);
485486
} else {
486-
self.patterns.push(row);
487+
self.rows.push(row);
487488
}
488489
}
489490

491+
fn rows<'a>(
492+
&'a self,
493+
) -> impl Iterator<Item = &'a PatStack<'p, 'tcx>> + Clone + DoubleEndedIterator + ExactSizeIterator
494+
{
495+
self.rows.iter()
496+
}
497+
490498
/// Iterate over the first component of each row
491499
fn heads<'a>(
492500
&'a self,
493501
) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Clone + Captures<'a> {
494-
self.patterns.iter().map(|r| r.head())
502+
self.rows().map(|r| r.head())
495503
}
496504

497505
/// This computes `S(constructor, self)`. See top of the file for explanations.
@@ -501,7 +509,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
501509
ctor: &Constructor<'tcx>,
502510
) -> Matrix<'p, 'tcx> {
503511
let mut matrix = Matrix::empty();
504-
for row in &self.patterns {
512+
for row in &self.rows {
505513
if ctor.is_covered_by(pcx, row.head().ctor()) {
506514
let new_row = row.pop_head_constructor(pcx, ctor);
507515
matrix.push(new_row);
@@ -524,12 +532,12 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
524532
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
525533
write!(f, "\n")?;
526534

527-
let Matrix { patterns: m, .. } = self;
535+
let Matrix { rows, .. } = self;
528536
let pretty_printed_matrix: Vec<Vec<String>> =
529-
m.iter().map(|row| row.iter().map(|pat| format!("{pat:?}")).collect()).collect();
537+
rows.iter().map(|row| row.iter().map(|pat| format!("{pat:?}")).collect()).collect();
530538

531-
let column_count = m.iter().map(|row| row.len()).next().unwrap_or(0);
532-
assert!(m.iter().all(|row| row.len() == column_count));
539+
let column_count = rows.iter().map(|row| row.len()).next().unwrap_or(0);
540+
assert!(rows.iter().all(|row| row.len() == column_count));
533541
let column_widths: Vec<usize> = (0..column_count)
534542
.map(|col| pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0))
535543
.collect();
@@ -811,19 +819,16 @@ fn is_useful<'p, 'tcx>(
811819
v: &PatStack<'p, 'tcx>,
812820
witness_preference: ArmType,
813821
lint_root: HirId,
814-
is_under_guard: bool,
815822
is_top_level: bool,
816823
) -> Usefulness<'tcx> {
817824
debug!(?matrix, ?v);
818-
let Matrix { patterns: rows, .. } = matrix;
819-
820825
// The base case. We are pattern-matching on () and the return value is
821826
// based on whether our matrix has a row or not.
822827
// NOTE: This could potentially be optimized by checking rows.is_empty()
823828
// first and then, if v is non-empty, the return value is based on whether
824829
// the type of the tuple we're checking is inhabited or not.
825830
if v.is_empty() {
826-
let ret = if rows.is_empty() {
831+
let ret = if matrix.rows().all(|r| r.is_under_guard) {
827832
Usefulness::new_useful(witness_preference)
828833
} else {
829834
Usefulness::new_not_useful(witness_preference)
@@ -832,7 +837,7 @@ fn is_useful<'p, 'tcx>(
832837
return ret;
833838
}
834839

835-
debug_assert!(rows.iter().all(|r| r.len() == v.len()));
840+
debug_assert!(matrix.rows().all(|r| r.len() == v.len()));
836841

837842
// If the first pattern is an or-pattern, expand it.
838843
let mut ret = Usefulness::new_not_useful(witness_preference);
@@ -843,24 +848,21 @@ fn is_useful<'p, 'tcx>(
843848
for v in v.expand_or_pat() {
844849
debug!(?v);
845850
let usefulness = ensure_sufficient_stack(|| {
846-
is_useful(cx, &matrix, &v, witness_preference, lint_root, is_under_guard, false)
851+
is_useful(cx, &matrix, &v, witness_preference, lint_root, false)
847852
});
848853
debug!(?usefulness);
849854
ret.extend(usefulness);
850-
// If pattern has a guard don't add it to the matrix.
851-
if !is_under_guard {
852-
// We push the already-seen patterns into the matrix in order to detect redundant
853-
// branches like `Some(_) | Some(0)`.
854-
matrix.push(v);
855-
}
855+
// We push the already-seen patterns into the matrix in order to detect redundant
856+
// branches like `Some(_) | Some(0)`.
857+
matrix.push(v);
856858
}
857859
} else {
858860
let mut ty = v.head().ty();
859861

860862
// Opaque types can't get destructured/split, but the patterns can
861863
// actually hint at hidden types, so we use the patterns' types instead.
862864
if let ty::Alias(ty::Opaque, ..) = ty.kind() {
863-
if let Some(row) = rows.first() {
865+
if let Some(row) = matrix.rows().next() {
864866
ty = row.head().ty();
865867
}
866868
}
@@ -880,15 +882,7 @@ fn is_useful<'p, 'tcx>(
880882
let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor);
881883
let v = v.pop_head_constructor(pcx, &ctor);
882884
let usefulness = ensure_sufficient_stack(|| {
883-
is_useful(
884-
cx,
885-
&spec_matrix,
886-
&v,
887-
witness_preference,
888-
lint_root,
889-
is_under_guard,
890-
false,
891-
)
885+
is_useful(cx, &spec_matrix, &v, witness_preference, lint_root, false)
892886
});
893887
let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor);
894888
ret.extend(usefulness);
@@ -1158,11 +1152,9 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
11581152
.copied()
11591153
.map(|arm| {
11601154
debug!(?arm);
1161-
let v = PatStack::from_pattern(arm.pat);
1162-
is_useful(cx, &matrix, &v, RealArm, arm.hir_id, arm.has_guard, true);
1163-
if !arm.has_guard {
1164-
matrix.push(v);
1165-
}
1155+
let v = PatStack::from_pattern(arm.pat, arm.has_guard);
1156+
is_useful(cx, &matrix, &v, RealArm, arm.hir_id, true);
1157+
matrix.push(v);
11661158
let reachability = if arm.pat.is_reachable() {
11671159
Reachability::Reachable(arm.pat.unreachable_spans())
11681160
} else {
@@ -1173,8 +1165,8 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
11731165
.collect();
11741166

11751167
let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty, DUMMY_SP));
1176-
let v = PatStack::from_pattern(wild_pattern);
1177-
let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, lint_root, false, true);
1168+
let v = PatStack::from_pattern(wild_pattern, false);
1169+
let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, lint_root, true);
11781170
let non_exhaustiveness_witnesses: Vec<_> = match usefulness {
11791171
WithWitnesses(witness_matrix) => witness_matrix.single_column(),
11801172
NoWitnesses { .. } => bug!(),

tests/ui/pattern/usefulness/issue-3601.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ fn main() {
3131
//~^ ERROR non-exhaustive patterns
3232
//~| NOTE the matched value is of type
3333
//~| NOTE match arms with guards don't count towards exhaustivity
34-
//~| NOTE pattern `box _` not covered
34+
//~| NOTE pattern `box ElementKind::HTMLImageElement(_)` not covered
3535
//~| NOTE `Box<ElementKind>` defined here
3636
box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => true,
3737
},

tests/ui/pattern/usefulness/issue-3601.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0004]: non-exhaustive patterns: `box _` not covered
1+
error[E0004]: non-exhaustive patterns: `box ElementKind::HTMLImageElement(_)` not covered
22
--> $DIR/issue-3601.rs:30:44
33
|
44
LL | box NodeKind::Element(ed) => match ed.kind {
5-
| ^^^^^^^ pattern `box _` not covered
5+
| ^^^^^^^ pattern `box ElementKind::HTMLImageElement(_)` not covered
66
|
77
note: `Box<ElementKind>` defined here
88
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
@@ -11,7 +11,7 @@ note: `Box<ElementKind>` defined here
1111
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
1212
|
1313
LL ~ box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => true,
14-
LL ~ box _ => todo!(),
14+
LL ~ box ElementKind::HTMLImageElement(_) => todo!(),
1515
|
1616

1717
error: aborting due to previous error

tests/ui/pattern/usefulness/match-non-exhaustive.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@ help: ensure that all possible cases are being handled by adding a match arm wit
1010
LL | match 0 { 1 => (), i32::MIN..=0_i32 | 2_i32..=i32::MAX => todo!() }
1111
| ++++++++++++++++++++++++++++++++++++++++++++++++
1212

13-
error[E0004]: non-exhaustive patterns: `_` not covered
13+
error[E0004]: non-exhaustive patterns: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
1414
--> $DIR/match-non-exhaustive.rs:3:11
1515
|
1616
LL | match 0 { 0 if false => () }
17-
| ^ pattern `_` not covered
17+
| ^ patterns `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
1818
|
1919
= note: the matched value is of type `i32`
2020
= note: match arms with guards don't count towards exhaustivity
21-
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
21+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
2222
|
23-
LL | match 0 { 0 if false => (), _ => todo!() }
24-
| ++++++++++++++
23+
LL | match 0 { 0 if false => (), i32::MIN..=-1_i32 | 1_i32..=i32::MAX => todo!() }
24+
| +++++++++++++++++++++++++++++++++++++++++++++++++
2525

2626
error: aborting due to 2 previous errors
2727

tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ fn main() {
4444
[] => {}
4545
}
4646
match s {
47-
//~^ ERROR `&_` not covered
47+
//~^ ERROR `&[]` and `&[_, ..]` not covered
4848
[..] if false => {}
4949
}
5050
match s {

tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,18 @@ LL ~ [] => {},
8989
LL + &[_, ..] => todo!()
9090
|
9191

92-
error[E0004]: non-exhaustive patterns: `&_` not covered
92+
error[E0004]: non-exhaustive patterns: `&[]` and `&[_, ..]` not covered
9393
--> $DIR/slice-patterns-exhaustiveness.rs:46:11
9494
|
9595
LL | match s {
96-
| ^ pattern `&_` not covered
96+
| ^ patterns `&[]` and `&[_, ..]` not covered
9797
|
9898
= note: the matched value is of type `&[bool]`
9999
= note: match arms with guards don't count towards exhaustivity
100-
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
100+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
101101
|
102102
LL ~ [..] if false => {},
103-
LL + &_ => todo!()
103+
LL + &[] | &[_, ..] => todo!()
104104
|
105105

106106
error[E0004]: non-exhaustive patterns: `&[_, _, ..]` not covered

0 commit comments

Comments
 (0)