Skip to content

Commit a742abe

Browse files
authored
Rollup merge of rust-lang#128033 - Nadrieril:explain-empty-wildcards, r=compiler-errors
Explain why we require `_` for empty patterns This adds a note to the "non-exhaustive patterns" diagnostic to explain why we sometimes require extra `_` patterns on empty types. This is one of the two diagnostic improvements I wanted to do before [stabilizing `min_exhaustive_patterns`](rust-lang#122792). r? `@compiler-errors`
2 parents 1c55399 + 8a49d83 commit a742abe

File tree

4 files changed

+37
-17
lines changed

4 files changed

+37
-17
lines changed

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+20-16
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
1616
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
1717
use rustc_pattern_analysis::errors::Uncovered;
1818
use rustc_pattern_analysis::rustc::{
19-
Constructor, DeconstructedPat, MatchArm, RustcPatCtxt as PatCtxt, Usefulness, UsefulnessReport,
20-
WitnessPat,
19+
Constructor, DeconstructedPat, MatchArm, RevealedTy, RustcPatCtxt as PatCtxt, Usefulness,
20+
UsefulnessReport, WitnessPat,
2121
};
2222
use rustc_session::lint::builtin::{
2323
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
@@ -998,27 +998,31 @@ fn report_non_exhaustive_match<'p, 'tcx>(
998998
err.note(format!("the matched value is of type `{}`", scrut_ty));
999999

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

1005-
for ty in non_exhaustive_tys {
1005+
for ty in special_tys {
10061006
if ty.is_ptr_sized_integral() {
1007-
if ty == cx.tcx.types.usize {
1007+
if ty.inner() == cx.tcx.types.usize {
10081008
err.note(format!(
10091009
"`{ty}` does not have a fixed maximum value, so half-open ranges are necessary to match \
10101010
exhaustively",
10111011
));
1012-
} else if ty == cx.tcx.types.isize {
1012+
} else if ty.inner() == cx.tcx.types.isize {
10131013
err.note(format!(
10141014
"`{ty}` does not have fixed minimum and maximum values, so half-open ranges are necessary to match \
10151015
exhaustively",
10161016
));
10171017
}
1018-
} else if ty == cx.tcx.types.str_ {
1018+
} else if ty.inner() == cx.tcx.types.str_ {
10191019
err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
1020-
} else if cx.is_foreign_non_exhaustive_enum(cx.reveal_opaque_ty(ty)) {
1020+
} else if cx.is_foreign_non_exhaustive_enum(ty) {
10211021
err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
1022+
} else if cx.is_uninhabited(ty.inner()) && cx.tcx.features().min_exhaustive_patterns {
1023+
// The type is uninhabited yet there is a witness: we must be in the `MaybeInvalid`
1024+
// case.
1025+
err.note(format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required"));
10221026
}
10231027
}
10241028
}
@@ -1168,22 +1172,22 @@ fn joined_uncovered_patterns<'p, 'tcx>(
11681172
}
11691173
}
11701174

1171-
fn collect_non_exhaustive_tys<'tcx>(
1175+
/// Collect types that require specific explanations when they show up in witnesses.
1176+
fn collect_special_tys<'tcx>(
11721177
cx: &PatCtxt<'_, 'tcx>,
11731178
pat: &WitnessPat<'_, 'tcx>,
1174-
non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
1179+
special_tys: &mut FxIndexSet<RevealedTy<'tcx>>,
11751180
) {
1176-
if matches!(pat.ctor(), Constructor::NonExhaustive) {
1177-
non_exhaustive_tys.insert(pat.ty().inner());
1181+
if matches!(pat.ctor(), Constructor::NonExhaustive | Constructor::Never) {
1182+
special_tys.insert(*pat.ty());
11781183
}
11791184
if let Constructor::IntRange(range) = pat.ctor() {
11801185
if cx.is_range_beyond_boundaries(range, *pat.ty()) {
11811186
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
1182-
non_exhaustive_tys.insert(pat.ty().inner());
1187+
special_tys.insert(*pat.ty());
11831188
}
11841189
}
1185-
pat.iter_fields()
1186-
.for_each(|field_pat| collect_non_exhaustive_tys(cx, field_pat, non_exhaustive_tys))
1190+
pat.iter_fields().for_each(|field_pat| collect_special_tys(cx, field_pat, special_tys))
11871191
}
11881192

11891193
fn report_adt_defined_here<'tcx>(

compiler/rustc_pattern_analysis/src/rustc.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,15 @@ pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcPatCtxt<'p, 'tcx>>;
4040
///
4141
/// Use `.inner()` or deref to get to the `Ty<'tcx>`.
4242
#[repr(transparent)]
43-
#[derive(Clone, Copy)]
43+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
4444
pub struct RevealedTy<'tcx>(Ty<'tcx>);
4545

46+
impl<'tcx> fmt::Display for RevealedTy<'tcx> {
47+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
48+
self.0.fmt(fmt)
49+
}
50+
}
51+
4652
impl<'tcx> fmt::Debug for RevealedTy<'tcx> {
4753
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
4854
self.0.fmt(fmt)

tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ note: `Option<Void>` defined here
204204
|
205205
= note: not covered
206206
= note: the matched value is of type `Option<Void>`
207+
= note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required
207208
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
208209
|
209210
LL ~ None => {},
@@ -349,6 +350,7 @@ LL | match slice_never {
349350
| ^^^^^^^^^^^ pattern `&[_, ..]` not covered
350351
|
351352
= note: the matched value is of type `&[!]`
353+
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
352354
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
353355
|
354356
LL ~ [] => {},
@@ -484,6 +486,7 @@ note: `Option<!>` defined here
484486
|
485487
= note: not covered
486488
= note: the matched value is of type `&Option<!>`
489+
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
487490
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
488491
|
489492
LL ~ &None => {},
@@ -502,6 +505,7 @@ note: `Option<!>` defined here
502505
|
503506
= note: not covered
504507
= note: the matched value is of type `Option<!>`
508+
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
505509
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
506510
|
507511
LL ~ None => {},
@@ -520,6 +524,7 @@ note: `Result<!, !>` defined here
520524
|
521525
= note: not covered
522526
= note: the matched value is of type `Result<!, !>`
527+
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
523528
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
524529
|
525530
LL ~ Ok(_) => {},
@@ -538,6 +543,7 @@ note: `Result<!, !>` defined here
538543
|
539544
= note: not covered
540545
= note: the matched value is of type `Result<!, !>`
546+
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
541547
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
542548
|
543549
LL ~ Ok(_a) => {},
@@ -589,6 +595,7 @@ LL | match ref_never {
589595
| ^^^^^^^^^ pattern `&_` not covered
590596
|
591597
= note: the matched value is of type `&!`
598+
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
592599
= note: references are always considered inhabited
593600
= note: match arms with guards don't count towards exhaustivity
594601
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
@@ -609,6 +616,7 @@ note: `Result<!, !>` defined here
609616
|
610617
= note: not covered
611618
= note: the matched value is of type `Result<!, !>`
619+
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
612620
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
613621
|
614622
LL ~ Err(_) => {},
@@ -627,6 +635,7 @@ note: `Option<Result<!, !>>` defined here
627635
|
628636
= note: not covered
629637
= note: the matched value is of type `Option<Result<!, !>>`
638+
= note: `Result<!, !>` is uninhabited but is not being matched by value, so a wildcard `_` is required
630639
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
631640
|
632641
LL ~ None => {},

tests/ui/pattern/usefulness/slice_of_empty.min_exhaustive_patterns.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ LL | match nevers {
55
| ^^^^^^ pattern `&[_, ..]` not covered
66
|
77
= note: the matched value is of type `&[!]`
8+
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
89
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
910
|
1011
LL ~ &[] => (),

0 commit comments

Comments
 (0)