Abstract over per-column pattern traversal
This commit is contained in:
parent
aa91057796
commit
beecd93316
1 changed files with 77 additions and 36 deletions
|
@ -307,7 +307,9 @@
|
||||||
|
|
||||||
use self::ArmType::*;
|
use self::ArmType::*;
|
||||||
use self::Usefulness::*;
|
use self::Usefulness::*;
|
||||||
use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, WitnessPat};
|
use super::deconstruct_pat::{
|
||||||
|
Constructor, ConstructorSet, DeconstructedPat, SplitConstructorSet, WitnessPat,
|
||||||
|
};
|
||||||
use crate::errors::{NonExhaustiveOmittedPattern, Uncovered};
|
use crate::errors::{NonExhaustiveOmittedPattern, Uncovered};
|
||||||
|
|
||||||
use rustc_data_structures::captures::Captures;
|
use rustc_data_structures::captures::Captures;
|
||||||
|
@ -875,22 +877,84 @@ fn is_useful<'p, 'tcx>(
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
|
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
|
||||||
/// in a given column. This traverses patterns column-by-column, where a column is the intuitive
|
/// inspect the same subvalue".
|
||||||
/// notion of "subpatterns that inspect the same subvalue".
|
/// This is used to traverse patterns column-by-column for lints. Despite similarities with
|
||||||
/// Despite similarities with `is_useful`, this traversal is different. Notably this is linear in the
|
/// `is_useful`, this is a different traversal. Notably this is linear in the depth of patterns,
|
||||||
/// depth of patterns, whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete).
|
/// whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete).
|
||||||
fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
|
#[derive(Debug)]
|
||||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
struct PatternColumn<'p, 'tcx> {
|
||||||
column: &[&DeconstructedPat<'p, 'tcx>],
|
patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>,
|
||||||
) -> Vec<WitnessPat<'tcx>> {
|
}
|
||||||
if column.is_empty() {
|
|
||||||
|
impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
|
||||||
|
fn new(patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>) -> Self {
|
||||||
|
Self { patterns }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.patterns.is_empty()
|
||||||
|
}
|
||||||
|
fn head_ty(&self) -> Option<Ty<'tcx>> {
|
||||||
|
self.patterns.get(0).map(|p| p.ty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'tcx> {
|
||||||
|
let column_ctors = self.patterns.iter().map(|p| p.ctor());
|
||||||
|
ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column_ctors)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does specialization: given a constructor, this takes the patterns from the column that match
|
||||||
|
/// the constructor, and outputs their fields.
|
||||||
|
/// This returns one column per field of the constructor. The normally all have the same length
|
||||||
|
/// (the number of patterns in `self` that matched `ctor`), except that we expand or-patterns
|
||||||
|
/// which may change the lengths.
|
||||||
|
fn specialize(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Vec<Self> {
|
||||||
|
let arity = ctor.arity(pcx);
|
||||||
|
if arity == 0 {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
let ty = column[0].ty();
|
|
||||||
|
// We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
|
||||||
|
// columns may have different lengths in the presence of or-patterns (this is why we can't
|
||||||
|
// reuse `Matrix`).
|
||||||
|
let mut specialized_columns: Vec<_> =
|
||||||
|
(0..arity).map(|_| Self { patterns: Vec::new() }).collect();
|
||||||
|
let relevant_patterns =
|
||||||
|
self.patterns.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor()));
|
||||||
|
for pat in relevant_patterns {
|
||||||
|
let specialized = pat.specialize(pcx, &ctor);
|
||||||
|
for (subpat, column) in specialized.iter().zip(&mut specialized_columns) {
|
||||||
|
if subpat.is_or_pat() {
|
||||||
|
column.patterns.extend(subpat.iter_fields())
|
||||||
|
} else {
|
||||||
|
column.patterns.push(subpat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
!specialized_columns[0].is_empty(),
|
||||||
|
"ctor {ctor:?} was listed as present but isn't;
|
||||||
|
there is an inconsistency between `Constructor::is_covered_by` and `ConstructorSet::split`"
|
||||||
|
);
|
||||||
|
specialized_columns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
|
||||||
|
/// in a given column.
|
||||||
|
#[instrument(level = "debug", skip(cx), ret)]
|
||||||
|
fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
|
||||||
|
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||||
|
column: &PatternColumn<'p, 'tcx>,
|
||||||
|
) -> Vec<WitnessPat<'tcx>> {
|
||||||
|
let Some(ty) = column.head_ty() else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false };
|
let pcx = &PatCtxt { cx, ty, span: DUMMY_SP, is_top_level: false };
|
||||||
|
|
||||||
let set = ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, column.iter().map(|p| p.ctor()));
|
let set = column.analyze_ctors(pcx);
|
||||||
if set.present.is_empty() {
|
if set.present.is_empty() {
|
||||||
// We can't consistently handle the case where no constructors are present (since this would
|
// We can't consistently handle the case where no constructors are present (since this would
|
||||||
// require digging deep through any type in case there's a non_exhaustive enum somewhere),
|
// 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>(
|
||||||
|
|
||||||
// Recurse into the fields.
|
// Recurse into the fields.
|
||||||
for ctor in set.present {
|
for ctor in set.present {
|
||||||
let arity = ctor.arity(pcx);
|
let specialized_columns = column.specialize(pcx, &ctor);
|
||||||
if arity == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
|
|
||||||
// columns may have different lengths in the presence of or-patterns (this is why we can't
|
|
||||||
// reuse `Matrix`).
|
|
||||||
let mut specialized_columns: Vec<Vec<_>> = (0..arity).map(|_| Vec::new()).collect();
|
|
||||||
let relevant_patterns = column.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor()));
|
|
||||||
for pat in relevant_patterns {
|
|
||||||
let specialized = pat.specialize(pcx, &ctor);
|
|
||||||
for (subpat, sub_column) in specialized.iter().zip(&mut specialized_columns) {
|
|
||||||
if subpat.is_or_pat() {
|
|
||||||
sub_column.extend(subpat.iter_fields())
|
|
||||||
} else {
|
|
||||||
sub_column.push(subpat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug_assert!(
|
|
||||||
!specialized_columns[0].is_empty(),
|
|
||||||
"ctor {ctor:?} was listed as present but isn't"
|
|
||||||
);
|
|
||||||
|
|
||||||
let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor);
|
let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor);
|
||||||
for (i, col_i) in specialized_columns.iter().enumerate() {
|
for (i, col_i) in specialized_columns.iter().enumerate() {
|
||||||
// Compute witnesses for each column.
|
// Compute witnesses for each column.
|
||||||
let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i.as_slice());
|
let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i);
|
||||||
// For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`,
|
// For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`,
|
||||||
// adding enough wildcards to match `arity`.
|
// adding enough wildcards to match `arity`.
|
||||||
for wit in wits_for_col_i {
|
for wit in wits_for_col_i {
|
||||||
|
@ -1032,6 +1072,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let pat_column = arms.iter().flat_map(|arm| arm.pat.flatten_or_pat()).collect::<Vec<_>>();
|
let pat_column = arms.iter().flat_map(|arm| arm.pat.flatten_or_pat()).collect::<Vec<_>>();
|
||||||
|
let pat_column = PatternColumn::new(pat_column);
|
||||||
let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column);
|
let witnesses = collect_nonexhaustive_missing_variants(cx, &pat_column);
|
||||||
|
|
||||||
if !witnesses.is_empty() {
|
if !witnesses.is_empty() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue