Skip to content

Commit b9da2b3

Browse files
committedNov 12, 2020
Factor out match usefulness computation in check_match
This make `_match` a lot more self-contained
1 parent d5a7ec0 commit b9da2b3

File tree

2 files changed

+118
-78
lines changed

2 files changed

+118
-78
lines changed
 

‎compiler/rustc_mir_build/src/thir/pattern/_match.rs

+70-9
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,14 @@ impl<'tcx> Pat<'tcx> {
364364
/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
365365
/// works well.
366366
#[derive(Debug, Clone)]
367-
crate struct PatStack<'p, 'tcx> {
367+
struct PatStack<'p, 'tcx> {
368368
pats: SmallVec<[&'p Pat<'tcx>; 2]>,
369369
/// Cache for the constructor of the head
370370
head_ctor: OnceCell<Constructor<'tcx>>,
371371
}
372372

373373
impl<'p, 'tcx> PatStack<'p, 'tcx> {
374-
crate fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
374+
fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
375375
Self::from_vec(smallvec![pat])
376376
}
377377

@@ -455,17 +455,17 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
455455

456456
/// A 2D matrix.
457457
#[derive(Clone, PartialEq)]
458-
crate struct Matrix<'p, 'tcx> {
458+
struct Matrix<'p, 'tcx> {
459459
patterns: Vec<PatStack<'p, 'tcx>>,
460460
}
461461

462462
impl<'p, 'tcx> Matrix<'p, 'tcx> {
463-
crate fn empty() -> Self {
463+
fn empty() -> Self {
464464
Matrix { patterns: vec![] }
465465
}
466466

467467
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it.
468-
crate fn push(&mut self, row: PatStack<'p, 'tcx>) {
468+
fn push(&mut self, row: PatStack<'p, 'tcx>) {
469469
if let Some(rows) = row.expand_or_pat() {
470470
for row in rows {
471471
// We recursively expand the or-patterns of the new rows.
@@ -588,7 +588,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
588588
}
589589

590590
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
591-
crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
591+
fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
592592
match ty.kind() {
593593
ty::Adt(def, ..) => {
594594
def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local()
@@ -1439,7 +1439,7 @@ impl<'tcx> Usefulness<'tcx> {
14391439
}
14401440

14411441
#[derive(Copy, Clone, Debug)]
1442-
crate enum WitnessPreference {
1442+
enum WitnessPreference {
14431443
ConstructWitness,
14441444
LeaveOutWitness,
14451445
}
@@ -1495,7 +1495,8 @@ struct PatCtxt<'a, 'p, 'tcx> {
14951495
crate struct Witness<'tcx>(Vec<Pat<'tcx>>);
14961496

14971497
impl<'tcx> Witness<'tcx> {
1498-
crate fn single_pattern(self) -> Pat<'tcx> {
1498+
/// Asserts that the witness contains a single pattern, and returns it.
1499+
fn single_pattern(self) -> Pat<'tcx> {
14991500
assert_eq!(self.0.len(), 1);
15001501
self.0.into_iter().next().unwrap()
15011502
}
@@ -2092,7 +2093,7 @@ impl<'tcx> MissingConstructors<'tcx> {
20922093
/// `is_under_guard` is used to inform if the pattern has a guard. If it
20932094
/// has one it must not be inserted into the matrix. This shouldn't be
20942095
/// relied on for soundness.
2095-
crate fn is_useful<'p, 'tcx>(
2096+
fn is_useful<'p, 'tcx>(
20962097
cx: &MatchCheckCtxt<'p, 'tcx>,
20972098
matrix: &Matrix<'p, 'tcx>,
20982099
v: &PatStack<'p, 'tcx>,
@@ -2289,3 +2290,63 @@ fn pat_constructor<'p, 'tcx>(
22892290
PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
22902291
}
22912292
}
2293+
2294+
/// The arm of a match expression.
2295+
#[derive(Clone, Copy)]
2296+
crate struct MatchArm<'p, 'tcx> {
2297+
/// The pattern must have been lowered through `MatchVisitor::lower_pattern`.
2298+
crate pat: &'p super::Pat<'tcx>,
2299+
crate hir_id: HirId,
2300+
crate has_guard: bool,
2301+
}
2302+
2303+
/// The output of checking a match for exhaustiveness and arm reachability.
2304+
crate struct UsefulnessReport<'p, 'tcx> {
2305+
/// For each arm of the input, whether that arm is reachable after the arms above it.
2306+
crate arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Usefulness<'tcx>)>,
2307+
/// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
2308+
/// exhaustiveness.
2309+
crate non_exhaustiveness_witnesses: Vec<super::Pat<'tcx>>,
2310+
}
2311+
2312+
/// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which
2313+
/// of its arms are reachable.
2314+
///
2315+
/// Note: the input patterns must have been lowered through `MatchVisitor::lower_pattern`.
2316+
crate fn compute_match_usefulness<'p, 'tcx>(
2317+
cx: &MatchCheckCtxt<'p, 'tcx>,
2318+
arms: &[MatchArm<'p, 'tcx>],
2319+
scrut_hir_id: HirId,
2320+
scrut_ty: Ty<'tcx>,
2321+
) -> UsefulnessReport<'p, 'tcx> {
2322+
let mut matrix = Matrix::empty();
2323+
let arm_usefulness: Vec<_> = arms
2324+
.iter()
2325+
.copied()
2326+
.map(|arm| {
2327+
let v = PatStack::from_pattern(arm.pat);
2328+
let usefulness =
2329+
is_useful(cx, &matrix, &v, LeaveOutWitness, arm.hir_id, arm.has_guard, true);
2330+
if !arm.has_guard {
2331+
matrix.push(v);
2332+
}
2333+
(arm, usefulness)
2334+
})
2335+
.collect();
2336+
2337+
let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(scrut_ty));
2338+
let v = PatStack::from_pattern(wild_pattern);
2339+
let usefulness = is_useful(cx, &matrix, &v, ConstructWitness, scrut_hir_id, false, true);
2340+
let non_exhaustiveness_witnesses = match usefulness {
2341+
NotUseful => vec![], // Wildcard pattern isn't useful, so the match is exhaustive.
2342+
UsefulWithWitness(pats) => {
2343+
if pats.is_empty() {
2344+
bug!("Exhaustiveness check returned no witnesses")
2345+
} else {
2346+
pats.into_iter().map(|w| w.single_pattern()).collect()
2347+
}
2348+
}
2349+
Useful(_) => bug!(),
2350+
};
2351+
UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
2352+
}

‎compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+48-69
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::_match::Usefulness::*;
2-
use super::_match::WitnessPreference::*;
3-
use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack};
2+
use super::_match::{
3+
compute_match_usefulness, expand_pattern, MatchArm, MatchCheckCtxt, UsefulnessReport,
4+
};
45
use super::{PatCtxt, PatKind, PatternError};
56

67
use rustc_arena::TypedArena;
@@ -169,39 +170,50 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
169170

170171
let mut have_errors = false;
171172

172-
let inlined_arms: Vec<_> = arms
173+
let arms: Vec<_> = arms
173174
.iter()
174-
.map(|hir::Arm { pat, guard, .. }| {
175-
(self.lower_pattern(&mut cx, pat, &mut have_errors).0, pat.hir_id, guard.is_some())
175+
.map(|hir::Arm { pat, guard, .. }| MatchArm {
176+
pat: self.lower_pattern(&mut cx, pat, &mut have_errors).0,
177+
hir_id: pat.hir_id,
178+
has_guard: guard.is_some(),
176179
})
177180
.collect();
178181

179-
// Bail out early if inlining failed.
182+
// Bail out early if lowering failed.
180183
if have_errors {
181184
return;
182185
}
183186

184-
// Fourth, check for unreachable arms.
185-
let matrix = check_arms(&mut cx, &inlined_arms, source);
187+
let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
188+
let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty);
189+
190+
// Report unreachable arms.
191+
report_arm_reachability(&cx, &report, source);
186192

187-
// Fifth, check if the match is exhaustive.
193+
// Check if the match is exhaustive.
188194
// Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
189195
// since an empty matrix can occur when there are arms, if those arms all have guards.
190-
let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
191-
let is_empty_match = inlined_arms.is_empty();
192-
check_exhaustive(&mut cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match);
196+
let is_empty_match = arms.is_empty();
197+
let witnesses = report.non_exhaustiveness_witnesses;
198+
if !witnesses.is_empty() {
199+
non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match);
200+
}
193201
}
194202

195203
fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
196204
let mut cx = self.new_cx(pat.hir_id);
197205

198206
let (pattern, pattern_ty) = self.lower_pattern(&mut cx, pat, &mut false);
199-
let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect();
200-
201-
let witnesses = match check_not_useful(&mut cx, pattern_ty, &pats, pat.hir_id) {
202-
Ok(_) => return,
203-
Err(err) => err,
204-
};
207+
let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
208+
let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
209+
210+
// Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
211+
// only care about exhaustiveness here.
212+
let witnesses = report.non_exhaustiveness_witnesses;
213+
if witnesses.is_empty() {
214+
// The pattern is irrefutable.
215+
return;
216+
}
205217

206218
let joined_patterns = joined_uncovered_patterns(&witnesses);
207219
let mut err = struct_span_err!(
@@ -354,17 +366,15 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::
354366
});
355367
}
356368

357-
/// Check for unreachable patterns.
358-
fn check_arms<'p, 'tcx>(
359-
cx: &mut MatchCheckCtxt<'p, 'tcx>,
360-
arms: &[(&'p super::Pat<'tcx>, HirId, bool)],
369+
/// Report unreachable arms, if any.
370+
fn report_arm_reachability<'p, 'tcx>(
371+
cx: &MatchCheckCtxt<'p, 'tcx>,
372+
report: &UsefulnessReport<'p, 'tcx>,
361373
source: hir::MatchSource,
362-
) -> Matrix<'p, 'tcx> {
363-
let mut seen = Matrix::empty();
374+
) {
364375
let mut catchall = None;
365-
for (arm_index, (pat, id, has_guard)) in arms.iter().copied().enumerate() {
366-
let v = PatStack::from_pattern(pat);
367-
match is_useful(cx, &seen, &v, LeaveOutWitness, id, has_guard, true) {
376+
for (arm_index, (arm, is_useful)) in report.arm_usefulness.iter().enumerate() {
377+
match is_useful {
368378
NotUseful => {
369379
match source {
370380
hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
@@ -373,15 +383,15 @@ fn check_arms<'p, 'tcx>(
373383
// Check which arm we're on.
374384
match arm_index {
375385
// The arm with the user-specified pattern.
376-
0 => unreachable_pattern(cx.tcx, pat.span, id, None),
386+
0 => unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None),
377387
// The arm with the wildcard pattern.
378-
1 => irrefutable_let_pattern(cx.tcx, pat.span, id, source),
388+
1 => irrefutable_let_pattern(cx.tcx, arm.pat.span, arm.hir_id, source),
379389
_ => bug!(),
380390
}
381391
}
382392

383393
hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
384-
unreachable_pattern(cx.tcx, pat.span, id, catchall);
394+
unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall);
385395
}
386396

387397
// Unreachable patterns in try and await expressions occur when one of
@@ -392,60 +402,29 @@ fn check_arms<'p, 'tcx>(
392402
Useful(unreachables) if unreachables.is_empty() => {}
393403
// The arm is reachable, but contains unreachable subpatterns (from or-patterns).
394404
Useful(unreachables) => {
395-
let mut unreachables: Vec<_> = unreachables.into_iter().flatten().collect();
405+
let mut unreachables: Vec<_> = unreachables.iter().flatten().copied().collect();
396406
// Emit lints in the order in which they occur in the file.
397407
unreachables.sort_unstable();
398408
for span in unreachables {
399-
unreachable_pattern(cx.tcx, span, id, None);
409+
unreachable_pattern(cx.tcx, span, arm.hir_id, None);
400410
}
401411
}
402412
UsefulWithWitness(_) => bug!(),
403413
}
404-
if !has_guard {
405-
seen.push(v);
406-
if catchall.is_none() && pat_is_catchall(pat) {
407-
catchall = Some(pat.span);
408-
}
414+
if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
415+
catchall = Some(arm.pat.span);
409416
}
410417
}
411-
seen
412418
}
413419

414-
fn check_not_useful<'p, 'tcx>(
415-
cx: &mut MatchCheckCtxt<'p, 'tcx>,
416-
ty: Ty<'tcx>,
417-
matrix: &Matrix<'p, 'tcx>,
418-
hir_id: HirId,
419-
) -> Result<(), Vec<super::Pat<'tcx>>> {
420-
let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
421-
let v = PatStack::from_pattern(wild_pattern);
422-
423-
// false is given for `is_under_guard` argument due to the wildcard
424-
// pattern not having a guard
425-
match is_useful(cx, matrix, &v, ConstructWitness, hir_id, false, true) {
426-
NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
427-
UsefulWithWitness(pats) => Err(if pats.is_empty() {
428-
bug!("Exhaustiveness check returned no witnesses")
429-
} else {
430-
pats.into_iter().map(|w| w.single_pattern()).collect()
431-
}),
432-
Useful(_) => bug!(),
433-
}
434-
}
435-
436-
fn check_exhaustive<'p, 'tcx>(
437-
cx: &mut MatchCheckCtxt<'p, 'tcx>,
420+
/// Report that a match is not exhaustive.
421+
fn non_exhaustive_match<'p, 'tcx>(
422+
cx: &MatchCheckCtxt<'p, 'tcx>,
438423
scrut_ty: Ty<'tcx>,
439424
sp: Span,
440-
matrix: &Matrix<'p, 'tcx>,
441-
hir_id: HirId,
425+
witnesses: Vec<super::Pat<'tcx>>,
442426
is_empty_match: bool,
443427
) {
444-
let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
445-
Ok(_) => return,
446-
Err(err) => err,
447-
};
448-
449428
let non_empty_enum = match scrut_ty.kind() {
450429
ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
451430
_ => false,

0 commit comments

Comments
 (0)
Please sign in to comment.