diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index e2ed925047442..37a9381271a8c 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -455,6 +455,7 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> { } /// A 2D matrix. +#[derive(Clone)] pub struct Matrix<'p, 'tcx>(Vec>); impl<'p, 'tcx> Matrix<'p, 'tcx> { @@ -1025,17 +1026,19 @@ impl<'tcx> Constructor<'tcx> { } #[derive(Clone, Debug)] -pub enum Usefulness<'tcx> { - Useful, +pub enum Usefulness<'tcx, 'p> { + /// Carries a list of unreachable subpatterns. Used only in the presence of or-patterns. + Useful(Vec<&'p Pat<'tcx>>), + /// Carries a list of witnesses of non-exhaustiveness. UsefulWithWitness(Vec>), NotUseful, } -impl<'tcx> Usefulness<'tcx> { +impl<'tcx, 'p> Usefulness<'tcx, 'p> { fn new_useful(preference: WitnessPreference) -> Self { match preference { ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), - LeaveOutWitness => Useful, + LeaveOutWitness => Useful(vec![]), } } @@ -1602,7 +1605,7 @@ pub fn is_useful<'p, 'tcx>( v: &PatStack<'p, 'tcx>, witness_preference: WitnessPreference, hir_id: HirId, -) -> Usefulness<'tcx> { +) -> Usefulness<'tcx, 'p> { let &Matrix(ref rows) = matrix; debug!("is_useful({:#?}, {:#?})", matrix, v); @@ -1623,11 +1626,26 @@ pub fn is_useful<'p, 'tcx>( // If the first pattern is an or-pattern, expand it. if let Some(vs) = v.expand_or_pat() { - return vs - .into_iter() - .map(|v| is_useful(cx, matrix, &v, witness_preference, hir_id)) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful); + // We need to push the already-seen patterns into the matrix in order to detect redundant + // branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns. + let mut matrix = matrix.clone(); + let mut unreachable_pats = Vec::new(); + let mut any_is_useful = false; + for v in vs { + let res = is_useful(cx, &matrix, &v, witness_preference, hir_id); + match res { + Useful(pats) => { + any_is_useful = true; + unreachable_pats.extend(pats); + } + NotUseful => unreachable_pats.push(v.head()), + UsefulWithWitness(_) => { + bug!("Encountered or-pat in `v` during exhaustiveness checking") + } + } + matrix.push(v); + } + return if any_is_useful { Useful(unreachable_pats) } else { NotUseful }; } let (ty, span) = matrix @@ -1768,7 +1786,7 @@ fn is_useful_specialized<'p, 'tcx>( lty: Ty<'tcx>, witness_preference: WitnessPreference, hir_id: HirId, -) -> Usefulness<'tcx> { +) -> Usefulness<'tcx, 'p> { debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); let ctor_wild_subpatterns = diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 62bc04b65f34f..a6a043c23dd06 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -468,7 +468,16 @@ fn check_arms<'p, 'tcx>( hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {} } } - Useful => (), + Useful(unreachable_subpatterns) => { + for pat in unreachable_subpatterns { + cx.tcx.lint_hir( + lint::builtin::UNREACHABLE_PATTERNS, + hir_pat.hir_id, + pat.span, + "unreachable pattern", + ); + } + } UsefulWithWitness(_) => bug!(), } if guard.is_none() { @@ -496,7 +505,7 @@ fn check_not_useful<'p, 'tcx>( } else { pats.into_iter().map(|w| w.single_pattern()).collect() }), - Useful => bug!(), + Useful(_) => bug!(), } } diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.rs b/src/test/ui/or-patterns/exhaustiveness-pass.rs index d49ae74db51b5..5c4e239b5e39f 100644 --- a/src/test/ui/or-patterns/exhaustiveness-pass.rs +++ b/src/test/ui/or-patterns/exhaustiveness-pass.rs @@ -43,13 +43,16 @@ fn main() { _ => {} } - // FIXME(or_patterns): Redundancies not detected for now. match (0,) { - (1 | 1,) => {} + (1 + | 1,) => {} //~ ERROR unreachable _ => {} } match [0; 2] { - [0 | 0, 0 | 0] => {} + [0 + | 0 //~ ERROR unreachable + , 0 + | 0] => {} //~ ERROR unreachable _ => {} } match &[][..] { @@ -57,12 +60,14 @@ fn main() { [0, _] => {} [0, _, _] => {} [1, ..] => {} - [1 | 2, ..] => {} + [1 //~ ERROR unreachable + | 2, ..] => {} _ => {} } match Some(0) { Some(0) => {} - Some(0 | 1) => {} + Some(0 //~ ERROR unreachable + | 1) => {} _ => {} } } diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.stderr b/src/test/ui/or-patterns/exhaustiveness-pass.stderr index 1f4278c4b8098..7ca02862b4567 100644 --- a/src/test/ui/or-patterns/exhaustiveness-pass.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-pass.stderr @@ -1,8 +1,44 @@ +error: unreachable pattern + --> $DIR/exhaustiveness-pass.rs:48:12 + | +LL | | 1,) => {} + | ^ + | +note: lint level defined here + --> $DIR/exhaustiveness-pass.rs:4:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/exhaustiveness-pass.rs:55:15 + | +LL | | 0] => {} + | ^ + +error: unreachable pattern + --> $DIR/exhaustiveness-pass.rs:53:15 + | +LL | | 0 + | ^ + +error: unreachable pattern + --> $DIR/exhaustiveness-pass.rs:63:10 + | +LL | [1 + | ^ + +error: unreachable pattern + --> $DIR/exhaustiveness-pass.rs:69:14 + | +LL | Some(0 + | ^ + error: or-patterns are not fully implemented yet --> $DIR/exhaustiveness-pass.rs:10:10 | LL | (0 | _,) => {} | ^^^^^ -error: aborting due to previous error +error: aborting due to 6 previous errors