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.
This commit is contained in:
parent
c00ecfa8d2
commit
d93c1b320c
4 changed files with 92 additions and 37 deletions
|
@ -597,6 +597,14 @@ enum Constructor<'tcx> {
|
|||
FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd),
|
||||
/// Array patterns of length `n`.
|
||||
FixedLenSlice(u64),
|
||||
/// Array patterns of length `len`, but for which we only care about the `prefix` first values
|
||||
/// and the `suffix` last values. This avoids unnecessarily going through values we know to be
|
||||
/// uninteresting, which can be a major problem for large arrays.
|
||||
LazyFixedLenSlice {
|
||||
len: u64, // The actual length of the array
|
||||
prefix: u64,
|
||||
suffix: u64,
|
||||
},
|
||||
/// Slice patterns. Captures any array constructor of `length >= i + j`.
|
||||
VarLenSlice(u64, u64),
|
||||
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
|
||||
|
@ -606,7 +614,7 @@ enum Constructor<'tcx> {
|
|||
impl<'tcx> Constructor<'tcx> {
|
||||
fn is_slice(&self) -> bool {
|
||||
match self {
|
||||
FixedLenSlice { .. } | VarLenSlice { .. } => true,
|
||||
FixedLenSlice { .. } | LazyFixedLenSlice { .. } | VarLenSlice { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -635,9 +643,9 @@ impl<'tcx> Constructor<'tcx> {
|
|||
Single | Variant(_) | ConstantValue(..) | FloatRange(..) => {
|
||||
if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] }
|
||||
}
|
||||
&FixedLenSlice(self_len) => {
|
||||
&FixedLenSlice(self_len) | &LazyFixedLenSlice { len: self_len, .. } => {
|
||||
let overlaps = |c: &Constructor<'_>| match *c {
|
||||
FixedLenSlice(other_len) => other_len == self_len,
|
||||
FixedLenSlice(len) | LazyFixedLenSlice { len, .. } => len == self_len,
|
||||
VarLenSlice(prefix, suffix) => prefix + suffix <= self_len,
|
||||
_ => false,
|
||||
};
|
||||
|
@ -657,7 +665,12 @@ impl<'tcx> Constructor<'tcx> {
|
|||
// Compute `pos_ctor \ neg_ctor`.
|
||||
match pos_ctor {
|
||||
FixedLenSlice(pos_len) => match *neg_ctor {
|
||||
FixedLenSlice(neg_len) if neg_len == pos_len => smallvec![],
|
||||
FixedLenSlice(neg_len)
|
||||
| LazyFixedLenSlice { len: neg_len, .. }
|
||||
if neg_len == pos_len =>
|
||||
{
|
||||
smallvec![]
|
||||
}
|
||||
VarLenSlice(neg_prefix, neg_suffix)
|
||||
if neg_prefix + neg_suffix <= pos_len =>
|
||||
{
|
||||
|
@ -668,7 +681,10 @@ impl<'tcx> Constructor<'tcx> {
|
|||
VarLenSlice(pos_prefix, pos_suffix) => {
|
||||
let pos_len = pos_prefix + pos_suffix;
|
||||
match *neg_ctor {
|
||||
FixedLenSlice(neg_len) if neg_len >= pos_len => {
|
||||
FixedLenSlice(neg_len)
|
||||
| LazyFixedLenSlice { len: neg_len, .. }
|
||||
if neg_len >= pos_len =>
|
||||
{
|
||||
(pos_len..neg_len)
|
||||
.map(FixedLenSlice)
|
||||
// We know that `neg_len + 1 >= pos_len >=
|
||||
|
@ -799,7 +815,7 @@ impl<'tcx> Constructor<'tcx> {
|
|||
}
|
||||
_ => vec![],
|
||||
},
|
||||
FixedLenSlice(_) | VarLenSlice(..) => match ty.kind {
|
||||
FixedLenSlice(_) | LazyFixedLenSlice { .. } | VarLenSlice(..) => match ty.kind {
|
||||
ty::Slice(ty) | ty::Array(ty, _) => {
|
||||
let arity = self.arity(cx, ty);
|
||||
(0..arity).map(|_| Pat::wildcard_from_ty(ty)).collect()
|
||||
|
@ -830,7 +846,9 @@ impl<'tcx> Constructor<'tcx> {
|
|||
_ => 0,
|
||||
},
|
||||
FixedLenSlice(length) => *length,
|
||||
VarLenSlice(prefix, suffix) => prefix + suffix,
|
||||
VarLenSlice(prefix, suffix) | LazyFixedLenSlice { prefix, suffix, .. } => {
|
||||
prefix + suffix
|
||||
}
|
||||
ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => 0,
|
||||
}
|
||||
}
|
||||
|
@ -888,8 +906,11 @@ impl<'tcx> Constructor<'tcx> {
|
|||
FixedLenSlice(_) => {
|
||||
PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
|
||||
}
|
||||
&VarLenSlice(prefix_len, _) => {
|
||||
let prefix = subpatterns.by_ref().take(prefix_len as usize).collect();
|
||||
LazyFixedLenSlice { len, prefix, suffix } if prefix + suffix == *len => {
|
||||
PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
|
||||
}
|
||||
VarLenSlice(prefix, _) | LazyFixedLenSlice { prefix, .. } => {
|
||||
let prefix = subpatterns.by_ref().take(*prefix as usize).collect();
|
||||
let suffix = subpatterns.collect();
|
||||
let wild = Pat::wildcard_from_ty(ty);
|
||||
PatKind::Slice { prefix, slice: Some(wild), suffix }
|
||||
|
@ -1106,7 +1127,11 @@ fn all_constructors<'a, 'tcx>(
|
|||
}
|
||||
ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
|
||||
let len = len.eval_usize(cx.tcx, cx.param_env);
|
||||
if len != 0 && cx.is_uninhabited(sub_ty) { vec![] } else { vec![FixedLenSlice(len)] }
|
||||
if len != 0 && cx.is_uninhabited(sub_ty) {
|
||||
vec![]
|
||||
} else {
|
||||
vec![LazyFixedLenSlice { len, prefix: 0, suffix: 0 }]
|
||||
}
|
||||
}
|
||||
// Treat arrays of a constant but unknown length like slices.
|
||||
ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => {
|
||||
|
@ -1694,10 +1719,19 @@ fn pat_constructor<'tcx>(
|
|||
Some(FloatRange(lo, hi, end))
|
||||
}
|
||||
}
|
||||
PatKind::Array { .. } => match pat.ty.kind {
|
||||
ty::Array(_, length) => Some(FixedLenSlice(length.eval_usize(tcx, param_env))),
|
||||
_ => span_bug!(pat.span, "bad ty {:?} for array pattern", pat.ty),
|
||||
},
|
||||
PatKind::Array { ref prefix, ref slice, ref suffix } => {
|
||||
let len = match pat.ty.kind {
|
||||
ty::Array(_, length) => length.eval_usize(tcx, param_env),
|
||||
_ => span_bug!(pat.span, "bad ty {:?} for array pattern", pat.ty),
|
||||
};
|
||||
let prefix = prefix.len() as u64;
|
||||
let suffix = suffix.len() as u64;
|
||||
if slice.is_some() {
|
||||
Some(LazyFixedLenSlice { len, prefix, suffix })
|
||||
} else {
|
||||
Some(FixedLenSlice(len))
|
||||
}
|
||||
}
|
||||
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
|
||||
let prefix = prefix.len() as u64;
|
||||
let suffix = suffix.len() as u64;
|
||||
|
@ -1833,6 +1867,7 @@ fn split_grouped_constructors<'p, 'tcx>(
|
|||
) -> Vec<Constructor<'tcx>> {
|
||||
let ty = pcx.ty;
|
||||
let mut split_ctors = Vec::with_capacity(ctors.len());
|
||||
debug!("split_grouped_constructors({:#?}, {:#?})", matrix, ctors);
|
||||
|
||||
for ctor in ctors.into_iter() {
|
||||
match ctor {
|
||||
|
@ -1920,7 +1955,8 @@ fn split_grouped_constructors<'p, 'tcx>(
|
|||
.map(IntRange),
|
||||
);
|
||||
}
|
||||
VarLenSlice(self_prefix, self_suffix) => {
|
||||
VarLenSlice(self_prefix, self_suffix)
|
||||
| LazyFixedLenSlice { prefix: self_prefix, suffix: self_suffix, .. } => {
|
||||
// The exhaustiveness-checking paper does not include any details on
|
||||
// checking variable-length slice patterns. However, they are matched
|
||||
// by an infinite collection of fixed-length array patterns.
|
||||
|
@ -2005,11 +2041,13 @@ fn split_grouped_constructors<'p, 'tcx>(
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
PatKind::Slice { ref prefix, slice: None, ref suffix } => {
|
||||
PatKind::Slice { ref prefix, slice: None, ref suffix }
|
||||
| PatKind::Array { ref prefix, slice: None, ref suffix } => {
|
||||
let fixed_len = prefix.len() as u64 + suffix.len() as u64;
|
||||
max_fixed_len = cmp::max(max_fixed_len, fixed_len);
|
||||
}
|
||||
PatKind::Slice { ref prefix, slice: Some(_), ref suffix } => {
|
||||
PatKind::Slice { ref prefix, slice: Some(_), ref suffix }
|
||||
| PatKind::Array { ref prefix, slice: Some(_), ref suffix } => {
|
||||
max_prefix_len = cmp::max(max_prefix_len, prefix.len() as u64);
|
||||
max_suffix_len = cmp::max(max_suffix_len, suffix.len() as u64);
|
||||
}
|
||||
|
@ -2027,20 +2065,37 @@ fn split_grouped_constructors<'p, 'tcx>(
|
|||
max_prefix_len = max_fixed_len + 1 - max_suffix_len;
|
||||
}
|
||||
|
||||
// `ctor` originally covered the range `(self_prefix + self_suffix..infinity)`. We
|
||||
// now split it into two: lengths smaller than `max_prefix_len + max_suffix_len`
|
||||
// are treated independently as fixed-lengths slices, and lengths above are
|
||||
// captured by a final VarLenSlice constructor.
|
||||
split_ctors.extend(
|
||||
(self_prefix + self_suffix..max_prefix_len + max_suffix_len).map(FixedLenSlice),
|
||||
);
|
||||
split_ctors.push(VarLenSlice(max_prefix_len, max_suffix_len));
|
||||
match ctor {
|
||||
LazyFixedLenSlice { len, .. } => {
|
||||
if max_prefix_len + max_suffix_len < len {
|
||||
split_ctors.push(LazyFixedLenSlice {
|
||||
len,
|
||||
prefix: max_prefix_len,
|
||||
suffix: max_suffix_len,
|
||||
});
|
||||
} else {
|
||||
split_ctors.push(FixedLenSlice(len));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// `ctor` originally covered the range `(self_prefix + self_suffix..infinity)`. We
|
||||
// now split it into two: lengths smaller than `max_prefix_len + max_suffix_len`
|
||||
// are treated independently as fixed-lengths slices, and lengths above are
|
||||
// captured by a final VarLenSlice constructor.
|
||||
split_ctors.extend(
|
||||
(self_prefix + self_suffix..max_prefix_len + max_suffix_len)
|
||||
.map(FixedLenSlice),
|
||||
);
|
||||
split_ctors.push(VarLenSlice(max_prefix_len, max_suffix_len));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Any other constructor can be used unchanged.
|
||||
_ => split_ctors.push(ctor),
|
||||
}
|
||||
}
|
||||
|
||||
debug!("split_grouped_constructors(..)={:#?}", split_ctors);
|
||||
split_ctors
|
||||
}
|
||||
|
||||
|
@ -2252,7 +2307,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>(
|
|||
|
||||
PatKind::Array { ref prefix, ref slice, ref suffix }
|
||||
| PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor {
|
||||
FixedLenSlice(..) | VarLenSlice(..) => {
|
||||
FixedLenSlice(..) | LazyFixedLenSlice { .. } | VarLenSlice(..) => {
|
||||
let pat_len = prefix.len() + suffix.len();
|
||||
if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) {
|
||||
if slice_count == 0 || slice.is_some() {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error[E0004]: non-exhaustive patterns: `&[_, _, _, _]` not covered
|
||||
error[E0004]: non-exhaustive patterns: `&[..]` not covered
|
||||
--> $DIR/match-byte-array-patterns-2.rs:4:11
|
||||
|
|
||||
LL | match buf {
|
||||
| ^^^ pattern `&[_, _, _, _]` not covered
|
||||
| ^^^ pattern `&[..]` not covered
|
||||
|
|
||||
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
|
||||
|
||||
|
|
|
@ -12,11 +12,11 @@ fn main() {
|
|||
[true, .., true] => {}
|
||||
}
|
||||
match s3 {
|
||||
//~^ ERROR `&[false, _, _]` not covered
|
||||
//~^ ERROR `&[false, .., _]` not covered
|
||||
[true, .., true] => {}
|
||||
}
|
||||
match s10 {
|
||||
//~^ ERROR `&[false, _, _, _, _, _, _, _, _, _]` not covered
|
||||
//~^ ERROR `&[false, .., _]` not covered
|
||||
[true, .., true] => {}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ fn main() {
|
|||
[.., false] => {}
|
||||
}
|
||||
match s3 {
|
||||
//~^ ERROR `&[false, _, true]` not covered
|
||||
//~^ ERROR `&[false, .., true]` not covered
|
||||
[true, ..] => {}
|
||||
[.., false] => {}
|
||||
}
|
||||
|
|
|
@ -6,19 +6,19 @@ LL | match s2 {
|
|||
|
|
||||
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `&[false, _, _]` not covered
|
||||
error[E0004]: non-exhaustive patterns: `&[false, .., _]` not covered
|
||||
--> $DIR/slice-patterns-exhaustiveness.rs:14:11
|
||||
|
|
||||
LL | match s3 {
|
||||
| ^^ pattern `&[false, _, _]` not covered
|
||||
| ^^ pattern `&[false, .., _]` not covered
|
||||
|
|
||||
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `&[false, _, _, _, _, _, _, _, _, _]` not covered
|
||||
error[E0004]: non-exhaustive patterns: `&[false, .., _]` not covered
|
||||
--> $DIR/slice-patterns-exhaustiveness.rs:18:11
|
||||
|
|
||||
LL | match s10 {
|
||||
| ^^^ pattern `&[false, _, _, _, _, _, _, _, _, _]` not covered
|
||||
| ^^^ pattern `&[false, .., _]` not covered
|
||||
|
|
||||
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
|
||||
|
||||
|
@ -30,11 +30,11 @@ LL | match s2 {
|
|||
|
|
||||
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `&[false, _, true]` not covered
|
||||
error[E0004]: non-exhaustive patterns: `&[false, .., true]` not covered
|
||||
--> $DIR/slice-patterns-exhaustiveness.rs:32:11
|
||||
|
|
||||
LL | match s3 {
|
||||
| ^^ pattern `&[false, _, true]` not covered
|
||||
| ^^ pattern `&[false, .., true]` not covered
|
||||
|
|
||||
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue