Skip to content

never patterns: suggest ! patterns on non-exhaustive matches #121823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 39 additions & 35 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
@@ -939,43 +939,30 @@ fn report_non_exhaustive_match<'p, 'tcx>(
};
// In the case of an empty match, replace the '`_` not covered' diagnostic with something more
// informative.
let mut err;
let pattern;
let patterns_len;
if is_empty_match && !non_empty_enum {
return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty {
cx,
expr_span,
span: sp,
ty: scrut_ty,
});
} else {
// FIXME: migration of this diagnostic will require list support
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
err = create_e0004(
cx.tcx.sess,
sp,
format!("non-exhaustive patterns: {joined_patterns} not covered"),
);
err.span_label(
sp,
format!(
"pattern{} {} not covered",
rustc_errors::pluralize!(witnesses.len()),
joined_patterns
),
);
patterns_len = witnesses.len();
pattern = if witnesses.len() < 4 {
witnesses
.iter()
.map(|witness| cx.hoist_witness_pat(witness).to_string())
.collect::<Vec<String>>()
.join(" | ")
} else {
"_".to_string()
};
};
}

// FIXME: migration of this diagnostic will require list support
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
let mut err = create_e0004(
cx.tcx.sess,
sp,
format!("non-exhaustive patterns: {joined_patterns} not covered"),
);
err.span_label(
sp,
format!(
"pattern{} {} not covered",
rustc_errors::pluralize!(witnesses.len()),
joined_patterns
),
);

// Point at the definition of non-covered `enum` variants.
if let Some(AdtDefinedHere { adt_def_span, ty, variants }) =
@@ -1022,6 +1009,23 @@ fn report_non_exhaustive_match<'p, 'tcx>(
}
}

// Whether we suggest the actual missing patterns or `_`.
let suggest_the_witnesses = witnesses.len() < 4;
let suggested_arm = if suggest_the_witnesses {
let pattern = witnesses
.iter()
.map(|witness| cx.hoist_witness_pat(witness).to_string())
.collect::<Vec<String>>()
.join(" | ");
if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns {
// Arms with a never pattern don't take a body.
pattern
} else {
format!("{pattern} => todo!()")
}
} else {
format!("_ => todo!()")
};
let mut suggestion = None;
let sm = cx.tcx.sess.source_map();
match arms {
@@ -1034,7 +1038,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
};
suggestion = Some((
sp.shrink_to_hi().with_hi(expr_span.hi()),
format!(" {{{indentation}{more}{pattern} => todo!(),{indentation}}}",),
format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
));
}
[only] => {
@@ -1060,7 +1064,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
};
suggestion = Some((
only.span.shrink_to_hi(),
format!("{comma}{pre_indentation}{pattern} => todo!()"),
format!("{comma}{pre_indentation}{suggested_arm}"),
));
}
[.., prev, last] => {
@@ -1083,7 +1087,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
if let Some(spacing) = spacing {
suggestion = Some((
last.span.shrink_to_hi(),
format!("{comma}{spacing}{pattern} => todo!()"),
format!("{comma}{spacing}{suggested_arm}"),
));
}
}
@@ -1094,13 +1098,13 @@ fn report_non_exhaustive_match<'p, 'tcx>(
let msg = format!(
"ensure that all possible cases are being handled by adding a match arm with a wildcard \
pattern{}{}",
if patterns_len > 1 && patterns_len < 4 && suggestion.is_some() {
if witnesses.len() > 1 && suggest_the_witnesses && suggestion.is_some() {
", a match arm with multiple or-patterns"
} else {
// we are either not suggesting anything, or suggesting `_`
""
},
match patterns_len {
match witnesses.len() {
// non-exhaustive enum case
0 if suggestion.is_some() => " as shown",
0 => "",
39 changes: 33 additions & 6 deletions compiler/rustc_pattern_analysis/src/constructor.rs
Original file line number Diff line number Diff line change
@@ -681,15 +681,19 @@ pub enum Constructor<Cx: TypeCx> {
Or,
/// Wildcard pattern.
Wildcard,
/// Never pattern. Only used in `WitnessPat`. An actual never pattern should be lowered as
/// `Wildcard`.
Never,
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used
/// for those types for which we cannot list constructors explicitly, like `f64` and `str`.
/// for those types for which we cannot list constructors explicitly, like `f64` and `str`. Only
/// used in `WitnessPat`.
NonExhaustive,
/// Fake extra constructor for variants that should not be mentioned in diagnostics.
/// We use this for variants behind an unstable gate as well as
/// `#[doc(hidden)]` ones.
/// Fake extra constructor for variants that should not be mentioned in diagnostics. We use this
/// for variants behind an unstable gate as well as `#[doc(hidden)]` ones. Only used in
/// `WitnessPat`.
Hidden,
/// Fake extra constructor for constructors that are not seen in the matrix, as explained at the
/// top of the file.
/// top of the file. Only used for specialization.
Missing,
/// Fake extra constructor that indicates and empty field that is private. When we encounter one
/// we skip the column entirely so we don't observe its emptiness. Only used for specialization.
@@ -711,6 +715,7 @@ impl<Cx: TypeCx> Clone for Constructor<Cx> {
Constructor::Str(value) => Constructor::Str(value.clone()),
Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()),
Constructor::Or => Constructor::Or,
Constructor::Never => Constructor::Never,
Constructor::Wildcard => Constructor::Wildcard,
Constructor::NonExhaustive => Constructor::NonExhaustive,
Constructor::Hidden => Constructor::Hidden,
@@ -1043,10 +1048,32 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
// 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);
missing_empty.push(Never);
}
}

SplitConstructorSet { present, missing, missing_empty }
}

/// Whether this set only contains empty constructors.
pub(crate) fn all_empty(&self) -> bool {
match self {
ConstructorSet::Bool
| ConstructorSet::Integers { .. }
| ConstructorSet::Ref
| ConstructorSet::Union
| ConstructorSet::Unlistable => false,
ConstructorSet::NoConstructors => true,
ConstructorSet::Struct { empty } => *empty,
ConstructorSet::Variants { variants, non_exhaustive } => {
!*non_exhaustive
&& variants
.iter()
.all(|visibility| matches!(visibility, VariantVisibility::Empty))
}
ConstructorSet::Slice { array_len, subtype_is_empty } => {
*subtype_is_empty && matches!(array_len, Some(1..))
}
}
}
}
21 changes: 18 additions & 3 deletions compiler/rustc_pattern_analysis/src/pat.rs
Original file line number Diff line number Diff line change
@@ -189,6 +189,7 @@ impl<Cx: TypeCx> fmt::Debug for DeconstructedPat<Cx> {
}
Ok(())
}
Never => write!(f, "!"),
Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => {
write!(f, "_ : {:?}", pat.ty())
}
@@ -291,18 +292,24 @@ impl<Cx: TypeCx> WitnessPat<Cx> {
pub(crate) fn new(ctor: Constructor<Cx>, fields: Vec<Self>, ty: Cx::Ty) -> Self {
Self { ctor, fields, ty }
}
pub(crate) fn wildcard(ty: Cx::Ty) -> Self {
Self::new(Wildcard, Vec::new(), ty)
/// Create a wildcard pattern for this type. If the type is empty, we create a `!` pattern.
pub(crate) fn wildcard(cx: &Cx, ty: Cx::Ty) -> Self {
let is_empty = cx.ctors_for_ty(&ty).is_ok_and(|ctors| ctors.all_empty());
let ctor = if is_empty { Never } else { Wildcard };
Self::new(ctor, Vec::new(), ty)
}

/// Construct a pattern that matches everything that starts with this constructor.
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
/// `Some(_)`.
pub(crate) fn wild_from_ctor(cx: &Cx, ctor: Constructor<Cx>, ty: Cx::Ty) -> Self {
if matches!(ctor, Wildcard) {
return Self::wildcard(cx, ty);
}
let fields = cx
.ctor_sub_tys(&ctor, &ty)
.filter(|(_, PrivateUninhabitedField(skip))| !skip)
.map(|(ty, _)| Self::wildcard(ty))
.map(|(ty, _)| Self::wildcard(cx, ty))
.collect();
Self::new(ctor, fields, ty)
}
@@ -314,6 +321,14 @@ impl<Cx: TypeCx> WitnessPat<Cx> {
&self.ty
}

pub fn is_never_pattern(&self) -> bool {
match self.ctor() {
Never => true,
Or => self.fields.iter().all(|p| p.is_never_pattern()),
_ => self.fields.iter().any(|p| p.is_never_pattern()),
}
}

pub fn iter_fields(&self) -> impl Iterator<Item = &WitnessPat<Cx>> {
self.fields.iter()
}
7 changes: 4 additions & 3 deletions compiler/rustc_pattern_analysis/src/rustc.rs
Original file line number Diff line number Diff line change
@@ -251,7 +251,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
},
Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
| NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[],
| Never | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[],
Or => {
bug!("called `Fields::wildcards` on an `Or` ctor")
}
@@ -279,7 +279,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
Ref => 1,
Slice(slice) => slice.arity(),
Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
| NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0,
| Never | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0,
Or => bug!("The `Or` constructor doesn't have a fixed arity"),
}
}
@@ -809,7 +809,8 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
}
}
&Str(value) => PatKind::Constant { value },
Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
Never if self.tcx.features().never_patterns => PatKind::Never,
Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
Missing { .. } => bug!(
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
`Missing` should have been processed in `apply_constructors`"
100 changes: 50 additions & 50 deletions tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
error: unreachable pattern
--> $DIR/empty-types.rs:49:9
--> $DIR/empty-types.rs:51:9
|
LL | _ => {}
| ^
|
note: the lint level is defined here
--> $DIR/empty-types.rs:15:9
--> $DIR/empty-types.rs:17:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:52:9
--> $DIR/empty-types.rs:54:9
|
LL | _x => {}
| ^^

error[E0004]: non-exhaustive patterns: type `&!` is non-empty
--> $DIR/empty-types.rs:56:11
--> $DIR/empty-types.rs:58:11
|
LL | match ref_never {}
| ^^^^^^^^^
@@ -32,31 +32,31 @@ LL + }
|

error: unreachable pattern
--> $DIR/empty-types.rs:71:9
--> $DIR/empty-types.rs:73:9
|
LL | (_, _) => {}
| ^^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:78:9
--> $DIR/empty-types.rs:80:9
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:81:9
--> $DIR/empty-types.rs:83:9
|
LL | (_, _) => {}
| ^^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:85:9
--> $DIR/empty-types.rs:87:9
|
LL | _ => {}
| ^

error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
--> $DIR/empty-types.rs:89:11
--> $DIR/empty-types.rs:91:11
|
LL | match res_u32_never {}
| ^^^^^^^^^^^^^ pattern `Ok(_)` not covered
@@ -75,19 +75,19 @@ LL + }
|

error: unreachable pattern
--> $DIR/empty-types.rs:97:9
--> $DIR/empty-types.rs:99:9
|
LL | Err(_) => {}
| ^^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:102:9
--> $DIR/empty-types.rs:104:9
|
LL | Err(_) => {}
| ^^^^^^

error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered
--> $DIR/empty-types.rs:99:11
--> $DIR/empty-types.rs:101:11
|
LL | match res_u32_never {
| ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered
@@ -105,7 +105,7 @@ LL ~ Ok(1_u32..=u32::MAX) => todo!()
|

error[E0005]: refutable pattern in local binding
--> $DIR/empty-types.rs:106:9
--> $DIR/empty-types.rs:108:9
|
LL | let Ok(_x) = res_u32_never.as_ref();
| ^^^^^^ pattern `Err(_)` not covered
@@ -119,121 +119,121 @@ LL | let Ok(_x) = res_u32_never.as_ref() else { todo!() };
| ++++++++++++++++

error: unreachable pattern
--> $DIR/empty-types.rs:117:9
--> $DIR/empty-types.rs:119:9
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:121:9
--> $DIR/empty-types.rs:123:9
|
LL | Ok(_) => {}
| ^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:124:9
--> $DIR/empty-types.rs:126:9
|
LL | Ok(_) => {}
| ^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:125:9
--> $DIR/empty-types.rs:127:9
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:128:9
--> $DIR/empty-types.rs:130:9
|
LL | Ok(_) => {}
| ^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:129:9
--> $DIR/empty-types.rs:131:9
|
LL | Err(_) => {}
| ^^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:138:13
--> $DIR/empty-types.rs:140:13
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:141:13
--> $DIR/empty-types.rs:143:13
|
LL | _ if false => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:150:13
--> $DIR/empty-types.rs:152:13
|
LL | Some(_) => {}
| ^^^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:154:13
--> $DIR/empty-types.rs:156:13
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:206:13
--> $DIR/empty-types.rs:208:13
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:211:13
--> $DIR/empty-types.rs:213:13
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:216:13
--> $DIR/empty-types.rs:218:13
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:221:13
--> $DIR/empty-types.rs:223:13
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:227:13
--> $DIR/empty-types.rs:229:13
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:286:9
--> $DIR/empty-types.rs:288:9
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:289:9
--> $DIR/empty-types.rs:291:9
|
LL | (_, _) => {}
| ^^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:292:9
--> $DIR/empty-types.rs:294:9
|
LL | Ok(_) => {}
| ^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:293:9
--> $DIR/empty-types.rs:295:9
|
LL | Err(_) => {}
| ^^^^^^

error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty
--> $DIR/empty-types.rs:325:11
--> $DIR/empty-types.rs:327:11
|
LL | match slice_never {}
| ^^^^^^^^^^^
@@ -247,7 +247,7 @@ LL + }
|

error[E0004]: non-exhaustive patterns: `&[]` not covered
--> $DIR/empty-types.rs:336:11
--> $DIR/empty-types.rs:338:11
|
LL | match slice_never {
| ^^^^^^^^^^^ pattern `&[]` not covered
@@ -260,7 +260,7 @@ LL + &[] => todo!()
|

error[E0004]: non-exhaustive patterns: `&[]` not covered
--> $DIR/empty-types.rs:349:11
--> $DIR/empty-types.rs:352:11
|
LL | match slice_never {
| ^^^^^^^^^^^ pattern `&[]` not covered
@@ -274,7 +274,7 @@ LL + &[] => todo!()
|

error[E0004]: non-exhaustive patterns: type `[!]` is non-empty
--> $DIR/empty-types.rs:355:11
--> $DIR/empty-types.rs:359:11
|
LL | match *slice_never {}
| ^^^^^^^^^^^^
@@ -288,25 +288,25 @@ LL + }
|

error: unreachable pattern
--> $DIR/empty-types.rs:365:9
--> $DIR/empty-types.rs:369:9
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:368:9
--> $DIR/empty-types.rs:372:9
|
LL | [_, _, _] => {}
| ^^^^^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:371:9
--> $DIR/empty-types.rs:375:9
|
LL | [_, ..] => {}
| ^^^^^^^

error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty
--> $DIR/empty-types.rs:385:11
--> $DIR/empty-types.rs:389:11
|
LL | match array_0_never {}
| ^^^^^^^^^^^^^
@@ -320,13 +320,13 @@ LL + }
|

error: unreachable pattern
--> $DIR/empty-types.rs:392:9
--> $DIR/empty-types.rs:396:9
|
LL | _ => {}
| ^

error[E0004]: non-exhaustive patterns: `[]` not covered
--> $DIR/empty-types.rs:394:11
--> $DIR/empty-types.rs:398:11
|
LL | match array_0_never {
| ^^^^^^^^^^^^^ pattern `[]` not covered
@@ -340,49 +340,49 @@ LL + [] => todo!()
|

error: unreachable pattern
--> $DIR/empty-types.rs:413:9
--> $DIR/empty-types.rs:417:9
|
LL | Some(_) => {}
| ^^^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:418:9
--> $DIR/empty-types.rs:422:9
|
LL | Some(_a) => {}
| ^^^^^^^^

error: unreachable pattern
--> $DIR/empty-types.rs:423:9
--> $DIR/empty-types.rs:427:9
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:428:9
--> $DIR/empty-types.rs:432:9
|
LL | _a => {}
| ^^

error: unreachable pattern
--> $DIR/empty-types.rs:600:9
--> $DIR/empty-types.rs:604:9
|
LL | _ => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:603:9
--> $DIR/empty-types.rs:607:9
|
LL | _x => {}
| ^^

error: unreachable pattern
--> $DIR/empty-types.rs:606:9
--> $DIR/empty-types.rs:610:9
|
LL | _ if false => {}
| ^

error: unreachable pattern
--> $DIR/empty-types.rs:609:9
--> $DIR/empty-types.rs:613:9
|
LL | _x if false => {}
| ^^
148 changes: 83 additions & 65 deletions tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr

Large diffs are not rendered by default.

644 changes: 644 additions & 0 deletions tests/ui/pattern/usefulness/empty-types.never_pats.stderr

Large diffs are not rendered by default.

118 changes: 68 additions & 50 deletions tests/ui/pattern/usefulness/empty-types.normal.stderr

Large diffs are not rendered by default.

57 changes: 35 additions & 22 deletions tests/ui/pattern/usefulness/empty-types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//@ revisions: normal min_exh_pats exhaustive_patterns
//@ revisions: normal min_exh_pats exhaustive_patterns never_pats
// gate-test-min_exhaustive_patterns
//
// This tests correct handling of empty types in exhaustiveness checking.
@@ -11,6 +11,8 @@
#![feature(never_type_fallback)]
#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
#![cfg_attr(min_exh_pats, feature(min_exhaustive_patterns))]
#![cfg_attr(never_pats, feature(never_patterns))]
//[never_pats]~^ WARN the feature `never_patterns` is incomplete
#![allow(dead_code, unreachable_code)]
#![deny(unreachable_patterns)]

@@ -66,14 +68,14 @@ fn basic(x: NeverBundle) {

let tuple_half_never: (u32, !) = x.tuple_half_never;
match tuple_half_never {}
//[normal]~^ ERROR non-empty
//[normal,never_pats]~^ ERROR non-empty
match tuple_half_never {
(_, _) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}

let tuple_never: (!, !) = x.tuple_never;
match tuple_never {}
//[normal]~^ ERROR non-empty
//[normal,never_pats]~^ ERROR non-empty
match tuple_never {
_ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
@@ -89,7 +91,7 @@ fn basic(x: NeverBundle) {
match res_u32_never {}
//~^ ERROR non-exhaustive
match res_u32_never {
//[normal]~^ ERROR non-exhaustive
//[normal,never_pats]~^ ERROR non-exhaustive
Ok(_) => {}
}
match res_u32_never {
@@ -102,22 +104,22 @@ fn basic(x: NeverBundle) {
Err(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
let Ok(_x) = res_u32_never;
//[normal]~^ ERROR refutable
//[normal,never_pats]~^ ERROR refutable
let Ok(_x) = res_u32_never.as_ref();
//~^ ERROR refutable
// Non-obvious difference: here there's an implicit dereference in the patterns, which makes the
// inner place !known_valid. `exhaustive_patterns` ignores this.
let Ok(_x) = &res_u32_never;
//[normal,min_exh_pats]~^ ERROR refutable
//[normal,min_exh_pats,never_pats]~^ ERROR refutable

let result_never: Result<!, !> = x.result_never;
match result_never {}
//[normal]~^ ERROR non-exhaustive
//[normal,never_pats]~^ ERROR non-exhaustive
match result_never {
_ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match result_never {
//[normal]~^ ERROR non-exhaustive
//[normal,never_pats]~^ ERROR non-exhaustive
Ok(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
match result_never {
@@ -142,7 +144,7 @@ fn void_same_as_never(x: NeverBundle) {
}
let opt_void: Option<Void> = None;
match opt_void {
//[normal]~^ ERROR non-exhaustive
//[normal,never_pats]~^ ERROR non-exhaustive
None => {}
}
match opt_void {
@@ -161,7 +163,7 @@ fn void_same_as_never(x: NeverBundle) {
}
let ref_opt_void: &Option<Void> = &None;
match *ref_opt_void {
//[normal,min_exh_pats]~^ ERROR non-exhaustive
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
None => {}
}
match *ref_opt_void {
@@ -311,21 +313,21 @@ fn invalid_empty_match(bundle: NeverBundle) {
match *x {}

let x: &(u32, !) = &bundle.tuple_half_never;
match *x {} //[normal,min_exh_pats]~ ERROR non-exhaustive
match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
let x: &(!, !) = &bundle.tuple_never;
match *x {} //[normal,min_exh_pats]~ ERROR non-exhaustive
match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
let x: &Result<!, !> = &bundle.result_never;
match *x {} //[normal,min_exh_pats]~ ERROR non-exhaustive
match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
let x: &[!; 3] = &bundle.array_3_never;
match *x {} //[normal,min_exh_pats]~ ERROR non-exhaustive
match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive
}

fn arrays_and_slices(x: NeverBundle) {
let slice_never: &[!] = &[];
match slice_never {}
//~^ ERROR non-empty
match slice_never {
//[normal,min_exh_pats]~^ ERROR not covered
//[normal,min_exh_pats,never_pats]~^ ERROR not covered
[] => {}
}
match slice_never {
@@ -336,6 +338,7 @@ fn arrays_and_slices(x: NeverBundle) {
match slice_never {
//[normal,min_exh_pats]~^ ERROR `&[]`, `&[_]` and `&[_, _]` not covered
//[exhaustive_patterns]~^^ ERROR `&[]` not covered
//[never_pats]~^^^ ERROR `&[]`, `&[!]` and `&[!, !]` not covered
[_, _, _, ..] => {}
}
match slice_never {
@@ -349,6 +352,7 @@ fn arrays_and_slices(x: NeverBundle) {
match slice_never {
//[normal,min_exh_pats]~^ ERROR `&[]` and `&[_, ..]` not covered
//[exhaustive_patterns]~^^ ERROR `&[]` not covered
//[never_pats]~^^^ ERROR `&[]` and `&[!, ..]` not covered
&[..] if false => {}
}

@@ -360,7 +364,7 @@ fn arrays_and_slices(x: NeverBundle) {

let array_3_never: [!; 3] = x.array_3_never;
match array_3_never {}
//[normal]~^ ERROR non-empty
//[normal,never_pats]~^ ERROR non-empty
match array_3_never {
_ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern
}
@@ -446,7 +450,7 @@ fn bindings(x: NeverBundle) {
&_a => {}
}
match ref_opt_never {
//[normal,min_exh_pats]~^ ERROR non-exhaustive
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
&None => {}
}
match ref_opt_never {
@@ -487,7 +491,7 @@ fn bindings(x: NeverBundle) {
ref _a => {}
}
match *ref_opt_never {
//[normal,min_exh_pats]~^ ERROR non-exhaustive
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
None => {}
}
match *ref_opt_never {
@@ -535,7 +539,7 @@ fn bindings(x: NeverBundle) {

let ref_res_never: &Result<!, !> = &x.result_never;
match *ref_res_never {
//[normal,min_exh_pats]~^ ERROR non-exhaustive
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
// useful, reachable
Ok(_) => {}
}
@@ -546,7 +550,7 @@ fn bindings(x: NeverBundle) {
_ => {}
}
match *ref_res_never {
//[normal,min_exh_pats]~^ ERROR non-exhaustive
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
// useful, !reachable
Ok(_a) => {}
}
@@ -565,7 +569,7 @@ fn bindings(x: NeverBundle) {

let ref_tuple_half_never: &(u32, !) = &x.tuple_half_never;
match *ref_tuple_half_never {}
//[normal,min_exh_pats]~^ ERROR non-empty
//[normal,min_exh_pats,never_pats]~^ ERROR non-empty
match *ref_tuple_half_never {
// useful, reachable
(_, _) => {}
@@ -632,7 +636,7 @@ fn guards_and_validity(x: NeverBundle) {
_a if false => {}
}
match ref_never {
//[normal,min_exh_pats]~^ ERROR non-exhaustive
//[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive
// useful, !reachable
&_a if false => {}
}
@@ -647,6 +651,14 @@ fn guards_and_validity(x: NeverBundle) {
// useful, !reachable
Err(_) => {}
}
match *ref_result_never {
//[normal,min_exh_pats]~^ ERROR `Ok(_)` not covered
//[never_pats]~^^ ERROR `Ok(!)` not covered
// useful, reachable
Ok(_) if false => {}
// useful, reachable
Err(_) => {}
}
let ref_tuple_never: &(!, !) = &x.tuple_never;
match *ref_tuple_never {
// useful, !reachable
@@ -661,6 +673,7 @@ fn diagnostics_subtlety(x: NeverBundle) {
let x: &Option<Result<!, !>> = &None;
match *x {
//[normal,min_exh_pats]~^ ERROR `Some(_)` not covered
//[never_pats]~^^ ERROR `Some(!)` not covered
None => {}
}
}
4 changes: 2 additions & 2 deletions tests/ui/rfcs/rfc-0000-never_patterns/check.rs
Original file line number Diff line number Diff line change
@@ -15,12 +15,12 @@ fn no_arms_or_guards(x: Void) {
//~^ ERROR a never pattern is always unreachable
None => {}
}
match None::<Void> { //~ ERROR: `Some(_)` not covered
match None::<Void> { //~ ERROR: `Some(!)` not covered
Some(!) if true,
//~^ ERROR guard on a never pattern
None => {}
}
match None::<Void> { //~ ERROR: `Some(_)` not covered
match None::<Void> { //~ ERROR: `Some(!)` not covered
Some(!) if true => {}
//~^ ERROR a never pattern is always unreachable
None => {}
12 changes: 6 additions & 6 deletions tests/ui/rfcs/rfc-0000-never_patterns/check.stderr
Original file line number Diff line number Diff line change
@@ -31,11 +31,11 @@ LL | Some(never!()) => {}
| this will never be executed
| help: remove this expression

error[E0004]: non-exhaustive patterns: `Some(_)` not covered
error[E0004]: non-exhaustive patterns: `Some(!)` not covered
--> $DIR/check.rs:18:11
|
LL | match None::<Void> {
| ^^^^^^^^^^^^ pattern `Some(_)` not covered
| ^^^^^^^^^^^^ pattern `Some(!)` not covered
|
note: `Option<Void>` defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
@@ -46,14 +46,14 @@ note: `Option<Void>` defined here
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 ~ None => {},
LL + Some(_) => todo!()
LL + Some(!)
|

error[E0004]: non-exhaustive patterns: `Some(_)` not covered
error[E0004]: non-exhaustive patterns: `Some(!)` not covered
--> $DIR/check.rs:23:11
|
LL | match None::<Void> {
| ^^^^^^^^^^^^ pattern `Some(_)` not covered
| ^^^^^^^^^^^^ pattern `Some(!)` not covered
|
note: `Option<Void>` defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
@@ -64,7 +64,7 @@ note: `Option<Void>` defined here
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 ~ None => {},
LL + Some(_) => todo!()
LL + Some(!)
|

error: aborting due to 6 previous errors