rustc_pattern_analysis/pat_column.rs
1use crate::constructor::{Constructor, SplitConstructorSet};
2use crate::pat::{DeconstructedPat, PatOrWild};
3use crate::{MatchArm, PatCx};
4
5/// A column of patterns in a match, where a column is the intuitive notion of "subpatterns that
6/// inspect the same subvalue/place".
7/// This is used to traverse patterns column-by-column for lints. Despite similarities with the
8/// algorithm in [`crate::usefulness`], this does a different traversal. Notably this is linear in
9/// the depth of patterns, whereas `compute_exhaustiveness_and_usefulness` is worst-case exponential
10/// (exhaustiveness is NP-complete). The core difference is that we treat sub-columns separately.
11///
12/// This is not used in the usefulness algorithm; only in lints.
13#[derive(Debug)]
14pub struct PatternColumn<'p, Cx: PatCx> {
15 /// This must not contain an or-pattern. `expand_and_push` takes care to expand them.
16 patterns: Vec<&'p DeconstructedPat<Cx>>,
17}
18
19impl<'p, Cx: PatCx> PatternColumn<'p, Cx> {
20 pub fn new(arms: &[MatchArm<'p, Cx>]) -> Self {
21 let patterns = Vec::with_capacity(arms.len());
22 let mut column = PatternColumn { patterns };
23 for arm in arms {
24 column.expand_and_push(PatOrWild::Pat(arm.pat));
25 }
26 column
27 }
28 /// Pushes a pattern onto the column, expanding any or-patterns into its subpatterns.
29 /// Internal method, prefer [`PatternColumn::new`].
30 fn expand_and_push(&mut self, pat: PatOrWild<'p, Cx>) {
31 // We flatten or-patterns and skip algorithm-generated wildcards.
32 if pat.is_or_pat() {
33 self.patterns.extend(
34 pat.flatten_or_pat().into_iter().filter_map(|pat_or_wild| pat_or_wild.as_pat()),
35 )
36 } else if let Some(pat) = pat.as_pat() {
37 self.patterns.push(pat)
38 }
39 }
40
41 pub fn head_ty(&self) -> Option<&Cx::Ty> {
42 self.patterns.first().map(|pat| pat.ty())
43 }
44 pub fn iter(&self) -> impl Iterator<Item = &'p DeconstructedPat<Cx>> {
45 self.patterns.iter().copied()
46 }
47
48 /// Do constructor splitting on the constructors of the column.
49 pub fn analyze_ctors(
50 &self,
51 cx: &Cx,
52 ty: &Cx::Ty,
53 ) -> Result<SplitConstructorSet<Cx>, Cx::Error> {
54 let column_ctors = self.patterns.iter().map(|p| p.ctor());
55 let ctors_for_ty = cx.ctors_for_ty(ty)?;
56 Ok(ctors_for_ty.split(column_ctors))
57 }
58
59 /// Does specialization: given a constructor, this takes the patterns from the column that match
60 /// the constructor, and outputs their fields.
61 /// This returns one column per field of the constructor. They usually all have the same length
62 /// (the number of patterns in `self` that matched `ctor`), except that we expand or-patterns
63 /// which may change the lengths.
64 pub fn specialize(
65 &self,
66 cx: &Cx,
67 ty: &Cx::Ty,
68 ctor: &Constructor<Cx>,
69 ) -> Vec<PatternColumn<'p, Cx>> {
70 let arity = ctor.arity(cx, ty);
71 if arity == 0 {
72 return Vec::new();
73 }
74
75 // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
76 // columns may have different lengths in the presence of or-patterns (this is why we can't
77 // reuse `Matrix`).
78 let mut specialized_columns: Vec<_> =
79 (0..arity).map(|_| Self { patterns: Vec::new() }).collect();
80 let relevant_patterns =
81 self.patterns.iter().filter(|pat| ctor.is_covered_by(cx, pat.ctor()).unwrap_or(false));
82 for pat in relevant_patterns {
83 let specialized = pat.specialize(ctor, arity);
84 for (subpat, column) in specialized.into_iter().zip(&mut specialized_columns) {
85 column.expand_and_push(subpat);
86 }
87 }
88 specialized_columns
89 }
90}