Skip to content

Commit 5d712e7

Browse files
committed
Use special enum for non-user-provided wildcards in the matrix
1 parent ba19c2d commit 5d712e7

File tree

3 files changed

+99
-48
lines changed

3 files changed

+99
-48
lines changed

compiler/rustc_pattern_analysis/src/lints.rs

+15-19
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::errors::{
1111
NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap,
1212
OverlappingRangeEndpoints, Uncovered,
1313
};
14+
use crate::pat::PatOrWild;
1415
use crate::rustc::{
1516
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
1617
SplitConstructorSet, WitnessPat,
@@ -33,15 +34,20 @@ pub(crate) struct PatternColumn<'p, 'tcx> {
3334

3435
impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
3536
pub(crate) fn new(arms: &[MatchArm<'p, 'tcx>]) -> Self {
36-
let mut patterns = Vec::with_capacity(arms.len());
37+
let mut column = PatternColumn { patterns: Vec::with_capacity(arms.len()) };
3738
for arm in arms {
38-
if arm.pat.is_or_pat() {
39-
patterns.extend(arm.pat.flatten_or_pat())
40-
} else {
41-
patterns.push(arm.pat)
42-
}
39+
column.expand_and_push(PatOrWild::Pat(arm.pat));
40+
}
41+
column
42+
}
43+
fn expand_and_push(&mut self, pat: PatOrWild<'p, RustcMatchCheckCtxt<'p, 'tcx>>) {
44+
if pat.is_or_pat() {
45+
self.patterns.extend(
46+
pat.flatten_or_pat().into_iter().filter_map(|pat_or_wild| pat_or_wild.as_pat()),
47+
)
48+
} else if let Some(pat) = pat.as_pat() {
49+
self.patterns.push(pat)
4350
}
44-
Self { patterns }
4551
}
4652

4753
fn is_empty(&self) -> bool {
@@ -85,20 +91,10 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
8591
self.patterns.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor()));
8692
for pat in relevant_patterns {
8793
let specialized = pat.specialize(pcx, ctor, pcx.ctor_sub_tys(ctor));
88-
for (subpat, column) in specialized.iter().zip(&mut specialized_columns) {
89-
if subpat.is_or_pat() {
90-
column.patterns.extend(subpat.flatten_or_pat())
91-
} else {
92-
column.patterns.push(subpat)
93-
}
94+
for (subpat, column) in specialized.into_iter().zip(&mut specialized_columns) {
95+
column.expand_and_push(subpat);
9496
}
9597
}
96-
97-
assert!(
98-
!specialized_columns[0].is_empty(),
99-
"ctor {ctor:?} was listed as present but isn't;
100-
there is an inconsistency between `Constructor::is_covered_by` and `ConstructorSet::split`"
101-
);
10298
specialized_columns
10399
}
104100
}

compiler/rustc_pattern_analysis/src/pat.rs

+76-21
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::usefulness::PlaceCtxt;
1010
use crate::{Captures, TypeCx};
1111

1212
use self::Constructor::*;
13+
use self::PatOrWild::*;
1314

1415
/// Values and patterns can be represented as a constructor applied to some fields. This represents
1516
/// a pattern in this form.
@@ -50,14 +51,6 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
5051
pub(crate) fn is_or_pat(&self) -> bool {
5152
matches!(self.ctor, Or)
5253
}
53-
/// Expand this (possibly-nested) or-pattern into its alternatives.
54-
pub(crate) fn flatten_or_pat(&self) -> SmallVec<[&Self; 1]> {
55-
if self.is_or_pat() {
56-
self.iter_fields().flat_map(|p| p.flatten_or_pat()).collect()
57-
} else {
58-
smallvec![self]
59-
}
60-
}
6154

