Skip to content

Commit d93c1b3

Browse files
committed
Introduce new FixedLenSlice constructor
It is used in the case where a variable-length slice pattern is used to match on an array of known size. This allows considering only those entries in the array that are captured by one of the patterns. As a side-effect, diagnostics improve a bit for those cases.
1 parent c00ecfa commit d93c1b3

File tree

4 files changed

+92
-37
lines changed

4 files changed

+92
-37
lines changed

src/librustc_mir/hair/pattern/_match.rs

Lines changed: 81 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,14 @@ enum Constructor<'tcx> {
597597
FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd),
598598
/// Array patterns of length `n`.
599599
FixedLenSlice(u64),
600+
/// Array patterns of length `len`, but for which we only care about the `prefix` first values
601+
/// and the `suffix` last values. This avoids unnecessarily going through values we know to be
602+
/// uninteresting, which can be a major problem for large arrays.
603+
LazyFixedLenSlice {
604+
len: u64, // The actual length of the array
605+
prefix: u64,
606+
suffix: u64,
607+
},
600608
/// Slice patterns. Captures any array constructor of `length >= i + j`.
601609
VarLenSlice(u64, u64),
602610
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
@@ -606,7 +614,7 @@ enum Constructor<'tcx> {
606614
impl<'tcx> Constructor<'tcx> {
607615
fn is_slice(&self) -> bool {
608616
match self {
609-
FixedLenSlice { .. } | VarLenSlice { .. } => true,
617+
FixedLenSlice { .. } | LazyFixedLenSlice { .. } | VarLenSlice { .. } => true,
610618
_ => false,
611619
}
612620
}
@@ -635,9 +643,9 @@ impl<'tcx> Constructor<'tcx> {
635643
Single | Variant(_) | ConstantValue(..) | FloatRange(..) => {
636644
if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] }
637645
}
638-
&FixedLenSlice(self_len) => {
646+
&FixedLenSlice(self_len) | &LazyFixedLenSlice { len: self_len, .. } => {
639647
let overlaps = |c: &Constructor<'_>| match *c {
640-
FixedLenSlice(other_len) => other_len == self_len,
648+
FixedLenSlice(len) | LazyFixedLenSlice { len, .. } => len == self_len,
641649
VarLenSlice(prefix, suffix) => prefix + suffix <= self_len,
642650
_ => false,
643651
};
@@ -657,7 +665,12 @@ impl<'tcx> Constructor<'tcx> {
657665
// Compute `pos_ctor \ neg_ctor`.
658666
match pos_ctor {
659667
FixedLenSlice(pos_len) => match *neg_ctor {
660-
FixedLenSlice(neg_len) if neg_len == pos_len => smallvec![],
668+
FixedLenSlice(neg_len)
669+
| LazyFixedLenSlice { len: neg_len, .. }
670+
if neg_len == pos_len =>
671+
{
672+
smallvec![]
673+
}
661674
VarLenSlice(neg_prefix, neg_suffix)
662675
if neg_prefix + neg_suffix <= pos_len =>
663676
{
@@ -668,7 +681,10 @@ impl<'tcx> Constructor<'tcx> {
668681
VarLenSlice(pos_prefix, pos_suffix) => {
669682
let pos_len = pos_prefix + pos_suffix;
670683
match *neg_ctor {
671-
FixedLenSlice(neg_len) if neg_len >= pos_len => {
684+
FixedLenSlice(neg_len)
685+
| LazyFixedLenSlice { len: neg_len, .. }
686+
if neg_len >= pos_len =>
687+
{
672688
(pos_len..neg_len)
673689
.map(FixedLenSlice)
674690
// We know that `neg_len + 1 >= pos_len >=
@@ -799,7 +815,7 @@ impl<'tcx> Constructor<'tcx> {
799815
}
800816
_ => vec![],
801817
},
802-
FixedLenSlice(_) | VarLenSlice(..) => match ty.kind {
818+
FixedLenSlice(_) | LazyFixedLenSlice { .. } | VarLenSlice(..) => match ty.kind {
803819
ty::Slice(ty) | ty::Array(ty, _) => {
804820
let arity = self.arity(cx, ty);
805821
(0..arity).map(|_| Pat::wildcard_from_ty(ty)).collect()
@@ -830,7 +846,9 @@ impl<'tcx> Constructor<'tcx> {
830846
_ => 0,
831847
},
832848
FixedLenSlice(length) => *length,
833-
VarLenSlice(prefix, suffix) => prefix + suffix,
849+
VarLenSlice(prefix, suffix) | LazyFixedLenSlice { prefix, suffix, .. } => {
850+
prefix + suffix
851+
}
834852
ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => 0,
835853
}
836854
}
@@ -888,8 +906,11 @@ impl<'tcx> Constructor<'tcx> {
888906
FixedLenSlice(_) => {
889907
PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
890908
}
891-
&VarLenSlice(prefix_len, _) => {
892-
let prefix = subpatterns.by_ref().take(prefix_len as usize).collect();
909+
LazyFixedLenSlice { len, prefix, suffix } if prefix + suffix == *len => {
910+
PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
911+
}
912+
VarLenSlice(prefix, _) | LazyFixedLenSlice { prefix, .. } => {
913+
let prefix = subpatterns.by_ref().take(*prefix as usize).collect();
893914
let suffix = subpatterns.collect();
894915
let wild = Pat::wildcard_from_ty(ty);
895916
PatKind::Slice { prefix, slice: Some(wild), suffix }
@@ -1106,7 +1127,11 @@ fn all_constructors<'a, 'tcx>(
11061127
}
11071128
ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
11081129
let len = len.eval_usize(cx.tcx, cx.param_env);
1109-
if len != 0 && cx.is_uninhabited(sub_ty) { vec![] } else { vec![FixedLenSlice(len)] }
1130+
if len != 0 && cx.is_uninhabited(sub_ty) {
1131+
vec![]
1132+
} else {
1133+
vec![LazyFixedLenSlice { len, prefix: 0, suffix: 0 }]
1134+
}
11101135
}
11111136
// Treat arrays of a constant but unknown length like slices.
11121137
ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => {
@@ -1694,10 +1719,19 @@ fn pat_constructor<'tcx>(
16941719
Some(FloatRange(lo, hi, end))
16951720
}
16961721
}
1697-
PatKind::Array { .. } => match pat.ty.kind {
1698-
ty::Array(_, length) => Some(FixedLenSlice(length.eval_usize(tcx, param_env))),
1699-
_ => span_bug!(pat.span, "bad ty {:?} for array pattern", pat.ty),
1700-
},
1722+
PatKind::Array { ref prefix, ref slice, ref suffix } => {
1723+
let len = match pat.ty.kind {
1724+
ty::Array(_, length) => length.eval_usize(tcx, param_env),
1725+
_ => span_bug!(pat.span, "bad ty {:?} for array pattern", pat.ty),
1726+
};
1727+
let prefix = prefix.len() as u64;
1728+
let suffix = suffix.len() as u64;
1729+
if slice.is_some() {
1730+
Some(LazyFixedLenSlice { len, prefix, suffix })
1731+
} else {
1732+
Some(FixedLenSlice(len))
1733+
}
1734+
}
17011735
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
17021736
let prefix = prefix.len() as u64;
17031737
let suffix = suffix.len() as u64;
@@ -1833,6 +1867,7 @@ fn split_grouped_constructors<'p, 'tcx>(
18331867
) -> Vec<Constructor<'tcx>> {
18341868
let ty = pcx.ty;
18351869
let mut split_ctors = Vec::with_capacity(ctors.len());
1870+
debug!("split_grouped_constructors({:#?}, {:#?})", matrix, ctors);
18361871

18371872
for ctor in ctors.into_iter() {
18381873
match ctor {
@@ -1920,7 +1955,8 @@ fn split_grouped_constructors<'p, 'tcx>(
19201955
.map(IntRange),
19211956
);
19221957
}
1923-
VarLenSlice(self_prefix, self_suffix) => {
1958+
VarLenSlice(self_prefix, self_suffix)
1959+
| LazyFixedLenSlice { prefix: self_prefix, suffix: self_suffix, .. } => {
19241960
// The exhaustiveness-checking paper does not include any details on
19251961
// checking variable-length slice patterns. However, they are matched
19261962
// by an infinite collection of fixed-length array patterns.
@@ -2005,11 +2041,13 @@ fn split_grouped_constructors<'p, 'tcx>(
20052041
_ => {}
20062042
}
20072043
}
2008-
PatKind::Slice { ref prefix, slice: None, ref suffix } => {
2044+
PatKind::Slice { ref prefix, slice: None, ref suffix }
2045+
| PatKind::Array { ref prefix, slice: None, ref suffix } => {
20092046
let fixed_len = prefix.len() as u64 + suffix.len() as u64;
20102047
max_fixed_len = cmp::max(max_fixed_len, fixed_len);
20112048
}
2012-
PatKind::Slice { ref prefix, slice: Some(_), ref suffix } => {
2049+
PatKind::Slice { ref prefix, slice: Some(_), ref suffix }
2050+
| PatKind::Array { ref prefix, slice: Some(_), ref suffix } => {
20132051
max_prefix_len = cmp::max(max_prefix_len, prefix.len() as u64);
20142052
max_suffix_len = cmp::max(max_suffix_len, suffix.len() as u64);
20152053
}
@@ -2027,20 +2065,37 @@ fn split_grouped_constructors<'p, 'tcx>(
20272065
max_prefix_len = max_fixed_len + 1 - max_suffix_len;
20282066
}
20292067

2030-
// `ctor` originally covered the range `(self_prefix + self_suffix..infinity)`. We
2031-
// now split it into two: lengths smaller than `max_prefix_len + max_suffix_len`
2032-
// are treated independently as fixed-lengths slices, and lengths above are
2033-
// captured by a final VarLenSlice constructor.
2034-
split_ctors.extend(
2035-
(self_prefix + self_suffix..max_prefix_len + max_suffix_len).map(FixedLenSlice),
2036-
);
2037-
split_ctors.push(VarLenSlice(max_prefix_len, max_suffix_len));
2068+
match ctor {
2069+
LazyFixedLenSlice { len, .. } => {
2070+
if max_prefix_len + max_suffix_len < len {
2071+
split_ctors.push(LazyFixedLenSlice {
2072+
len,
2073+
prefix: max_prefix_len,
2074+
suffix: max_suffix_len,
2075+
});
2076+
} else {
2077+
split_ctors.push(FixedLenSlice(len));
2078+
}
2079+
}
2080+
_ => {
2081+
// `ctor` originally covered the range `(self_prefix + self_suffix..infinity)`. We
2082+
// now split it into two: lengths smaller than `max_prefix_len + max_suffix_len`
2083+
// are treated independently as fixed-lengths slices, and lengths above are
2084+
// captured by a final VarLenSlice constructor.
2085+
split_ctors.extend(
2086+
(self_prefix + self_suffix..max_prefix_len + max_suffix_len)
2087+
.map(FixedLenSlice),
2088+
);
2089+
split_ctors.push(VarLenSlice(max_prefix_len, max_suffix_len));
2090+
}
2091+
}
20382092
}
20392093
// Any other constructor can be used unchanged.
20402094
_ => split_ctors.push(ctor),
20412095
}
20422096
}
20432097

2098+
debug!("split_grouped_constructors(..)={:#?}", split_ctors);
20442099
split_ctors
20452100
}
20462101

@@ -2252,7 +2307,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>(
22522307

22532308
PatKind::Array { ref prefix, ref slice, ref suffix }
22542309
| PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor {
2255-
FixedLenSlice(..) | VarLenSlice(..) => {
2310+
FixedLenSlice(..) | LazyFixedLenSlice { .. } | VarLenSlice(..) => {
22562311
let pat_len = prefix.len() + suffix.len();
22572312
if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) {
22582313
if slice_count == 0 || slice.is_some() {

src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0004]: non-exhaustive patterns: `&[_, _, _, _]` not covered
1+
error[E0004]: non-exhaustive patterns: `&[..]` not covered
22
--> $DIR/match-byte-array-patterns-2.rs:4:11
33
|
44
LL | match buf {
5-
| ^^^ pattern `&[_, _, _, _]` not covered
5+
| ^^^ pattern `&[..]` not covered
66
|
77
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
88

src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ fn main() {
1212
[true, .., true] => {}
1313
}
1414
match s3 {
15-
//~^ ERROR `&[false, _, _]` not covered
15+
//~^ ERROR `&[false, .., _]` not covered
1616
[true, .., true] => {}
1717
}
1818
match s10 {
19-
//~^ ERROR `&[false, _, _, _, _, _, _, _, _, _]` not covered
19+
//~^ ERROR `&[false, .., _]` not covered
2020
[true, .., true] => {}
2121
}
2222

@@ -30,7 +30,7 @@ fn main() {
3030
[.., false] => {}
3131
}
3232
match s3 {
33-
//~^ ERROR `&[false, _, true]` not covered
33+
//~^ ERROR `&[false, .., true]` not covered
3434
[true, ..] => {}
3535
[.., false] => {}
3636
}

src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@ LL | match s2 {
66
|
77
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
88

9-
error[E0004]: non-exhaustive patterns: `&[false, _, _]` not covered
9+
error[E0004]: non-exhaustive patterns: `&[false, .., _]` not covered
1010
--> $DIR/slice-patterns-exhaustiveness.rs:14:11
1111
|
1212
LL | match s3 {
13-
| ^^ pattern `&[false, _, _]` not covered
13+
| ^^ pattern `&[false, .., _]` not covered
1414
|
1515
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1616

17-
error[E0004]: non-exhaustive patterns: `&[false, _, _, _, _, _, _, _, _, _]` not covered
17+
error[E0004]: non-exhaustive patterns: `&[false, .., _]` not covered
1818
--> $DIR/slice-patterns-exhaustiveness.rs:18:11
1919
|
2020
LL | match s10 {
21-
| ^^^ pattern `&[false, _, _, _, _, _, _, _, _, _]` not covered
21+
| ^^^ pattern `&[false, .., _]` not covered
2222
|
2323
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
2424

@@ -30,11 +30,11 @@ LL | match s2 {
3030
|
3131
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
3232

33-
error[E0004]: non-exhaustive patterns: `&[false, _, true]` not covered
33+
error[E0004]: non-exhaustive patterns: `&[false, .., true]` not covered
3434
--> $DIR/slice-patterns-exhaustiveness.rs:32:11
3535
|
3636
LL | match s3 {
37-
| ^^ pattern `&[false, _, true]` not covered
37+
| ^^ pattern `&[false, .., true]` not covered
3838
|
3939
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
4040

0 commit comments

Comments
 (0)