307307
308308use self::ArmType::*;
309309use self::Usefulness::*;
310- use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, WitnessPat};
310+ use super::deconstruct_pat::{
311+ Constructor, ConstructorSet, DeconstructedPat, SplitConstructorSet, WitnessPat,
312+ };
311313use crate::errors::{NonExhaustiveOmittedPattern, Uncovered};
312314
313315use rustc_data_structures::captures::Captures;
@@ -875,22 +877,84 @@ fn is_useful<'p, 'tcx>(
875877 ret
876878}
877879
880+ /// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
881+ /// inspect the same subvalue".
882+ /// This is used to traverse patterns column-by-column for lints. Despite similarities with
883+ /// `is_useful`, this is a different traversal. Notably this is linear in the depth of patterns,
884+ /// whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete).
885+ #[derive(Debug)]
886+ struct PatternColumn<'p, 'tcx> {
887+ patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>,
888+ }
889+
890+ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
891+ fn new(patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>) -> Self {
892+ Self { patterns }
893+ }
894+
895+ fn is_empty(&self) -> bool {
896+ self.patterns.is_empty()
897+ }
898+ fn head_ty(&self) -> Option<Ty<'tcx>> {
899+ self.patterns.get(0).map(|p| p.ty())
900+ }
901+
902+ fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'tcx> {
903+ let column_ctors = self.patterns.iter().map(|p| p.ctor());
904+ ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column_ctors)
905+ }
906+
907+ /// Does specialization: given a constructor, this takes the patterns from the column that match
908+ /// the constructor, and outputs their fields.
909+ /// This returns one column per field of the constructor. The normally all have the same length
910+ /// (the number of patterns in `self` that matched `ctor`), except that we expand or-patterns
911+ /// which may change the lengths.
912+ fn specialize(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Vec<Self> {
913+ let arity = ctor.arity(pcx);
914+ if arity == 0 {
915+ return Vec::new();
916+ }
917+
918+ // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
919+ // columns may have different lengths in the presence of or-patterns (this is why we can't
920+ // reuse `Matrix`).
921+ let mut specialized_columns: Vec<_> =
922+ (0..arity).map(|_| Self { patterns: Vec::new() }).collect();
923+ let relevant_patterns =
924+ self.patterns.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor()));
925+ for pat in relevant_patterns {
926+ let specialized = pat.specialize(pcx, &ctor);
927+ for (subpat, column) in specialized.iter().zip(&mut specialized_columns) {
928+ if subpat.is_or_pat() {
929+ column.patterns.extend(subpat.iter_fields())
930+ } else {
931+ column.patterns.push(subpat)
932+ }
933+ }
934+ }
935+
936+ assert!(
937+ !specialized_columns[0].is_empty(),
938+ "ctor {ctor:?} was listed as present but isn't;
939+ there is an inconsistency between `Constructor::is_covered_by` and `ConstructorSet::split`"
940+ );
941+ specialized_columns
942+ }
943+ }
944+
878945/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
879- /// in a given column. This traverses patterns column-by-column, where a column is the intuitive
880- /// notion of "subpatterns that inspect the same subvalue".
881- /// Despite similarities with `is_useful`, this traversal is different. Notably this is linear in the
882- /// depth of patterns, whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete).
946+ /// in a given column.
947+ #[instrument(level = "debug", skip(cx), ret)]
883948fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
884949 cx: &MatchCheckCtxt<'p, 'tcx>,
885- column: &[&DeconstructedPat <'p, 'tcx>] ,
950+ column: &PatternColumn <'p, 'tcx>,
886951) -> Vec<WitnessPat<'tcx>> {
887- if column.is_empty() {
952+ let Some(ty) = column.head_ty() else {
888953 return Vec::new();
889- }
890- let ty = column[0].ty();
954+ };
891955 let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false };
892956
893- let set = ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column.iter().map(|p| p.ctor()) );
957+ let set = column.analyze_ctors(pcx );
894958 if set.present.is_empty() {
895959 // We can't consistently handle the case where no constructors are present (since this would
896960 // require digging deep through any type in case there's a non_exhaustive enum somewhere),
@@ -911,35 +975,11 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
911975
912976 // Recurse into the fields.
913977 for ctor in set.present {
914- let arity = ctor.arity(pcx);
915- if arity == 0 {
916- continue;
917- }
918-
919- // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
920- // columns may have different lengths in the presence of or-patterns (this is why we can't
921- // reuse `Matrix`).
922- let mut specialized_columns: Vec<Vec<_>> = (0..arity).map(|_| Vec::new()).collect();
923- let relevant_patterns = column.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor()));
924- for pat in relevant_patterns {
925- let specialized = pat.specialize(pcx, &ctor);
926- for (subpat, sub_column) in specialized.iter().zip(&mut specialized_columns) {
927- if subpat.is_or_pat() {
928- sub_column.extend(subpat.iter_fields())
929- } else {
930- sub_column.push(subpat)
931- }
932- }
933- }
934- debug_assert!(
935- !specialized_columns[0].is_empty(),
936- "ctor {ctor:?} was listed as present but isn't"
937- );
938-
978+ let specialized_columns = column.specialize(pcx, &ctor);
939979 let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor);
940980 for (i, col_i) in specialized_columns.iter().enumerate() {
941981 // Compute witnesses for each column.
942- let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i.as_slice() );
982+ let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i);
943983 // For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`,
944984 // adding enough wildcards to match `arity`.
945985 for wit in wits_for_col_i {
@@ -1032,6 +1072,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
10321072 )
10331073 {
10341074 let pat_column = arms.iter().flat_map(|arm| arm.pat.flatten_or_pat()).collect::<Vec<_>>();
1075+ let pat_column = PatternColumn::new(pat_column);
10351076 let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column);
10361077
10371078 if !witnesses.is_empty() {
0 commit comments