Make PatternColumn
part of the public API
This commit is contained in:
parent
83e88c6dfc
commit
6ef836246b
@ -948,10 +948,10 @@ pub enum ConstructorSet<Cx: TypeCx> {
|
|||||||
/// of the `ConstructorSet` for the type, yet if we forgot to include them in `present` we would be
|
/// of the `ConstructorSet` for the type, yet if we forgot to include them in `present` we would be
|
||||||
/// ignoring any row with `Opaque`s in the algorithm. Hence the importance of point 4.
|
/// ignoring any row with `Opaque`s in the algorithm. Hence the importance of point 4.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct SplitConstructorSet<Cx: TypeCx> {
|
pub struct SplitConstructorSet<Cx: TypeCx> {
|
||||||
pub(crate) present: SmallVec<[Constructor<Cx>; 1]>,
|
pub present: SmallVec<[Constructor<Cx>; 1]>,
|
||||||
pub(crate) missing: Vec<Constructor<Cx>>,
|
pub missing: Vec<Constructor<Cx>>,
|
||||||
pub(crate) missing_empty: Vec<Constructor<Cx>>,
|
pub missing_empty: Vec<Constructor<Cx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Cx: TypeCx> ConstructorSet<Cx> {
|
impl<Cx: TypeCx> ConstructorSet<Cx> {
|
||||||
@ -960,7 +960,7 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
|
|||||||
/// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation
|
/// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation
|
||||||
/// and its invariants.
|
/// and its invariants.
|
||||||
#[instrument(level = "debug", skip(self, ctors), ret)]
|
#[instrument(level = "debug", skip(self, ctors), ret)]
|
||||||
pub(crate) fn split<'a>(
|
pub fn split<'a>(
|
||||||
&self,
|
&self,
|
||||||
ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone,
|
ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone,
|
||||||
) -> SplitConstructorSet<Cx>
|
) -> SplitConstructorSet<Cx>
|
||||||
|
@ -6,6 +6,7 @@ pub mod errors;
|
|||||||
#[cfg(feature = "rustc")]
|
#[cfg(feature = "rustc")]
|
||||||
pub(crate) mod lints;
|
pub(crate) mod lints;
|
||||||
pub mod pat;
|
pub mod pat;
|
||||||
|
pub mod pat_column;
|
||||||
#[cfg(feature = "rustc")]
|
#[cfg(feature = "rustc")]
|
||||||
pub mod rustc;
|
pub mod rustc;
|
||||||
pub mod usefulness;
|
pub mod usefulness;
|
||||||
@ -67,8 +68,9 @@ use rustc_span::ErrorGuaranteed;
|
|||||||
|
|
||||||
use crate::constructor::{Constructor, ConstructorSet, IntRange};
|
use crate::constructor::{Constructor, ConstructorSet, IntRange};
|
||||||
#[cfg(feature = "rustc")]
|
#[cfg(feature = "rustc")]
|
||||||
use crate::lints::{lint_nonexhaustive_missing_variants, PatternColumn};
|
use crate::lints::lint_nonexhaustive_missing_variants;
|
||||||
use crate::pat::DeconstructedPat;
|
use crate::pat::DeconstructedPat;
|
||||||
|
use crate::pat_column::PatternColumn;
|
||||||
#[cfg(feature = "rustc")]
|
#[cfg(feature = "rustc")]
|
||||||
use crate::rustc::RustcMatchCheckCtxt;
|
use crate::rustc::RustcMatchCheckCtxt;
|
||||||
#[cfg(feature = "rustc")]
|
#[cfg(feature = "rustc")]
|
||||||
|
@ -1,92 +1,11 @@
|
|||||||
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||||
use rustc_span::ErrorGuaranteed;
|
use rustc_span::ErrorGuaranteed;
|
||||||
|
|
||||||
use crate::constructor::{Constructor, SplitConstructorSet};
|
use crate::constructor::Constructor;
|
||||||
use crate::errors::{NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered};
|
use crate::errors::{NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered};
|
||||||
use crate::pat::{DeconstructedPat, PatOrWild};
|
use crate::pat_column::PatternColumn;
|
||||||
use crate::rustc::{RevealedTy, RustcMatchCheckCtxt, WitnessPat};
|
use crate::rustc::{RevealedTy, RustcMatchCheckCtxt, WitnessPat};
|
||||||
use crate::{MatchArm, TypeCx};
|
use crate::MatchArm;
|
||||||
|
|
||||||
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
|
|
||||||
/// inspect the same subvalue/place".
|
|
||||||
/// This is used to traverse patterns column-by-column for lints. Despite similarities with the
|
|
||||||
/// algorithm in [`crate::usefulness`], this does a different traversal. Notably this is linear in
|
|
||||||
/// the depth of patterns, whereas `compute_exhaustiveness_and_usefulness` is worst-case exponential
|
|
||||||
/// (exhaustiveness is NP-complete). The core difference is that we treat sub-columns separately.
|
|
||||||
///
|
|
||||||
/// This must not contain an or-pattern. `expand_and_push` takes care to expand them.
|
|
||||||
///
|
|
||||||
/// This is not used in the usefulness algorithm; only in lints.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct PatternColumn<'p, Cx: TypeCx> {
|
|
||||||
patterns: Vec<&'p DeconstructedPat<'p, Cx>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'p, Cx: TypeCx> PatternColumn<'p, Cx> {
|
|
||||||
pub(crate) fn new(arms: &[MatchArm<'p, Cx>]) -> Self {
|
|
||||||
let patterns = Vec::with_capacity(arms.len());
|
|
||||||
let mut column = PatternColumn { patterns };
|
|
||||||
for arm in arms {
|
|
||||||
column.expand_and_push(PatOrWild::Pat(arm.pat));
|
|
||||||
}
|
|
||||||
column
|
|
||||||
}
|
|
||||||
/// Pushes a pattern onto the column, expanding any or-patterns into its subpatterns.
|
|
||||||
/// Internal method, prefer [`PatternColumn::new`].
|
|
||||||
fn expand_and_push(&mut self, pat: PatOrWild<'p, Cx>) {
|
|
||||||
// We flatten or-patterns and skip algorithm-generated wildcards.
|
|
||||||
if pat.is_or_pat() {
|
|
||||||
self.patterns.extend(
|
|
||||||
pat.flatten_or_pat().into_iter().filter_map(|pat_or_wild| pat_or_wild.as_pat()),
|
|
||||||
)
|
|
||||||
} else if let Some(pat) = pat.as_pat() {
|
|
||||||
self.patterns.push(pat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn head_ty(&self) -> Option<&Cx::Ty> {
|
|
||||||
self.patterns.first().map(|pat| pat.ty())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Do constructor splitting on the constructors of the column.
|
|
||||||
fn analyze_ctors(&self, cx: &Cx, ty: &Cx::Ty) -> Result<SplitConstructorSet<Cx>, Cx::Error> {
|
|
||||||
let column_ctors = self.patterns.iter().map(|p| p.ctor());
|
|
||||||
let ctors_for_ty = cx.ctors_for_ty(ty)?;
|
|
||||||
Ok(ctors_for_ty.split(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. They usually 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,
|
|
||||||
cx: &Cx,
|
|
||||||
ty: &Cx::Ty,
|
|
||||||
ctor: &Constructor<Cx>,
|
|
||||||
) -> Vec<PatternColumn<'p, Cx>> {
|
|
||||||
let arity = ctor.arity(cx, ty);
|
|
||||||
if arity == 0 {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(cx, pat.ctor()));
|
|
||||||
for pat in relevant_patterns {
|
|
||||||
let specialized = pat.specialize(ctor, arity);
|
|
||||||
for (subpat, column) in specialized.into_iter().zip(&mut specialized_columns) {
|
|
||||||
column.expand_and_push(subpat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
specialized_columns
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
|
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
|
||||||
/// in a given column.
|
/// in a given column.
|
||||||
|
90
compiler/rustc_pattern_analysis/src/pat_column.rs
Normal file
90
compiler/rustc_pattern_analysis/src/pat_column.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
use crate::constructor::{Constructor, SplitConstructorSet};
|
||||||
|
use crate::pat::{DeconstructedPat, PatOrWild};
|
||||||
|
use crate::{Captures, MatchArm, TypeCx};
|
||||||
|
|
||||||
|
/// A column of patterns in a match, where a column is the intuitive notion of "subpatterns that
|
||||||
|
/// inspect the same subvalue/place".
|
||||||
|
/// This is used to traverse patterns column-by-column for lints. Despite similarities with the
|
||||||
|
/// algorithm in [`crate::usefulness`], this does a different traversal. Notably this is linear in
|
||||||
|
/// the depth of patterns, whereas `compute_exhaustiveness_and_usefulness` is worst-case exponential
|
||||||
|
/// (exhaustiveness is NP-complete). The core difference is that we treat sub-columns separately.
|
||||||
|
///
|
||||||
|
/// This is not used in the usefulness algorithm; only in lints.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PatternColumn<'p, Cx: TypeCx> {
|
||||||
|
/// This must not contain an or-pattern. `expand_and_push` takes care to expand them.
|
||||||
|
patterns: Vec<&'p DeconstructedPat<'p, Cx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'p, Cx: TypeCx> PatternColumn<'p, Cx> {
|
||||||
|
pub fn new(arms: &[MatchArm<'p, Cx>]) -> Self {
|
||||||
|
let patterns = Vec::with_capacity(arms.len());
|
||||||
|
let mut column = PatternColumn { patterns };
|
||||||
|
for arm in arms {
|
||||||
|
column.expand_and_push(PatOrWild::Pat(arm.pat));
|
||||||
|
}
|
||||||
|
column
|
||||||
|
}
|
||||||
|
/// Pushes a pattern onto the column, expanding any or-patterns into its subpatterns.
|
||||||
|
/// Internal method, prefer [`PatternColumn::new`].
|
||||||
|
fn expand_and_push(&mut self, pat: PatOrWild<'p, Cx>) {
|
||||||
|
// We flatten or-patterns and skip algorithm-generated wildcards.
|
||||||
|
if pat.is_or_pat() {
|
||||||
|
self.patterns.extend(
|
||||||
|
pat.flatten_or_pat().into_iter().filter_map(|pat_or_wild| pat_or_wild.as_pat()),
|
||||||
|
)
|
||||||
|
} else if let Some(pat) = pat.as_pat() {
|
||||||
|
self.patterns.push(pat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn head_ty(&self) -> Option<&Cx::Ty> {
|
||||||
|
self.patterns.first().map(|pat| pat.ty())
|
||||||
|
}
|
||||||
|
pub fn iter<'a>(&'a self) -> impl Iterator<Item = &'p DeconstructedPat<'p, Cx>> + Captures<'a> {
|
||||||
|
self.patterns.iter().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do constructor splitting on the constructors of the column.
|
||||||
|
pub fn analyze_ctors(
|
||||||
|
&self,
|
||||||
|
cx: &Cx,
|
||||||
|
ty: &Cx::Ty,
|
||||||
|
) -> Result<SplitConstructorSet<Cx>, Cx::Error> {
|
||||||
|
let column_ctors = self.patterns.iter().map(|p| p.ctor());
|
||||||
|
let ctors_for_ty = cx.ctors_for_ty(ty)?;
|
||||||
|
Ok(ctors_for_ty.split(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. They usually 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.
|
||||||
|
pub fn specialize(
|
||||||
|
&self,
|
||||||
|
cx: &Cx,
|
||||||
|
ty: &Cx::Ty,
|
||||||
|
ctor: &Constructor<Cx>,
|
||||||
|
) -> Vec<PatternColumn<'p, Cx>> {
|
||||||
|
let arity = ctor.arity(cx, ty);
|
||||||
|
if arity == 0 {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(cx, pat.ctor()));
|
||||||
|
for pat in relevant_patterns {
|
||||||
|
let specialized = pat.specialize(ctor, arity);
|
||||||
|
for (subpat, column) in specialized.into_iter().zip(&mut specialized_columns) {
|
||||||
|
column.expand_and_push(subpat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
specialized_columns
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user