Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Explain why we require _ for empty patterns #128033

Merged
merged 2 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_pattern_analysis::errors::Uncovered;
use rustc_pattern_analysis::rustc::{
Constructor, DeconstructedPat, MatchArm, RustcPatCtxt as PatCtxt, Usefulness, UsefulnessReport,
WitnessPat,
Constructor, DeconstructedPat, MatchArm, RevealedTy, RustcPatCtxt as PatCtxt, Usefulness,
UsefulnessReport, WitnessPat,
};
use rustc_session::lint::builtin::{
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
Expand Down Expand Up @@ -998,27 +998,31 @@ fn report_non_exhaustive_match<'p, 'tcx>(
err.note(format!("the matched value is of type `{}`", scrut_ty));

if !is_empty_match {
let mut non_exhaustive_tys = FxIndexSet::default();
let mut special_tys = FxIndexSet::default();
// Look at the first witness.
collect_non_exhaustive_tys(cx, &witnesses[0], &mut non_exhaustive_tys);
collect_special_tys(cx, &witnesses[0], &mut special_tys);

for ty in non_exhaustive_tys {
for ty in special_tys {
if ty.is_ptr_sized_integral() {
if ty == cx.tcx.types.usize {
if ty.inner() == cx.tcx.types.usize {
err.note(format!(
"`{ty}` does not have a fixed maximum value, so half-open ranges are necessary to match \
exhaustively",
));
} else if ty == cx.tcx.types.isize {
} else if ty.inner() == cx.tcx.types.isize {
err.note(format!(
"`{ty}` does not have fixed minimum and maximum values, so half-open ranges are necessary to match \
exhaustively",
));
}
} else if ty == cx.tcx.types.str_ {
} else if ty.inner() == cx.tcx.types.str_ {
err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
} else if cx.is_foreign_non_exhaustive_enum(cx.reveal_opaque_ty(ty)) {
Nadrieril marked this conversation as resolved.
Show resolved Hide resolved
} else if cx.is_foreign_non_exhaustive_enum(ty) {
err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
} else if cx.is_uninhabited(ty.inner()) && cx.tcx.features().min_exhaustive_patterns {
// The type is uninhabited yet there is a witness: we must be in the `MaybeInvalid`
// case.
err.note(format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required"));
}
}
}
Expand Down Expand Up @@ -1168,22 +1172,22 @@ fn joined_uncovered_patterns<'p, 'tcx>(
}
}

fn collect_non_exhaustive_tys<'tcx>(
/// Collect types that require specific explanations when they show up in witnesses.
fn collect_special_tys<'tcx>(
cx: &PatCtxt<'_, 'tcx>,
pat: &WitnessPat<'_, 'tcx>,
non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
special_tys: &mut FxIndexSet<RevealedTy<'tcx>>,
Nadrieril marked this conversation as resolved.
Show resolved Hide resolved
) {
if matches!(pat.ctor(), Constructor::NonExhaustive) {
non_exhaustive_tys.insert(pat.ty().inner());
if matches!(pat.ctor(), Constructor::NonExhaustive | Constructor::Never) {
special_tys.insert(*pat.ty());
}
if let Constructor::IntRange(range) = pat.ctor() {
if cx.is_range_beyond_boundaries(range, *pat.ty()) {
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
non_exhaustive_tys.insert(pat.ty().inner());
special_tys.insert(*pat.ty());
}
}
pat.iter_fields()
.for_each(|field_pat| collect_non_exhaustive_tys(cx, field_pat, non_exhaustive_tys))
pat.iter_fields().for_each(|field_pat| collect_special_tys(cx, field_pat, special_tys))
}

fn report_adt_defined_here<'tcx>(
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_pattern_analysis/src/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,15 @@ pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcPatCtxt<'p, 'tcx>>;
///
/// Use `.inner()` or deref to get to the `Ty<'tcx>`.
#[repr(transparent)]
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct RevealedTy<'tcx>(Ty<'tcx>);

impl<'tcx> fmt::Display for RevealedTy<'tcx> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(fmt)
}
}

impl<'tcx> fmt::Debug for RevealedTy<'tcx> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(fmt)
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ note: `Option<Void>` defined here
|
= note: not covered
= note: the matched value is of type `Option<Void>`
= note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required
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 => {},
Expand Down Expand Up @@ -349,6 +350,7 @@ LL | match slice_never {
| ^^^^^^^^^^^ pattern `&[_, ..]` not covered
|
= note: the matched value is of type `&[!]`
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
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 ~ [] => {},
Expand Down Expand Up @@ -484,6 +486,7 @@ note: `Option<!>` defined here
|
= note: not covered
= note: the matched value is of type `&Option<!>`
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
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 => {},
Expand All @@ -502,6 +505,7 @@ note: `Option<!>` defined here
|
= note: not covered
= note: the matched value is of type `Option<!>`
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
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 => {},
Expand All @@ -520,6 +524,7 @@ note: `Result<!, !>` defined here
|
= note: not covered
= note: the matched value is of type `Result<!, !>`
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
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(_) => {},
Expand All @@ -538,6 +543,7 @@ note: `Result<!, !>` defined here
|
= note: not covered
= note: the matched value is of type `Result<!, !>`
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
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) => {},
Expand Down Expand Up @@ -589,6 +595,7 @@ LL | match ref_never {
| ^^^^^^^^^ pattern `&_` not covered
|
= note: the matched value is of type `&!`
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
= 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
Expand All @@ -609,6 +616,7 @@ note: `Result<!, !>` defined here
|
= note: not covered
= note: the matched value is of type `Result<!, !>`
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
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(_) => {},
Expand All @@ -627,6 +635,7 @@ note: `Option<Result<!, !>>` defined here
|
= note: not covered
= note: the matched value is of type `Option<Result<!, !>>`
= note: `Result<!, !>` is uninhabited but is not being matched by value, so a wildcard `_` is required
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 => {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ LL | match nevers {
| ^^^^^^ pattern `&[_, ..]` not covered
|
= note: the matched value is of type `&[!]`
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
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 ~ &[] => (),
Expand Down
Loading