From 9a7ed3625f57ad21d646f835975ab42929214b60 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 9 Apr 2023 08:09:43 +0000 Subject: [PATCH] Emit diagnostic for privately uninhabited uncovered witnesses. --- compiler/rustc_mir_build/messages.ftl | 2 ++ compiler/rustc_mir_build/src/errors.rs | 2 ++ .../src/thir/pattern/check_match.rs | 18 ++++++++++++++++++ tests/ui/never_type/exhaustive_patterns.stderr | 1 + .../ui/uninhabited/uninhabited-irrefutable.rs | 7 +++++++ .../uninhabited/uninhabited-irrefutable.stderr | 4 +++- 6 files changed, 33 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index f346cd48347..0282492a261 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -340,6 +340,8 @@ mir_build_uncovered = {$count -> *[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more } not covered +mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future + mir_build_pattern_not_covered = refutable pattern in {$origin} .pattern_ty = the matched value is of type `{$pattern_ty}` diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index dcdeaf008d6..7c0df201bc2 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -781,6 +781,8 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> { pub interpreted_as_const: Option, #[subdiagnostic] pub adt_defined_here: Option>, + #[note(mir_build_privately_uninhabited)] + pub witness_1_is_privately_uninhabited: Option<()>, #[note(mir_build_pattern_ty)] pub _p: (), pub pattern_ty: Ty<'tcx>, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index ca25f83e643..5559e8b3940 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -479,12 +479,30 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { AdtDefinedHere { adt_def_span, ty, variants } }; + // Emit an extra note if the first uncovered witness is + // visibly uninhabited anywhere in the current crate. + let witness_1_is_privately_uninhabited = + if cx.tcx.features().exhaustive_patterns + && let Some(witness_1) = witnesses.get(0) + && let ty::Adt(adt, substs) = witness_1.ty().kind() + && adt.is_enum() + && let Constructor::Variant(variant_index) = witness_1.ctor() + { + let variant = adt.variant(*variant_index); + let inhabited = variant.inhabited_predicate(cx.tcx, *adt).subst(cx.tcx, substs); + assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module)); + !inhabited.apply_ignore_module(cx.tcx, cx.param_env) + } else { + false + }; + self.error = Err(self.tcx.sess.emit_err(PatternNotCovered { span: pat.span, origin, uncovered: Uncovered::new(pat.span, &cx, witnesses), inform, interpreted_as_const, + witness_1_is_privately_uninhabited: witness_1_is_privately_uninhabited.then_some(()), _p: (), pattern_ty, let_suggestion, diff --git a/tests/ui/never_type/exhaustive_patterns.stderr b/tests/ui/never_type/exhaustive_patterns.stderr index 5fed903eb70..f7bf8581582 100644 --- a/tests/ui/never_type/exhaustive_patterns.stderr +++ b/tests/ui/never_type/exhaustive_patterns.stderr @@ -14,6 +14,7 @@ LL | enum Either { LL | A(A), LL | B(inner::Wrapper), | - not covered + = note: pattern `Either::B(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future = note: the matched value is of type `Either<(), !>` help: you might want to use `if let` to ignore the variant that isn't matched | diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.rs b/tests/ui/uninhabited/uninhabited-irrefutable.rs index 4b001aca2d1..cfd60a8d903 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.rs +++ b/tests/ui/uninhabited/uninhabited-irrefutable.rs @@ -16,7 +16,9 @@ struct NotSoSecretlyEmpty { } enum Foo { + //~^ NOTE `Foo` defined here A(foo::SecretlyEmpty), + //~^ NOTE not covered B(foo::NotSoSecretlyEmpty), C(NotSoSecretlyEmpty), D(u32, u32), @@ -27,4 +29,9 @@ fn main() { let Foo::D(_y, _z) = x; //~^ ERROR refutable pattern in local binding //~| `Foo::A(_)` not covered + //~| NOTE `let` bindings require an "irrefutable pattern" + //~| NOTE for more information + //~| NOTE pattern `Foo::A(_)` is currently uninhabited + //~| NOTE the matched value is of type `Foo` + //~| HELP you might want to use `let else` } diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.stderr b/tests/ui/uninhabited/uninhabited-irrefutable.stderr index 8cafea555c1..daf75f51b5a 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.stderr +++ b/tests/ui/uninhabited/uninhabited-irrefutable.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in local binding - --> $DIR/uninhabited-irrefutable.rs:27:9 + --> $DIR/uninhabited-irrefutable.rs:29:9 | LL | let Foo::D(_y, _z) = x; | ^^^^^^^^^^^^^^ pattern `Foo::A(_)` not covered @@ -11,8 +11,10 @@ note: `Foo` defined here | LL | enum Foo { | ^^^ +LL | LL | A(foo::SecretlyEmpty), | - not covered + = note: pattern `Foo::A(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future = note: the matched value is of type `Foo` help: you might want to use `let else` to handle the variant that isn't matched |