Skip to content

Commit 6ae9fa3

Browse files
committed
Store field indices in DeconstructedPat to avoid virtual wildcards
1 parent c1e6886 commit 6ae9fa3

File tree

4 files changed

+99
-81
lines changed

4 files changed

+99
-81
lines changed

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,9 @@ fn report_arm_reachability<'p, 'tcx>(
917917
fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
918918
match pat.ctor() {
919919
Constructor::Wildcard => true,
920-
Constructor::Struct | Constructor::Ref => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
920+
Constructor::Struct | Constructor::Ref => {
921+
pat.iter_fields().all(|ipat| pat_is_catchall(&ipat.pat))
922+
}
921923
_ => false,
922924
}
923925
}

compiler/rustc_pattern_analysis/src/pat.rs

+60-50
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@ impl PatId {
2020
}
2121
}
2222

23+
/// A pattern with an index denoting which field it corresponds to.
24+
pub struct IndexedPat<Cx: TypeCx> {
25+
pub idx: usize,
26+
pub pat: DeconstructedPat<Cx>,
27+
}
28+
2329
/// Values and patterns can be represented as a constructor applied to some fields. This represents
2430
/// a pattern in this form. A `DeconstructedPat` will almost always come from user input; the only
2531
/// exception are some `Wildcard`s introduced during pattern lowering.
2632
pub struct DeconstructedPat<Cx: TypeCx> {
2733
ctor: Constructor<Cx>,
28-
fields: Vec<DeconstructedPat<Cx>>,
34+
fields: Vec<IndexedPat<Cx>>,
2935
/// The number of fields in this pattern. E.g. if the pattern is `SomeStruct { field12: true, ..
3036
/// }` this would be the total number of fields of the struct.
3137
/// This is also the same as `self.ctor.arity(self.ty)`.
@@ -39,27 +45,20 @@ pub struct DeconstructedPat<Cx: TypeCx> {
3945
}
4046

4147
impl<Cx: TypeCx> DeconstructedPat<Cx> {
42-
pub fn wildcard(ty: Cx::Ty) -> Self {
43-
DeconstructedPat {
44-
ctor: Wildcard,
45-
fields: Vec::new(),
46-
arity: 0,
47-
ty,
48-
data: None,
49-
uid: PatId::new(),
50-
}
51-
}
52-
5348
pub fn new(
5449
ctor: Constructor<Cx>,
55-
fields: Vec<DeconstructedPat<Cx>>,
50+
fields: Vec<IndexedPat<Cx>>,
5651
arity: usize,
5752
ty: Cx::Ty,
5853
data: Cx::PatData,
5954
) -> Self {
6055
DeconstructedPat { ctor, fields, arity, ty, data: Some(data), uid: PatId::new() }
6156
}
6257

58+
pub fn at_index(self, idx: usize) -> IndexedPat<Cx> {
59+
IndexedPat { idx, pat: self }
60+
}
61+
6362
pub(crate) fn is_or_pat(&self) -> bool {
6463
matches!(self.ctor, Or)
6564
}
@@ -75,8 +74,11 @@ impl<Cx: TypeCx> DeconstructedPat<Cx> {
7574
pub fn data(&self) -> Option<&Cx::PatData> {
7675
self.data.as_ref()
7776
}
77+
pub fn arity(&self) -> usize {
78+
self.arity
79+
}
7880

79-
pub fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'a DeconstructedPat<Cx>> {
81+
pub fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'a IndexedPat<Cx>> {
8082
self.fields.iter()
8183
}
8284

@@ -85,36 +87,40 @@ impl<Cx: TypeCx> DeconstructedPat<Cx> {
8587
pub(crate) fn specialize<'a>(
8688
&'a self,
8789
other_ctor: &Constructor<Cx>,
88-
ctor_arity: usize,
90+
other_ctor_arity: usize,
8991
) -> SmallVec<[PatOrWild<'a, Cx>; 2]> {
90-
let wildcard_sub_tys = || (0..ctor_arity).map(|_| PatOrWild::Wild).collect();
91-
match (&self.ctor, other_ctor) {
92-
// Return a wildcard for each field of `other_ctor`.
93-
(Wildcard, _) => wildcard_sub_tys(),
92+
if matches!(other_ctor, PrivateUninhabited) {
9493
// Skip this column.
95-
(_, PrivateUninhabited) => smallvec![],
96-
// The only non-trivial case: two slices of different arity. `other_slice` is
97-
// guaranteed to have a larger arity, so we fill the middle part with enough
98-
// wildcards to reach the length of the new, larger slice.
99-
(
100-
&Slice(self_slice @ Slice { kind: SliceKind::VarLen(prefix, suffix), .. }),
101-
&Slice(other_slice),
102-
) if self_slice.arity() != other_slice.arity() => {
103-
// Start with a slice of wildcards of the appropriate length.
104-
let mut fields: SmallVec<[_; 2]> = wildcard_sub_tys();
105-
// Fill in the fields from both ends.
106-
let new_arity = fields.len();
107-
for i in 0..prefix {
108-
fields[i] = PatOrWild::Pat(&self.fields[i]);
94+
return smallvec![];
95+
}
96+
97+
// Start with a slice of wildcards of the appropriate length.
98+
let mut fields: SmallVec<[_; 2]> = (0..other_ctor_arity).map(|_| PatOrWild::Wild).collect();
99+
// Fill `fields` with our fields. The arities are known to be compatible.
100+
match self.ctor {
101+
// The only non-trivial case: two slices of different arity. `other_ctor` is guaranteed
102+
// to have a larger arity, so we adjust the indices of the patterns in the suffix so
103+
// that they are correctly positioned in the larger slice.
104+
Slice(Slice { kind: SliceKind::VarLen(prefix, _), .. })
105+
if self.arity != other_ctor_arity =>
106+
{
107+
for ipat in &self.fields {
108+
let new_idx = if ipat.idx < prefix {
109+
ipat.idx
110+
} else {
111+
// Adjust the indices in the suffix.
112+
ipat.idx + other_ctor_arity - self.arity
113+
};
114+
fields[new_idx] = PatOrWild::Pat(&ipat.pat);
109115
}
110-
for i in 0..suffix {
111-
fields[new_arity - 1 - i] =
112-
PatOrWild::Pat(&self.fields[self.fields.len() - 1 - i]);
116+
}
117+
_ => {
118+
for ipat in &self.fields {
119+
fields[ipat.idx] = PatOrWild::Pat(&ipat.pat);
113120
}
114-
fields
115121
}
116-
_ => self.fields.iter().map(PatOrWild::Pat).collect(),
117122
}
123+
fields
118124
}
119125

120126
/// Walk top-down and call `it` in each place where a pattern occurs
@@ -126,7 +132,7 @@ impl<Cx: TypeCx> DeconstructedPat<Cx> {
126132
}
127133

128134
for p in self.iter_fields() {
129-
p.walk(it)
135+
p.pat.walk(it)
130136
}
131137
}
132138
}
@@ -146,14 +152,19 @@ impl<Cx: TypeCx> fmt::Debug for DeconstructedPat<Cx> {
146152
};
147153
let mut start_or_comma = || start_or_continue(", ");
148154

155+
let mut fields: Vec<_> = (0..self.arity).map(|_| PatOrWild::Wild).collect();
156+
for ipat in self.iter_fields() {
157+
fields[ipat.idx] = PatOrWild::Pat(&ipat.pat);
158+
}
159+
149160
match pat.ctor() {
150161
Struct | Variant(_) | UnionField => {
151162
Cx::write_variant_name(f, pat)?;
152163
// Without `cx`, we can't know which field corresponds to which, so we can't
153164
// get the names of the fields. Instead we just display everything as a tuple
154165
// struct, which should be good enough.
155166
write!(f, "(")?;
156-
for p in pat.iter_fields() {
167+
for p in fields {
157168
write!(f, "{}", start_or_comma())?;
158169
write!(f, "{p:?}")?;
159170
}
@@ -163,25 +174,23 @@ impl<Cx: TypeCx> fmt::Debug for DeconstructedPat<Cx> {
163174
// be careful to detect strings here. However a string literal pattern will never
164175
// be reported as a non-exhaustiveness witness, so we can ignore this issue.
165176
Ref => {
166-
let subpattern = pat.iter_fields().next().unwrap();
167-
write!(f, "&{:?}", subpattern)
177+
write!(f, "&{:?}", &fields[0])
168178
}
169179
Slice(slice) => {
170-
let mut subpatterns = pat.iter_fields();
171180
write!(f, "[")?;
172181
match slice.kind {
173182
SliceKind::FixedLen(_) => {
174-
for p in subpatterns {
183+
for p in fields {
175184
write!(f, "{}{:?}", start_or_comma(), p)?;
176185
}
177186
}
178187
SliceKind::VarLen(prefix_len, _) => {
179-
for p in subpatterns.by_ref().take(prefix_len) {
188+
for p in &fields[..prefix_len] {
180189
write!(f, "{}{:?}", start_or_comma(), p)?;
181190
}
182191
write!(f, "{}", start_or_comma())?;
183192
write!(f, "..")?;
184-
for p in subpatterns {
193+
for p in &fields[prefix_len..] {
185194
write!(f, "{}{:?}", start_or_comma(), p)?;
186195
}
187196
}
@@ -196,7 +205,7 @@ impl<Cx: TypeCx> fmt::Debug for DeconstructedPat<Cx> {
196205
Str(value) => write!(f, "{value:?}"),
197206
Opaque(..) => write!(f, "<constant pattern>"),
198207
Or => {
199-
for pat in pat.iter_fields() {
208+
for pat in fields {
200209
write!(f, "{}{:?}", start_or_continue(" | "), pat)?;
201210
}
202211
Ok(())
@@ -254,9 +263,10 @@ impl<'p, Cx: TypeCx> PatOrWild<'p, Cx> {
254263
/// Expand this (possibly-nested) or-pattern into its alternatives.
255264
pub(crate) fn flatten_or_pat(self) -> SmallVec<[Self; 1]> {
256265
match self {
257-
PatOrWild::Pat(pat) if pat.is_or_pat() => {
258-
pat.iter_fields().flat_map(|p| PatOrWild::Pat(p).flatten_or_pat()).collect()
259-
}
266+
PatOrWild::Pat(pat) if pat.is_or_pat() => pat
267+
.iter_fields()
268+
.flat_map(|ipat| PatOrWild::Pat(&ipat.pat).flatten_or_pat())
269+
.collect(),
260270
_ => smallvec![self],
261271
}
262272
}

compiler/rustc_pattern_analysis/src/rustc.rs

+26-23
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
446446
let ty = cx.reveal_opaque_ty(pat.ty);
447447
let ctor;
448448
let arity;
449-
let mut fields: Vec<_>;
449+
let fields: Vec<_>;
450450
match &pat.kind {
451451
PatKind::AscribeUserType { subpattern, .. }
452452
| PatKind::InlineConstant { subpattern, .. } => return self.lower_pat(subpattern),
@@ -457,7 +457,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
457457
arity = 0;
458458
}
459459
PatKind::Deref { subpattern } => {
460-
fields = vec![self.lower_pat(subpattern)];
460+
fields = vec![self.lower_pat(subpattern).at_index(0)];
461461
arity = 1;
462462
ctor = match ty.kind() {
463463
// This is a box pattern.
@@ -471,16 +471,12 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
471471
ty::Tuple(fs) => {
472472
ctor = Struct;
473473
arity = fs.len();
474-
fields = fs
474+
fields = subpatterns
475475
.iter()
476-
.map(|ty| cx.reveal_opaque_ty(ty))
477-
.map(|ty| DeconstructedPat::wildcard(ty))
476+
.map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
478477
.collect();
479-
for pat in subpatterns {
480-
fields[pat.field.index()] = self.lower_pat(&pat.pattern);
481-
}
482478
}
483-
ty::Adt(adt, args) if adt.is_box() => {
479+
ty::Adt(adt, _) if adt.is_box() => {
484480
// The only legal patterns of type `Box` (outside `std`) are `_` and box
485481
// patterns. If we're here we can assume this is a box pattern.
486482
// FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
@@ -494,13 +490,12 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
494490
// solution when we introduce generalized deref patterns. Also need to
495491
// prevent mixing of those two options.
496492
let pattern = subpatterns.into_iter().find(|pat| pat.field.index() == 0);
497-
let pat = if let Some(pat) = pattern {
498-
self.lower_pat(&pat.pattern)
493+
if let Some(pat) = pattern {
494+
fields = vec![self.lower_pat(&pat.pattern).at_index(0)];
499495
} else {
500-
DeconstructedPat::wildcard(self.reveal_opaque_ty(args.type_at(0)))
501-
};
496+
fields = vec![];
497+
}
502498
ctor = Struct;
503-
fields = vec![pat];
504499
arity = 1;
505500
}
506501
ty::Adt(adt, _) => {
@@ -513,13 +508,10 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
513508
let variant =
514509
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
515510
arity = variant.fields.len();
516-
fields = cx
517-
.variant_sub_tys(ty, variant)
518-
.map(|(_, ty)| DeconstructedPat::wildcard(ty))
511+
fields = subpatterns
512+
.iter()
513+
.map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
519514
.collect();
520-
for pat in subpatterns {
521-
fields[pat.field.index()] = self.lower_pat(&pat.pattern);
522-
}
523515
}
524516
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty),
525517
}
@@ -586,7 +578,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
586578
let ty = self.reveal_opaque_ty(*t);
587579
let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), 0, ty, pat);
588580
ctor = Ref;
589-
fields = vec![subpattern];
581+
fields = vec![subpattern.at_index(0)];
590582
arity = 1;
591583
}
592584
// All constants that can be structurally matched have already been expanded
@@ -651,13 +643,24 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
651643
SliceKind::FixedLen(prefix.len() + suffix.len())
652644
};
653645
ctor = Slice(Slice::new(array_len, kind));
654-
fields = prefix.iter().chain(suffix.iter()).map(|p| self.lower_pat(&*p)).collect();
646+
fields = prefix
647+
.iter()
648+
.chain(suffix.iter())
649+
.map(|p| self.lower_pat(&*p))
650+
.enumerate()
651+
.map(|(i, p)| p.at_index(i))
652+
.collect();
655653
arity = kind.arity();
656654
}
657655
PatKind::Or { .. } => {
658656
ctor = Or;
659657
let pats = expand_or_pat(pat);
660-
fields = pats.into_iter().map(|p| self.lower_pat(p)).collect();
658+
fields = pats
659+
.into_iter()
660+
.map(|p| self.lower_pat(p))
661+
.enumerate()
662+
.map(|(i, p)| p.at_index(i))
663+
.collect();
661664
arity = fields.len();
662665
}
663666
PatKind::Never => {

compiler/rustc_pattern_analysis/src/usefulness.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -1006,15 +1006,17 @@ impl<'p, Cx: TypeCx> PatStack<'p, Cx> {
10061006
ctor_arity: usize,
10071007
ctor_is_relevant: bool,
10081008
) -> Result<PatStack<'p, Cx>, Cx::Error> {
1009-
// We pop the head pattern and push the new fields extracted from the arguments of
1010-
// `self.head()`.
1011-
let mut new_pats = self.head().specialize(ctor, ctor_arity);
1012-
if new_pats.len() != ctor_arity {
1009+
let head_pat = self.head();
1010+
if head_pat.as_pat().is_some_and(|pat| pat.arity() > ctor_arity) {
1011+
// Arity can be smaller in case of variable-length slices, but mustn't be larger.
10131012
return Err(cx.bug(format_args!(
1014-
"uncaught type error: pattern {:?} has inconsistent arity (expected arity {ctor_arity})",
1015-
self.head().as_pat().unwrap()
1013+
"uncaught type error: pattern {:?} has inconsistent arity (expected arity <= {ctor_arity})",
1014+
head_pat.as_pat().unwrap()
10161015
)));
10171016
}
1017+
// We pop the head pattern and push the new fields extracted from the arguments of
1018+
// `self.head()`.
1019+
let mut new_pats = head_pat.specialize(ctor, ctor_arity);
10181020
new_pats.extend_from_slice(&self.pats[1..]);
10191021
// `ctor` is relevant for this row if it is the actual constructor of this row, or if the
10201022
// row has a wildcard and `ctor` is relevant for wildcards.
@@ -1706,7 +1708,8 @@ fn collect_pattern_usefulness<'p, Cx: TypeCx>(
17061708
) -> bool {
17071709
if useful_subpatterns.contains(&pat.uid) {
17081710
true
1709-
} else if pat.is_or_pat() && pat.iter_fields().any(|f| pat_is_useful(useful_subpatterns, f))
1711+
} else if pat.is_or_pat()
1712+
&& pat.iter_fields().any(|f| pat_is_useful(useful_subpatterns, &f.pat))
17101713
{
17111714
// We always expand or patterns in the matrix, so we will never see the actual
17121715
// or-pattern (the one with constructor `Or`) in the column. As such, it will not be

0 commit comments

Comments
 (0)