6255
pub fn ctor(&self) -> &Constructor<Cx> {
6356
&self.ctor
@@ -82,14 +75,8 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
8275
pcx: &PlaceCtxt<'_, 'p, Cx>,
8376
other_ctor: &Constructor<Cx>,
8477
ctor_sub_tys: &[Cx::Ty],
85-
) -> SmallVec<[&'p DeconstructedPat<'p, Cx>; 2]> {
86-
let wildcard_sub_tys = || {
87-
ctor_sub_tys
88-
.iter()
89-
.map(|ty| DeconstructedPat::wildcard(*ty))
90-
.map(|pat| pcx.mcx.wildcard_arena.alloc(pat) as &_)
91-
.collect()
92-
};
78+
) -> SmallVec<[PatOrWild<'p, Cx>; 2]> {
79+
let wildcard_sub_tys = || ctor_sub_tys.iter().map(|_| Wild).collect();
9380
match (&self.ctor, other_ctor) {
9481
// Return a wildcard for each field of `other_ctor`.
9582
(Wildcard, _) => wildcard_sub_tys(),
@@ -105,14 +92,14 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
10592
// Fill in the fields from both ends.
10693
let new_arity = fields.len();
10794
for i in 0..prefix {
108-
fields[i] = &self.fields[i];
95+
fields[i] = Pat(&self.fields[i]);
10996
}
11097
for i in 0..suffix {
111-
fields[new_arity - 1 - i] = &self.fields[self.fields.len() - 1 - i];
98+
fields[new_arity - 1 - i] = Pat(&self.fields[self.fields.len() - 1 - i]);
11299
}
113100
fields
114101
}
115-
_ => self.fields.iter().collect(),
102+
_ => self.fields.iter().map(Pat).collect(),
116103
}
117104
}
118105

@@ -153,14 +140,82 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
153140
}
154141
}
155142

156-
/// This is mostly copied from the `Pat` impl. This is best effort and not good enough for a
157-
/// `Display` impl.
143+
/// This is best effort and not good enough for a `Display` impl.
158144
impl<'p, Cx: TypeCx> fmt::Debug for DeconstructedPat<'p, Cx> {
159145
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160146
Cx::debug_pat(f, self)
161147
}
162148
}
163149

150+
/// Represents either a pattern obtained from user input or a wildcard constructed during the
151+
/// algorithm. Do not use `Wild` to represent a wildcard pattern comping from user input.
152+
#[derive(derivative::Derivative)]
153+
#[derivative(Clone(bound = ""), Copy(bound = ""))]
154+
pub(crate) enum PatOrWild<'p, Cx: TypeCx> {
155+
/// A non-user-provided wildcard, created during specialization.
156+
Wild,
157+
/// A user-provided pattern.
158+
Pat(&'p DeconstructedPat<'p, Cx>),
159+
}
160+
161+
impl<'p, Cx: TypeCx> PatOrWild<'p, Cx> {
162+
pub(crate) fn as_pat(&self) -> Option<&'p DeconstructedPat<'p, Cx>> {
163+
match self {
164+
Wild => None,
165+
Pat(pat) => Some(pat),
166+
}
167+
}
168+
pub(crate) fn ctor(self) -> &'p Constructor<Cx> {
169+
match self {
170+
Wild => &Wildcard,
171+
Pat(pat) => pat.ctor(),
172+
}
173+
}
174+
175+
pub(crate) fn is_or_pat(&self) -> bool {
176+
matches!(self.ctor(), Or)
177+
}
178+
179+
/// Expand this (possibly-nested) or-pattern into its alternatives.
180+
pub(crate) fn flatten_or_pat(self) -> SmallVec<[Self; 1]> {
181+
match self {
182+
Pat(pat) if pat.is_or_pat() => {
183+
pat.iter_fields().flat_map(|p| Pat(p).flatten_or_pat()).collect()
184+
}
185+
_ => smallvec![self],
186+
}
187+
}
188+
189+
/// Specialize this pattern with a constructor.
190+
/// `other_ctor` can be different from `self.ctor`, but must be covered by it.
191+
pub(crate) fn specialize(
192+
&self,
193+
pcx: &PlaceCtxt<'_, 'p, Cx>,
194+
other_ctor: &Constructor<Cx>,
195+
ctor_sub_tys: &[Cx::Ty],
196+
) -> SmallVec<[PatOrWild<'p, Cx>; 2]> {
197+
match self {
198+
Wild => ctor_sub_tys.iter().map(|_| Wild).collect(),
199+
Pat(pat) => pat.specialize(pcx, other_ctor, ctor_sub_tys),
200+
}
201+
}
202+
203+
pub(crate) fn set_useful(&self) {
204+
if let Pat(pat) = self {
205+
pat.set_useful()
206+
}
207+
}
208+
}
209+
210+
impl<'p, Cx: TypeCx> fmt::Debug for PatOrWild<'p, Cx> {
211+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212+
match self {
213+
Wild => write!(f, "_"),
214+
Pat(pat) => pat.fmt(f),
215+
}
216+
}
217+
}
218+
164219
/// Same idea as `DeconstructedPat`, except this is a fictitious pattern built up for diagnostics
165220
/// purposes. As such they don't use interning and can be cloned.
166221
#[derive(derivative::Derivative)]

compiler/rustc_pattern_analysis/src/usefulness.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ use smallvec::{smallvec, SmallVec};
716716
use std::fmt;
717717

718718
use crate::constructor::{Constructor, ConstructorSet};
719-
use crate::pat::{DeconstructedPat, WitnessPat};
719+
use crate::pat::{DeconstructedPat, PatOrWild, WitnessPat};
720720
use crate::{Captures, MatchArm, MatchCtxt, TypeCx, TypedArena};
721721

722722
use self::ValidityConstraint::*;
@@ -827,7 +827,7 @@ impl fmt::Display for ValidityConstraint {
827827
#[derivative(Clone(bound = ""))]
828828
struct PatStack<'p, Cx: TypeCx> {
829829
// Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well.
830-
pats: SmallVec<[&'p DeconstructedPat<'p, Cx>; 2]>,
830+
pats: SmallVec<[PatOrWild<'p, Cx>; 2]>,
831831
/// Sometimes we know that as far as this row is concerned, the current case is already handled
832832
/// by a different, more general, case. When the case is irrelevant for all rows this allows us
833833
/// to skip a case entirely. This is purely an optimization. See at the top for details.
@@ -836,7 +836,7 @@ struct PatStack<'p, Cx: TypeCx> {
836836

837837
impl<'p, Cx: TypeCx> PatStack<'p, Cx> {
838838
fn from_pattern(pat: &'p DeconstructedPat<'p, Cx>) -> Self {
839-
PatStack { pats: smallvec![pat], relevant: true }
839+
PatStack { pats: smallvec![PatOrWild::Pat(pat)], relevant: true }
840840
}
841841

842842
fn is_empty(&self) -> bool {
@@ -847,11 +847,11 @@ impl<'p, Cx: TypeCx> PatStack<'p, Cx> {
847847
self.pats.len()
848848
}
849849

850-
fn head(&self) -> &'p DeconstructedPat<'p, Cx> {
850+
fn head(&self) -> PatOrWild<'p, Cx> {
851851
self.pats[0]
852852
}
853853

854-
fn iter(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, Cx>> + Captures<'_> {
854+
fn iter(&self) -> impl Iterator<Item = PatOrWild<'p, Cx>> + Captures<'_> {
855855
self.pats.iter().copied()
856856
}
857857

@@ -923,11 +923,11 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
923923
self.pats.len()
924924
}
925925

926-
fn head(&self) -> &'p DeconstructedPat<'p, Cx> {
926+
fn head(&self) -> PatOrWild<'p, Cx> {
927927
self.pats.head()
928928
}
929929

930-
fn iter(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, Cx>> + Captures<'_> {
930+
fn iter(&self) -> impl Iterator<Item = PatOrWild<'p, Cx>> + Captures<'_> {
931931
self.pats.iter()
932932
}
933933

@@ -1052,7 +1052,7 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
10521052
}
10531053

10541054
/// Iterate over the first pattern of each row.
1055-
fn heads(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, Cx>> + Clone + Captures<'_> {
1055+
fn heads(&self) -> impl Iterator<Item = PatOrWild<'p, Cx>> + Clone + Captures<'_> {
10561056
self.rows().map(|r| r.head())
10571057
}
10581058

0 commit comments

Comments
 (0)