From 24eb56890e6ea1ced9afe2961a0271cd36b3c4cc Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 18 Nov 2023 21:39:57 +0100 Subject: [PATCH] Don't warn an empty pattern unreachable if we're not sure the data is valid --- .../src/thir/pattern/deconstruct_pat.rs | 44 +- .../src/thir/pattern/usefulness.rs | 85 +++- .../empty-types.exhaustive_patterns.stderr | 389 +----------------- .../usefulness/empty-types.normal.stderr | 130 +----- tests/ui/pattern/usefulness/empty-types.rs | 128 +++--- tests/ui/pattern/usefulness/slice_of_empty.rs | 6 +- .../pattern/usefulness/slice_of_empty.stderr | 31 +- tests/ui/uninhabited/uninhabited-patterns.rs | 4 +- .../uninhabited/uninhabited-patterns.stderr | 20 +- 9 files changed, 183 insertions(+), 654 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index eea35e6bbacbb..3404621dd89a1 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -783,9 +783,6 @@ pub(super) enum Constructor<'tcx> { } impl<'tcx> Constructor<'tcx> { - pub(super) fn is_wildcard(&self) -> bool { - matches!(self, Wildcard) - } pub(super) fn is_non_exhaustive(&self) -> bool { matches!(self, NonExhaustive) } @@ -969,15 +966,17 @@ pub(super) enum ConstructorSet { /// constructors that exist in the type but are not present in the column. /// /// More formally, if we discard wildcards from the column, this respects the following constraints: -/// 1. the union of `present` and `missing` covers the whole type +/// 1. the union of `present`, `missing` an `missing_empty` covers all the constructors of the type /// 2. each constructor in `present` is covered by something in the column -/// 3. no constructor in `missing` is covered by anything in the column +/// 3. no constructor in `missing` or `missing_empty` is covered by anything in the column /// 4. each constructor in the column is equal to the union of one or more constructors in `present` /// 5. `missing` does not contain empty constructors (see discussion about emptiness at the top of /// the file); -/// 6. constructors in `present` and `missing` are split for the column; in other words, they are -/// either fully included in or fully disjoint from each constructor in the column. In other -/// words, there are no non-trivial intersections like between `0..10` and `5..15`. +/// 6. `missing_empty` contains only empty constructors +/// 7. constructors in `present`, `missing` and `missing_empty` are split for the column; in other +/// words, they are either fully included in or fully disjoint from each constructor in the +/// column. In yet other words, there are no non-trivial intersections like between `0..10` and +/// `5..15`. /// /// We must be particularly careful with weird constructors like `Opaque`: they're not formally part /// of the `ConstructorSet` for the type, yet if we forgot to include them in `present` we would be @@ -986,6 +985,7 @@ pub(super) enum ConstructorSet { pub(super) struct SplitConstructorSet<'tcx> { pub(super) present: SmallVec<[Constructor<'tcx>; 1]>, pub(super) missing: Vec>, + pub(super) missing_empty: Vec>, } impl ConstructorSet { @@ -1134,10 +1134,10 @@ impl ConstructorSet { // Constructors in `ctors`, except wildcards and opaques. let mut seen = Vec::new(); for ctor in ctors.cloned() { - if let Constructor::Opaque(..) = ctor { - present.push(ctor); - } else if !ctor.is_wildcard() { - seen.push(ctor); + match ctor { + Opaque(..) => present.push(ctor), + Wildcard => {} // discard wildcards + _ => seen.push(ctor), } } @@ -1241,16 +1241,24 @@ impl ConstructorSet { missing.push(NonExhaustive); } ConstructorSet::NoConstructors => { - if !pcx.is_top_level { - missing_empty.push(NonExhaustive); - } + // In a `MaybeInvalid` place even an empty pattern may be reachable. We therefore + // add a dummy empty constructor here, which will be ignored if the place is + // `ValidOnly`. + missing_empty.push(NonExhaustive); } } - if !pcx.cx.tcx.features().exhaustive_patterns { - missing.extend(missing_empty); + // We have now grouped all the constructors into 3 buckets: present, missing, missing_empty. + // In the absence of the `exhaustive_patterns` feature however, we don't count nested empty + // types as empty. Only non-nested `!` or `enum Foo {}` are considered empty. + if !pcx.cx.tcx.features().exhaustive_patterns + && !(pcx.is_top_level && matches!(self, Self::NoConstructors)) + { + // Treat all missing constructors as nonempty. + missing.extend(missing_empty.drain(..)); } - SplitConstructorSet { present, missing } + + SplitConstructorSet { present, missing, missing_empty } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index f0fc50547dd15..18ca57f9efd16 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -636,12 +636,18 @@ impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> { } } -/// In the matrix, tracks whether a given place (aka column) is known to contain a valid value or -/// not. +/// Serves two purposes: +/// - in a wildcard, tracks whether the wildcard matches only valid values (i.e. is a binding `_a`) +/// or also invalid values (i.e. is a true `_` pattern). +/// - in the matrix, track whether a given place (aka column) is known to contain a valid value or +/// not. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(super) enum ValidityConstraint { ValidOnly, MaybeInvalid, + /// Option for backwards compatibility: the place is not known to be valid but we allow omitting + /// `useful && !reachable` arms anyway. + MaybeInvalidButAllowOmittingArms, } impl ValidityConstraint { @@ -649,19 +655,37 @@ impl ValidityConstraint { if is_valid_only { ValidOnly } else { MaybeInvalid } } + fn allow_omitting_side_effecting_arms(self) -> Self { + match self { + MaybeInvalid | MaybeInvalidButAllowOmittingArms => MaybeInvalidButAllowOmittingArms, + // There are no side-effecting empty arms here, nothing to do. + ValidOnly => ValidOnly, + } + } + + pub(super) fn is_known_valid(self) -> bool { + matches!(self, ValidOnly) + } + pub(super) fn allows_omitting_empty_arms(self) -> bool { + matches!(self, ValidOnly | MaybeInvalidButAllowOmittingArms) + } + /// If the place has validity given by `self` and we read that the value at the place has /// constructor `ctor`, this computes what we can assume about the validity of the constructor /// fields. /// /// Pending further opsem decisions, the current behavior is: validity is preserved, except - /// under `&` where validity is reset to `MaybeInvalid`. + /// inside `&` and union fields where validity is reset to `MaybeInvalid`. pub(super) fn specialize<'tcx>( self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor<'tcx>, ) -> Self { - // We preserve validity except when we go under a reference. - if matches!(ctor, Constructor::Single) && matches!(pcx.ty.kind(), ty::Ref(..)) { + // We preserve validity except when we go inside a reference or a union field. + if matches!(ctor, Constructor::Single) + && (matches!(pcx.ty.kind(), ty::Ref(..)) + || matches!(pcx.ty.kind(), ty::Adt(def, ..) if def.is_union())) + { // Validity of `x: &T` does not imply validity of `*x: T`. MaybeInvalid } else { @@ -674,7 +698,7 @@ impl fmt::Display for ValidityConstraint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match self { ValidOnly => "✓", - MaybeInvalid => "?", + MaybeInvalid | MaybeInvalidButAllowOmittingArms => "?", }; write!(f, "{s}") } @@ -1197,9 +1221,9 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>( for row in matrix.rows_mut() { // All rows are useful until they're not. row.useful = true; + // When there's an unguarded row, the match is exhaustive and any subsequent row is not + // useful. if !row.is_under_guard { - // There's an unguarded row, so the match is exhaustive, and any subsequent row is - // unreachable. return WitnessMatrix::empty(); } } @@ -1210,26 +1234,37 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>( debug!("ty: {ty:?}"); let pcx = &PatCtxt { cx, ty, is_top_level }; + // Whether the place/column we are inspecting is known to contain valid data. + let place_validity = matrix.place_validity[0]; + // For backwards compability we allow omitting some empty arms that we ideally shouldn't. + let place_validity = place_validity.allow_omitting_side_effecting_arms(); + // Analyze the constructors present in this column. let ctors = matrix.heads().map(|p| p.ctor()); - let split_set = ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, ctors); - + let split_set = ConstructorSet::for_ty(cx, ty).split(pcx, ctors); let all_missing = split_set.present.is_empty(); - let always_report_all = is_top_level && !IntRange::is_integral(pcx.ty); - // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing". - let report_individual_missing_ctors = always_report_all || !all_missing; + // Build the set of constructors we will specialize with. It must cover the whole type. let mut split_ctors = split_set.present; - let mut only_report_missing = false; if !split_set.missing.is_empty() { // We need to iterate over a full set of constructors, so we add `Missing` to represent the // missing ones. This is explained under "Constructor Splitting" at the top of this file. split_ctors.push(Constructor::Missing); - // For diagnostic purposes we choose to only report the constructors that are missing. Since - // `Missing` matches only the wildcard rows, it matches fewer rows than any normal - // constructor and is therefore guaranteed to result in more witnesses. So skipping the - // other constructors does not jeopardize correctness. - only_report_missing = true; + } else if !split_set.missing_empty.is_empty() && !place_validity.is_known_valid() { + // The missing empty constructors are reachable if the place can contain invalid data. + split_ctors.push(Constructor::Missing); + } + + // Decide what constructors to report. + let always_report_all = is_top_level && !IntRange::is_integral(pcx.ty); + // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing". + let report_individual_missing_ctors = always_report_all || !all_missing; + // Which constructors are considered missing. We ensure that `!missing_ctors.is_empty() => + // split_ctors.contains(Missing)`. The converse usually holds except in the + // `MaybeInvalidButAllowOmittingArms` backwards-compatibility case. + let mut missing_ctors = split_set.missing; + if !place_validity.allows_omitting_empty_arms() { + missing_ctors.extend(split_set.missing_empty); } let mut ret = WitnessMatrix::empty(); @@ -1241,11 +1276,19 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>( compute_exhaustiveness_and_usefulness(cx, &mut spec_matrix, false) }); - if !only_report_missing || matches!(ctor, Constructor::Missing) { + let counts_for_exhaustiveness = match ctor { + Constructor::Missing => !missing_ctors.is_empty(), + // If there are missing constructors we'll report those instead. Since `Missing` matches + // only the wildcard rows, it matches fewer rows than this constructor, and is therefore + // guaranteed to result in the same or more witnesses. So skipping this does not + // jeopardize correctness. + _ => missing_ctors.is_empty(), + }; + if counts_for_exhaustiveness { // Transform witnesses for `spec_matrix` into witnesses for `matrix`. witnesses.apply_constructor( pcx, - &split_set.missing, + &missing_ctors, &ctor, report_individual_missing_ctors, ); diff --git a/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr index 9a53b54704e9b..367aba3bdd6e6 100644 --- a/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr @@ -31,12 +31,6 @@ LL + _ => todo!(), LL + } | -error: unreachable pattern - --> $DIR/empty-types.rs:62:9 - | -LL | &_ => {} - | ^^ - error: unreachable pattern --> $DIR/empty-types.rs:69:9 | @@ -184,42 +178,6 @@ error: unreachable pattern LL | _ => {} | ^ -error: unreachable pattern - --> $DIR/empty-types.rs:158:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:167:13 - | -LL | Some(_) => {} - | ^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:171:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:175:13 - | -LL | _a => {} - | ^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:180:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:185:13 - | -LL | _ => {} - | ^ - error: unreachable pattern --> $DIR/empty-types.rs:204:13 | @@ -250,48 +208,6 @@ error: unreachable pattern LL | _ => {} | ^ -error: unreachable pattern - --> $DIR/empty-types.rs:234:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:239:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:245:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:251:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:256:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:262:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:268:13 - | -LL | _ => {} - | ^ - error: unreachable pattern --> $DIR/empty-types.rs:284:9 | @@ -316,18 +232,6 @@ error: unreachable pattern LL | Err(_) => {} | ^^^^^^ -error: unreachable pattern - --> $DIR/empty-types.rs:296:9 - | -LL | &_ => {} - | ^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:299:9 - | -LL | Uninit { value: _ } => {} - | ^^^^^^^^^^^^^^^^^^^ - error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty --> $DIR/empty-types.rs:323:11 | @@ -342,24 +246,6 @@ LL + _ => todo!(), LL + } | -error: unreachable pattern - --> $DIR/empty-types.rs:331:9 - | -LL | [_] => {} - | ^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:332:9 - | -LL | [_, _, ..] => {} - | ^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:337:9 - | -LL | [_, _, _, ..] => {} - | ^^^^^^^^^^^^^ - error[E0004]: non-exhaustive patterns: `&[]` not covered --> $DIR/empty-types.rs:334:11 | @@ -369,20 +255,9 @@ LL | match slice_never { = note: the matched value is of type `&[!]` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | -LL | [_, _, _, ..] => {}, &[] => todo!() - | ++++++++++++++++ - -error: unreachable pattern - --> $DIR/empty-types.rs:341:9 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:345:9 +LL ~ [_, _, _, ..] => {}, +LL + &[] => todo!() | -LL | _x => {} - | ^^ error[E0004]: non-exhaustive patterns: `&[]` not covered --> $DIR/empty-types.rs:347:11 @@ -430,18 +305,6 @@ error: unreachable pattern LL | [_, ..] => {} | ^^^^^^^ -error: unreachable pattern - --> $DIR/empty-types.rs:375:9 - | -LL | &[_, _, _] => {} - | ^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:379:9 - | -LL | &[_x, _, _] => {} - | ^^^^^^^^^^^ - error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty --> $DIR/empty-types.rs:383:11 | @@ -500,186 +363,6 @@ error: unreachable pattern LL | _a => {} | ^^ -error: unreachable pattern - --> $DIR/empty-types.rs:436:9 - | -LL | &_ => {} - | ^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:444:9 - | -LL | &_a => {} - | ^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:453:9 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:458:9 - | -LL | _a => {} - | ^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:463:9 - | -LL | &_ => {} - | ^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:468:9 - | -LL | &_a => {} - | ^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:475:9 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:479:9 - | -LL | _a => {} - | ^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:485:9 - | -LL | ref _a => {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:494:9 - | -LL | Some(_) => {} - | ^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:499:9 - | -LL | Some(_a) => {} - | ^^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:504:9 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:509:9 - | -LL | _a => {} - | ^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:514:14 - | -LL | _a @ Some(_) => {} - | ^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:521:9 - | -LL | ref _a => {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:526:18 - | -LL | ref _a @ Some(_) => {} - | ^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:531:18 - | -LL | ref _a @ Some(_b) => {} - | ^^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:538:9 - | -LL | Ok(_) => {} - | ^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:542:9 - | -LL | Ok(_) => {} - | ^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:544:9 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:549:9 - | -LL | Ok(_a) => {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:553:9 - | -LL | Ok(_a) => {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:555:9 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:559:9 - | -LL | Ok(_a) => {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:561:9 - | -LL | Err(_) => {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:569:9 - | -LL | (_, _) => {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:573:9 - | -LL | (_x, _) => {} - | ^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:577:9 - | -LL | (_, _x) => {} - | ^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:581:9 - | -LL | (0, _x) => {} - | ^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:583:9 - | -LL | (1.., _) => {} - | ^^^^^^^^ - error: unreachable pattern --> $DIR/empty-types.rs:598:9 | @@ -704,73 +387,7 @@ error: unreachable pattern LL | _x if false => {} | ^^ -error: unreachable pattern - --> $DIR/empty-types.rs:613:9 - | -LL | _ if false => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:615:9 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:622:9 - | -LL | _a if false => {} - | ^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:624:9 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:629:9 - | -LL | _a if false => {} - | ^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:634:9 - | -LL | &_a if false => {} - | ^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:641:9 - | -LL | Ok(_x) if false => {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:643:9 - | -LL | Ok(_) => {} - | ^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:645:9 - | -LL | Err(_) => {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:650:9 - | -LL | (_, _x) if false => {} - | ^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:652:9 - | -LL | (_, _) => {} - | ^^^^^^ - -error: aborting due to 113 previous errors +error: aborting due to 49 previous errors Some errors have detailed explanations: E0004, E0005. For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/empty-types.normal.stderr b/tests/ui/pattern/usefulness/empty-types.normal.stderr index b066393a61e60..133a95a821ba3 100644 --- a/tests/ui/pattern/usefulness/empty-types.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-types.normal.stderr @@ -234,12 +234,6 @@ LL ~ None => {}, LL + Some(_) => todo!() | -error: unreachable pattern - --> $DIR/empty-types.rs:158:13 - | -LL | _ => {} - | ^ - error[E0004]: non-exhaustive patterns: `Some(_)` not covered --> $DIR/empty-types.rs:161:15 | @@ -258,18 +252,6 @@ LL ~ None => {}, LL + Some(_) => todo!() | -error: unreachable pattern - --> $DIR/empty-types.rs:180:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:185:13 - | -LL | _ => {} - | ^ - error: unreachable pattern --> $DIR/empty-types.rs:204:13 | @@ -300,48 +282,6 @@ error: unreachable pattern LL | _ => {} | ^ -error: unreachable pattern - --> $DIR/empty-types.rs:234:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:239:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:245:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:251:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:256:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:262:13 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:268:13 - | -LL | _ => {} - | ^ - error: unreachable pattern --> $DIR/empty-types.rs:284:9 | @@ -448,8 +388,9 @@ LL | match slice_never { = note: the matched value is of type `&[!]` 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 | -LL | [_, _, _, ..] => {}, &[] | &[_] | &[_, _] => todo!() - | +++++++++++++++++++++++++++++++++ +LL ~ [_, _, _, ..] => {}, +LL + &[] | &[_] | &[_, _] => todo!() + | error[E0004]: non-exhaustive patterns: `&[]` and `&[_, ..]` not covered --> $DIR/empty-types.rs:347:11 @@ -545,24 +486,6 @@ LL ~ &None => {}, LL + &Some(_) => todo!() | -error: unreachable pattern - --> $DIR/empty-types.rs:475:9 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:479:9 - | -LL | _a => {} - | ^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:485:9 - | -LL | ref _a => {} - | ^^^^^^ - error[E0004]: non-exhaustive patterns: `Some(_)` not covered --> $DIR/empty-types.rs:487:11 | @@ -595,8 +518,9 @@ note: `Result` defined here = note: the matched value is of type `Result` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | -LL | Ok(_) => {}, Err(_) => todo!() - | +++++++++++++++++++ +LL ~ Ok(_) => {}, +LL + Err(_) => todo!() + | error[E0004]: non-exhaustive patterns: `Err(_)` not covered --> $DIR/empty-types.rs:546:11 @@ -612,8 +536,9 @@ note: `Result` defined here = note: the matched value is of type `Result` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | -LL | Ok(_a) => {}, Err(_) => todo!() - | +++++++++++++++++++ +LL ~ Ok(_a) => {}, +LL + Err(_) => todo!() + | error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty --> $DIR/empty-types.rs:565:11 @@ -653,36 +578,6 @@ error: unreachable pattern LL | _x if false => {} | ^^ -error: unreachable pattern - --> $DIR/empty-types.rs:613:9 - | -LL | _ if false => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:615:9 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:622:9 - | -LL | _a if false => {} - | ^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:624:9 - | -LL | _ => {} - | ^ - -error: unreachable pattern - --> $DIR/empty-types.rs:629:9 - | -LL | _a if false => {} - | ^^ - error[E0004]: non-exhaustive patterns: `&_` not covered --> $DIR/empty-types.rs:631:11 | @@ -694,8 +589,9 @@ LL | match ref_never { = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | -LL | &_a if false => {}, &_ => todo!() - | +++++++++++++++ +LL ~ &_a if false => {}, +LL + &_ => todo!() + | error[E0004]: non-exhaustive patterns: `Some(_)` not covered --> $DIR/empty-types.rs:659:11 @@ -715,7 +611,7 @@ LL ~ None => {}, LL + Some(_) => todo!() | -error: aborting due to 66 previous errors +error: aborting due to 48 previous errors Some errors have detailed explanations: E0004, E0005. For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/empty-types.rs b/tests/ui/pattern/usefulness/empty-types.rs index fc422298a3192..1e1d23e446d4c 100644 --- a/tests/ui/pattern/usefulness/empty-types.rs +++ b/tests/ui/pattern/usefulness/empty-types.rs @@ -59,7 +59,7 @@ fn basic(x: NeverBundle) { } match ref_never { // useful, reachable - &_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern + &_ => {} } let tuple_half_never: (u32, !) = x.tuple_half_never; @@ -155,7 +155,7 @@ fn void_same_as_never(x: NeverBundle) { let ref_void: &Void = &x.void; match *ref_void {} match *ref_void { - _ => {} //~ ERROR unreachable pattern + _ => {} } let ref_opt_void: &Option = &None; match *ref_opt_void { @@ -164,25 +164,25 @@ fn void_same_as_never(x: NeverBundle) { } match *ref_opt_void { None => {} - Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Some(_) => {} } match *ref_opt_void { None => {} - _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern + _ => {} } match *ref_opt_void { None => {} - _a => {} //[exhaustive_patterns]~ ERROR unreachable pattern + _a => {} } let union_void = Uninit::::new(); match union_void.value {} match union_void.value { - _ => {} //~ ERROR unreachable pattern + _ => {} } let ptr_void: *const Void = std::ptr::null(); match *ptr_void {} match *ptr_void { - _ => {} //~ ERROR unreachable pattern + _ => {} } } } @@ -231,41 +231,41 @@ fn invalid_scrutinees(x: NeverBundle) { // A pointer may point to a place with an invalid value. match *ptr_never {} match *ptr_never { - _ => {} //~ ERROR unreachable pattern + _ => {} } // A reference may point to a place with an invalid value. match *ref_never {} match *ref_never { - _ => {} //~ ERROR unreachable pattern + _ => {} } // This field access is a dereference. let ref_x: &NeverBundle = &x; match ref_x.never {} match ref_x.never { - _ => {} //~ ERROR unreachable pattern + _ => {} } // This nested field access is a dereference. let nested_ref_x: &NestedNeverBundle = &nested_x; match nested_ref_x.0.never {} match nested_ref_x.0.never { - _ => {} //~ ERROR unreachable pattern + _ => {} } // A cast does not load. match (*ptr_never as Void) {} match (*ptr_never as Void) { - _ => {} //~ ERROR unreachable pattern + _ => {} } // A union field may contain invalid data. let union_never = Uninit::::new(); match union_never.value {} match union_never.value { - _ => {} //~ ERROR unreachable pattern + _ => {} } // Indexing is like a field access. This one accesses behind a reference. let slice_never: &[!] = &[]; match slice_never[0] {} match slice_never[0] { - _ => {} //~ ERROR unreachable pattern + _ => {} } } } @@ -293,10 +293,10 @@ fn nested_validity_tracking(bundle: NeverBundle) { // These should be considered !known_valid and not warn unreachable. match ref_never { - &_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern + &_ => {} } match union_never { - Uninit { value: _ } => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Uninit { value: _ } => {} } } @@ -328,21 +328,21 @@ fn arrays_and_slices(x: NeverBundle) { } match slice_never { [] => {} - [_] => {} //[exhaustive_patterns]~ ERROR unreachable pattern - [_, _, ..] => {} //[exhaustive_patterns]~ ERROR unreachable pattern + [_] => {} + [_, _, ..] => {} } match slice_never { //[normal]~^ ERROR `&[]`, `&[_]` and `&[_, _]` not covered //[exhaustive_patterns]~^^ ERROR `&[]` not covered - [_, _, _, ..] => {} //[exhaustive_patterns]~ ERROR unreachable pattern + [_, _, _, ..] => {} } match slice_never { [] => {} - _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern + _ => {} } match slice_never { [] => {} - _x => {} //[exhaustive_patterns]~ ERROR unreachable pattern + _x => {} } match slice_never { //[normal]~^ ERROR `&[]` and `&[_, ..]` not covered @@ -372,11 +372,11 @@ fn arrays_and_slices(x: NeverBundle) { let ref_array_3_never: &[!; 3] = &array_3_never; match ref_array_3_never { // useful, reachable - &[_, _, _] => {} //[exhaustive_patterns]~ ERROR unreachable pattern + &[_, _, _] => {} } match ref_array_3_never { // useful, !reachable - &[_x, _, _] => {} //[exhaustive_patterns]~ ERROR unreachable pattern + &[_x, _, _] => {} } let array_0_never: [!; 0] = []; @@ -433,7 +433,7 @@ fn bindings(x: NeverBundle) { } match ref_never { // useful, reachable - &_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern + &_ => {} } match ref_never { // useful, reachable @@ -441,7 +441,7 @@ fn bindings(x: NeverBundle) { } match ref_never { // useful, !reachable - &_a => {} //[exhaustive_patterns]~ ERROR unreachable pattern + &_a => {} } match ref_opt_never { //[normal]~^ ERROR non-exhaustive @@ -450,39 +450,39 @@ fn bindings(x: NeverBundle) { match ref_opt_never { &None => {} // useful, reachable - _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern + _ => {} } match ref_opt_never { &None => {} // useful, reachable - _a => {} //[exhaustive_patterns]~ ERROR unreachable pattern + _a => {} } match ref_opt_never { &None => {} // useful, reachable - &_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern + &_ => {} } match ref_opt_never { &None => {} // useful, !reachable - &_a => {} //[exhaustive_patterns]~ ERROR unreachable pattern + &_a => {} } // On a !known_valid place. match *ref_never {} match *ref_never { // useful, reachable - _ => {} //~ ERROR unreachable pattern + _ => {} } match *ref_never { // useful, !reachable - _a => {} //~ ERROR unreachable pattern + _a => {} } // This is equivalent to `match ref_never { _a => {} }`. In other words, it asserts validity of // `ref_never` but says nothing of the data at `*ref_never`. match *ref_never { // useful, reachable - ref _a => {} //~ ERROR unreachable pattern + ref _a => {} } match *ref_opt_never { //[normal]~^ ERROR non-exhaustive @@ -491,74 +491,74 @@ fn bindings(x: NeverBundle) { match *ref_opt_never { None => {} // useful, reachable - Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Some(_) => {} } match *ref_opt_never { None => {} // useful, !reachable - Some(_a) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Some(_a) => {} } match *ref_opt_never { None => {} // useful, reachable - _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern + _ => {} } match *ref_opt_never { None => {} // useful, !reachable - _a => {} //[exhaustive_patterns]~ ERROR unreachable pattern + _a => {} } match *ref_opt_never { None => {} // useful, !reachable - _a @ Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + _a @ Some(_) => {} } // This is equivalent to `match ref_opt_never { None => {}, _a => {} }`. In other words, it // asserts validity of `ref_opt_never` but says nothing of the data at `*ref_opt_never`. match *ref_opt_never { None => {} // useful, reachable - ref _a => {} //[exhaustive_patterns]~ ERROR unreachable pattern + ref _a => {} } match *ref_opt_never { None => {} // useful, reachable - ref _a @ Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + ref _a @ Some(_) => {} } match *ref_opt_never { None => {} // useful, !reachable - ref _a @ Some(_b) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + ref _a @ Some(_b) => {} } let ref_res_never: &Result = &x.result_never; match *ref_res_never { //[normal]~^ ERROR non-exhaustive // useful, reachable - Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Ok(_) => {} } match *ref_res_never { // useful, reachable - Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Ok(_) => {} // useful, reachable - _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern + _ => {} } match *ref_res_never { //[normal]~^ ERROR non-exhaustive // useful, !reachable - Ok(_a) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Ok(_a) => {} } match *ref_res_never { // useful, !reachable - Ok(_a) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Ok(_a) => {} // useful, reachable - _ => {} //[exhaustive_patterns]~ ERROR unreachable pattern + _ => {} } match *ref_res_never { // useful, !reachable - Ok(_a) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Ok(_a) => {} // useful, reachable - Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Err(_) => {} } let ref_tuple_half_never: &(u32, !) = &x.tuple_half_never; @@ -566,21 +566,21 @@ fn bindings(x: NeverBundle) { //[normal]~^ ERROR non-empty match *ref_tuple_half_never { // useful, reachable - (_, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + (_, _) => {} } match *ref_tuple_half_never { // useful, reachable - (_x, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + (_x, _) => {} } match *ref_tuple_half_never { // useful, !reachable - (_, _x) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + (_, _x) => {} } match *ref_tuple_half_never { // useful, !reachable - (0, _x) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + (0, _x) => {} // useful, reachable - (1.., _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + (1.., _) => {} } } @@ -610,46 +610,46 @@ fn guards_and_validity(x: NeverBundle) { // If the pattern under the guard doesn't load, all is normal. match *ref_never { // useful, reachable - _ if false => {} //~ ERROR unreachable pattern + _ if false => {} // useful, reachable - _ => {} //~ ERROR unreachable pattern + _ => {} } // Now the madness commences. The guard caused a load of the value thus asserting validity. So // there's no invalid value for `_` to catch. So the second pattern is unreachable despite the // guard not being taken. match *ref_never { // useful, !reachable - _a if false => {} //~ ERROR unreachable pattern + _a if false => {} // !useful, !reachable - _ => {} //~ ERROR unreachable pattern + _ => {} } // The above still applies to the implicit `_` pattern used for exhaustiveness. match *ref_never { // useful, !reachable - _a if false => {} //~ ERROR unreachable pattern + _a if false => {} } match ref_never { //[normal]~^ ERROR non-exhaustive // useful, !reachable - &_a if false => {} //[exhaustive_patterns]~ ERROR unreachable pattern + &_a if false => {} } // Same but with subpatterns. let ref_result_never: &Result = &x.result_never; match *ref_result_never { // useful, !reachable - Ok(_x) if false => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Ok(_x) if false => {} // !useful, !reachable - Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Ok(_) => {} // useful, !reachable - Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + Err(_) => {} } let ref_tuple_never: &(!, !) = &x.tuple_never; match *ref_tuple_never { // useful, !reachable - (_, _x) if false => {} //[exhaustive_patterns]~ ERROR unreachable pattern + (_, _x) if false => {} // !useful, !reachable - (_, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern + (_, _) => {} } } diff --git a/tests/ui/pattern/usefulness/slice_of_empty.rs b/tests/ui/pattern/usefulness/slice_of_empty.rs index fe068871195a2..3cbd0eba57fd1 100644 --- a/tests/ui/pattern/usefulness/slice_of_empty.rs +++ b/tests/ui/pattern/usefulness/slice_of_empty.rs @@ -11,12 +11,12 @@ fn foo(nevers: &[!]) { match nevers { &[] => (), - &[_] => (), //~ ERROR unreachable pattern - &[_, _, ..] => (), //~ ERROR unreachable pattern + &[_] => (), + &[_, _, ..] => (), }; match nevers { //~^ ERROR non-exhaustive patterns: `&[]` not covered - &[_] => (), //~ ERROR unreachable pattern + &[_] => (), }; } diff --git a/tests/ui/pattern/usefulness/slice_of_empty.stderr b/tests/ui/pattern/usefulness/slice_of_empty.stderr index 07bb6b3a67d8e..d56360d4cec07 100644 --- a/tests/ui/pattern/usefulness/slice_of_empty.stderr +++ b/tests/ui/pattern/usefulness/slice_of_empty.stderr @@ -1,27 +1,3 @@ -error: unreachable pattern - --> $DIR/slice_of_empty.rs:14:9 - | -LL | &[_] => (), - | ^^^^ - | -note: the lint level is defined here - --> $DIR/slice_of_empty.rs:3:9 - | -LL | #![deny(unreachable_patterns)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/slice_of_empty.rs:15:9 - | -LL | &[_, _, ..] => (), - | ^^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/slice_of_empty.rs:20:9 - | -LL | &[_] => (), - | ^^^^ - error[E0004]: non-exhaustive patterns: `&[]` not covered --> $DIR/slice_of_empty.rs:18:11 | @@ -31,9 +7,10 @@ LL | match nevers { = note: the matched value is of type `&[!]` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | -LL | &[_] => (), &[] => todo!(), - | ++++++++++++++++ +LL ~ &[_] => (), +LL ~ &[] => todo!(), + | -error: aborting due to 4 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/uninhabited/uninhabited-patterns.rs b/tests/ui/uninhabited/uninhabited-patterns.rs index 43b19e790e2f8..4e90691e5c809 100644 --- a/tests/ui/uninhabited/uninhabited-patterns.rs +++ b/tests/ui/uninhabited/uninhabited-patterns.rs @@ -22,14 +22,14 @@ fn main() { match x { &[] => (), - &[..] => (), //~ ERROR unreachable pattern + &[..] => (), }; let x: Result, &[Result]> = Err(&[]); match x { Ok(box _) => (), //~ ERROR unreachable pattern Err(&[]) => (), - Err(&[..]) => (), //~ ERROR unreachable pattern + Err(&[..]) => (), } let x: Result> = Err(Err(123)); diff --git a/tests/ui/uninhabited/uninhabited-patterns.stderr b/tests/ui/uninhabited/uninhabited-patterns.stderr index 19f34a52bdbe5..a6fda88f03281 100644 --- a/tests/ui/uninhabited/uninhabited-patterns.stderr +++ b/tests/ui/uninhabited/uninhabited-patterns.stderr @@ -1,8 +1,8 @@ error: unreachable pattern - --> $DIR/uninhabited-patterns.rs:25:9 + --> $DIR/uninhabited-patterns.rs:30:9 | -LL | &[..] => (), - | ^^^^^ +LL | Ok(box _) => (), + | ^^^^^^^^^ | note: the lint level is defined here --> $DIR/uninhabited-patterns.rs:4:9 @@ -10,18 +10,6 @@ note: the lint level is defined here LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ -error: unreachable pattern - --> $DIR/uninhabited-patterns.rs:30:9 - | -LL | Ok(box _) => (), - | ^^^^^^^^^ - -error: unreachable pattern - --> $DIR/uninhabited-patterns.rs:32:9 - | -LL | Err(&[..]) => (), - | ^^^^^^^^^^ - error: unreachable pattern --> $DIR/uninhabited-patterns.rs:39:9 | @@ -34,5 +22,5 @@ error: unreachable pattern LL | while let Some(_y) = foo() { | ^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors