Auto merge of #118308 - Nadrieril:sound-exhaustive-patterns-take-3, r=compiler-errors

Don't warn an empty pattern unreachable if we're not sure the data is valid

Exhaustiveness checking used to be naive about the possibility of a place containing invalid data. This could cause it to emit an "unreachable pattern" lint on an arm that was in fact reachable, as in https://github.com/rust-lang/rust/issues/117119.

This PR fixes that. We now track whether a place that is matched on may hold invalid data. This also forced me to be extra precise about how exhaustiveness manages empty types.

Note that this now errs in the opposite direction: the following arm is truly unreachable (because the binding causes a read of the value) but not linted as such. I'd rather not recommend writing a `match ... {}` that has the implicit side-effect of loading the value. [Never patterns](https://github.com/rust-lang/rust/issues/118155) will solve this cleanly.
```rust
match union.value {
    _x => unreachable!(),
}
```

I recommend reviewing commit by commit. I went all-in on the test suite because this went through a lot of iterations and I kept everything. The bit I'm least confident in is `is_known_valid_scrutinee` in `check_match.rs`.

Fixes https://github.com/rust-lang/rust/issues/117119.
This commit is contained in:
bors 2023-12-09 19:03:03 +00:00
commit 06e02d5b25
18 changed files with 2815 additions and 857 deletions

View File

@ -1,3 +1,5 @@
use smallvec::SmallVec;
use crate::ty::context::TyCtxt;
use crate::ty::{self, DefId, ParamEnv, Ty};
@ -31,27 +33,31 @@ impl<'tcx> InhabitedPredicate<'tcx> {
/// Returns true if the corresponding type is inhabited in the given
/// `ParamEnv` and module
pub fn apply(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, module_def_id: DefId) -> bool {
let Ok(result) = self
.apply_inner::<!>(tcx, param_env, &|id| Ok(tcx.is_descendant_of(module_def_id, id)));
let Ok(result) = self.apply_inner::<!>(tcx, param_env, &mut Default::default(), &|id| {
Ok(tcx.is_descendant_of(module_def_id, id))
});
result
}
/// Same as `apply`, but returns `None` if self contains a module predicate
pub fn apply_any_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
self.apply_inner(tcx, param_env, &|_| Err(())).ok()
self.apply_inner(tcx, param_env, &mut Default::default(), &|_| Err(())).ok()
}
/// Same as `apply`, but `NotInModule(_)` predicates yield `false`. That is,
/// privately uninhabited types are considered always uninhabited.
pub fn apply_ignore_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> bool {
let Ok(result) = self.apply_inner::<!>(tcx, param_env, &|_| Ok(true));
let Ok(result) =
self.apply_inner::<!>(tcx, param_env, &mut Default::default(), &|_| Ok(true));
result
}
fn apply_inner<E>(
#[instrument(level = "debug", skip(tcx, param_env, in_module), ret)]
fn apply_inner<E: std::fmt::Debug>(
self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
eval_stack: &mut SmallVec<[Ty<'tcx>; 1]>, // for cycle detection
in_module: &impl Fn(DefId) -> Result<bool, E>,
) -> Result<bool, E> {
match self {
@ -71,11 +77,25 @@ impl<'tcx> InhabitedPredicate<'tcx> {
match normalized_pred {
// We don't have more information than we started with, so consider inhabited.
Self::GenericType(_) => Ok(true),
pred => pred.apply_inner(tcx, param_env, in_module),
pred => {
// A type which is cyclic when monomorphized can happen here since the
// layout error would only trigger later. See e.g. `tests/ui/sized/recursive-type-2.rs`.
if eval_stack.contains(&t) {
return Ok(true); // Recover; this will error later.
}
eval_stack.push(t);
let ret = pred.apply_inner(tcx, param_env, eval_stack, in_module);
eval_stack.pop();
ret
}
}
}
Self::And([a, b]) => try_and(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
Self::Or([a, b]) => try_or(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
Self::And([a, b]) => {
try_and(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module))
}
Self::Or([a, b]) => {
try_or(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module))
}
}
}
@ -197,7 +217,7 @@ impl<'tcx> InhabitedPredicate<'tcx> {
// this is basically like `f(a)? && f(b)?` but different in the case of
// `Ok(false) && Err(_) -> Ok(false)`
fn try_and<T, E>(a: T, b: T, f: impl Fn(T) -> Result<bool, E>) -> Result<bool, E> {
fn try_and<T, E>(a: T, b: T, mut f: impl FnMut(T) -> Result<bool, E>) -> Result<bool, E> {
let a = f(a);
if matches!(a, Ok(false)) {
return Ok(false);
@ -209,7 +229,7 @@ fn try_and<T, E>(a: T, b: T, f: impl Fn(T) -> Result<bool, E>) -> Result<bool, E
}
}
fn try_or<T, E>(a: T, b: T, f: impl Fn(T) -> Result<bool, E>) -> Result<bool, E> {
fn try_or<T, E>(a: T, b: T, mut f: impl FnMut(T) -> Result<bool, E>) -> Result<bool, E> {
let a = f(a);
if matches!(a, Ok(true)) {
return Ok(true);

View File

@ -103,6 +103,7 @@ impl<'tcx> VariantDef {
}
impl<'tcx> Ty<'tcx> {
#[instrument(level = "debug", skip(tcx), ret)]
pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> {
match self.kind() {
// For now, unions are always considered inhabited

View File

@ -1,6 +1,6 @@
use super::deconstruct_pat::{Constructor, DeconstructedPat, WitnessPat};
use super::usefulness::{
compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport,
compute_match_usefulness, MatchArm, MatchCheckCtxt, Usefulness, UsefulnessReport,
};
use crate::errors::*;
@ -42,7 +42,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
for param in thir.params.iter() {
if let Some(box ref pattern) = param.pat {
visitor.check_binding_is_irrefutable(pattern, "function argument", None);
visitor.check_binding_is_irrefutable(pattern, "function argument", None, None);
}
}
visitor.error
@ -254,10 +254,11 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
self.with_lint_level(lint_level, |this| this.visit_land_rhs(&this.thir[value]))
}
ExprKind::Let { box ref pat, expr } => {
let expr = &self.thir()[expr];
self.with_let_source(LetSource::None, |this| {
this.visit_expr(&this.thir()[expr]);
this.visit_expr(expr);
});
Ok(Some((ex.span, self.is_let_irrefutable(pat)?)))
Ok(Some((ex.span, self.is_let_irrefutable(pat, Some(expr))?)))
}
_ => {
self.with_let_source(LetSource::None, |this| {
@ -287,35 +288,114 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
}
}
/// Inspects the match scrutinee expression to determine whether the place it evaluates to may
/// hold invalid data.
fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool {
use ExprKind::*;
match &scrutinee.kind {
// Both pointers and references can validly point to a place with invalid data.
Deref { .. } => false,
// Inherit validity of the parent place, unless the parent is an union.
Field { lhs, .. } => {
let lhs = &self.thir()[*lhs];
match lhs.ty.kind() {
ty::Adt(def, _) if def.is_union() => false,
_ => self.is_known_valid_scrutinee(lhs),
}
}
// Essentially a field access.
Index { lhs, .. } => {
let lhs = &self.thir()[*lhs];
self.is_known_valid_scrutinee(lhs)
}
// No-op.
Scope { value, .. } => self.is_known_valid_scrutinee(&self.thir()[*value]),
// Casts don't cause a load.
NeverToAny { source }
| Cast { source }
| Use { source }
| PointerCoercion { source, .. }
| PlaceTypeAscription { source, .. }
| ValueTypeAscription { source, .. } => {
self.is_known_valid_scrutinee(&self.thir()[*source])
}
// These diverge.
Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true,
// These are statements that evaluate to `()`.
Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
// These evaluate to a value.
AddressOf { .. }
| Adt { .. }
| Array { .. }
| Binary { .. }
| Block { .. }
| Borrow { .. }
| Box { .. }
| Call { .. }
| Closure { .. }
| ConstBlock { .. }
| ConstParam { .. }
| If { .. }
| Literal { .. }
| LogicalOp { .. }
| Loop { .. }
| Match { .. }
| NamedConst { .. }
| NonHirLiteral { .. }
| OffsetOf { .. }
| Repeat { .. }
| StaticRef { .. }
| ThreadLocalRef { .. }
| Tuple { .. }
| Unary { .. }
| UpvarRef { .. }
| VarRef { .. }
| ZstLiteral { .. }
| Yield { .. } => true,
}
}
fn new_cx(
&self,
refutability: RefutableFlag,
match_span: Option<Span>,
whole_match_span: Option<Span>,
scrutinee: Option<&Expr<'tcx>>,
scrut_span: Span,
) -> MatchCheckCtxt<'p, 'tcx> {
let refutable = match refutability {
Irrefutable => false,
Refutable => true,
};
// If we don't have a scrutinee we're either a function parameter or a `let x;`. Both cases
// require validity.
let known_valid_scrutinee =
scrutinee.map(|scrut| self.is_known_valid_scrutinee(scrut)).unwrap_or(true);
MatchCheckCtxt {
tcx: self.tcx,
param_env: self.param_env,
module: self.tcx.parent_module(self.lint_level).to_def_id(),
pattern_arena: self.pattern_arena,
match_lint_level: self.lint_level,
match_span,
whole_match_span,
scrut_span,
refutable,
known_valid_scrutinee,
}
}
#[instrument(level = "trace", skip(self))]
fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
assert!(self.let_source != LetSource::None);
let scrut = scrutinee.map(|id| &self.thir[id]);
if let LetSource::PlainLet = self.let_source {
self.check_binding_is_irrefutable(pat, "local binding", Some(span))
self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span))
} else {
let Ok(refutability) = self.is_let_irrefutable(pat) else { return };
let Ok(refutability) = self.is_let_irrefutable(pat, scrut) else { return };
if matches!(refutability, Irrefutable) {
report_irrefutable_let_patterns(
self.tcx,
@ -336,7 +416,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
expr_span: Span,
) {
let scrut = &self.thir[scrut];
let cx = self.new_cx(Refutable, Some(expr_span), scrut.span);
let cx = self.new_cx(Refutable, Some(expr_span), Some(scrut), scrut.span);
let mut tarms = Vec::with_capacity(arms.len());
for &arm in arms {
@ -377,7 +457,12 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() };
let [pat_field] = &subpatterns[..] else { bug!() };
self.check_binding_is_irrefutable(&pat_field.pattern, "`for` loop binding", None);
self.check_binding_is_irrefutable(
&pat_field.pattern,
"`for` loop binding",
None,
None,
);
} else {
self.error = Err(report_non_exhaustive_match(
&cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
@ -457,16 +542,21 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
&mut self,
pat: &Pat<'tcx>,
refutability: RefutableFlag,
scrut: Option<&Expr<'tcx>>,
) -> Result<(MatchCheckCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
let cx = self.new_cx(refutability, None, pat.span);
let cx = self.new_cx(refutability, None, scrut, pat.span);
let pat = self.lower_pattern(&cx, pat)?;
let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }];
let report = compute_match_usefulness(&cx, &arms, pat.ty());
Ok((cx, report))
}
fn is_let_irrefutable(&mut self, pat: &Pat<'tcx>) -> Result<RefutableFlag, ErrorGuaranteed> {
let (cx, report) = self.analyze_binding(pat, Refutable)?;
fn is_let_irrefutable(
&mut self,
pat: &Pat<'tcx>,
scrut: Option<&Expr<'tcx>>,
) -> Result<RefutableFlag, ErrorGuaranteed> {
let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
// Report if the pattern is unreachable, which can only occur when the type is uninhabited.
// This also reports unreachable sub-patterns.
report_arm_reachability(&cx, &report);
@ -476,10 +566,16 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
}
#[instrument(level = "trace", skip(self))]
fn check_binding_is_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
fn check_binding_is_irrefutable(
&mut self,
pat: &Pat<'tcx>,
origin: &str,
scrut: Option<&Expr<'tcx>>,
sp: Option<Span>,
) {
let pattern_ty = pat.ty;
let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable) else { return };
let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable, scrut) else { return };
let witnesses = report.non_exhaustiveness_witnesses;
if witnesses.is_empty() {
// The pattern is irrefutable.
@ -749,18 +845,18 @@ fn report_arm_reachability<'p, 'tcx>(
);
};
use Reachability::*;
use Usefulness::*;
let mut catchall = None;
for (arm, is_useful) in report.arm_usefulness.iter() {
match is_useful {
Unreachable => report_unreachable_pattern(arm.pat.span(), arm.hir_id, catchall),
Reachable(unreachables) if unreachables.is_empty() => {}
// The arm is reachable, but contains unreachable subpatterns (from or-patterns).
Reachable(unreachables) => {
let mut unreachables = unreachables.clone();
Redundant => report_unreachable_pattern(arm.pat.span(), arm.hir_id, catchall),
Useful(redundant_spans) if redundant_spans.is_empty() => {}
// The arm is reachable, but contains redundant subpatterns (from or-patterns).
Useful(redundant_spans) => {
let mut redundant_spans = redundant_spans.clone();
// Emit lints in the order in which they occur in the file.
unreachables.sort_unstable();
for span in unreachables {
redundant_spans.sort_unstable();
for span in redundant_spans {
report_unreachable_pattern(span, arm.hir_id, None);
}
}

View File

@ -10,6 +10,7 @@
//! precisely here.
//!
//!
//!
//! # Constructor grouping and splitting
//!
//! As explained in the corresponding section in [`super::usefulness`], to make usefulness tractable
@ -49,6 +50,7 @@
//! [`IntRange::split`]) and slice splitting (see [`Slice::split`]).
//!
//!
//!
//! # The `Missing` constructor
//!
//! We detail a special case of constructor splitting that is a bit subtle. Take the following:
@ -77,6 +79,69 @@
//!
//!
//!
//! ## Empty types, empty constructors, and the `exhaustive_patterns` feature
//!
//! An empty type is a type that has no valid value, like `!`, `enum Void {}`, or `Result<!, !>`.
//! They require careful handling.
//!
//! First, for soundness reasons related to the possible existence of invalid values, by default we
//! don't treat empty types as empty. We force them to be matched with wildcards. Except if the
//! `exhaustive_patterns` feature is turned on, in which case we do treat them as empty. And also
//! except if the type has no constructors (like `enum Void {}` but not like `Result<!, !>`), we
//! specifically allow `match void {}` to be exhaustive. There are additionally considerations of
//! place validity that are handled in `super::usefulness`. Yes this is a bit tricky.
//!
//! The second thing is that regardless of the above, it is always allowed to use all the
//! constructors of a type. For example, all the following is ok:
//!
//! ```rust,ignore(example)
//! # #![feature(never_type)]
//! # #![feature(exhaustive_patterns)]
//! fn foo(x: Option<!>) {
//! match x {
//! None => {}
//! Some(_) => {}
//! }
//! }
//! fn bar(x: &[!]) -> u32 {
//! match x {
//! [] => 1,
//! [_] => 2,
//! [_, _] => 3,
//! }
//! }
//! ```
//!
//! Moreover, take the following:
//!
//! ```rust
//! # #![feature(never_type)]
//! # #![feature(exhaustive_patterns)]
//! # let x = None::<!>;
//! match x {
//! None => {}
//! }
//! ```
//!
//! On a normal type, we would identify `Some` as missing and tell the user. If `x: Option<!>`
//! however (and `exhaustive_patterns` is on), it's ok to omit `Some`. When listing the constructors
//! of a type, we must therefore track which can be omitted.
//!
//! Let's call "empty" a constructor that matches no valid value for the type, like `Some` for the
//! type `Option<!>`. What this all means is that `ConstructorSet` must know which constructors are
//! empty. The difference between empty and nonempty constructors is that empty constructors need
//! not be present for the match to be exhaustive.
//!
//! A final remark: empty constructors of arity 0 break specialization, we must avoid them. The
//! reason is that if we specialize by them, nothing remains to witness the emptiness; the rest of
//! the algorithm can't distinguish them from a nonempty constructor. The only known case where this
//! could happen is the `[..]` pattern on `[!; N]` with `N > 0` so we must take care to not emit it.
//!
//! This is all handled by [`ConstructorSet::for_ty`] and [`ConstructorSet::split`]. The invariants
//! of [`SplitConstructorSet`] are also of interest.
//!
//!
//!
//! ## Opaque patterns
//!
//! Some patterns, such as constants that are not allowed to be matched structurally, cannot be
@ -95,7 +160,7 @@ use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::RangeEnd;
use rustc_index::Idx;
use rustc_index::{Idx, IndexVec};
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::mir;
use rustc_middle::mir::interpret::Scalar;
@ -482,8 +547,12 @@ pub(super) struct Slice {
impl Slice {
fn new(array_len: Option<usize>, kind: SliceKind) -> Self {
let kind = match (array_len, kind) {
// If the middle `..` is empty, we effectively have a fixed-length pattern.
(Some(len), VarLen(prefix, suffix)) if prefix + suffix >= len => FixedLen(len),
// If the middle `..` has length 0, we effectively have a fixed-length pattern.
(Some(len), VarLen(prefix, suffix)) if prefix + suffix == len => FixedLen(len),
(Some(len), VarLen(prefix, suffix)) if prefix + suffix > len => bug!(
"Slice pattern of length {} longer than its array length {len}",
prefix + suffix
),
_ => kind,
};
Slice { array_len, kind }
@ -583,17 +652,18 @@ impl Slice {
let mut seen_fixed_lens = FxHashSet::default();
match &mut max_slice {
VarLen(max_prefix_len, max_suffix_len) => {
// A length larger than any fixed-length slice encountered.
// We start at 1 in case the subtype is empty because in that case the zero-length
// slice must be treated separately from the rest.
let mut fixed_len_upper_bound = 1;
// We grow `max_slice` to be larger than all slices encountered, as described above.
// For diagnostics, we keep the prefix and suffix lengths separate, but grow them so that
// `L = max_prefix_len + max_suffix_len`.
let mut max_fixed_len = 0;
// `L` is `max_slice.arity()`. For diagnostics, we keep the prefix and suffix
// lengths separate.
for slice in column_slices {
match slice.kind {
FixedLen(len) => {
max_fixed_len = cmp::max(max_fixed_len, len);
if arity <= len {
seen_fixed_lens.insert(len);
}
fixed_len_upper_bound = cmp::max(fixed_len_upper_bound, len + 1);
seen_fixed_lens.insert(len);
}
VarLen(prefix, suffix) => {
*max_prefix_len = cmp::max(*max_prefix_len, prefix);
@ -602,12 +672,11 @@ impl Slice {
}
}
}
// We want `L = max(L, max_fixed_len + 1)`, modulo the fact that we keep prefix and
// suffix separate.
if max_fixed_len + 1 >= *max_prefix_len + *max_suffix_len {
// The subtraction can't overflow thanks to the above check.
// The new `max_prefix_len` is larger than its previous value.
*max_prefix_len = max_fixed_len + 1 - *max_suffix_len;
// If `fixed_len_upper_bound >= L`, we set `L` to `fixed_len_upper_bound`.
if let Some(delta) =
fixed_len_upper_bound.checked_sub(*max_prefix_len + *max_suffix_len)
{
*max_prefix_len += delta
}
// We cap the arity of `max_slice` at the array size.
@ -718,9 +787,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)
}
@ -856,34 +922,46 @@ impl<'tcx> Constructor<'tcx> {
}
}
/// Describes the set of all constructors for a type.
#[derive(Debug, Clone, Copy)]
pub(super) enum VariantVisibility {
/// Variant that doesn't fit the other cases, i.e. most variants.
Visible,
/// Variant behind an unstable gate or with the `#[doc(hidden)]` attribute. It will not be
/// mentioned in diagnostics unless the user mentioned it first.
Hidden,
/// Variant that matches no value. E.g. `Some::<Option<!>>` if the `exhaustive_patterns` feature
/// is enabled. Like `Hidden`, it will not be mentioned in diagnostics unless the user mentioned
/// it first.
Empty,
}
/// Describes the set of all constructors for a type. For details, in particular about the emptiness
/// of constructors, see the top of the file.
///
/// In terms of division of responsibility, [`ConstructorSet::split`] handles all of the
/// `exhaustive_patterns` feature.
#[derive(Debug)]
pub(super) enum ConstructorSet {
/// The type has a single constructor, e.g. `&T` or a struct.
Single,
/// This type has the following list of constructors.
/// Some variants are hidden, which means they won't be mentioned in diagnostics unless the user
/// mentioned them first. We use this for variants behind an unstable gate as well as
/// `#[doc(hidden)]` ones.
Variants {
visible_variants: Vec<VariantIdx>,
hidden_variants: Vec<VariantIdx>,
non_exhaustive: bool,
},
/// The type has a single constructor, e.g. `&T` or a struct. `empty` tracks whether the
/// constructor is empty.
Single { empty: bool },
/// This type has the following list of constructors. If `variants` is empty and
/// `non_exhaustive` is false, don't use this; use `NoConstructors` instead.
Variants { variants: IndexVec<VariantIdx, VariantVisibility>, non_exhaustive: bool },
/// Booleans.
Bool,
/// The type is spanned by integer values. The range or ranges give the set of allowed values.
/// The second range is only useful for `char`.
Integers { range_1: IntRange, range_2: Option<IntRange> },
/// The type is matched by slices. The usize is the compile-time length of the array, if known.
Slice(Option<usize>),
/// The type is matched by slices whose elements are uninhabited.
SliceOfEmpty,
/// The type is matched by slices. `array_len` is the compile-time length of the array, if
/// known. If `subtype_is_empty`, all constructors are empty except possibly the zero-length
/// slice `[]`.
Slice { array_len: Option<usize>, subtype_is_empty: bool },
/// The constructors cannot be listed, and the type cannot be matched exhaustively. E.g. `str`,
/// floats.
Unlistable,
/// The type has no inhabitants.
Uninhabited,
/// The type has no constructors (not even empty ones). This is `!` and empty enums.
NoConstructors,
}
/// Describes the result of analyzing the constructors in a column of a match.
@ -892,15 +970,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` and `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
@ -909,10 +989,13 @@ pub(super) enum ConstructorSet {
pub(super) struct SplitConstructorSet<'tcx> {
pub(super) present: SmallVec<[Constructor<'tcx>; 1]>,
pub(super) missing: Vec<Constructor<'tcx>>,
pub(super) missing_empty: Vec<Constructor<'tcx>>,
}
impl ConstructorSet {
/// Creates a set that represents all the constructors of `ty`.
///
/// See at the top of the file for considerations of emptiness.
#[instrument(level = "debug", skip(cx), ret)]
pub(super) fn for_ty<'p, 'tcx>(cx: &MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>) -> Self {
let make_range = |start, end| {
@ -924,12 +1007,6 @@ impl ConstructorSet {
};
// This determines the set of all possible constructors for the type `ty`. For numbers,
// arrays and slices we use ranges and variable-length slices when appropriate.
//
// If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that
// are statically impossible. E.g., for `Option<!>`, we do not include `Some(_)` in the
// returned list of constructors.
// Invariant: this is `Uninhabited` if and only if the type is uninhabited (as determined by
// `cx.is_uninhabited()`).
match ty.kind() {
ty::Bool => Self::Bool,
ty::Char => {
@ -963,82 +1040,73 @@ impl ConstructorSet {
};
Self::Integers { range_1: range, range_2: None }
}
ty::Array(sub_ty, len) if len.try_eval_target_usize(cx.tcx, cx.param_env).is_some() => {
let len = len.eval_target_usize(cx.tcx, cx.param_env) as usize;
if len != 0 && cx.is_uninhabited(*sub_ty) {
Self::Uninhabited
} else {
Self::Slice(Some(len))
}
ty::Slice(sub_ty) => {
Self::Slice { array_len: None, subtype_is_empty: cx.is_uninhabited(*sub_ty) }
}
// Treat arrays of a constant but unknown length like slices.
ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
if cx.is_uninhabited(*sub_ty) {
Self::SliceOfEmpty
} else {
Self::Slice(None)
ty::Array(sub_ty, len) => {
// We treat arrays of a constant but unknown length like slices.
Self::Slice {
array_len: len.try_eval_target_usize(cx.tcx, cx.param_env).map(|l| l as usize),
subtype_is_empty: cx.is_uninhabited(*sub_ty),
}
}
ty::Adt(def, args) if def.is_enum() => {
// If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an
// additional "unknown" constructor.
// There is no point in enumerating all possible variants, because the user can't
// actually match against them all themselves. So we always return only the fictitious
// constructor.
// E.g., in an example like:
//
// ```
// let err: io::ErrorKind = ...;
// match err {
// io::ErrorKind::NotFound => {},
// }
// ```
//
// we don't want to show every possible IO error, but instead have only `_` as the
// witness.
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
if def.variants().is_empty() && !is_declared_nonexhaustive {
Self::Uninhabited
Self::NoConstructors
} else {
let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns;
let (hidden_variants, visible_variants) = def
.variants()
.iter_enumerated()
.filter(|(_, v)| {
// If `exhaustive_patterns` is enabled, we exclude variants known to be
// uninhabited.
!is_exhaustive_pat_feature
|| v.inhabited_predicate(cx.tcx, *def)
.instantiate(cx.tcx, args)
.apply(cx.tcx, cx.param_env, cx.module)
})
.map(|(idx, _)| idx)
.partition(|idx| {
let variant_def_id = def.variant(*idx).def_id;
// Filter variants that depend on a disabled unstable feature.
let is_unstable = matches!(
cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
EvalResult::Deny { .. }
);
// Filter foreign `#[doc(hidden)]` variants.
let is_doc_hidden =
cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
is_unstable || is_doc_hidden
});
Self::Variants {
visible_variants,
hidden_variants,
non_exhaustive: is_declared_nonexhaustive,
let mut variants =
IndexVec::from_elem(VariantVisibility::Visible, def.variants());
for (idx, v) in def.variants().iter_enumerated() {
let variant_def_id = def.variant(idx).def_id;
// Visibly uninhabited variants.
let is_inhabited = v
.inhabited_predicate(cx.tcx, *def)
.instantiate(cx.tcx, args)
.apply(cx.tcx, cx.param_env, cx.module);
// Variants that depend on a disabled unstable feature.
let is_unstable = matches!(
cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
EvalResult::Deny { .. }
);
// Foreign `#[doc(hidden)]` variants.
let is_doc_hidden =
cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local();
let visibility = if !is_inhabited {
// FIXME: handle empty+hidden
VariantVisibility::Empty
} else if is_unstable || is_doc_hidden {
VariantVisibility::Hidden
} else {
VariantVisibility::Visible
};
variants[idx] = visibility;
}
Self::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
}
}
ty::Never => Self::Uninhabited,
_ if cx.is_uninhabited(ty) => Self::Uninhabited,
ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => Self::Single,
ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => {
Self::Single { empty: cx.is_uninhabited(ty) }
}
ty::Never => Self::NoConstructors,
// This type is one for which we cannot list constructors, like `str` or `f64`.
_ => Self::Unlistable,
// FIXME(Nadrieril): which of these are actually allowed?
ty::Float(_)
| ty::Str
| ty::Foreign(_)
| ty::RawPtr(_)
| ty::FnDef(_, _)
| ty::FnPtr(_)
| ty::Dynamic(_, _, _)
| ty::Closure(_, _)
| ty::Coroutine(_, _, _)
| ty::Alias(_, _)
| ty::Param(_)
| ty::Error(_) => Self::Unlistable,
ty::CoroutineWitness(_, _) | ty::Bound(_, _) | ty::Placeholder(_) | ty::Infer(_) => {
bug!("Encountered unexpected type in `ConstructorSet::for_ty`: {ty:?}")
}
}
}
@ -1056,50 +1124,51 @@ impl ConstructorSet {
'tcx: 'a,
{
let mut present: SmallVec<[_; 1]> = SmallVec::new();
// Empty constructors found missing.
let mut missing_empty = Vec::new();
// Nonempty constructors found missing.
let mut missing = Vec::new();
// 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),
}
}
match self {
ConstructorSet::Single => {
if seen.is_empty() {
missing.push(Single);
} else {
ConstructorSet::Single { empty } => {
if !seen.is_empty() {
present.push(Single);
} else if *empty {
missing_empty.push(Single);
} else {
missing.push(Single);
}
}
ConstructorSet::Variants { visible_variants, hidden_variants, non_exhaustive } => {
ConstructorSet::Variants { variants, non_exhaustive } => {
let seen_set: FxHashSet<_> = seen.iter().map(|c| c.as_variant().unwrap()).collect();
let mut skipped_a_hidden_variant = false;
for variant in visible_variants {
let ctor = Variant(*variant);
if seen_set.contains(variant) {
for (idx, visibility) in variants.iter_enumerated() {
let ctor = Variant(idx);
if seen_set.contains(&idx) {
present.push(ctor);
} else {
missing.push(ctor);
// We only put visible variants directly into `missing`.
match visibility {
VariantVisibility::Visible => missing.push(ctor),
VariantVisibility::Hidden => skipped_a_hidden_variant = true,
VariantVisibility::Empty => missing_empty.push(ctor),
}
}
}
for variant in hidden_variants {
let ctor = Variant(*variant);
if seen_set.contains(variant) {
present.push(ctor);
} else {
skipped_a_hidden_variant = true;
}
}
if skipped_a_hidden_variant {
missing.push(Hidden);
}
if *non_exhaustive {
missing.push(NonExhaustive);
}
@ -1143,33 +1212,22 @@ impl ConstructorSet {
}
}
}
&ConstructorSet::Slice(array_len) => {
ConstructorSet::Slice { array_len, subtype_is_empty } => {
let seen_slices = seen.iter().map(|c| c.as_slice().unwrap());
let base_slice = Slice::new(array_len, VarLen(0, 0));
for (seen, splitted_slice) in base_slice.split(seen_slices) {
let ctor = Slice(splitted_slice);
match seen {
Presence::Unseen => missing.push(ctor),
Presence::Seen => present.push(ctor),
}
}
}
ConstructorSet::SliceOfEmpty => {
// This one is tricky because even though there's only one possible value of this
// type (namely `[]`), slice patterns of all lengths are allowed, they're just
// unreachable if length != 0.
// We still gather the seen constructors in `present`, but the only slice that can
// go in `missing` is `[]`.
let seen_slices = seen.iter().map(|c| c.as_slice().unwrap());
let base_slice = Slice::new(None, VarLen(0, 0));
let base_slice = Slice::new(*array_len, VarLen(0, 0));
for (seen, splitted_slice) in base_slice.split(seen_slices) {
let ctor = Slice(splitted_slice);
match seen {
Presence::Seen => present.push(ctor),
Presence::Unseen if splitted_slice.arity() == 0 => {
missing.push(Slice(Slice::new(None, FixedLen(0))))
Presence::Unseen => {
if *subtype_is_empty && splitted_slice.arity() != 0 {
// We have subpatterns of an empty type, so the constructor is
// empty.
missing_empty.push(ctor);
} else {
missing.push(ctor);
}
}
Presence::Unseen => {}
}
}
}
@ -1179,18 +1237,25 @@ impl ConstructorSet {
present.extend(seen);
missing.push(NonExhaustive);
}
// If `exhaustive_patterns` is disabled and our scrutinee is an empty type, we cannot
// expose its emptiness. The exception is if the pattern is at the top level, because we
// want empty matches to be considered exhaustive.
ConstructorSet::Uninhabited
if !pcx.cx.tcx.features().exhaustive_patterns && !pcx.is_top_level =>
{
missing.push(NonExhaustive);
ConstructorSet::NoConstructors => {
// 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);
}
ConstructorSet::Uninhabited => {}
}
SplitConstructorSet { present, missing }
// 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, missing_empty }
}
}
@ -1263,7 +1328,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
// `field.ty()` doesn't normalize after substituting.
let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
let is_uninhabited = cx.is_uninhabited(ty);
let is_uninhabited = cx.tcx.features().exhaustive_patterns && cx.is_uninhabited(ty);
if is_uninhabited && (!is_visible || is_non_exhaustive) {
None
@ -1339,7 +1404,8 @@ pub(crate) struct DeconstructedPat<'p, 'tcx> {
fields: Fields<'p, 'tcx>,
ty: Ty<'tcx>,
span: Span,
reachable: Cell<bool>,
/// Whether removing this arm would change the behavior of the match expression.
useful: Cell<bool>,
}
impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
@ -1353,7 +1419,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
ty: Ty<'tcx>,
span: Span,
) -> Self {
DeconstructedPat { ctor, fields, ty, span, reachable: Cell::new(false) }
DeconstructedPat { ctor, fields, ty, span, useful: Cell::new(false) }
}
/// Note: the input patterns must have been lowered through
@ -1634,38 +1700,38 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
}
}
/// We keep track for each pattern if it was ever reachable during the analysis. This is used
/// with `unreachable_spans` to report unreachable subpatterns arising from or patterns.
pub(super) fn set_reachable(&self) {
self.reachable.set(true)
/// We keep track for each pattern if it was ever useful during the analysis. This is used
/// with `redundant_spans` to report redundant subpatterns arising from or patterns.
pub(super) fn set_useful(&self) {
self.useful.set(true)
}
pub(super) fn is_reachable(&self) -> bool {
if self.reachable.get() {
pub(super) fn is_useful(&self) -> bool {
if self.useful.get() {
true
} else if self.is_or_pat() && self.iter_fields().any(|f| f.is_reachable()) {
} else if self.is_or_pat() && self.iter_fields().any(|f| f.is_useful()) {
// We always expand or patterns in the matrix, so we will never see the actual
// or-pattern (the one with constructor `Or`) in the column. As such, it will not be
// marked as reachable itself, only its children will. We recover this information here.
self.set_reachable();
// marked as useful itself, only its children will. We recover this information here.
self.set_useful();
true
} else {
false
}
}
/// Report the spans of subpatterns that were not reachable, if any.
pub(super) fn unreachable_spans(&self) -> Vec<Span> {
/// Report the spans of subpatterns that were not useful, if any.
pub(super) fn redundant_spans(&self) -> Vec<Span> {
let mut spans = Vec::new();
self.collect_unreachable_spans(&mut spans);
self.collect_redundant_spans(&mut spans);
spans
}
fn collect_unreachable_spans(&self, spans: &mut Vec<Span>) {
// We don't look at subpatterns if we already reported the whole pattern as unreachable.
if !self.is_reachable() {
fn collect_redundant_spans(&self, spans: &mut Vec<Span>) {
// We don't look at subpatterns if we already reported the whole pattern as redundant.
if !self.is_useful() {
spans.push(self.span);
} else {
for p in self.iter_fields() {
p.collect_unreachable_spans(spans);
p.collect_redundant_spans(spans);
}
}
}

View File

@ -1,8 +1,8 @@
//! # Match exhaustiveness and reachability algorithm
//! # Match exhaustiveness and redundancy algorithm
//!
//! This file contains the logic for exhaustiveness and reachability checking for pattern-matching.
//! This file contains the logic for exhaustiveness and usefulness checking for pattern-matching.
//! Specifically, given a list of patterns in a match, we can tell whether:
//! (a) a given pattern is reachable (reachability)
//! (a) a given pattern is redundant
//! (b) the patterns cover every possible value for the type (exhaustiveness)
//!
//! The algorithm implemented here is inspired from the one described in [this
@ -19,15 +19,15 @@
//! The algorithm is given as input a list of patterns, one for each arm of a match, and computes
//! the following:
//! - a set of values that match none of the patterns (if any),
//! - for each subpattern (taking into account or-patterns), whether it would catch any value that
//! isn't caught by a pattern before it, i.e. whether it is reachable.
//! - for each subpattern (taking into account or-patterns), whether removing it would change
//! anything about how the match executes, i.e. whether it is useful/not redundant.
//!
//! To a first approximation, the algorithm works by exploring all possible values for the type
//! being matched on, and determining which arm(s) catch which value. To make this tractable we
//! cleverly group together values, as we'll see below.
//!
//! The entrypoint of this file is the [`compute_match_usefulness`] function, which computes
//! reachability for each subpattern and exhaustiveness for the whole match.
//! usefulness for each subpattern and exhaustiveness for the whole match.
//!
//! In this page we explain the necessary concepts to understand how the algorithm works.
//!
@ -39,17 +39,17 @@
//! none of the `p_i`. We write `usefulness(p_1 .. p_n, q)` for a function that returns a list of
//! such values. The aim of this file is to compute it efficiently.
//!
//! This is enough to compute reachability: a pattern in a `match` expression is reachable iff it is
//! useful w.r.t. the patterns above it:
//! This is enough to compute usefulness: a pattern in a `match` expression is redundant iff it is
//! not useful w.r.t. the patterns above it:
//! ```compile_fail,E0004
//! # #![feature(exclusive_range_pattern)]
//! # fn foo() {
//! match Some(0u32) {
//! Some(0..100) => {},
//! Some(90..190) => {}, // reachable: `Some(150)` is matched by this but not the branch above
//! Some(50..150) => {}, // unreachable: all the values this matches are already matched by
//! Some(90..190) => {}, // useful: `Some(150)` is matched by this but not the branch above
//! Some(50..150) => {}, // redundant: all the values this matches are already matched by
//! // the branches above
//! None => {}, // reachable: `None` is matched by this but not the branches above
//! None => {}, // useful: `None` is matched by this but not the branches above
//! }
//! # }
//! ```
@ -246,18 +246,17 @@
//!
//!
//!
//! # Computing reachability and exhaustiveness in one go
//! # Computing usefulness and exhaustiveness in one go
//!
//! The algorithm we have described so far computes usefulness of each pattern in turn to check if
//! it is reachable, and ends by checking if `_` is useful to determine exhaustiveness of the whole
//! match. In practice, instead of doing "for each pattern { for each constructor { ... } }", we do
//! "for each constructor { for each pattern { ... } }". This allows us to compute everything in one
//! go.
//! The algorithm we have described so far computes usefulness of each pattern in turn, and ends by
//! checking if `_` is useful to determine exhaustiveness of the whole match. In practice, instead
//! of doing "for each pattern { for each constructor { ... } }", we do "for each constructor { for
//! each pattern { ... } }". This allows us to compute everything in one go.
//!
//! [`Matrix`] stores the set of pattern-tuples under consideration. We track reachability of each
//! [`Matrix`] stores the set of pattern-tuples under consideration. We track usefulness of each
//! row mutably in the matrix as we go along. We ignore witnesses of usefulness of the match rows.
//! We gather witnesses of the usefulness of `_` in [`WitnessMatrix`]. The algorithm that computes
//! all this is in [`compute_exhaustiveness_and_reachability`].
//! all this is in [`compute_exhaustiveness_and_usefulness`].
//!
//! See the full example at the bottom of this documentation.
//!
@ -279,7 +278,7 @@
//! ```
//!
//! In this example, trying any of `0`, `1`, .., `49` will give the same specialized matrix, and
//! thus the same reachability/exhaustiveness results. We can thus accelerate the algorithm by
//! thus the same usefulness/exhaustiveness results. We can thus accelerate the algorithm by
//! trying them all at once. Here in fact, the only cases we need to consider are: `0..50`,
//! `50..=100`, `101..=150`,`151..=200` and `201..`.
//!
@ -299,15 +298,16 @@
//! This is done in [`ConstructorSet::split`] and explained in [`super::deconstruct_pat`].
//!
//!
//!
//! # Or-patterns
//!
//! What we have described so far works well if there are no or-patterns. To handle them, if the
//! first pattern of a row in the matrix is an or-pattern, we expand it by duplicating the rest of
//! the row as necessary. This is handled automatically in [`Matrix`].
//!
//! This makes reachability tracking subtle, because we also want to compute whether an alternative
//! of an or-pattern is unreachable, e.g. in `Some(_) | Some(0)`. We track reachability of each
//! subpattern by interior mutability in [`DeconstructedPat`] with `set_reachable`/`is_reachable`.
//! This makes usefulness tracking subtle, because we also want to compute whether an alternative
//! of an or-pattern is redundant, e.g. in `Some(_) | Some(0)`. We track usefulness of each
//! subpattern by interior mutability in [`DeconstructedPat`] with `set_useful`/`is_useful`.
//!
//! It's unfortunate that we have to use interior mutability, but believe me (Nadrieril), I have
//! tried [other](https://github.com/rust-lang/rust/pull/80104)
@ -332,6 +332,69 @@
//!
//!
//!
//! # Usefulness vs reachability, validity, and empty patterns
//!
//! This is likely the subtlest aspect of the algorithm. To be fully precise, a match doesn't
//! operate on a value, it operates on a place. In certain unsafe circumstances, it is possible for
//! a place to not contain valid data for its type. This has subtle consequences for empty types.
//! Take the following:
//!
//! ```rust
//! enum Void {}
//! let x: u8 = 0;
//! let ptr: *const Void = &x as *const u8 as *const Void;
//! unsafe {
//! match *ptr {
//! _ => println!("Reachable!"),
//! }
//! }
//! ```
//!
//! In this example, `ptr` is a valid pointer pointing to a place with invalid data. The `_` pattern
//! does not look at the contents of `*ptr`, so this is ok and the arm is taken. In other words,
//! despite the place we are inspecting being of type `Void`, there is a reachable arm. If the
//! arm had a binding however:
//!
//! ```rust
//! # #[derive(Copy, Clone)]
//! # enum Void {}
//! # let x: u8 = 0;
//! # let ptr: *const Void = &x as *const u8 as *const Void;
//! # unsafe {
//! match *ptr {
//! _a => println!("Unreachable!"),
//! }
//! # }
//! ```
//!
//! Here the binding loads the value of type `Void` from the `*ptr` place. In this example, this
//! causes UB since the data is not valid. In the general case, this asserts validity of the data at
//! `*ptr`. Either way, this arm will never be taken.
//!
//! Finally, let's consider the empty match `match *ptr {}`. If we consider this exhaustive, then
//! having invalid data at `*ptr` is invalid. In other words, the empty match is semantically
//! equivalent to the `_a => ...` match. In the interest of explicitness, we prefer the case with an
//! arm, hence we won't tell the user to remove the `_a` arm. In other words, the `_a` arm is
//! unreachable yet not redundant. This is why we lint on redundant arms rather than unreachable
//! arms, despite the fact that the lint says "unreachable".
//!
//! These considerations only affects certain places, namely those that can contain non-valid data
//! without UB. These are: pointer dereferences, reference dereferences, and union field accesses.
//! We track in the algorithm whether a given place is known to contain valid data. This is done
//! first by inspecting the scrutinee syntactically (which gives us `cx.known_valid_scrutinee`), and
//! then by tracking validity of each column of the matrix (which correspond to places) as we
//! recurse into subpatterns. That second part is done through [`ValidityConstraint`], most notably
//! [`ValidityConstraint::specialize`].
//!
//! Having said all that, in practice we don't fully follow what's been presented in this section.
//! Under `exhaustive_patterns`, we allow omitting empty arms even in `!known_valid` places, for
//! backwards-compatibility until we have a better alternative. Without `exhaustive_patterns`, we
//! mostly treat empty types as inhabited, except specifically a non-nested `!` or empty enum. In
//! this specific case we also allow the empty match regardless of place validity, for
//! backwards-compatibility. Hopefully we can eventually deprecate this.
//!
//!
//!
//! # Full example
//!
//! We illustrate a full run of the algorithm on the following match.
@ -348,7 +411,7 @@
//! ```
//!
//! We keep track of the original row for illustration purposes, this is not what the algorithm
//! actually does (it tracks reachability as a boolean on each row).
//! actually does (it tracks usefulness as a boolean on each row).
//!
//! ```text
//! ┐ Patterns:
@ -377,7 +440,7 @@
//! │ │ │ ├─┐ Patterns:
//! │ │ │ │ │ 1. `[]`
//! │ │ │ │ │
//! │ │ │ │ │ We note arm 1 is reachable (by `Pair(Some(0), true)`).
//! │ │ │ │ │ We note arm 1 is useful (by `Pair(Some(0), true)`).
//! │ │ │ ├─┘
//! │ │ │ │
//! │ │ │ │ Specialize with `false`:
@ -385,7 +448,7 @@
//! │ │ │ │ │ 1. `[]`
//! │ │ │ │ │ 3. `[]`
//! │ │ │ │ │
//! │ │ │ │ │ We note arm 1 is reachable (by `Pair(Some(0), false)`).
//! │ │ │ │ │ We note arm 1 is useful (by `Pair(Some(0), false)`).
//! │ │ │ ├─┘
//! │ │ ├─┘
//! │ │ │
@ -408,7 +471,7 @@
//! │ │ │ ├─┐ Patterns:
//! │ │ │ │ │ 2. `[]`
//! │ │ │ │ │
//! │ │ │ │ │ We note arm 2 is reachable (by `Pair(Some(1..), false)`).
//! │ │ │ │ │ We note arm 2 is useful (by `Pair(Some(1..), false)`).
//! │ │ │ ├─┘
//! │ │ │ │
//! │ │ │ │ Total witnesses for `1..`:
@ -442,7 +505,7 @@
//! │ │ ├─┐ Patterns:
//! │ │ │ │ 2. `[]`
//! │ │ │ │
//! │ │ │ │ We note arm 2 is reachable (by `Pair(None, false)`).
//! │ │ │ │ We note arm 2 is useful (by `Pair(None, false)`).
//! │ │ ├─┘
//! │ │ │
//! │ │ │ Total witnesses for `None`:
@ -466,7 +529,7 @@
//! ```
//!
//! We conclude:
//! - Arm 3 is unreachable (it was never marked as reachable);
//! - Arm 3 is redundant (it was never marked as useful);
//! - The match is not exhaustive;
//! - Adding arms with `Pair(Some(1..), true)` and `Pair(None, true)` would make the match exhaustive.
//!
@ -488,6 +551,7 @@
//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
//! reason not to, for example if they crucially depend on a particular feature like `or_patterns`.
use self::ValidityConstraint::*;
use super::deconstruct_pat::{
Constructor, ConstructorSet, DeconstructedPat, IntRange, MaybeInfiniteInt, SplitConstructorSet,
WitnessPat,
@ -524,20 +588,19 @@ pub(crate) struct MatchCheckCtxt<'p, 'tcx> {
/// Lint level at the match.
pub(crate) match_lint_level: HirId,
/// The span of the whole match, if applicable.
pub(crate) match_span: Option<Span>,
pub(crate) whole_match_span: Option<Span>,
/// Span of the scrutinee.
pub(crate) scrut_span: Span,
/// Only produce `NON_EXHAUSTIVE_OMITTED_PATTERNS` lint on refutable patterns.
pub(crate) refutable: bool,
/// Whether the data at the scrutinee is known to be valid. This is false if the scrutinee comes
/// from a union field, a pointer deref, or a reference deref (pending opsem decisions).
pub(crate) known_valid_scrutinee: bool,
}
impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
pub(super) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
if self.tcx.features().exhaustive_patterns {
!ty.is_inhabited_from(self.tcx, self.module, self.param_env)
} else {
false
}
!ty.is_inhabited_from(self.tcx, self.module, self.param_env)
}
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
@ -561,12 +624,87 @@ pub(super) struct PatCtxt<'a, 'p, 'tcx> {
pub(super) is_top_level: bool,
}
impl<'a, 'p, 'tcx> PatCtxt<'a, 'p, 'tcx> {
/// A `PatCtxt` when code other than `is_useful` needs one.
fn new_dummy(cx: &'a MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>) -> Self {
PatCtxt { cx, ty, is_top_level: false }
}
}
impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PatCtxt").field("ty", &self.ty).finish()
}
}
/// 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 {
pub(super) fn from_bool(is_valid_only: bool) -> Self {
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
/// 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 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 {
self
}
}
}
impl fmt::Display for ValidityConstraint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
ValidOnly => "",
MaybeInvalid | MaybeInvalidButAllowOmittingArms => "?",
};
write!(f, "{s}")
}
}
/// Represents a pattern-tuple under investigation.
#[derive(Clone)]
struct PatStack<'p, 'tcx> {
@ -639,13 +777,13 @@ struct MatrixRow<'p, 'tcx> {
/// Whether the original arm had a guard. This is inherited when specializing.
is_under_guard: bool,
/// When we specialize, we remember which row of the original matrix produced a given row of the
/// specialized matrix. When we unspecialize, we use this to propagate reachability back up the
/// specialized matrix. When we unspecialize, we use this to propagate usefulness back up the
/// callstack.
parent_row: usize,
/// False when the matrix is just built. This is set to `true` by
/// [`compute_exhaustiveness_and_reachability`] if the arm is found to be reachable.
/// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful.
/// This is reset to `false` when specializing.
reachable: bool,
useful: bool,
}
impl<'p, 'tcx> MatrixRow<'p, 'tcx> {
@ -672,7 +810,7 @@ impl<'p, 'tcx> MatrixRow<'p, 'tcx> {
pats: patstack,
parent_row: self.parent_row,
is_under_guard: self.is_under_guard,
reachable: false,
useful: false,
})
}
@ -688,7 +826,7 @@ impl<'p, 'tcx> MatrixRow<'p, 'tcx> {
pats: self.pats.pop_head_constructor(pcx, ctor),
parent_row,
is_under_guard: self.is_under_guard,
reachable: false,
useful: false,
}
}
}
@ -711,10 +849,15 @@ impl<'p, 'tcx> fmt::Debug for MatrixRow<'p, 'tcx> {
/// the matrix will correspond to `scrutinee.0.Some.0` and the second column to `scrutinee.1`.
#[derive(Clone)]
struct Matrix<'p, 'tcx> {
/// Vector of rows. The rows must form a rectangular 2D array. Moreover, all the patterns of
/// each column must have the same type. Each column corresponds to a place within the
/// scrutinee.
rows: Vec<MatrixRow<'p, 'tcx>>,
/// Stores an extra fictitious row full of wildcards. Mostly used to keep track of the type of
/// each column. This must obey the same invariants as the real rows.
wildcard_row: PatStack<'p, 'tcx>,
/// Track for each column/place whether it contains a known valid value.
place_validity: SmallVec<[ValidityConstraint; 2]>,
}
impl<'p, 'tcx> Matrix<'p, 'tcx> {
@ -732,16 +875,28 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
}
/// Build a new matrix from an iterator of `MatchArm`s.
fn new(cx: &MatchCheckCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>) -> Self {
fn new<'a>(
cx: &MatchCheckCtxt<'p, 'tcx>,
arms: &[MatchArm<'p, 'tcx>],
scrut_ty: Ty<'tcx>,
scrut_validity: ValidityConstraint,
) -> Self
where
'p: 'a,
{
let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty, DUMMY_SP));
let wildcard_row = PatStack::from_pattern(wild_pattern);
let mut matrix = Matrix { rows: Vec::with_capacity(arms.len()), wildcard_row };
let mut matrix = Matrix {
rows: Vec::with_capacity(arms.len()),
wildcard_row,
place_validity: smallvec![scrut_validity],
};
for (row_id, arm) in arms.iter().enumerate() {
let v = MatrixRow {
pats: PatStack::from_pattern(arm.pat),
parent_row: row_id, // dummy, we won't read it
is_under_guard: arm.has_guard,
reachable: false,
useful: false,
};
matrix.expand_and_push(v);
}
@ -799,7 +954,13 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
ctor: &Constructor<'tcx>,
) -> Matrix<'p, 'tcx> {
let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor);
let mut matrix = Matrix { rows: Vec::new(), wildcard_row };
let new_validity = self.place_validity[0].specialize(pcx, ctor);
let new_place_validity = std::iter::repeat(new_validity)
.take(ctor.arity(pcx))
.chain(self.place_validity[1..].iter().copied())
.collect();
let mut matrix =
Matrix { rows: Vec::new(), wildcard_row, place_validity: new_place_validity };
for (i, row) in self.rows().enumerate() {
if ctor.is_covered_by(pcx, row.head().ctor()) {
let new_row = row.pop_head_constructor(pcx, ctor, i);
@ -818,27 +979,38 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
/// + true + [Second(true)] +
/// + false + [_] +
/// + _ + [_, _, tail @ ..] +
/// | ✓ | ? | // column validity
/// ```
impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\n")?;
let Matrix { rows, .. } = self;
let pretty_printed_matrix: Vec<Vec<String>> =
rows.iter().map(|row| row.iter().map(|pat| format!("{pat:?}")).collect()).collect();
let mut pretty_printed_matrix: Vec<Vec<String>> = self
.rows
.iter()
.map(|row| row.iter().map(|pat| format!("{pat:?}")).collect())
.collect();
pretty_printed_matrix
.push(self.place_validity.iter().map(|validity| format!("{validity}")).collect());
let column_count = rows.iter().map(|row| row.len()).next().unwrap_or(0);
assert!(rows.iter().all(|row| row.len() == column_count));
let column_count = self.column_count();
assert!(self.rows.iter().all(|row| row.len() == column_count));
assert!(self.place_validity.len() == column_count);
let column_widths: Vec<usize> = (0..column_count)
.map(|col| pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0))
.collect();
for row in pretty_printed_matrix {
write!(f, "+")?;
for (row_i, row) in pretty_printed_matrix.into_iter().enumerate() {
let is_validity_row = row_i == self.rows.len();
let sep = if is_validity_row { "|" } else { "+" };
write!(f, "{sep}")?;
for (column, pat_str) in row.into_iter().enumerate() {
write!(f, " ")?;
write!(f, "{:1$}", pat_str, column_widths[column])?;
write!(f, " +")?;
write!(f, " {sep}")?;
}
if is_validity_row {
write!(f, " // column validity")?;
}
write!(f, "\n")?;
}
@ -940,7 +1112,7 @@ impl<'tcx> WitnessStack<'tcx> {
/// Represents a set of pattern-tuples that are witnesses of non-exhaustiveness for error
/// reporting. This has similar invariants as `Matrix` does.
///
/// The `WitnessMatrix` returned by [`compute_exhaustiveness_and_reachability`] obeys the invariant
/// The `WitnessMatrix` returned by [`compute_exhaustiveness_and_usefulness`] obeys the invariant
/// that the union of the input `Matrix` and the output `WitnessMatrix` together matches the type
/// exhaustively.
///
@ -1029,7 +1201,7 @@ impl<'tcx> WitnessMatrix<'tcx> {
/// The core of the algorithm.
///
/// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks
/// usefulness of each row in the matrix (in `row.reachable`). We track reachability of each
/// usefulness of each row in the matrix (in `row.useful`). We track usefulness of each
/// subpattern using interior mutability in `DeconstructedPat`.
///
/// The input `Matrix` and the output `WitnessMatrix` together match the type exhaustively.
@ -1038,10 +1210,10 @@ impl<'tcx> WitnessMatrix<'tcx> {
/// - specialization, where we dig into the rows that have a specific constructor and call ourselves
/// recursively;
/// - unspecialization, where we lift the results from the previous step into results for this step
/// (using `apply_constructor` and by updating `row.reachable` for each parent row).
/// (using `apply_constructor` and by updating `row.useful` for each parent row).
/// This is all explained at the top of the file.
#[instrument(level = "debug", skip(cx, is_top_level), ret)]
fn compute_exhaustiveness_and_reachability<'p, 'tcx>(
fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
matrix: &mut Matrix<'p, 'tcx>,
is_top_level: bool,
@ -1050,13 +1222,13 @@ fn compute_exhaustiveness_and_reachability<'p, 'tcx>(
let Some(ty) = matrix.head_ty() else {
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
// A row is reachable iff it has no (unguarded) rows above it.
// A row is useful iff it has no (unguarded) rows above it.
for row in matrix.rows_mut() {
// All rows are reachable until we find one without a guard.
row.reachable = true;
// 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();
}
}
@ -1067,26 +1239,37 @@ fn compute_exhaustiveness_and_reachability<'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();
@ -1095,14 +1278,22 @@ fn compute_exhaustiveness_and_reachability<'p, 'tcx>(
// Dig into rows that match `ctor`.
let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor);
let mut witnesses = ensure_sufficient_stack(|| {
compute_exhaustiveness_and_reachability(cx, &mut spec_matrix, false)
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,
);
@ -1113,14 +1304,14 @@ fn compute_exhaustiveness_and_reachability<'p, 'tcx>(
// A parent row is useful if any of its children is.
for child_row in spec_matrix.rows() {
let parent_row = &mut matrix.rows[child_row.parent_row];
parent_row.reachable = parent_row.reachable || child_row.reachable;
parent_row.useful = parent_row.useful || child_row.useful;
}
}
// Record that the subpattern is reachable.
// Record usefulness in the patterns.
for row in matrix.rows() {
if row.reachable {
row.head().set_reachable();
if row.useful {
row.head().set_useful();
}
}
@ -1130,8 +1321,8 @@ fn compute_exhaustiveness_and_reachability<'p, 'tcx>(
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
/// inspect the same subvalue/place".
/// This is used to traverse patterns column-by-column for lints. Despite similarities with
/// [`compute_exhaustiveness_and_reachability`], this does a different traversal. Notably this is
/// linear in the depth of patterns, whereas `compute_exhaustiveness_and_reachability` is worst-case
/// [`compute_exhaustiveness_and_usefulness`], this does a different traversal. Notably this is
/// linear in the depth of patterns, whereas `compute_exhaustiveness_and_usefulness` is worst-case
/// exponential (exhaustiveness is NP-complete). The core difference is that we treat sub-columns
/// separately.
///
@ -1228,7 +1419,7 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
let Some(ty) = column.head_ty() else {
return Vec::new();
};
let pcx = &PatCtxt { cx, ty, is_top_level: false };
let pcx = &PatCtxt::new_dummy(cx, ty);
let set = column.analyze_ctors(pcx);
if set.present.is_empty() {
@ -1277,7 +1468,7 @@ fn lint_overlapping_range_endpoints<'p, 'tcx>(
let Some(ty) = column.head_ty() else {
return;
};
let pcx = &PatCtxt { cx, ty, is_top_level: false };
let pcx = &PatCtxt::new_dummy(cx, ty);
let set = column.analyze_ctors(pcx);
@ -1351,37 +1542,38 @@ pub(crate) struct MatchArm<'p, 'tcx> {
pub(crate) has_guard: bool,
}
/// Indicates whether or not a given arm is reachable.
/// Indicates whether or not a given arm is useful.
#[derive(Clone, Debug)]
pub(crate) enum Reachability {
/// The arm is reachable. This additionally carries a set of or-pattern branches that have been
/// found to be unreachable despite the overall arm being reachable. Used only in the presence
/// of or-patterns, otherwise it stays empty.
Reachable(Vec<Span>),
/// The arm is unreachable.
Unreachable,
pub(crate) enum Usefulness {
/// The arm is useful. This additionally carries a set of or-pattern branches that have been
/// found to be redundant despite the overall arm being useful. Used only in the presence of
/// or-patterns, otherwise it stays empty.
Useful(Vec<Span>),
/// The arm is redundant and can be removed without changing the behavior of the match
/// expression.
Redundant,
}
/// The output of checking a match for exhaustiveness and arm reachability.
/// The output of checking a match for exhaustiveness and arm usefulness.
pub(crate) struct UsefulnessReport<'p, 'tcx> {
/// For each arm of the input, whether that arm is reachable after the arms above it.
pub(crate) arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Reachability)>,
/// For each arm of the input, whether that arm is useful after the arms above it.
pub(crate) arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Usefulness)>,
/// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
/// exhaustiveness.
pub(crate) non_exhaustiveness_witnesses: Vec<WitnessPat<'tcx>>,
}
/// The entrypoint for this file. Computes whether a match is exhaustive and which of its arms are
/// reachable.
/// useful.
#[instrument(skip(cx, arms), level = "debug")]
pub(crate) fn compute_match_usefulness<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
arms: &[MatchArm<'p, 'tcx>],
scrut_ty: Ty<'tcx>,
) -> UsefulnessReport<'p, 'tcx> {
let mut matrix = Matrix::new(cx, arms, scrut_ty);
let non_exhaustiveness_witnesses =
compute_exhaustiveness_and_reachability(cx, &mut matrix, true);
let scrut_validity = ValidityConstraint::from_bool(cx.known_valid_scrutinee);
let mut matrix = Matrix::new(cx, arms, scrut_ty, scrut_validity);
let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(cx, &mut matrix, true);
let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column();
let arm_usefulness: Vec<_> = arms
@ -1389,12 +1581,13 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
.copied()
.map(|arm| {
debug!(?arm);
let reachability = if arm.pat.is_reachable() {
Reachability::Reachable(arm.pat.unreachable_spans())
// We warn when a pattern is not useful.
let usefulness = if arm.pat.is_useful() {
Usefulness::Useful(arm.pat.redundant_spans())
} else {
Reachability::Unreachable
Usefulness::Redundant
};
(arm, reachability)
(arm, usefulness)
})
.collect();
let report = UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses };
@ -1436,7 +1629,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
if !matches!(lint_level, rustc_session::lint::Level::Allow) {
let decorator = NonExhaustiveOmittedPatternLintOnArm {
lint_span: lint_level_source.span(),
suggest_lint_on_match: cx.match_span.map(|span| span.shrink_to_lo()),
suggest_lint_on_match: cx.whole_match_span.map(|span| span.shrink_to_lo()),
lint_level: lint_level.as_str(),
lint_name: "non_exhaustive_omitted_patterns",
};

View File

@ -0,0 +1,63 @@
error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:17:9
|
LL | _ => {}
| ^
|
note: the lint level is defined here
--> $DIR/empty-match-check-notes.rs:7:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:20:9
|
LL | _ if false => {}
| ^
error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:27:9
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:30:9
|
LL | _ if false => {}
| ^
error[E0005]: refutable pattern in local binding
--> $DIR/empty-match-check-notes.rs:35:9
|
LL | let None = x;
| ^^^^ pattern `Some(_)` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: pattern `Some(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
= note: the matched value is of type `Option<SecretlyUninhabitedForeignStruct>`
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let None = x { todo!() };
| ++ +++++++++++
error[E0004]: non-exhaustive patterns: `_` not covered
--> $DIR/empty-match-check-notes.rs:45:11
|
LL | match 0u8 {
| ^^^ pattern `_` not covered
|
= note: the matched value is of type `u8`
= 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 ~ _ if false => {},
LL + _ => todo!()
|
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0004, E0005.
For more information about an error, try `rustc --explain E0004`.

View File

@ -0,0 +1,62 @@
error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:17:9
|
LL | _ => {}
| ^
|
note: the lint level is defined here
--> $DIR/empty-match-check-notes.rs:7:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:20:9
|
LL | _ if false => {}
| ^
error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:27:9
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-match-check-notes.rs:30:9
|
LL | _ if false => {}
| ^
error[E0005]: refutable pattern in local binding
--> $DIR/empty-match-check-notes.rs:35:9
|
LL | let None = x;
| ^^^^ pattern `Some(_)` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `Option<SecretlyUninhabitedForeignStruct>`
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let None = x { todo!() };
| ++ +++++++++++
error[E0004]: non-exhaustive patterns: `_` not covered
--> $DIR/empty-match-check-notes.rs:45:11
|
LL | match 0u8 {
| ^^^ pattern `_` not covered
|
= note: the matched value is of type `u8`
= 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 ~ _ if false => {},
LL + _ => todo!()
|
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0004, E0005.
For more information about an error, try `rustc --explain E0004`.

View File

@ -0,0 +1,52 @@
// aux-build:empty.rs
// revisions: normal exhaustive_patterns
//
// This tests a match with no arms on various types, and checks NOTEs.
#![feature(never_type)]
#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
#![deny(unreachable_patterns)]
//~^ NOTE the lint level is defined here
extern crate empty;
enum EmptyEnum {}
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 empty_foreign_enum_private(x: Option<empty::SecretlyUninhabitedForeignStruct>) {
let None = x;
//~^ ERROR refutable pattern in local binding
//~| NOTE `let` bindings require an "irrefutable pattern"
//~| NOTE for more information, visit
//~| NOTE the matched value is of type
//~| NOTE pattern `Some(_)` not covered
//[exhaustive_patterns]~| NOTE currently uninhabited, but this variant contains private fields
}
fn main() {
match 0u8 {
//~^ ERROR `_` not covered
//~| NOTE the matched value is of type
//~| NOTE match arms with guards don't count towards exhaustivity
//~| NOTE pattern `_` not covered
_ if false => {}
}
}

View File

@ -1,62 +1,5 @@
error: unreachable pattern
--> $DIR/empty-match.rs:68:9
|
LL | _ => {},
| ^
|
note: the lint level is defined here
--> $DIR/empty-match.rs:8:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern
--> $DIR/empty-match.rs:71:9
|
LL | _ if false => {},
| ^
error: unreachable pattern
--> $DIR/empty-match.rs:78:9
|
LL | _ => {},
| ^
error: unreachable pattern
--> $DIR/empty-match.rs:81:9
|
LL | _ if false => {},
| ^
error[E0005]: refutable pattern in local binding
--> $DIR/empty-match.rs:86:9
|
LL | let None = x;
| ^^^^ pattern `Some(_)` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: pattern `Some(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
= note: the matched value is of type `Option<SecretlyUninhabitedForeignStruct>`
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let None = x { todo!() };
| ++ +++++++++++
error: unreachable pattern
--> $DIR/empty-match.rs:98:9
|
LL | _ => {},
| ^
error: unreachable pattern
--> $DIR/empty-match.rs:101:9
|
LL | _ if false => {},
| ^
error[E0004]: non-exhaustive patterns: type `u8` is non-empty
--> $DIR/empty-match.rs:119:20
--> $DIR/empty-match.rs:46:20
|
LL | match_no_arms!(0u8);
| ^^^
@ -65,122 +8,121 @@ LL | match_no_arms!(0u8);
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty
--> $DIR/empty-match.rs:121:20
--> $DIR/empty-match.rs:47:20
|
LL | match_no_arms!(NonEmptyStruct1);
| ^^^^^^^^^^^^^^^
|
note: `NonEmptyStruct1` defined here
--> $DIR/empty-match.rs:15:8
--> $DIR/empty-match.rs:22:12
|
LL | struct NonEmptyStruct1;
| ^^^^^^^^^^^^^^^
LL | struct NonEmptyStruct1;
| ^^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyStruct1`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty
--> $DIR/empty-match.rs:123:20
--> $DIR/empty-match.rs:48:20
|
LL | match_no_arms!(NonEmptyStruct2(true));
| ^^^^^^^^^^^^^^^^^^^^^
|
note: `NonEmptyStruct2` defined here
--> $DIR/empty-match.rs:18:8
--> $DIR/empty-match.rs:23:12
|
LL | struct NonEmptyStruct2(bool);
| ^^^^^^^^^^^^^^^
LL | struct NonEmptyStruct2(bool);
| ^^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyStruct2`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
--> $DIR/empty-match.rs:125:20
--> $DIR/empty-match.rs:49:20
|
LL | match_no_arms!((NonEmptyUnion1 { foo: () }));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: `NonEmptyUnion1` defined here
--> $DIR/empty-match.rs:21:7
--> $DIR/empty-match.rs:24:11
|
LL | union NonEmptyUnion1 {
| ^^^^^^^^^^^^^^
LL | union NonEmptyUnion1 {
| ^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyUnion1`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
--> $DIR/empty-match.rs:127:20
--> $DIR/empty-match.rs:50:20
|
LL | match_no_arms!((NonEmptyUnion2 { foo: () }));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: `NonEmptyUnion2` defined here
--> $DIR/empty-match.rs:26:7
--> $DIR/empty-match.rs:27:11
|
LL | union NonEmptyUnion2 {
| ^^^^^^^^^^^^^^
LL | union NonEmptyUnion2 {
| ^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyUnion2`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
--> $DIR/empty-match.rs:129:20
--> $DIR/empty-match.rs:51:20
|
LL | match_no_arms!(NonEmptyEnum1::Foo(true));
| ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
|
note: `NonEmptyEnum1` defined here
--> $DIR/empty-match.rs:32:6
--> $DIR/empty-match.rs:31:10
|
LL | enum NonEmptyEnum1 {
| ^^^^^^^^^^^^^
...
LL | Foo(bool),
| --- not covered
LL | enum NonEmptyEnum1 {
| ^^^^^^^^^^^^^
LL | Foo(bool),
| --- not covered
= note: the matched value is of type `NonEmptyEnum1`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern
error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
--> $DIR/empty-match.rs:132:20
--> $DIR/empty-match.rs:52:20
|
LL | match_no_arms!(NonEmptyEnum2::Foo(true));
| ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
|
note: `NonEmptyEnum2` defined here
--> $DIR/empty-match.rs:39:6
--> $DIR/empty-match.rs:34:10
|
LL | enum NonEmptyEnum2 {
| ^^^^^^^^^^^^^
...
LL | Foo(bool),
| --- not covered
...
LL | Bar,
| --- not covered
LL | enum NonEmptyEnum2 {
| ^^^^^^^^^^^^^
LL | Foo(bool),
| --- not covered
LL | Bar,
| --- not covered
= note: the matched value is of type `NonEmptyEnum2`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
--> $DIR/empty-match.rs:135:20
--> $DIR/empty-match.rs:53:20
|
LL | match_no_arms!(NonEmptyEnum5::V1);
| ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
|
note: `NonEmptyEnum5` defined here
--> $DIR/empty-match.rs:49:6
--> $DIR/empty-match.rs:38:10
|
LL | enum NonEmptyEnum5 {
| ^^^^^^^^^^^^^
...
LL | V1, V2, V3, V4, V5,
| -- -- -- -- -- not covered
| | | | |
| | | | not covered
| | | not covered
| | not covered
| not covered
LL | enum NonEmptyEnum5 {
| ^^^^^^^^^^^^^
LL | V1,
| -- not covered
LL | V2,
| -- not covered
LL | V3,
| -- not covered
LL | V4,
| -- not covered
LL | V5,
| -- not covered
= note: the matched value is of type `NonEmptyEnum5`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
error[E0004]: non-exhaustive patterns: `_` not covered
--> $DIR/empty-match.rs:139:24
--> $DIR/empty-match.rs:55:24
|
LL | match_guarded_arm!(0u8);
| ^^^ pattern `_` not covered
@ -189,161 +131,159 @@ LL | match_guarded_arm!(0u8);
= 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 ~ _ if false => {},
LL + _ => todo!()
LL ~ _ if false => {},
LL + _ => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
--> $DIR/empty-match.rs:144:24
--> $DIR/empty-match.rs:56:24
|
LL | match_guarded_arm!(NonEmptyStruct1);
| ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
|
note: `NonEmptyStruct1` defined here
--> $DIR/empty-match.rs:15:8
--> $DIR/empty-match.rs:22:12
|
LL | struct NonEmptyStruct1;
| ^^^^^^^^^^^^^^^
LL | struct NonEmptyStruct1;
| ^^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyStruct1`
= 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 ~ _ if false => {},
LL + NonEmptyStruct1 => todo!()
LL ~ _ if false => {},
LL + NonEmptyStruct1 => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
--> $DIR/empty-match.rs:149:24
--> $DIR/empty-match.rs:57:24
|
LL | match_guarded_arm!(NonEmptyStruct2(true));
| ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
|
note: `NonEmptyStruct2` defined here
--> $DIR/empty-match.rs:18:8
--> $DIR/empty-match.rs:23:12
|
LL | struct NonEmptyStruct2(bool);
| ^^^^^^^^^^^^^^^
LL | struct NonEmptyStruct2(bool);
| ^^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyStruct2`
= 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 ~ _ if false => {},
LL + NonEmptyStruct2(_) => todo!()
LL ~ _ if false => {},
LL + NonEmptyStruct2(_) => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
--> $DIR/empty-match.rs:154:24
--> $DIR/empty-match.rs:58:24
|
LL | match_guarded_arm!((NonEmptyUnion1 { foo: () }));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
|
note: `NonEmptyUnion1` defined here
--> $DIR/empty-match.rs:21:7
--> $DIR/empty-match.rs:24:11
|
LL | union NonEmptyUnion1 {
| ^^^^^^^^^^^^^^
LL | union NonEmptyUnion1 {
| ^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyUnion1`
= 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 ~ _ if false => {},
LL + NonEmptyUnion1 { .. } => todo!()
LL ~ _ if false => {},
LL + NonEmptyUnion1 { .. } => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
--> $DIR/empty-match.rs:159:24
--> $DIR/empty-match.rs:59:24
|
LL | match_guarded_arm!((NonEmptyUnion2 { foo: () }));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
|
note: `NonEmptyUnion2` defined here
--> $DIR/empty-match.rs:26:7
--> $DIR/empty-match.rs:27:11
|
LL | union NonEmptyUnion2 {
| ^^^^^^^^^^^^^^
LL | union NonEmptyUnion2 {
| ^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyUnion2`
= 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 ~ _ if false => {},
LL + NonEmptyUnion2 { .. } => todo!()
LL ~ _ if false => {},
LL + NonEmptyUnion2 { .. } => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
--> $DIR/empty-match.rs:164:24
--> $DIR/empty-match.rs:60:24
|
LL | match_guarded_arm!(NonEmptyEnum1::Foo(true));
| ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
|
note: `NonEmptyEnum1` defined here
--> $DIR/empty-match.rs:32:6
--> $DIR/empty-match.rs:31:10
|
LL | enum NonEmptyEnum1 {
| ^^^^^^^^^^^^^
...
LL | Foo(bool),
| --- not covered
LL | enum NonEmptyEnum1 {
| ^^^^^^^^^^^^^
LL | Foo(bool),
| --- not covered
= note: the matched value is of type `NonEmptyEnum1`
= 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 ~ _ if false => {},
LL + NonEmptyEnum1::Foo(_) => todo!()
LL ~ _ if false => {},
LL + NonEmptyEnum1::Foo(_) => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
--> $DIR/empty-match.rs:169:24
--> $DIR/empty-match.rs:61:24
|
LL | match_guarded_arm!(NonEmptyEnum2::Foo(true));
| ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
|
note: `NonEmptyEnum2` defined here
--> $DIR/empty-match.rs:39:6
--> $DIR/empty-match.rs:34:10
|
LL | enum NonEmptyEnum2 {
| ^^^^^^^^^^^^^
...
LL | Foo(bool),
| --- not covered
...
LL | Bar,
| --- not covered
LL | enum NonEmptyEnum2 {
| ^^^^^^^^^^^^^
LL | Foo(bool),
| --- not covered
LL | Bar,
| --- not covered
= note: the matched value is of type `NonEmptyEnum2`
= 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, a match arm with multiple or-patterns as shown, or multiple match arms
|
LL ~ _ if false => {},
LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
LL ~ _ if false => {},
LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
--> $DIR/empty-match.rs:174:24
--> $DIR/empty-match.rs:62:24
|
LL | match_guarded_arm!(NonEmptyEnum5::V1);
| ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
|
note: `NonEmptyEnum5` defined here
--> $DIR/empty-match.rs:49:6
--> $DIR/empty-match.rs:38:10
|
LL | enum NonEmptyEnum5 {
| ^^^^^^^^^^^^^
...
LL | V1, V2, V3, V4, V5,
| -- -- -- -- -- not covered
| | | | |
| | | | not covered
| | | not covered
| | not covered
| not covered
LL | enum NonEmptyEnum5 {
| ^^^^^^^^^^^^^
LL | V1,
| -- not covered
LL | V2,
| -- not covered
LL | V3,
| -- not covered
LL | V4,
| -- not covered
LL | V5,
| -- not covered
= note: the matched value is of type `NonEmptyEnum5`
= 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 as shown, or multiple match arms
|
LL ~ _ if false => {},
LL + _ => todo!()
LL ~ _ if false => {},
LL + _ => todo!()
|
error: aborting due to 23 previous errors
error: aborting due to 16 previous errors
Some errors have detailed explanations: E0004, E0005.
For more information about an error, try `rustc --explain E0004`.
For more information about this error, try `rustc --explain E0004`.

View File

@ -1,61 +1,5 @@
error: unreachable pattern
--> $DIR/empty-match.rs:68:9
|
LL | _ => {},
| ^
|
note: the lint level is defined here
--> $DIR/empty-match.rs:8:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern
--> $DIR/empty-match.rs:71:9
|
LL | _ if false => {},
| ^
error: unreachable pattern
--> $DIR/empty-match.rs:78:9
|
LL | _ => {},
| ^
error: unreachable pattern
--> $DIR/empty-match.rs:81:9
|
LL | _ if false => {},
| ^
error[E0005]: refutable pattern in local binding
--> $DIR/empty-match.rs:86:9
|
LL | let None = x;
| ^^^^ pattern `Some(_)` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `Option<SecretlyUninhabitedForeignStruct>`
help: you might want to use `if let` to ignore the variant that isn't matched
|
LL | if let None = x { todo!() };
| ++ +++++++++++
error: unreachable pattern
--> $DIR/empty-match.rs:98:9
|
LL | _ => {},
| ^
error: unreachable pattern
--> $DIR/empty-match.rs:101:9
|
LL | _ if false => {},
| ^
error[E0004]: non-exhaustive patterns: type `u8` is non-empty
--> $DIR/empty-match.rs:119:20
--> $DIR/empty-match.rs:46:20
|
LL | match_no_arms!(0u8);
| ^^^
@ -64,122 +8,121 @@ LL | match_no_arms!(0u8);
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty
--> $DIR/empty-match.rs:121:20
--> $DIR/empty-match.rs:47:20
|
LL | match_no_arms!(NonEmptyStruct1);
| ^^^^^^^^^^^^^^^
|
note: `NonEmptyStruct1` defined here
--> $DIR/empty-match.rs:15:8
--> $DIR/empty-match.rs:22:12
|
LL | struct NonEmptyStruct1;
| ^^^^^^^^^^^^^^^
LL | struct NonEmptyStruct1;
| ^^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyStruct1`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty
--> $DIR/empty-match.rs:123:20
--> $DIR/empty-match.rs:48:20
|
LL | match_no_arms!(NonEmptyStruct2(true));
| ^^^^^^^^^^^^^^^^^^^^^
|
note: `NonEmptyStruct2` defined here
--> $DIR/empty-match.rs:18:8
--> $DIR/empty-match.rs:23:12
|
LL | struct NonEmptyStruct2(bool);
| ^^^^^^^^^^^^^^^
LL | struct NonEmptyStruct2(bool);
| ^^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyStruct2`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
--> $DIR/empty-match.rs:125:20
--> $DIR/empty-match.rs:49:20
|
LL | match_no_arms!((NonEmptyUnion1 { foo: () }));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: `NonEmptyUnion1` defined here
--> $DIR/empty-match.rs:21:7
--> $DIR/empty-match.rs:24:11
|
LL | union NonEmptyUnion1 {
| ^^^^^^^^^^^^^^
LL | union NonEmptyUnion1 {
| ^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyUnion1`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
--> $DIR/empty-match.rs:127:20
--> $DIR/empty-match.rs:50:20
|
LL | match_no_arms!((NonEmptyUnion2 { foo: () }));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: `NonEmptyUnion2` defined here
--> $DIR/empty-match.rs:26:7
--> $DIR/empty-match.rs:27:11
|
LL | union NonEmptyUnion2 {
| ^^^^^^^^^^^^^^
LL | union NonEmptyUnion2 {
| ^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyUnion2`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
--> $DIR/empty-match.rs:129:20
--> $DIR/empty-match.rs:51:20
|
LL | match_no_arms!(NonEmptyEnum1::Foo(true));
| ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
|
note: `NonEmptyEnum1` defined here
--> $DIR/empty-match.rs:32:6
--> $DIR/empty-match.rs:31:10
|
LL | enum NonEmptyEnum1 {
| ^^^^^^^^^^^^^
...
LL | Foo(bool),
| --- not covered
LL | enum NonEmptyEnum1 {
| ^^^^^^^^^^^^^
LL | Foo(bool),
| --- not covered
= note: the matched value is of type `NonEmptyEnum1`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern
error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
--> $DIR/empty-match.rs:132:20
--> $DIR/empty-match.rs:52:20
|
LL | match_no_arms!(NonEmptyEnum2::Foo(true));
| ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
|
note: `NonEmptyEnum2` defined here
--> $DIR/empty-match.rs:39:6
--> $DIR/empty-match.rs:34:10
|
LL | enum NonEmptyEnum2 {
| ^^^^^^^^^^^^^
...
LL | Foo(bool),
| --- not covered
...
LL | Bar,
| --- not covered
LL | enum NonEmptyEnum2 {
| ^^^^^^^^^^^^^
LL | Foo(bool),
| --- not covered
LL | Bar,
| --- not covered
= note: the matched value is of type `NonEmptyEnum2`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
--> $DIR/empty-match.rs:135:20
--> $DIR/empty-match.rs:53:20
|
LL | match_no_arms!(NonEmptyEnum5::V1);
| ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
|
note: `NonEmptyEnum5` defined here
--> $DIR/empty-match.rs:49:6
--> $DIR/empty-match.rs:38:10
|
LL | enum NonEmptyEnum5 {
| ^^^^^^^^^^^^^
...
LL | V1, V2, V3, V4, V5,
| -- -- -- -- -- not covered
| | | | |
| | | | not covered
| | | not covered
| | not covered
| not covered
LL | enum NonEmptyEnum5 {
| ^^^^^^^^^^^^^
LL | V1,
| -- not covered
LL | V2,
| -- not covered
LL | V3,
| -- not covered
LL | V4,
| -- not covered
LL | V5,
| -- not covered
= note: the matched value is of type `NonEmptyEnum5`
= help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
error[E0004]: non-exhaustive patterns: `_` not covered
--> $DIR/empty-match.rs:139:24
--> $DIR/empty-match.rs:55:24
|
LL | match_guarded_arm!(0u8);
| ^^^ pattern `_` not covered
@ -188,161 +131,159 @@ LL | match_guarded_arm!(0u8);
= 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 ~ _ if false => {},
LL + _ => todo!()
LL ~ _ if false => {},
LL + _ => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
--> $DIR/empty-match.rs:144:24
--> $DIR/empty-match.rs:56:24
|
LL | match_guarded_arm!(NonEmptyStruct1);
| ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
|
note: `NonEmptyStruct1` defined here
--> $DIR/empty-match.rs:15:8
--> $DIR/empty-match.rs:22:12
|
LL | struct NonEmptyStruct1;
| ^^^^^^^^^^^^^^^
LL | struct NonEmptyStruct1;
| ^^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyStruct1`
= 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 ~ _ if false => {},
LL + NonEmptyStruct1 => todo!()
LL ~ _ if false => {},
LL + NonEmptyStruct1 => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
--> $DIR/empty-match.rs:149:24
--> $DIR/empty-match.rs:57:24
|
LL | match_guarded_arm!(NonEmptyStruct2(true));
| ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
|
note: `NonEmptyStruct2` defined here
--> $DIR/empty-match.rs:18:8
--> $DIR/empty-match.rs:23:12
|
LL | struct NonEmptyStruct2(bool);
| ^^^^^^^^^^^^^^^
LL | struct NonEmptyStruct2(bool);
| ^^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyStruct2`
= 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 ~ _ if false => {},
LL + NonEmptyStruct2(_) => todo!()
LL ~ _ if false => {},
LL + NonEmptyStruct2(_) => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
--> $DIR/empty-match.rs:154:24
--> $DIR/empty-match.rs:58:24
|
LL | match_guarded_arm!((NonEmptyUnion1 { foo: () }));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
|
note: `NonEmptyUnion1` defined here
--> $DIR/empty-match.rs:21:7
--> $DIR/empty-match.rs:24:11
|
LL | union NonEmptyUnion1 {
| ^^^^^^^^^^^^^^
LL | union NonEmptyUnion1 {
| ^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyUnion1`
= 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 ~ _ if false => {},
LL + NonEmptyUnion1 { .. } => todo!()
LL ~ _ if false => {},
LL + NonEmptyUnion1 { .. } => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
--> $DIR/empty-match.rs:159:24
--> $DIR/empty-match.rs:59:24
|
LL | match_guarded_arm!((NonEmptyUnion2 { foo: () }));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
|
note: `NonEmptyUnion2` defined here
--> $DIR/empty-match.rs:26:7
--> $DIR/empty-match.rs:27:11
|
LL | union NonEmptyUnion2 {
| ^^^^^^^^^^^^^^
LL | union NonEmptyUnion2 {
| ^^^^^^^^^^^^^^
= note: the matched value is of type `NonEmptyUnion2`
= 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 ~ _ if false => {},
LL + NonEmptyUnion2 { .. } => todo!()
LL ~ _ if false => {},
LL + NonEmptyUnion2 { .. } => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
--> $DIR/empty-match.rs:164:24
--> $DIR/empty-match.rs:60:24
|
LL | match_guarded_arm!(NonEmptyEnum1::Foo(true));
| ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
|
note: `NonEmptyEnum1` defined here
--> $DIR/empty-match.rs:32:6
--> $DIR/empty-match.rs:31:10
|
LL | enum NonEmptyEnum1 {
| ^^^^^^^^^^^^^
...
LL | Foo(bool),
| --- not covered
LL | enum NonEmptyEnum1 {
| ^^^^^^^^^^^^^
LL | Foo(bool),
| --- not covered
= note: the matched value is of type `NonEmptyEnum1`
= 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 ~ _ if false => {},
LL + NonEmptyEnum1::Foo(_) => todo!()
LL ~ _ if false => {},
LL + NonEmptyEnum1::Foo(_) => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
--> $DIR/empty-match.rs:169:24
--> $DIR/empty-match.rs:61:24
|
LL | match_guarded_arm!(NonEmptyEnum2::Foo(true));
| ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
|
note: `NonEmptyEnum2` defined here
--> $DIR/empty-match.rs:39:6
--> $DIR/empty-match.rs:34:10
|
LL | enum NonEmptyEnum2 {
| ^^^^^^^^^^^^^
...
LL | Foo(bool),
| --- not covered
...
LL | Bar,
| --- not covered
LL | enum NonEmptyEnum2 {
| ^^^^^^^^^^^^^
LL | Foo(bool),
| --- not covered
LL | Bar,
| --- not covered
= note: the matched value is of type `NonEmptyEnum2`
= 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, a match arm with multiple or-patterns as shown, or multiple match arms
|
LL ~ _ if false => {},
LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
LL ~ _ if false => {},
LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
|
error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
--> $DIR/empty-match.rs:174:24
--> $DIR/empty-match.rs:62:24
|
LL | match_guarded_arm!(NonEmptyEnum5::V1);
| ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
|
note: `NonEmptyEnum5` defined here
--> $DIR/empty-match.rs:49:6
--> $DIR/empty-match.rs:38:10
|
LL | enum NonEmptyEnum5 {
| ^^^^^^^^^^^^^
...
LL | V1, V2, V3, V4, V5,
| -- -- -- -- -- not covered
| | | | |
| | | | not covered
| | | not covered
| | not covered
| not covered
LL | enum NonEmptyEnum5 {
| ^^^^^^^^^^^^^
LL | V1,
| -- not covered
LL | V2,
| -- not covered
LL | V3,
| -- not covered
LL | V4,
| -- not covered
LL | V5,
| -- not covered
= note: the matched value is of type `NonEmptyEnum5`
= 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 as shown, or multiple match arms
|
LL ~ _ if false => {},
LL + _ => todo!()
LL ~ _ if false => {},
LL + _ => todo!()
|
error: aborting due to 23 previous errors
error: aborting due to 16 previous errors
Some errors have detailed explanations: E0004, E0005.
For more information about an error, try `rustc --explain E0004`.
For more information about this error, try `rustc --explain E0004`.

View File

@ -1,179 +1,65 @@
// aux-build:empty.rs
// revisions: normal exhaustive_patterns
//
// This tests a match with no arms on various types.
#![feature(never_type)]
#![feature(never_type_fallback)]
#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
#![deny(unreachable_patterns)]
//~^ NOTE the lint level is defined here
extern crate empty;
enum EmptyEnum {}
struct NonEmptyStruct1;
//~^ NOTE `NonEmptyStruct1` defined here
//~| NOTE `NonEmptyStruct1` defined here
struct NonEmptyStruct2(bool);
//~^ NOTE `NonEmptyStruct2` defined here
//~| NOTE `NonEmptyStruct2` defined here
union NonEmptyUnion1 {
//~^ NOTE `NonEmptyUnion1` defined here
//~| NOTE `NonEmptyUnion1` defined here
foo: (),
}
union NonEmptyUnion2 {
//~^ NOTE `NonEmptyUnion2` defined here
//~| NOTE `NonEmptyUnion2` defined here
foo: (),
bar: (),
}
enum NonEmptyEnum1 {
//~^ NOTE `NonEmptyEnum1` defined here
//~| NOTE `NonEmptyEnum1` defined here
Foo(bool),
//~^ NOTE not covered
//~| NOTE not covered
}
enum NonEmptyEnum2 {
//~^ NOTE `NonEmptyEnum2` defined here
//~| NOTE `NonEmptyEnum2` defined here
Foo(bool),
//~^ NOTE not covered
//~| NOTE not covered
Bar,
//~^ NOTE not covered
//~| NOTE not covered
}
enum NonEmptyEnum5 {
//~^ NOTE `NonEmptyEnum5` defined here
//~| NOTE `NonEmptyEnum5` defined here
V1, V2, V3, V4, V5,
//~^ NOTE not covered
//~| NOTE not covered
//~| NOTE not covered
//~| NOTE not covered
//~| NOTE not covered
//~| NOTE not covered
//~| NOTE not covered
//~| NOTE not covered
//~| NOTE not covered
//~| NOTE not covered
}
fn empty_enum(x: EmptyEnum) {
match x {} // ok
match x {
_ => {}, //~ ERROR unreachable pattern
fn nonempty() {
macro_rules! match_no_arms {
($e:expr) => {
match $e {}
};
}
match x {
_ if false => {}, //~ ERROR unreachable pattern
macro_rules! match_guarded_arm {
($e:expr) => {
match $e {
_ if false => {}
}
};
}
}
fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
match x {} // ok
match x {
_ => {}, //~ ERROR unreachable pattern
struct NonEmptyStruct1;
struct NonEmptyStruct2(bool);
union NonEmptyUnion1 {
foo: (),
}
match x {
_ if false => {}, //~ ERROR unreachable pattern
union NonEmptyUnion2 {
foo: (),
bar: !,
}
}
fn empty_foreign_enum_private(x: Option<empty::SecretlyUninhabitedForeignStruct>) {
let None = x;
//~^ ERROR refutable pattern in local binding
//~| NOTE `let` bindings require an "irrefutable pattern"
//~| NOTE for more information, visit
//~| NOTE the matched value is of type
//~| NOTE pattern `Some(_)` not covered
//[exhaustive_patterns]~| NOTE currently uninhabited, but this variant contains private fields
}
fn never(x: !) {
match x {} // ok
match x {
_ => {}, //~ ERROR unreachable pattern
enum NonEmptyEnum1 {
Foo(bool),
}
match x {
_ if false => {}, //~ ERROR unreachable pattern
enum NonEmptyEnum2 {
Foo(bool),
Bar,
}
enum NonEmptyEnum5 {
V1,
V2,
V3,
V4,
V5,
}
}
macro_rules! match_no_arms {
($e:expr) => {
match $e {}
};
}
macro_rules! match_guarded_arm {
($e:expr) => {
match $e {
_ if false => {}
}
};
}
fn main() {
match_no_arms!(0u8); //~ ERROR type `u8` is non-empty
//~| NOTE the matched value is of type
match_no_arms!(NonEmptyStruct1); //~ ERROR type `NonEmptyStruct1` is non-empty
//~| NOTE the matched value is of type
match_no_arms!(NonEmptyStruct2(true)); //~ ERROR type `NonEmptyStruct2` is non-empty
//~| NOTE the matched value is of type
match_no_arms!((NonEmptyUnion1 { foo: () })); //~ ERROR type `NonEmptyUnion1` is non-empty
//~| NOTE the matched value is of type
match_no_arms!((NonEmptyUnion2 { foo: () })); //~ ERROR type `NonEmptyUnion2` is non-empty
//~| NOTE the matched value is of type
match_no_arms!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered
//~| NOTE pattern `NonEmptyEnum1::Foo(_)` not covered
//~| NOTE the matched value is of type
match_no_arms!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
//~| NOTE patterns `NonEmptyEnum2::Foo(_)` and
//~| NOTE the matched value is of type
match_no_arms!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
//~| NOTE patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`
//~| NOTE the matched value is of type
match_guarded_arm!(0u8); //~ ERROR `_` not covered
//~| NOTE the matched value is of type
//~| NOTE match arms with guards don't count towards exhaustivity
//~| NOTE pattern `_` not covered
//~| NOTE in this expansion of match_guarded_arm!
match_guarded_arm!(NonEmptyStruct1); //~ ERROR `NonEmptyStruct1` not covered
//~| NOTE pattern `NonEmptyStruct1` not covered
//~| NOTE the matched value is of type
//~| NOTE match arms with guards don't count towards exhaustivity
//~| NOTE in this expansion of match_guarded_arm!
match_guarded_arm!(NonEmptyStruct2(true)); //~ ERROR `NonEmptyStruct2(_)` not covered
//~| NOTE the matched value is of type
//~| NOTE pattern `NonEmptyStruct2(_)` not covered
//~| NOTE match arms with guards don't count towards exhaustivity
//~| NOTE in this expansion of match_guarded_arm!
match_guarded_arm!((NonEmptyUnion1 { foo: () })); //~ ERROR `NonEmptyUnion1 { .. }` not covered
//~| NOTE the matched value is of type
//~| NOTE pattern `NonEmptyUnion1 { .. }` not covered
//~| NOTE match arms with guards don't count towards exhaustivity
//~| NOTE in this expansion of match_guarded_arm!
match_guarded_arm!((NonEmptyUnion2 { foo: () })); //~ ERROR `NonEmptyUnion2 { .. }` not covered
//~| NOTE the matched value is of type
//~| NOTE pattern `NonEmptyUnion2 { .. }` not covered
//~| NOTE match arms with guards don't count towards exhaustivity
//~| NOTE in this expansion of match_guarded_arm!
match_guarded_arm!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered
//~| NOTE the matched value is of type
//~| NOTE pattern `NonEmptyEnum1::Foo(_)` not covered
//~| NOTE match arms with guards don't count towards exhaustivity
//~| NOTE in this expansion of match_guarded_arm!
match_guarded_arm!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
//~| NOTE the matched value is of type
//~| NOTE patterns `NonEmptyEnum2::Foo(_)` and
//~| NOTE match arms with guards don't count towards exhaustivity
//~| NOTE in this expansion of match_guarded_arm!
match_guarded_arm!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
//~| NOTE the matched value is of type
//~| NOTE patterns `NonEmptyEnum5::V1`,
//~| NOTE match arms with guards don't count towards exhaustivity
//~| NOTE in this expansion of match_guarded_arm!
}
fn main() {}

View File

@ -0,0 +1,393 @@
error: unreachable pattern
--> $DIR/empty-types.rs:47:9
|
LL | _ => {}
| ^
|
note: the lint level is defined here
--> $DIR/empty-types.rs:13:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:50:9
|
LL | _x => {}
| ^^
error[E0004]: non-exhaustive patterns: type `&!` is non-empty
--> $DIR/empty-types.rs:54:11
|
LL | match ref_never {}
| ^^^^^^^^^
|
= note: the matched value is of type `&!`
= note: references are always considered inhabited
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match ref_never {
LL + _ => todo!(),
LL + }
|
error: unreachable pattern
--> $DIR/empty-types.rs:69:9
|
LL | (_, _) => {}
| ^^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:76:9
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:79:9
|
LL | (_, _) => {}
| ^^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:83:9
|
LL | _ => {}
| ^
error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
--> $DIR/empty-types.rs:87:11
|
LL | match res_u32_never {}
| ^^^^^^^^^^^^^ pattern `Ok(_)` not covered
|
note: `Result<u32, !>` defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
= note: the matched value is of type `Result<u32, !>`
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 ~ match res_u32_never {
LL + Ok(_) => todo!(),
LL + }
|
error: unreachable pattern
--> $DIR/empty-types.rs:95:9
|
LL | Err(_) => {}
| ^^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:100:9
|
LL | Err(_) => {}
| ^^^^^^
error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered
--> $DIR/empty-types.rs:97:11
|
LL | match res_u32_never {
| ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered
|
note: `Result<u32, !>` defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
= note: the matched value is of type `Result<u32, !>`
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 ~ Err(_) => {},
LL ~ Ok(1_u32..=u32::MAX) => todo!()
|
error[E0005]: refutable pattern in local binding
--> $DIR/empty-types.rs:104:9
|
LL | let Ok(_x) = res_u32_never.as_ref();
| ^^^^^^ pattern `Err(_)` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `Result<&u32, &!>`
help: you might want to use `let else` to handle the variant that isn't matched
|
LL | let Ok(_x) = res_u32_never.as_ref() else { todo!() };
| ++++++++++++++++
error: unreachable pattern
--> $DIR/empty-types.rs:115:9
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:119:9
|
LL | Ok(_) => {}
| ^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:122:9
|
LL | Ok(_) => {}
| ^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:123:9
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:126:9
|
LL | Ok(_) => {}
| ^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:127:9
|
LL | Err(_) => {}
| ^^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:136:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:139:13
|
LL | _ if false => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:148:13
|
LL | Some(_) => {}
| ^^^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:152:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:204:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:209:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:214:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:219:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:225:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:284:9
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:287:9
|
LL | (_, _) => {}
| ^^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:290:9
|
LL | Ok(_) => {}
| ^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:291:9
|
LL | Err(_) => {}
| ^^^^^^
error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty
--> $DIR/empty-types.rs:323:11
|
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 as shown
|
LL ~ match slice_never {
LL + _ => todo!(),
LL + }
|
error[E0004]: non-exhaustive patterns: `&[]` not covered
--> $DIR/empty-types.rs:334:11
|
LL | match slice_never {
| ^^^^^^^^^^^ pattern `&[]` not covered
|
= 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 ~ [_, _, _, ..] => {},
LL + &[] => todo!()
|
error[E0004]: non-exhaustive patterns: `&[]` not covered
--> $DIR/empty-types.rs:347:11
|
LL | match slice_never {
| ^^^^^^^^^^^ pattern `&[]` not covered
|
= note: the matched value is of type `&[!]`
= 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 ~ &[..] if false => {},
LL + &[] => todo!()
|
error[E0004]: non-exhaustive patterns: type `[!]` is non-empty
--> $DIR/empty-types.rs:353:11
|
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 as shown
|
LL ~ match *slice_never {
LL + _ => todo!(),
LL + }
|
error: unreachable pattern
--> $DIR/empty-types.rs:363:9
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:366:9
|
LL | [_, _, _] => {}
| ^^^^^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:369:9
|
LL | [_, ..] => {}
| ^^^^^^^
error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty
--> $DIR/empty-types.rs:383:11
|
LL | match array_0_never {}
| ^^^^^^^^^^^^^
|
= note: the matched value is of type `[!; 0]`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match array_0_never {
LL + _ => todo!(),
LL + }
|
error: unreachable pattern
--> $DIR/empty-types.rs:390:9
|
LL | _ => {}
| ^
error[E0004]: non-exhaustive patterns: `[]` not covered
--> $DIR/empty-types.rs:392:11
|
LL | match array_0_never {
| ^^^^^^^^^^^^^ pattern `[]` not covered
|
= note: the matched value is of type `[!; 0]`
= 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 ~ [..] if false => {},
LL + [] => todo!()
|
error: unreachable pattern
--> $DIR/empty-types.rs:411:9
|
LL | Some(_) => {}
| ^^^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:416:9
|
LL | Some(_a) => {}
| ^^^^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:421:9
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:426:9
|
LL | _a => {}
| ^^
error: unreachable pattern
--> $DIR/empty-types.rs:598:9
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:601:9
|
LL | _x => {}
| ^^
error: unreachable pattern
--> $DIR/empty-types.rs:604:9
|
LL | _ if false => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:607:9
|
LL | _x if false => {}
| ^^
error: aborting due to 49 previous errors
Some errors have detailed explanations: E0004, E0005.
For more information about an error, try `rustc --explain E0004`.

View File

@ -0,0 +1,617 @@
error: unreachable pattern
--> $DIR/empty-types.rs:47:9
|
LL | _ => {}
| ^
|
note: the lint level is defined here
--> $DIR/empty-types.rs:13:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern
--> $DIR/empty-types.rs:50:9
|
LL | _x => {}
| ^^
error[E0004]: non-exhaustive patterns: type `&!` is non-empty
--> $DIR/empty-types.rs:54:11
|
LL | match ref_never {}
| ^^^^^^^^^
|
= note: the matched value is of type `&!`
= note: references are always considered inhabited
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match ref_never {
LL + _ => todo!(),
LL + }
|
error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
--> $DIR/empty-types.rs:66:11
|
LL | match tuple_half_never {}
| ^^^^^^^^^^^^^^^^
|
= note: the matched value is of type `(u32, !)`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match tuple_half_never {
LL + _ => todo!(),
LL + }
|
error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty
--> $DIR/empty-types.rs:73:11
|
LL | match tuple_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 as shown
|
LL ~ match tuple_never {
LL + _ => todo!(),
LL + }
|
error: unreachable pattern
--> $DIR/empty-types.rs:83:9
|
LL | _ => {}
| ^
error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered
--> $DIR/empty-types.rs:87:11
|
LL | match res_u32_never {}
| ^^^^^^^^^^^^^ patterns `Ok(_)` and `Err(_)` not covered
|
note: `Result<u32, !>` defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
= note: the matched value is of type `Result<u32, !>`
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 ~ match res_u32_never {
LL + Ok(_) | Err(_) => todo!(),
LL + }
|
error[E0004]: non-exhaustive patterns: `Err(_)` not covered
--> $DIR/empty-types.rs:89:11
|
LL | match res_u32_never {
| ^^^^^^^^^^^^^ pattern `Err(_)` not covered
|
note: `Result<u32, !>` defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
= note: the matched value is of type `Result<u32, !>`
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(_) => {},
LL + Err(_) => todo!()
|
error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered
--> $DIR/empty-types.rs:97:11
|
LL | match res_u32_never {
| ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered
|
note: `Result<u32, !>` defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
= note: the matched value is of type `Result<u32, !>`
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 ~ Err(_) => {},
LL ~ Ok(1_u32..=u32::MAX) => todo!()
|
error[E0005]: refutable pattern in local binding
--> $DIR/empty-types.rs:102:9
|
LL | let Ok(_x) = res_u32_never;
| ^^^^^^ pattern `Err(_)` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `Result<u32, !>`
help: you might want to use `let else` to handle the variant that isn't matched
|
LL | let Ok(_x) = res_u32_never else { todo!() };
| ++++++++++++++++
error[E0005]: refutable pattern in local binding
--> $DIR/empty-types.rs:104:9
|
LL | let Ok(_x) = res_u32_never.as_ref();
| ^^^^^^ pattern `Err(_)` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `Result<&u32, &!>`
help: you might want to use `let else` to handle the variant that isn't matched
|
LL | let Ok(_x) = res_u32_never.as_ref() else { todo!() };
| ++++++++++++++++
error[E0005]: refutable pattern in local binding
--> $DIR/empty-types.rs:108:9
|
LL | let Ok(_x) = &res_u32_never;
| ^^^^^^ pattern `&Err(_)` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `&Result<u32, !>`
help: you might want to use `let else` to handle the variant that isn't matched
|
LL | let Ok(_x) = &res_u32_never else { todo!() };
| ++++++++++++++++
error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered
--> $DIR/empty-types.rs:112:11
|
LL | match result_never {}
| ^^^^^^^^^^^^ patterns `Ok(_)` and `Err(_)` not covered
|
note: `Result<!, !>` defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
= 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, a match arm with multiple or-patterns as shown, or multiple match arms
|
LL ~ match result_never {
LL + Ok(_) | Err(_) => todo!(),
LL + }
|
error[E0004]: non-exhaustive patterns: `Err(_)` not covered
--> $DIR/empty-types.rs:117:11
|
LL | match result_never {
| ^^^^^^^^^^^^ pattern `Err(_)` not covered
|
note: `Result<!, !>` defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
= 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!()
| +++++++++++++++++++
error: unreachable pattern
--> $DIR/empty-types.rs:136:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:139:13
|
LL | _ if false => {}
| ^
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
--> $DIR/empty-types.rs:142:15
|
LL | match opt_void {
| ^^^^^^^^ pattern `Some(_)` not covered
|
note: `Option<Void>` defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
::: $SRC_DIR/core/src/option.rs:LL:COL
|
= note: not covered
= note: the matched value is of type `Option<Void>`
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!()
|
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
--> $DIR/empty-types.rs:161:15
|
LL | match *ref_opt_void {
| ^^^^^^^^^^^^^ pattern `Some(_)` not covered
|
note: `Option<Void>` defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
::: $SRC_DIR/core/src/option.rs:LL:COL
|
= note: not covered
= note: the matched value is of type `Option<Void>`
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!()
|
error: unreachable pattern
--> $DIR/empty-types.rs:204:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:209:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:214:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:219:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:225:13
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:284:9
|
LL | _ => {}
| ^
error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
--> $DIR/empty-types.rs:312:11
|
LL | match *x {}
| ^^
|
= note: the matched value is of type `(u32, !)`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match *x {
LL + _ => todo!(),
LL ~ }
|
error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty
--> $DIR/empty-types.rs:314:11
|
LL | match *x {}
| ^^
|
= 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 as shown
|
LL ~ match *x {
LL + _ => todo!(),
LL ~ }
|
error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered
--> $DIR/empty-types.rs:316:11
|
LL | match *x {}
| ^^ patterns `Ok(_)` and `Err(_)` not covered
|
note: `Result<!, !>` defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
= 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, a match arm with multiple or-patterns as shown, or multiple match arms
|
LL ~ match *x {
LL + Ok(_) | Err(_) => todo!(),
LL ~ }
|
error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty
--> $DIR/empty-types.rs:318:11
|
LL | match *x {}
| ^^
|
= note: the matched value is of type `[!; 3]`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match *x {
LL + _ => todo!(),
LL ~ }
|
error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty
--> $DIR/empty-types.rs:323:11
|
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 as shown
|
LL ~ match slice_never {
LL + _ => todo!(),
LL + }
|
error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered
--> $DIR/empty-types.rs:325:11
|
LL | match slice_never {
| ^^^^^^^^^^^ pattern `&[_, ..]` not covered
|
= 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 ~ [] => {},
LL + &[_, ..] => todo!()
|
error[E0004]: non-exhaustive patterns: `&[]`, `&[_]` and `&[_, _]` not covered
--> $DIR/empty-types.rs:334:11
|
LL | match slice_never {
| ^^^^^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _]` not covered
|
= 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 ~ [_, _, _, ..] => {},
LL + &[] | &[_] | &[_, _] => todo!()
|
error[E0004]: non-exhaustive patterns: `&[]` and `&[_, ..]` not covered
--> $DIR/empty-types.rs:347:11
|
LL | match slice_never {
| ^^^^^^^^^^^ patterns `&[]` and `&[_, ..]` not covered
|
= note: the matched value is of type `&[!]`
= 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, a match arm with multiple or-patterns as shown, or multiple match arms
|
LL ~ &[..] if false => {},
LL + &[] | &[_, ..] => todo!()
|
error[E0004]: non-exhaustive patterns: type `[!]` is non-empty
--> $DIR/empty-types.rs:353:11
|
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 as shown
|
LL ~ match *slice_never {
LL + _ => todo!(),
LL + }
|
error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty
--> $DIR/empty-types.rs:360:11
|
LL | match array_3_never {}
| ^^^^^^^^^^^^^
|
= note: the matched value is of type `[!; 3]`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match array_3_never {
LL + _ => todo!(),
LL + }
|
error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty
--> $DIR/empty-types.rs:383:11
|
LL | match array_0_never {}
| ^^^^^^^^^^^^^
|
= note: the matched value is of type `[!; 0]`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match array_0_never {
LL + _ => todo!(),
LL + }
|
error: unreachable pattern
--> $DIR/empty-types.rs:390:9
|
LL | _ => {}
| ^
error[E0004]: non-exhaustive patterns: `[]` not covered
--> $DIR/empty-types.rs:392:11
|
LL | match array_0_never {
| ^^^^^^^^^^^^^ pattern `[]` not covered
|
= note: the matched value is of type `[!; 0]`
= 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 ~ [..] if false => {},
LL + [] => todo!()
|
error[E0004]: non-exhaustive patterns: `&Some(_)` not covered
--> $DIR/empty-types.rs:446:11
|
LL | match ref_opt_never {
| ^^^^^^^^^^^^^ pattern `&Some(_)` not covered
|
note: `Option<!>` defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
::: $SRC_DIR/core/src/option.rs:LL:COL
|
= note: not covered
= note: the matched value is of type `&Option<!>`
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!()
|
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
--> $DIR/empty-types.rs:487:11
|
LL | match *ref_opt_never {
| ^^^^^^^^^^^^^^ pattern `Some(_)` not covered
|
note: `Option<!>` defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
::: $SRC_DIR/core/src/option.rs:LL:COL
|
= note: not covered
= note: the matched value is of type `Option<!>`
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!()
|
error[E0004]: non-exhaustive patterns: `Err(_)` not covered
--> $DIR/empty-types.rs:535:11
|
LL | match *ref_res_never {
| ^^^^^^^^^^^^^^ pattern `Err(_)` not covered
|
note: `Result<!, !>` defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
= 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(_) => {},
LL + Err(_) => todo!()
|
error[E0004]: non-exhaustive patterns: `Err(_)` not covered
--> $DIR/empty-types.rs:546:11
|
LL | match *ref_res_never {
| ^^^^^^^^^^^^^^ pattern `Err(_)` not covered
|
note: `Result<!, !>` defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
::: $SRC_DIR/core/src/result.rs:LL:COL
|
= note: not covered
= 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) => {},
LL + Err(_) => todo!()
|
error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty
--> $DIR/empty-types.rs:565:11
|
LL | match *ref_tuple_half_never {}
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: the matched value is of type `(u32, !)`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match *ref_tuple_half_never {
LL + _ => todo!(),
LL + }
|
error: unreachable pattern
--> $DIR/empty-types.rs:598:9
|
LL | _ => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:601:9
|
LL | _x => {}
| ^^
error: unreachable pattern
--> $DIR/empty-types.rs:604:9
|
LL | _ if false => {}
| ^
error: unreachable pattern
--> $DIR/empty-types.rs:607:9
|
LL | _x if false => {}
| ^^
error[E0004]: non-exhaustive patterns: `&_` not covered
--> $DIR/empty-types.rs:631:11
|
LL | match ref_never {
| ^^^^^^^^^ pattern `&_` not covered
|
= note: the matched value is of type `&!`
= note: references are always considered inhabited
= 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 => {},
LL + &_ => todo!()
|
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
--> $DIR/empty-types.rs:659:11
|
LL | match *x {
| ^^ pattern `Some(_)` not covered
|
note: `Option<Result<!, !>>` defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
::: $SRC_DIR/core/src/option.rs:LL:COL
|
= note: not covered
= note: the matched value is of type `Option<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 ~ None => {},
LL + Some(_) => todo!()
|
error: aborting due to 48 previous errors
Some errors have detailed explanations: E0004, E0005.
For more information about an error, try `rustc --explain E0004`.

View File

@ -0,0 +1,665 @@
// revisions: normal exhaustive_patterns
//
// This tests correct handling of empty types in exhaustiveness checking.
//
// Most of the subtlety of this file happens in scrutinee places which are not required to hold
// valid data, namely dereferences and union field accesses. In these cases, empty arms can
// generally not be omitted, except with `exhaustive_patterns` which ignores this..
#![feature(never_type)]
// This feature is useful to avoid `!` falling back to `()` all the time.
#![feature(never_type_fallback)]
#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
#![allow(dead_code, unreachable_code)]
#![deny(unreachable_patterns)]
#[derive(Copy, Clone)]
enum Void {}
/// A bunch of never situations that can't be normally constructed.
#[derive(Copy, Clone)]
struct NeverBundle {
never: !,
void: Void,
tuple_never: (!, !),
tuple_half_never: (u32, !),
array_3_never: [!; 3],
result_never: Result<!, !>,
}
/// A simplified `MaybeUninit` to test union field accesses.
#[derive(Copy, Clone)]
union Uninit<T: Copy> {
value: T,
uninit: (),
}
impl<T: Copy> Uninit<T> {
fn new() -> Self {
Self { uninit: () }
}
}
// Simple cases of omitting empty arms, all with known_valid scrutinees.
fn basic(x: NeverBundle) {
let never: ! = x.never;
match never {}
match never {
_ => {} //~ ERROR unreachable pattern
}
match never {
_x => {} //~ ERROR unreachable pattern
}
let ref_never: &! = &x.never;
match ref_never {}
//~^ ERROR non-empty
match ref_never {
// useful, reachable
_ => {}
}
match ref_never {
// useful, reachable
&_ => {}
}
let tuple_half_never: (u32, !) = x.tuple_half_never;
match tuple_half_never {}
//[normal]~^ ERROR non-empty
match tuple_half_never {
(_, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
let tuple_never: (!, !) = x.tuple_never;
match tuple_never {}
//[normal]~^ ERROR non-empty
match tuple_never {
_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match tuple_never {
(_, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match tuple_never.0 {}
match tuple_never.0 {
_ => {} //~ ERROR unreachable pattern
}
let res_u32_never: Result<u32, !> = Ok(0);
match res_u32_never {}
//~^ ERROR non-exhaustive
match res_u32_never {
//[normal]~^ ERROR non-exhaustive
Ok(_) => {}
}
match res_u32_never {
Ok(_) => {}
Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match res_u32_never {
//~^ ERROR non-exhaustive
Ok(0) => {}
Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
let Ok(_x) = res_u32_never;
//[normal]~^ 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]~^ ERROR refutable
let result_never: Result<!, !> = x.result_never;
match result_never {}
//[normal]~^ ERROR non-exhaustive
match result_never {
_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match result_never {
//[normal]~^ ERROR non-exhaustive
Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match result_never {
Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match result_never {
Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
}
// Check for a few cases that `Void` and `!` are treated the same.
fn void_same_as_never(x: NeverBundle) {
unsafe {
match x.void {}
match x.void {
_ => {} //~ ERROR unreachable pattern
}
match x.void {
_ if false => {} //~ ERROR unreachable pattern
}
let opt_void: Option<Void> = None;
match opt_void {
//[normal]~^ ERROR non-exhaustive
None => {}
}
match opt_void {
None => {}
Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match opt_void {
None => {}
_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
let ref_void: &Void = &x.void;
match *ref_void {}
match *ref_void {
_ => {}
}
let ref_opt_void: &Option<Void> = &None;
match *ref_opt_void {
//[normal]~^ ERROR non-exhaustive
None => {}
}
match *ref_opt_void {
None => {}
Some(_) => {}
}
match *ref_opt_void {
None => {}
_ => {}
}
match *ref_opt_void {
None => {}
_a => {}
}
let union_void = Uninit::<Void>::new();
match union_void.value {}
match union_void.value {
_ => {}
}
let ptr_void: *const Void = std::ptr::null();
match *ptr_void {}
match *ptr_void {
_ => {}
}
}
}
// Test if we correctly determine validity from the scrutinee expression.
fn invalid_scrutinees(x: NeverBundle) {
let ptr_never: *const ! = std::ptr::null();
let never: ! = x.never;
let ref_never: &! = &never;
struct NestedNeverBundle(NeverBundle);
let nested_x = NestedNeverBundle(x);
// These should be considered known_valid and warn unreachable.
unsafe {
// A plain `!` value must be valid.
match never {}
match never {
_ => {} //~ ERROR unreachable pattern
}
// A block forces a copy.
match { *ptr_never } {}
match { *ptr_never } {
_ => {} //~ ERROR unreachable pattern
}
// This field access is not a dereference.
match x.never {}
match x.never {
_ => {} //~ ERROR unreachable pattern
}
// This nested field access is not a dereference.
match nested_x.0.never {}
match nested_x.0.never {
_ => {} //~ ERROR unreachable pattern
}
// Indexing is like a field access. This one does not access behind a reference.
let array_3_never: [!; 3] = x.array_3_never;
match array_3_never[0] {}
match array_3_never[0] {
_ => {} //~ ERROR unreachable pattern
}
}
// These should be considered !known_valid and not warn unreachable.
unsafe {
// A pointer may point to a place with an invalid value.
match *ptr_never {}
match *ptr_never {
_ => {}
}
// A reference may point to a place with an invalid value.
match *ref_never {}
match *ref_never {
_ => {}
}
// This field access is a dereference.
let ref_x: &NeverBundle = &x;
match ref_x.never {}
match ref_x.never {
_ => {}
}
// 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 {
_ => {}
}
// A cast does not load.
match (*ptr_never as Void) {}
match (*ptr_never as Void) {
_ => {}
}
// A union field may contain invalid data.
let union_never = Uninit::<!>::new();
match union_never.value {}
match union_never.value {
_ => {}
}
// Indexing is like a field access. This one accesses behind a reference.
let slice_never: &[!] = &[];
match slice_never[0] {}
match slice_never[0] {
_ => {}
}
}
}
// Test we correctly track validity as we dig into patterns. Validity changes when we go under a
// dereference or a union field access, and it otherwise preserved.
fn nested_validity_tracking(bundle: NeverBundle) {
let never: ! = bundle.never;
let ref_never: &! = &never;
let tuple_never: (!, !) = bundle.tuple_never;
let result_never: Result<!, !> = bundle.result_never;
let union_never = Uninit::<!>::new();
// These should be considered known_valid and warn unreachable.
match never {
_ => {} //~ ERROR unreachable pattern
}
match tuple_never {
(_, _) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match result_never {
Ok(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
Err(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
// These should be considered !known_valid and not warn unreachable.
match ref_never {
&_ => {}
}
match union_never {
Uninit { value: _ } => {}
}
}
// Test we don't allow empty matches on empty types if the scrutinee is `!known_valid`.
fn invalid_empty_match(bundle: NeverBundle) {
// We allow these two for backwards-compability.
let x: &! = &bundle.never;
match *x {}
let x: &Void = &bundle.void;
match *x {}
let x: &(u32, !) = &bundle.tuple_half_never;
match *x {} //[normal]~ ERROR non-exhaustive
let x: &(!, !) = &bundle.tuple_never;
match *x {} //[normal]~ ERROR non-exhaustive
let x: &Result<!, !> = &bundle.result_never;
match *x {} //[normal]~ ERROR non-exhaustive
let x: &[!; 3] = &bundle.array_3_never;
match *x {} //[normal]~ ERROR non-exhaustive
}
fn arrays_and_slices(x: NeverBundle) {
let slice_never: &[!] = &[];
match slice_never {}
//~^ ERROR non-empty
match slice_never {
//[normal]~^ ERROR not covered
[] => {}
}
match slice_never {
[] => {}
[_] => {}
[_, _, ..] => {}
}
match slice_never {
//[normal]~^ ERROR `&[]`, `&[_]` and `&[_, _]` not covered
//[exhaustive_patterns]~^^ ERROR `&[]` not covered
[_, _, _, ..] => {}
}
match slice_never {
[] => {}
_ => {}
}
match slice_never {
[] => {}
_x => {}
}
match slice_never {
//[normal]~^ ERROR `&[]` and `&[_, ..]` not covered
//[exhaustive_patterns]~^^ ERROR `&[]` not covered
&[..] if false => {}
}
match *slice_never {}
//~^ ERROR non-empty
match *slice_never {
_ => {}
}
let array_3_never: [!; 3] = x.array_3_never;
match array_3_never {}
//[normal]~^ ERROR non-empty
match array_3_never {
_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match array_3_never {
[_, _, _] => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match array_3_never {
[_, ..] => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
let ref_array_3_never: &[!; 3] = &array_3_never;
match ref_array_3_never {
// useful, reachable
&[_, _, _] => {}
}
match ref_array_3_never {
// useful, !reachable
&[_x, _, _] => {}
}
let array_0_never: [!; 0] = [];
match array_0_never {}
//~^ ERROR non-empty
match array_0_never {
[] => {}
}
match array_0_never {
[] => {}
_ => {} //~ ERROR unreachable pattern
}
match array_0_never {
//~^ ERROR `[]` not covered
[..] if false => {}
}
}
// The difference between `_` and `_a` patterns is that `_a` loads the value. In case of an empty
// type, this asserts validity of the value, and thus the binding is unreachable. We don't yet
// distinguish these cases since we don't lint "unreachable" on `useful && !reachable` arms.
// Once/if never patterns are a thing, we can warn that the `_a` cases should be never patterns.
fn bindings(x: NeverBundle) {
let opt_never: Option<!> = None;
let ref_never: &! = &x.never;
let ref_opt_never: &Option<!> = &None;
// On a known_valid place.
match opt_never {
None => {}
// !useful, !reachable
Some(_) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match opt_never {
None => {}
// !useful, !reachable
Some(_a) => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match opt_never {
None => {}
// !useful, !reachable
_ => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
match opt_never {
None => {}
// !useful, !reachable
_a => {} //[exhaustive_patterns]~ ERROR unreachable pattern
}
// The scrutinee is known_valid, but under the `&` isn't anymore.
match ref_never {
// useful, reachable
_ => {}
}
match ref_never {
// useful, reachable
&_ => {}
}
match ref_never {
// useful, reachable
_a => {}
}
match ref_never {
// useful, !reachable
&_a => {}
}
match ref_opt_never {
//[normal]~^ ERROR non-exhaustive
&None => {}
}
match ref_opt_never {
&None => {}
// useful, reachable
_ => {}
}
match ref_opt_never {
&None => {}
// useful, reachable
_a => {}
}
match ref_opt_never {
&None => {}
// useful, reachable
&_ => {}
}
match ref_opt_never {
&None => {}
// useful, !reachable
&_a => {}
}
// On a !known_valid place.
match *ref_never {}
match *ref_never {
// useful, reachable
_ => {}
}
match *ref_never {
// useful, !reachable
_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 => {}
}
match *ref_opt_never {
//[normal]~^ ERROR non-exhaustive
None => {}
}
match *ref_opt_never {
None => {}
// useful, reachable
Some(_) => {}
}
match *ref_opt_never {
None => {}
// useful, !reachable
Some(_a) => {}
}
match *ref_opt_never {
None => {}
// useful, reachable
_ => {}
}
match *ref_opt_never {
None => {}
// useful, !reachable
_a => {}
}
match *ref_opt_never {
None => {}
// useful, !reachable
_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 => {}
}
match *ref_opt_never {
None => {}
// useful, reachable
ref _a @ Some(_) => {}
}
match *ref_opt_never {
None => {}
// useful, !reachable
ref _a @ Some(_b) => {}
}
let ref_res_never: &Result<!, !> = &x.result_never;
match *ref_res_never {
//[normal]~^ ERROR non-exhaustive
// useful, reachable
Ok(_) => {}
}
match *ref_res_never {
// useful, reachable
Ok(_) => {}
// useful, reachable
_ => {}
}
match *ref_res_never {
//[normal]~^ ERROR non-exhaustive
// useful, !reachable
Ok(_a) => {}
}
match *ref_res_never {
// useful, !reachable
Ok(_a) => {}
// useful, reachable
_ => {}
}
match *ref_res_never {
// useful, !reachable
Ok(_a) => {}
// useful, reachable
Err(_) => {}
}
let ref_tuple_half_never: &(u32, !) = &x.tuple_half_never;
match *ref_tuple_half_never {}
//[normal]~^ ERROR non-empty
match *ref_tuple_half_never {
// useful, reachable
(_, _) => {}
}
match *ref_tuple_half_never {
// useful, reachable
(_x, _) => {}
}
match *ref_tuple_half_never {
// useful, !reachable
(_, _x) => {}
}
match *ref_tuple_half_never {
// useful, !reachable
(0, _x) => {}
// useful, reachable
(1.., _) => {}
}
}
// When we execute the condition for a guard we loads from all bindings. This asserts validity at
// all places with bindings. Surprisingly this can make subsequent arms unreachable. We choose to
// not detect this in exhaustiveness because this is rather subtle. With never patterns, we would
// recommend using a never pattern instead.
fn guards_and_validity(x: NeverBundle) {
let never: ! = x.never;
let ref_never: &! = &never;
// Basic guard behavior when known_valid.
match never {}
match never {
_ => {} //~ ERROR unreachable pattern
}
match never {
_x => {} //~ ERROR unreachable pattern
}
match never {
_ if false => {} //~ ERROR unreachable pattern
}
match never {
_x if false => {} //~ ERROR unreachable pattern
}
// If the pattern under the guard doesn't load, all is normal.
match *ref_never {
// useful, reachable
_ if false => {}
// useful, reachable
_ => {}
}
// 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 => {}
// !useful, !reachable
_ => {}
}
// The above still applies to the implicit `_` pattern used for exhaustiveness.
match *ref_never {
// useful, !reachable
_a if false => {}
}
match ref_never {
//[normal]~^ ERROR non-exhaustive
// useful, !reachable
&_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 => {}
// !useful, !reachable
Ok(_) => {}
// useful, !reachable
Err(_) => {}
}
let ref_tuple_never: &(!, !) = &x.tuple_never;
match *ref_tuple_never {
// useful, !reachable
(_, _x) if false => {}
// !useful, !reachable
(_, _) => {}
}
}
fn diagnostics_subtlety(x: NeverBundle) {
// Regression test for diagnostics: don't report `Some(Ok(_))` and `Some(Err(_))`.
let x: &Option<Result<!, !>> = &None;
match *x {
//[normal]~^ ERROR `Some(_)` not covered
None => {}
}
}
fn main() {}

View File

@ -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
&[_] => (),
};
}

View File

@ -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`.

View File

@ -1,8 +1,6 @@
#![feature(box_patterns)]
#![feature(never_type)]
#![feature(exhaustive_patterns)]
#![deny(unreachable_patterns)]
mod foo {
@ -23,22 +21,22 @@ fn main() {
let x: &[!] = &[];
match x {
&[] => (),
&[..] => (), //~ ERROR unreachable pattern
&[] => (),
&[..] => (),
};
let x: Result<Box<NotSoSecretlyEmpty>, &[Result<!, !>]> = Err(&[]);
match x {
Ok(box _) => (), //~ ERROR unreachable pattern
Ok(box _) => (), //~ ERROR unreachable pattern
Err(&[]) => (),
Err(&[..]) => (), //~ ERROR unreachable pattern
Err(&[..]) => (),
}
let x: Result<foo::SecretlyEmpty, Result<NotSoSecretlyEmpty, u32>> = Err(Err(123));
match x {
Ok(_y) => (),
Err(Err(_y)) => (),
Err(Ok(_y)) => (), //~ ERROR unreachable pattern
Err(Ok(_y)) => (), //~ ERROR unreachable pattern
}
while let Some(_y) = foo() {

View File

@ -1,38 +1,26 @@
error: unreachable pattern
--> $DIR/uninhabited-patterns.rs:27:9
--> $DIR/uninhabited-patterns.rs:30:9
|
LL | &[..] => (),
| ^^^^^
LL | Ok(box _) => (),
| ^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/uninhabited-patterns.rs:6:9
--> $DIR/uninhabited-patterns.rs:4:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
error: unreachable pattern
--> $DIR/uninhabited-patterns.rs:32:9
|
LL | Ok(box _) => (),
| ^^^^^^^^^
error: unreachable pattern
--> $DIR/uninhabited-patterns.rs:34:9
|
LL | Err(&[..]) => (),
| ^^^^^^^^^^
error: unreachable pattern
--> $DIR/uninhabited-patterns.rs:41:9
--> $DIR/uninhabited-patterns.rs:39:9
|
LL | Err(Ok(_y)) => (),
| ^^^^^^^^^^^
error: unreachable pattern
--> $DIR/uninhabited-patterns.rs:44:15
--> $DIR/uninhabited-patterns.rs:42:15
|
LL | while let Some(_y) = foo() {
| ^^^^^^^^
error: aborting due to 5 previous errors
error: aborting due to 3 previous errors