Skip to content

Commit 9a7ed36

Browse files
committed
Emit diagnostic for privately uninhabited uncovered witnesses.
1 parent b3cbf7c commit 9a7ed36

File tree

6 files changed

+33
-1
lines changed

6 files changed

+33
-1
lines changed

compiler/rustc_mir_build/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,8 @@ mir_build_uncovered = {$count ->
340340
*[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more
341341
} not covered
342342
343+
mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
344+
343345
mir_build_pattern_not_covered = refutable pattern in {$origin}
344346
.pattern_ty = the matched value is of type `{$pattern_ty}`
345347

compiler/rustc_mir_build/src/errors.rs

+2
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,8 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> {
781781
pub interpreted_as_const: Option<InterpretedAsConst>,
782782
#[subdiagnostic]
783783
pub adt_defined_here: Option<AdtDefinedHere<'tcx>>,
784+
#[note(mir_build_privately_uninhabited)]
785+
pub witness_1_is_privately_uninhabited: Option<()>,
784786
#[note(mir_build_pattern_ty)]
785787
pub _p: (),
786788
pub pattern_ty: Ty<'tcx>,

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

+18
Original file line numberDiff line numberDiff line change
@@ -479,12 +479,30 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
479479
AdtDefinedHere { adt_def_span, ty, variants }
480480
};
481481

482+
// Emit an extra note if the first uncovered witness is
483+
// visibly uninhabited anywhere in the current crate.
484+
let witness_1_is_privately_uninhabited =
485+
if cx.tcx.features().exhaustive_patterns
486+
&& let Some(witness_1) = witnesses.get(0)
487+
&& let ty::Adt(adt, substs) = witness_1.ty().kind()
488+
&& adt.is_enum()
489+
&& let Constructor::Variant(variant_index) = witness_1.ctor()
490+
{
491+
let variant = adt.variant(*variant_index);
492+
let inhabited = variant.inhabited_predicate(cx.tcx, *adt).subst(cx.tcx, substs);
493+
assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module));
494+
!inhabited.apply_ignore_module(cx.tcx, cx.param_env)
495+
} else {
496+
false
497+
};
498+
482499
self.error = Err(self.tcx.sess.emit_err(PatternNotCovered {
483500
span: pat.span,
484501
origin,
485502
uncovered: Uncovered::new(pat.span, &cx, witnesses),
486503
inform,
487504
interpreted_as_const,
505+
witness_1_is_privately_uninhabited: witness_1_is_privately_uninhabited.then_some(()),
488506
_p: (),
489507
pattern_ty,
490508
let_suggestion,

tests/ui/never_type/exhaustive_patterns.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ LL | enum Either<A, B> {
1414
LL | A(A),
1515
LL | B(inner::Wrapper<B>),
1616
| - not covered
17+
= note: pattern `Either::B(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
1718
= note: the matched value is of type `Either<(), !>`
1819
help: you might want to use `if let` to ignore the variant that isn't matched
1920
|

tests/ui/uninhabited/uninhabited-irrefutable.rs

+7
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ struct NotSoSecretlyEmpty {
1616
}
1717

1818
enum Foo {
19+
//~^ NOTE `Foo` defined here
1920
A(foo::SecretlyEmpty),
21+
//~^ NOTE not covered
2022
B(foo::NotSoSecretlyEmpty),
2123
C(NotSoSecretlyEmpty),
2224
D(u32, u32),
@@ -27,4 +29,9 @@ fn main() {
2729
let Foo::D(_y, _z) = x;
2830
//~^ ERROR refutable pattern in local binding
2931
//~| `Foo::A(_)` not covered
32+
//~| NOTE `let` bindings require an "irrefutable pattern"
33+
//~| NOTE for more information
34+
//~| NOTE pattern `Foo::A(_)` is currently uninhabited
35+
//~| NOTE the matched value is of type `Foo`
36+
//~| HELP you might want to use `let else`
3037
}

tests/ui/uninhabited/uninhabited-irrefutable.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0005]: refutable pattern in local binding
2-
--> $DIR/uninhabited-irrefutable.rs:27:9
2+
--> $DIR/uninhabited-irrefutable.rs:29:9
33
|
44
LL | let Foo::D(_y, _z) = x;
55
| ^^^^^^^^^^^^^^ pattern `Foo::A(_)` not covered
@@ -11,8 +11,10 @@ note: `Foo` defined here
1111
|
1212
LL | enum Foo {
1313
| ^^^
14+
LL |
1415
LL | A(foo::SecretlyEmpty),
1516
| - not covered
17+
= note: pattern `Foo::A(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
1618
= note: the matched value is of type `Foo`
1719
help: you might want to use `let else` to handle the variant that isn't matched
1820
|

0 commit comments

Comments
 (0)