From b025813f03033380f8761c1b0183fbe33884d3ec Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 12 Nov 2020 17:28:55 +0000 Subject: [PATCH] Handle empty matches cleanly --- .../src/thir/pattern/_match.rs | 36 ++++++---- .../src/thir/pattern/check_match.rs | 18 ----- src/test/ui/pattern/usefulness/match-empty.rs | 10 ++- .../ui/pattern/usefulness/match-empty.stderr | 69 +++++++++++-------- .../enum_same_crate_empty_match.rs | 2 +- .../enum_same_crate_empty_match.stderr | 14 +++- 6 files changed, 78 insertions(+), 71 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 5e7e81eba627..f3fa0ec5fb70 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -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| { @@ -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 @@ -1585,11 +1587,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 +2022,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 +2050,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. @@ -2200,7 +2206,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 +2221,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); 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 205ad850c0c8..c6fd7bcbf90d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -439,24 +439,6 @@ fn check_exhaustive<'p, 'tcx>( hir_id: HirId, 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, diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs index 609660a88f9b..835df1f551b1 100644 --- a/src/test/ui/pattern/usefulness/match-empty.rs +++ b/src/test/ui/pattern/usefulness/match-empty.rs @@ -44,22 +44,20 @@ macro_rules! match_false { fn foo(x: Foo) { match x {} // ok match x { - _ => {}, // Not detected as unreachable, see #55123. + _ => {}, //~ ERROR unreachable pattern } match x { - //~^ ERROR non-exhaustive patterns: `_` not covered - _ if false => {}, // Not detected as unreachable nor exhaustive. + _ if false => {}, //~ ERROR unreachable pattern } } fn never(x: !) { match x {} // ok match x { - _ => {}, // Not detected as unreachable. + _ => {}, //~ ERROR unreachable pattern } match x { - //~^ ERROR non-exhaustive patterns: `_` not covered - _ if false => {}, // Not detected as unreachable nor exhaustive. + _ if false => {}, //~ ERROR unreachable pattern } } diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr index 0218b6fda50b..af666b3a9218 100644 --- a/src/test/ui/pattern/usefulness/match-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -1,26 +1,35 @@ -error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:49:11 +error: unreachable pattern + --> $DIR/match-empty.rs:47:9 | -LL | enum Foo {} - | ----------- `Foo` defined here -... -LL | match x { - | ^ 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:3:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ -error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/match-empty.rs:60:11 +error: unreachable pattern + --> $DIR/match-empty.rs:50:9 | -LL | match x { - | ^ pattern `_` not covered +LL | _ if false => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty.rs:57:9 | - = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - = note: the matched value is of type `!` +LL | _ => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty.rs:60:9 + | +LL | _ if false => {}, + | ^ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/match-empty.rs:77:18 + --> $DIR/match-empty.rs:75:18 | LL | match_empty!(0u8); | ^^^ @@ -29,7 +38,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:79:18 + --> $DIR/match-empty.rs:77:18 | LL | struct NonEmptyStruct(bool); | ---------------------------- `NonEmptyStruct` defined here @@ -41,7 +50,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:81:18 + --> $DIR/match-empty.rs:79:18 | LL | / union NonEmptyUnion1 { LL | | foo: (), @@ -55,7 +64,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:83:18 + --> $DIR/match-empty.rs:81:18 | LL | / union NonEmptyUnion2 { LL | | foo: (), @@ -70,7 +79,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:85:18 + --> $DIR/match-empty.rs:83:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), @@ -87,7 +96,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:87:18 + --> $DIR/match-empty.rs:85:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), @@ -108,7 +117,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:89:18 + --> $DIR/match-empty.rs:87:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, @@ -122,7 +131,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:92:18 + --> $DIR/match-empty.rs:90:18 | LL | match_false!(0u8); | ^^^ pattern `_` not covered @@ -131,7 +140,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:94:18 + --> $DIR/match-empty.rs:92:18 | LL | struct NonEmptyStruct(bool); | ---------------------------- `NonEmptyStruct` defined here @@ -143,7 +152,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:96:18 + --> $DIR/match-empty.rs:94:18 | LL | / union NonEmptyUnion1 { LL | | foo: (), @@ -157,7 +166,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:98:18 + --> $DIR/match-empty.rs:96:18 | LL | / union NonEmptyUnion2 { LL | | foo: (), @@ -172,7 +181,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:100:18 + --> $DIR/match-empty.rs:98:18 | LL | / enum NonEmptyEnum1 { LL | | Foo(bool), @@ -189,7 +198,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:102:18 + --> $DIR/match-empty.rs:100:18 | LL | / enum NonEmptyEnum2 { LL | | Foo(bool), @@ -210,7 +219,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:104:18 + --> $DIR/match-empty.rs:102:18 | LL | / enum NonEmptyEnum5 { LL | | V1, V2, V3, V4, V5, @@ -223,6 +232,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 16 previous errors +error: aborting due to 18 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 afd6d996c15a..9b52c5b59918 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 752b08b2b65f..07582e0ce32b 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`.