diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 5e7e81eba6273..6bea2381862e9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -364,14 +364,14 @@ impl<'tcx> Pat<'tcx> { /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` /// works well. #[derive(Debug, Clone)] -crate struct PatStack<'p, 'tcx> { +struct PatStack<'p, 'tcx> { pats: SmallVec<[&'p Pat<'tcx>; 2]>, /// Cache for the constructor of the head head_ctor: OnceCell>, } impl<'p, 'tcx> PatStack<'p, 'tcx> { - crate fn from_pattern(pat: &'p Pat<'tcx>) -> Self { + fn from_pattern(pat: &'p Pat<'tcx>) -> Self { Self::from_vec(smallvec![pat]) } @@ -455,17 +455,17 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> { /// A 2D matrix. #[derive(Clone, PartialEq)] -crate struct Matrix<'p, 'tcx> { +struct Matrix<'p, 'tcx> { patterns: Vec>, } impl<'p, 'tcx> Matrix<'p, 'tcx> { - crate fn empty() -> Self { + fn empty() -> Self { Matrix { patterns: vec![] } } /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it. - crate fn push(&mut self, row: PatStack<'p, 'tcx>) { + 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. @@ -588,7 +588,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. - crate fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { + fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { match ty.kind() { ty::Adt(def, ..) => { def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local() @@ -1392,13 +1392,12 @@ impl<'tcx> Usefulness<'tcx> { pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, - is_top_level: bool, ) -> Self { match self { UsefulWithWitness(witnesses) => { let new_witnesses = if ctor.is_wildcard() { let missing_ctors = MissingConstructors::new(pcx); - let new_patterns = missing_ctors.report_patterns(pcx, is_top_level); + let new_patterns = missing_ctors.report_patterns(pcx); witnesses .into_iter() .flat_map(|witness| { @@ -1440,7 +1439,7 @@ impl<'tcx> Usefulness<'tcx> { } #[derive(Copy, Clone, Debug)] -crate enum WitnessPreference { +enum WitnessPreference { ConstructWitness, LeaveOutWitness, } @@ -1454,6 +1453,9 @@ struct PatCtxt<'a, 'p, 'tcx> { ty: Ty<'tcx>, /// Span of the current pattern under investigation. span: Span, + /// Whether the current pattern is the whole pattern as found in a match arm, or if it's a + /// subpattern. + is_top_level: bool, } /// A witness of non-exhaustiveness for error reporting, represented @@ -1493,7 +1495,8 @@ struct PatCtxt<'a, 'p, 'tcx> { crate struct Witness<'tcx>(Vec>); impl<'tcx> Witness<'tcx> { - crate fn single_pattern(self) -> Pat<'tcx> { + /// Asserts that the witness contains a single pattern, and returns it. + fn single_pattern(self) -> Pat<'tcx> { assert_eq!(self.0.len(), 1); self.0.into_iter().next().unwrap() } @@ -1585,11 +1588,12 @@ fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec { + vec![NonExhaustive] + } + ty::Never => vec![], _ if cx.is_uninhabited(pcx.ty) => vec![], ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => vec![Single], // This type is one for which we cannot list constructors, like `str` or `f64`. @@ -2012,11 +2023,7 @@ impl<'tcx> MissingConstructors<'tcx> { /// List the patterns corresponding to the missing constructors. In some cases, instead of /// listing all constructors of a given type, we prefer to simply report a wildcard. - fn report_patterns<'p>( - &self, - pcx: PatCtxt<'_, 'p, 'tcx>, - is_top_level: bool, - ) -> SmallVec<[Pat<'tcx>; 1]> { + fn report_patterns<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Pat<'tcx>; 1]> { // There are 2 ways we can report a witness here. // Commonly, we can report all the "free" // constructors as witnesses, e.g., if we have: @@ -2044,7 +2051,7 @@ impl<'tcx> MissingConstructors<'tcx> { // `used_ctors` is empty. // The exception is: if we are at the top-level, for example in an empty match, we // sometimes prefer reporting the list of constructors instead of just `_`. - let report_when_all_missing = is_top_level && !IntRange::is_integral(pcx.ty); + let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty); if self.used_ctors.is_empty() && !report_when_all_missing { // All constructors are unused. Report only a wildcard // rather than each individual constructor. @@ -2086,7 +2093,7 @@ impl<'tcx> MissingConstructors<'tcx> { /// `is_under_guard` is used to inform if the pattern has a guard. If it /// has one it must not be inserted into the matrix. This shouldn't be /// relied on for soundness. -crate fn is_useful<'p, 'tcx>( +fn is_useful<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, matrix: &Matrix<'p, 'tcx>, v: &PatStack<'p, 'tcx>, @@ -2200,7 +2207,7 @@ crate fn is_useful<'p, 'tcx>( // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476). let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty); - let pcx = PatCtxt { cx, matrix, ty, span: v.head().span }; + let pcx = PatCtxt { cx, matrix, ty, span: v.head().span, is_top_level }; debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head()); @@ -2215,7 +2222,7 @@ crate fn is_useful<'p, 'tcx>( let v = v.pop_head_constructor(&ctor_wild_subpatterns); let usefulness = is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); - usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns, is_top_level) + usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns) }) .find(|result| result.is_useful()) .unwrap_or(NotUseful); @@ -2283,3 +2290,63 @@ fn pat_constructor<'p, 'tcx>( PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."), } } + +/// The arm of a match expression. +#[derive(Clone, Copy)] +crate struct MatchArm<'p, 'tcx> { + /// The pattern must have been lowered through `MatchVisitor::lower_pattern`. + crate pat: &'p super::Pat<'tcx>, + crate hir_id: HirId, + crate has_guard: bool, +} + +/// The output of checking a match for exhaustiveness and arm reachability. +crate struct UsefulnessReport<'p, 'tcx> { + /// For each arm of the input, whether that arm is reachable after the arms above it. + crate arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Usefulness<'tcx>)>, + /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of + /// exhaustiveness. + crate non_exhaustiveness_witnesses: Vec>, +} + +/// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which +/// of its arms are reachable. +/// +/// Note: the input patterns must have been lowered through `MatchVisitor::lower_pattern`. +crate fn compute_match_usefulness<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + arms: &[MatchArm<'p, 'tcx>], + scrut_hir_id: HirId, + scrut_ty: Ty<'tcx>, +) -> UsefulnessReport<'p, 'tcx> { + let mut matrix = Matrix::empty(); + let arm_usefulness: Vec<_> = arms + .iter() + .copied() + .map(|arm| { + let v = PatStack::from_pattern(arm.pat); + let usefulness = + is_useful(cx, &matrix, &v, LeaveOutWitness, arm.hir_id, arm.has_guard, true); + if !arm.has_guard { + matrix.push(v); + } + (arm, usefulness) + }) + .collect(); + + let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(scrut_ty)); + let v = PatStack::from_pattern(wild_pattern); + let usefulness = is_useful(cx, &matrix, &v, ConstructWitness, scrut_hir_id, false, true); + let non_exhaustiveness_witnesses = match usefulness { + NotUseful => vec![], // Wildcard pattern isn't useful, so the match is exhaustive. + UsefulWithWitness(pats) => { + if pats.is_empty() { + bug!("Exhaustiveness check returned no witnesses") + } else { + pats.into_iter().map(|w| w.single_pattern()).collect() + } + } + Useful(_) => bug!(), + }; + UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses } +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 205ad850c0c80..9a447d9a6aec3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1,6 +1,7 @@ use super::_match::Usefulness::*; -use super::_match::WitnessPreference::*; -use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack}; +use super::_match::{ + compute_match_usefulness, expand_pattern, MatchArm, MatchCheckCtxt, UsefulnessReport, +}; use super::{PatCtxt, PatKind, PatternError}; use rustc_arena::TypedArena; @@ -169,39 +170,50 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { let mut have_errors = false; - let inlined_arms: Vec<_> = arms + let arms: Vec<_> = arms .iter() - .map(|hir::Arm { pat, guard, .. }| { - (self.lower_pattern(&mut cx, pat, &mut have_errors).0, pat.hir_id, guard.is_some()) + .map(|hir::Arm { pat, guard, .. }| MatchArm { + pat: self.lower_pattern(&mut cx, pat, &mut have_errors).0, + hir_id: pat.hir_id, + has_guard: guard.is_some(), }) .collect(); - // Bail out early if inlining failed. + // Bail out early if lowering failed. if have_errors { return; } - // Fourth, check for unreachable arms. - let matrix = check_arms(&mut cx, &inlined_arms, source); + let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut); + let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty); + + // Report unreachable arms. + report_arm_reachability(&cx, &report, source); - // Fifth, check if the match is exhaustive. + // Check if the match is exhaustive. // Note: An empty match isn't the same as an empty matrix for diagnostics purposes, // since an empty matrix can occur when there are arms, if those arms all have guards. - let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut); - let is_empty_match = inlined_arms.is_empty(); - check_exhaustive(&mut cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match); + let is_empty_match = arms.is_empty(); + let witnesses = report.non_exhaustiveness_witnesses; + if !witnesses.is_empty() { + non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match); + } } fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option) { let mut cx = self.new_cx(pat.hir_id); let (pattern, pattern_ty) = self.lower_pattern(&mut cx, pat, &mut false); - let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect(); - - let witnesses = match check_not_useful(&mut cx, pattern_ty, &pats, pat.hir_id) { - Ok(_) => return, - Err(err) => err, - }; + let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }]; + let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty); + + // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We + // only care about exhaustiveness here. + let witnesses = report.non_exhaustiveness_witnesses; + if witnesses.is_empty() { + // The pattern is irrefutable. + return; + } let joined_patterns = joined_uncovered_patterns(&witnesses); let mut err = struct_span_err!( @@ -354,17 +366,15 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir:: }); } -/// Check for unreachable patterns. -fn check_arms<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, - arms: &[(&'p super::Pat<'tcx>, HirId, bool)], +/// Report unreachable arms, if any. +fn report_arm_reachability<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + report: &UsefulnessReport<'p, 'tcx>, source: hir::MatchSource, -) -> Matrix<'p, 'tcx> { - let mut seen = Matrix::empty(); +) { let mut catchall = None; - for (arm_index, (pat, id, has_guard)) in arms.iter().copied().enumerate() { - let v = PatStack::from_pattern(pat); - match is_useful(cx, &seen, &v, LeaveOutWitness, id, has_guard, true) { + for (arm_index, (arm, is_useful)) in report.arm_usefulness.iter().enumerate() { + match is_useful { NotUseful => { match source { hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(), @@ -373,15 +383,15 @@ fn check_arms<'p, 'tcx>( // Check which arm we're on. match arm_index { // The arm with the user-specified pattern. - 0 => unreachable_pattern(cx.tcx, pat.span, id, None), + 0 => unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None), // The arm with the wildcard pattern. - 1 => irrefutable_let_pattern(cx.tcx, pat.span, id, source), + 1 => irrefutable_let_pattern(cx.tcx, arm.pat.span, arm.hir_id, source), _ => bug!(), } } hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => { - unreachable_pattern(cx.tcx, pat.span, id, catchall); + unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall); } // Unreachable patterns in try and await expressions occur when one of @@ -389,79 +399,32 @@ fn check_arms<'p, 'tcx>( hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {} } } + Useful(unreachables) if unreachables.is_empty() => {} + // The arm is reachable, but contains unreachable subpatterns (from or-patterns). Useful(unreachables) => { - let mut unreachables: Vec<_> = unreachables.into_iter().flatten().collect(); + let mut unreachables: Vec<_> = unreachables.iter().flatten().copied().collect(); // Emit lints in the order in which they occur in the file. unreachables.sort_unstable(); for span in unreachables { - unreachable_pattern(cx.tcx, span, id, None); + unreachable_pattern(cx.tcx, span, arm.hir_id, None); } } UsefulWithWitness(_) => bug!(), } - if !has_guard { - seen.push(v); - if catchall.is_none() && pat_is_catchall(pat) { - catchall = Some(pat.span); - } + if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) { + catchall = Some(arm.pat.span); } } - seen } -fn check_not_useful<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, - ty: Ty<'tcx>, - matrix: &Matrix<'p, 'tcx>, - hir_id: HirId, -) -> Result<(), Vec>> { - let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty)); - let v = PatStack::from_pattern(wild_pattern); - - // false is given for `is_under_guard` argument due to the wildcard - // pattern not having a guard - match is_useful(cx, matrix, &v, ConstructWitness, hir_id, false, true) { - NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable. - UsefulWithWitness(pats) => Err(if pats.is_empty() { - bug!("Exhaustiveness check returned no witnesses") - } else { - pats.into_iter().map(|w| w.single_pattern()).collect() - }), - Useful(_) => bug!(), - } -} - -fn check_exhaustive<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, +/// Report that a match is not exhaustive. +fn non_exhaustive_match<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, scrut_ty: Ty<'tcx>, sp: Span, - matrix: &Matrix<'p, 'tcx>, - hir_id: HirId, + witnesses: Vec>, is_empty_match: bool, ) { - // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by - // `is_useful` to exhaustively match uninhabited types, so we manually check here. - if is_empty_match && !cx.tcx.features().exhaustive_patterns { - let scrutinee_is_visibly_uninhabited = match scrut_ty.kind() { - ty::Never => true, - ty::Adt(def, _) => { - def.is_enum() - && def.variants.is_empty() - && !cx.is_foreign_non_exhaustive_enum(scrut_ty) - } - _ => false, - }; - if scrutinee_is_visibly_uninhabited { - // If the type *is* uninhabited, an empty match is vacuously exhaustive. - return; - } - } - - let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) { - Ok(_) => return, - Err(err) => err, - }; - let non_empty_enum = match scrut_ty.kind() { ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(), _ => false, diff --git a/src/test/ui/pattern/usefulness/auxiliary/empty.rs b/src/test/ui/pattern/usefulness/auxiliary/empty.rs new file mode 100644 index 0000000000000..0b0719f48ee02 --- /dev/null +++ b/src/test/ui/pattern/usefulness/auxiliary/empty.rs @@ -0,0 +1,2 @@ +#![crate_type = "rlib"] +pub enum EmptyForeignEnum {} diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs index 57b6b910ca1da..c5c3a214f9aff 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs @@ -1,7 +1,12 @@ +// aux-build:empty.rs #![feature(never_type)] +#![feature(never_type_fallback)] #![feature(exhaustive_patterns)] #![deny(unreachable_patterns)] -enum Foo {} + +extern crate empty; + +enum EmptyEnum {} struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here @@ -41,8 +46,28 @@ macro_rules! match_false { }; } -fn foo(x: Foo) { - match_empty!(x); // ok +fn empty_enum(x: EmptyEnum) { + match x {} // ok + match x { + _ => {}, //~ ERROR unreachable pattern + } + match x { + _ if false => {}, //~ ERROR unreachable pattern + } +} + +fn empty_foreign_enum(x: empty::EmptyForeignEnum) { + match x {} // ok + match x { + _ => {}, //~ ERROR unreachable pattern + } + match x { + _ if false => {}, //~ ERROR unreachable pattern + } +} + +fn never(x: !) { + match x {} // ok match x { _ => {}, //~ ERROR unreachable pattern } @@ -56,7 +81,7 @@ fn main() { None => {} Some(_) => {} //~ ERROR unreachable pattern } - match None:: { + match None:: { None => {} Some(_) => {} //~ ERROR unreachable pattern } diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr index 1f6503e3e9c71..9d8b5f38e8cf5 100644 --- a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -1,35 +1,59 @@ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:47:9 + --> $DIR/match-empty-exhaustive_patterns.rs:52:9 | LL | _ => {}, | ^ | note: the lint level is defined here - --> $DIR/match-empty-exhaustive_patterns.rs:3:9 + --> $DIR/match-empty-exhaustive_patterns.rs:5:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:50:9 + --> $DIR/match-empty-exhaustive_patterns.rs:55:9 | LL | _ if false => {}, | ^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:57:9 + --> $DIR/match-empty-exhaustive_patterns.rs:62:9 + | +LL | _ => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:65:9 + | +LL | _ if false => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:72:9 + | +LL | _ => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:75:9 + | +LL | _ if false => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:82:9 | LL | Some(_) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/match-empty-exhaustive_patterns.rs:61:9 + --> $DIR/match-empty-exhaustive_patterns.rs:86:9 | LL | Some(_) => {} | ^^^^^^^ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:64:18 + --> $DIR/match-empty-exhaustive_patterns.rs:89:18 | LL | match_empty!(0u8); | ^^^ @@ -38,7 +62,7 @@ LL | match_empty!(0u8); = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:66:18 + --> $DIR/match-empty-exhaustive_patterns.rs:91:18 | LL | struct NonEmptyStruct(bool); | ---------------------------- `NonEmptyStruct` defined here @@ -50,7 +74,7 @@ LL | match_empty!(NonEmptyStruct(true)); = note: the matched value is of type `NonEmptyStruct` error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:68:18 + --> $DIR/match-empty-exhaustive_patterns.rs:93:18 | LL | / union NonEmptyUnion1 { LL | | foo: (), @@ -64,7 +88,7 @@ LL | match_empty!((NonEmptyUnion1 { foo: () })); = note: the matched value is of type `NonEmptyUnion1` error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/match-empty-exhaustive_patterns.rs:70:18 + --> $DIR/match-empty-exhaustive_patterns.rs:95:18 | LL | / union NonEmptyUnion2 { LL | | foo: (), @@ -79,7 +103,7 @@ LL | match_empty!((NonEmptyUnion2 { foo: () })); = note: the matched value is of type `NonEmptyUnion2` error[E0004]: non-exhaustive patterns: `Foo(_)` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:72:18 + --> $DIR/match-empty-exhaustive_patterns.rs:97:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), @@ -96,7 +120,7 @@ LL | match_empty!(NonEmptyEnum1::Foo(true)); = note: the matched value is of type `NonEmptyEnum1` error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:74:18 + --> $DIR/match-empty-exhaustive_patterns.rs:99:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), @@ -117,7 +141,7 @@ LL | match_empty!(NonEmptyEnum2::Foo(true)); = note: the matched value is of type `NonEmptyEnum2` error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered - --> $DIR/match-empty-exhaustive_patterns.rs:76:18 + --> $DIR/match-empty-exhaustive_patterns.rs:101:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, @@ -131,7 +155,7 @@ LL | match_empty!(NonEmptyEnum5::V1); = note: the matched value is of type `NonEmptyEnum5` error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:79:18 + --> $DIR/match-empty-exhaustive_patterns.rs:104:18 | LL | match_false!(0u8); | ^^^ pattern `_` not covered @@ -140,7 +164,7 @@ LL | match_false!(0u8); = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:81:18 + --> $DIR/match-empty-exhaustive_patterns.rs:106:18 | LL | struct NonEmptyStruct(bool); | ---------------------------- `NonEmptyStruct` defined here @@ -152,7 +176,7 @@ LL | match_false!(NonEmptyStruct(true)); = note: the matched value is of type `NonEmptyStruct` error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:83:18 + --> $DIR/match-empty-exhaustive_patterns.rs:108:18 | LL | / union NonEmptyUnion1 { LL | | foo: (), @@ -166,7 +190,7 @@ LL | match_false!((NonEmptyUnion1 { foo: () })); = note: the matched value is of type `NonEmptyUnion1` error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:85:18 + --> $DIR/match-empty-exhaustive_patterns.rs:110:18 | LL | / union NonEmptyUnion2 { LL | | foo: (), @@ -181,7 +205,7 @@ LL | match_false!((NonEmptyUnion2 { foo: () })); = note: the matched value is of type `NonEmptyUnion2` error[E0004]: non-exhaustive patterns: `Foo(_)` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:87:18 + --> $DIR/match-empty-exhaustive_patterns.rs:112:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), @@ -198,7 +222,7 @@ LL | match_false!(NonEmptyEnum1::Foo(true)); = note: the matched value is of type `NonEmptyEnum1` error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered - --> $DIR/match-empty-exhaustive_patterns.rs:89:18 + --> $DIR/match-empty-exhaustive_patterns.rs:114:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), @@ -219,7 +243,7 @@ LL | match_false!(NonEmptyEnum2::Foo(true)); = note: the matched value is of type `NonEmptyEnum2` error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered - --> $DIR/match-empty-exhaustive_patterns.rs:91:18 + --> $DIR/match-empty-exhaustive_patterns.rs:116:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, @@ -232,6 +256,6 @@ LL | match_false!(NonEmptyEnum5::V1); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `NonEmptyEnum5` -error: aborting due to 18 previous errors +error: aborting due to 22 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs index f7577125d8a3f..10ea2a10406e3 100644 --- a/src/test/ui/pattern/usefulness/match-empty.rs +++ b/src/test/ui/pattern/usefulness/match-empty.rs @@ -1,6 +1,11 @@ +// aux-build:empty.rs #![feature(never_type)] +#![feature(never_type_fallback)] #![deny(unreachable_patterns)] -enum Foo {} + +extern crate empty; + +enum EmptyEnum {} struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here @@ -40,12 +45,33 @@ macro_rules! match_false { }; } -fn foo(x: Foo) { - match_empty!(x); // ok - match_false!(x); // Not detected as unreachable nor exhaustive. - //~^ ERROR non-exhaustive patterns: `_` not covered +fn empty_enum(x: EmptyEnum) { + match x {} // ok + match x { + _ => {}, //~ ERROR unreachable pattern + } + match x { + _ if false => {}, //~ ERROR unreachable pattern + } +} + +fn empty_foreign_enum(x: empty::EmptyForeignEnum) { + match x {} // ok + match x { + _ => {}, //~ ERROR unreachable pattern + } + match x { + _ if false => {}, //~ ERROR unreachable pattern + } +} + +fn never(x: !) { + match x {} // ok + match x { + _ => {}, //~ ERROR unreachable pattern + } match x { - _ => {}, // Not detected as unreachable, see #55123. + _ if false => {}, //~ ERROR unreachable pattern } } @@ -55,7 +81,7 @@ fn main() { None => {} Some(_) => {} } - match None:: { + match None:: { None => {} Some(_) => {} } diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr index 08095f6e7fb12..6065c552390e4 100644 --- a/src/test/ui/pattern/usefulness/match-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -1,17 +1,47 @@ -error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:45:18 +error: unreachable pattern + --> $DIR/match-empty.rs:51:9 | -LL | enum Foo {} - | ----------- `Foo` defined here -... -LL | match_false!(x); // Not detected as unreachable nor exhaustive. - | ^ pattern `_` not covered +LL | _ => {}, + | ^ | - = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `Foo` +note: the lint level is defined here + --> $DIR/match-empty.rs:4:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/match-empty.rs:54:9 + | +LL | _ if false => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty.rs:61:9 + | +LL | _ => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty.rs:64:9 + | +LL | _ if false => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty.rs:71:9 + | +LL | _ => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty.rs:74:9 + | +LL | _ if false => {}, + | ^ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/match-empty.rs:63:18 + --> $DIR/match-empty.rs:89:18 | LL | match_empty!(0u8); | ^^^ @@ -20,7 +50,7 @@ LL | match_empty!(0u8); = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty - --> $DIR/match-empty.rs:65:18 + --> $DIR/match-empty.rs:91:18 | LL | struct NonEmptyStruct(bool); | ---------------------------- `NonEmptyStruct` defined here @@ -32,7 +62,7 @@ LL | match_empty!(NonEmptyStruct(true)); = note: the matched value is of type `NonEmptyStruct` error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/match-empty.rs:67:18 + --> $DIR/match-empty.rs:93:18 | LL | / union NonEmptyUnion1 { LL | | foo: (), @@ -46,7 +76,7 @@ LL | match_empty!((NonEmptyUnion1 { foo: () })); = note: the matched value is of type `NonEmptyUnion1` error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/match-empty.rs:69:18 + --> $DIR/match-empty.rs:95:18 | LL | / union NonEmptyUnion2 { LL | | foo: (), @@ -61,7 +91,7 @@ LL | match_empty!((NonEmptyUnion2 { foo: () })); = note: the matched value is of type `NonEmptyUnion2` error[E0004]: non-exhaustive patterns: `Foo(_)` not covered - --> $DIR/match-empty.rs:71:18 + --> $DIR/match-empty.rs:97:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), @@ -78,7 +108,7 @@ LL | match_empty!(NonEmptyEnum1::Foo(true)); = note: the matched value is of type `NonEmptyEnum1` error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered - --> $DIR/match-empty.rs:73:18 + --> $DIR/match-empty.rs:99:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), @@ -99,7 +129,7 @@ LL | match_empty!(NonEmptyEnum2::Foo(true)); = note: the matched value is of type `NonEmptyEnum2` error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered - --> $DIR/match-empty.rs:75:18 + --> $DIR/match-empty.rs:101:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, @@ -113,7 +143,7 @@ LL | match_empty!(NonEmptyEnum5::V1); = note: the matched value is of type `NonEmptyEnum5` error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:78:18 + --> $DIR/match-empty.rs:104:18 | LL | match_false!(0u8); | ^^^ pattern `_` not covered @@ -122,7 +152,7 @@ LL | match_false!(0u8); = note: the matched value is of type `u8` error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered - --> $DIR/match-empty.rs:80:18 + --> $DIR/match-empty.rs:106:18 | LL | struct NonEmptyStruct(bool); | ---------------------------- `NonEmptyStruct` defined here @@ -134,7 +164,7 @@ LL | match_false!(NonEmptyStruct(true)); = note: the matched value is of type `NonEmptyStruct` error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered - --> $DIR/match-empty.rs:82:18 + --> $DIR/match-empty.rs:108:18 | LL | / union NonEmptyUnion1 { LL | | foo: (), @@ -148,7 +178,7 @@ LL | match_false!((NonEmptyUnion1 { foo: () })); = note: the matched value is of type `NonEmptyUnion1` error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered - --> $DIR/match-empty.rs:84:18 + --> $DIR/match-empty.rs:110:18 | LL | / union NonEmptyUnion2 { LL | | foo: (), @@ -163,7 +193,7 @@ LL | match_false!((NonEmptyUnion2 { foo: () })); = note: the matched value is of type `NonEmptyUnion2` error[E0004]: non-exhaustive patterns: `Foo(_)` not covered - --> $DIR/match-empty.rs:86:18 + --> $DIR/match-empty.rs:112:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), @@ -180,7 +210,7 @@ LL | match_false!(NonEmptyEnum1::Foo(true)); = note: the matched value is of type `NonEmptyEnum1` error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered - --> $DIR/match-empty.rs:88:18 + --> $DIR/match-empty.rs:114:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), @@ -201,7 +231,7 @@ LL | match_false!(NonEmptyEnum2::Foo(true)); = note: the matched value is of type `NonEmptyEnum2` error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered - --> $DIR/match-empty.rs:90:18 + --> $DIR/match-empty.rs:116:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, @@ -214,6 +244,6 @@ LL | match_false!(NonEmptyEnum5::V1); = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `NonEmptyEnum5` -error: aborting due to 15 previous errors +error: aborting due to 20 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs index afd6d996c15a8..70253a4fc901b 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs @@ -25,7 +25,7 @@ pub enum EmptyNonExhaustiveEnum {} fn empty_non_exhaustive(x: EmptyNonExhaustiveEnum) { match x {} match x { - _ => {} // not detected as unreachable + _ => {} //~ ERROR unreachable pattern } } diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr index 752b08b2b65f1..966f3a2e41485 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr @@ -1,3 +1,15 @@ +error: unreachable pattern + --> $DIR/enum_same_crate_empty_match.rs:28:9 + | +LL | _ => {} + | ^ + | +note: the lint level is defined here + --> $DIR/enum_same_crate_empty_match.rs:1:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + error[E0004]: non-exhaustive patterns: `Unit`, `Tuple(_)` and `Struct { .. }` not covered --> $DIR/enum_same_crate_empty_match.rs:33:11 | @@ -42,6 +54,6 @@ LL | match NormalEnum::Unit {} = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `NormalEnum` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0004`.