From 2bb00741d463143a10e632bb118d4ed336dbb75f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 19 Jul 2025 11:28:31 +0200 Subject: pattern_analysis: add option to get a full set of witnesses --- compiler/rustc_pattern_analysis/src/constructor.rs | 4 +--- compiler/rustc_pattern_analysis/src/lib.rs | 7 +++++++ compiler/rustc_pattern_analysis/src/usefulness.rs | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'compiler/rustc_pattern_analysis/src') diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 09685640e50..9a9e0db964c 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -950,9 +950,7 @@ impl Constructor { } } Never => write!(f, "!")?, - Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => { - write!(f, "_ : {:?}", ty)? - } + Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => write!(f, "_")?, } Ok(()) } diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 66df35f9ee4..d9bb93a829b 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -57,6 +57,13 @@ pub trait PatCx: Sized + fmt::Debug { fn is_exhaustive_patterns_feature_on(&self) -> bool; + /// Whether to ensure the non-exhaustiveness witnesses we report for a complete set. This is + /// `false` by default to avoid some exponential blowup cases such as + /// . + fn exhaustive_witnesses(&self) -> bool { + false + } + /// The number of fields for this constructor. fn ctor_arity(&self, ctor: &Constructor, ty: &Self::Ty) -> usize; diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index b1c646e9884..91377b2f2bd 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -1747,7 +1747,9 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>( // `ctor` is *irrelevant* if there's another constructor in `split_ctors` that matches // strictly fewer rows. In that case we can sometimes skip it. See the top of the file for // details. - let ctor_is_relevant = matches!(ctor, Constructor::Missing) || missing_ctors.is_empty(); + let ctor_is_relevant = matches!(ctor, Constructor::Missing) + || missing_ctors.is_empty() + || mcx.tycx.exhaustive_witnesses(); let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor, ctor_is_relevant)?; let mut witnesses = ensure_sufficient_stack(|| { compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix) -- cgit 1.4.1-3-g733a5 From 9b01de20e10376d379ae32baa6a315d8e30cb351 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 19 Jul 2025 21:18:14 +0200 Subject: List all the variants of non-exhaustive enums in exhaustive mode --- compiler/rustc_pattern_analysis/src/usefulness.rs | 3 ++- .../rustc_pattern_analysis/tests/common/mod.rs | 27 +++++++++++++++++++--- .../rustc_pattern_analysis/tests/exhaustiveness.rs | 22 ++++++++++++++++-- 3 files changed, 46 insertions(+), 6 deletions(-) (limited to 'compiler/rustc_pattern_analysis/src') diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 91377b2f2bd..19446a1efe9 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -994,7 +994,8 @@ impl PlaceInfo { if !missing_ctors.is_empty() && !report_individual_missing_ctors { // Report `_` as missing. missing_ctors = vec![Constructor::Wildcard]; - } else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) { + } else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) && !cx.exhaustive_witnesses() + { // We need to report a `_` anyway, so listing other constructors would be redundant. // `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked // up by diagnostics to add a note about why `_` is required here. diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs index 7f2c2afcb5b..8497521881a 100644 --- a/compiler/rustc_pattern_analysis/tests/common/mod.rs +++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use rustc_pattern_analysis::constructor::{ Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, RangeEnd, VariantVisibility, }; @@ -22,8 +23,10 @@ fn init_tracing() { .try_init(); } +pub const UNIT: Ty = Ty::Tuple(&[]); +pub const NEVER: Ty = Ty::Enum(&[]); + /// A simple set of types. -#[allow(dead_code)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(super) enum Ty { /// Booleans @@ -38,6 +41,8 @@ pub(super) enum Ty { BigStruct { arity: usize, ty: &'static Ty }, /// A enum with `arity` variants of type `ty`. BigEnum { arity: usize, ty: &'static Ty }, + /// Like `Enum` but non-exhaustive. + NonExhaustiveEnum(&'static [Ty]), } /// The important logic. @@ -47,7 +52,7 @@ impl Ty { match (ctor, *self) { (Struct, Ty::Tuple(tys)) => tys.iter().copied().collect(), (Struct, Ty::BigStruct { arity, ty }) => (0..arity).map(|_| *ty).collect(), - (Variant(i), Ty::Enum(tys)) => vec![tys[*i]], + (Variant(i), Ty::Enum(tys) | Ty::NonExhaustiveEnum(tys)) => vec![tys[*i]], (Variant(_), Ty::BigEnum { ty, .. }) => vec![*ty], (Bool(..) | IntRange(..) | NonExhaustive | Missing | Wildcard, _) => vec![], _ => panic!("Unexpected ctor {ctor:?} for type {self:?}"), @@ -61,6 +66,7 @@ impl Ty { Ty::Enum(tys) => tys.iter().all(|ty| ty.is_empty()), Ty::BigStruct { arity, ty } => arity != 0 && ty.is_empty(), Ty::BigEnum { arity, ty } => arity == 0 || ty.is_empty(), + Ty::NonExhaustiveEnum(..) => false, } } @@ -90,6 +96,19 @@ impl Ty { .collect(), non_exhaustive: false, }, + Ty::NonExhaustiveEnum(tys) => ConstructorSet::Variants { + variants: tys + .iter() + .map(|ty| { + if ty.is_empty() { + VariantVisibility::Empty + } else { + VariantVisibility::Visible + } + }) + .collect(), + non_exhaustive: true, + }, Ty::BigEnum { arity: 0, .. } => ConstructorSet::NoConstructors, Ty::BigEnum { arity, ty } => { let vis = if ty.is_empty() { @@ -113,7 +132,9 @@ impl Ty { match (*self, ctor) { (Ty::Tuple(..), _) => Ok(()), (Ty::BigStruct { .. }, _) => write!(f, "BigStruct"), - (Ty::Enum(..), Constructor::Variant(i)) => write!(f, "Enum::Variant{i}"), + (Ty::Enum(..) | Ty::NonExhaustiveEnum(..), Constructor::Variant(i)) => { + write!(f, "Enum::Variant{i}") + } (Ty::BigEnum { .. }, Constructor::Variant(i)) => write!(f, "BigEnum::Variant{i}"), _ => write!(f, "{:?}::{:?}", self, ctor), } diff --git a/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs index 961693f79c3..14ca0d057f0 100644 --- a/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs +++ b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs @@ -117,7 +117,7 @@ fn test_nested() { #[test] fn test_witnesses() { // TY = Option - const TY: Ty = Ty::Enum(&[Ty::Bool, Ty::Tuple(&[])]); + const TY: Ty = Ty::Enum(&[Ty::Bool, UNIT]); // ty = (Option, Option) let ty = Ty::Tuple(&[TY, TY]); assert_witnesses(AllOfThem, ty, vec![], vec!["(_, _)"]); @@ -158,12 +158,30 @@ fn test_witnesses() { ), vec!["(_, Enum::Variant0(true))", "(_, Enum::Variant1(_))"], ); + + let ty = Ty::NonExhaustiveEnum(&[UNIT, UNIT, UNIT]); + assert_witnesses( + OnlySome, + ty, + pats!(ty; + Variant.0, + ), + vec!["_"], + ); + assert_witnesses( + AllOfThem, + ty, + pats!(ty; + Variant.0, + ), + vec!["Enum::Variant1(_)", "Enum::Variant2(_)", "_"], + ); } #[test] fn test_empty() { // `TY = Result` - const TY: Ty = Ty::Enum(&[Ty::Bool, Ty::Enum(&[])]); + const TY: Ty = Ty::Enum(&[Ty::Bool, NEVER]); assert_exhaustive(pats!(TY; Variant.0, )); -- cgit 1.4.1-3-g733a5