Factor out slice constructor struct and simplify

This commit is contained in:
Nadrieril 2019-11-17 17:33:39 +00:00
parent d0cfef32a8
commit 54e97e889b

View file

@ -583,21 +583,110 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum SliceKind {
/// Array patterns of length `n`.
/// Patterns of length `n` (`[x, y]`).
FixedLen(u64),
/// Slice patterns. Captures any array constructor of `length >= i + j`.
/// Patterns using the `..` notation (`[x, .., y]`). Captures any array constructor of `length
/// >= i + j`. In the case where `array_len` is `Some(_)`, this indicates that we only care
/// about the first `i` and the last `j` values of the array, and everything in between is a
/// wildcard `_`.
VarLen(u64, u64),
}
impl SliceKind {
fn arity(self) -> u64 {
/// A constructor for array and slice patterns.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct Slice {
/// `None` if the matched value is a slice, `Some(n)` if it is an array of size `n`.
array_len: Option<u64>,
/// The kind of pattern it is: fixed-length `[x, y]` or variable length `[x, .., y]`.
kind: SliceKind,
}
impl Slice {
/// Returns what patterns this constructor covers: either fixed-length patterns or
/// variable-length patterns.
fn pattern_kind(self) -> SliceKind {
match self {
Slice { array_len: Some(len), kind: VarLen(prefix, suffix) }
if prefix + suffix == len =>
{
FixedLen(len)
}
_ => self.kind,
}
}
/// Returns what values this constructor covers: either values of only one given length, or
/// values of length above a given length.
/// This is different from `pattern_kind()` because in some cases the pattern only takes into
/// account a subset of the entries of the array, but still only captures values of a given
/// length.
fn value_kind(self) -> SliceKind {
match self {
Slice { array_len: Some(len), kind: VarLen(_, _) } => FixedLen(len),
_ => self.kind,
}
}
fn arity(self) -> u64 {
match self.pattern_kind() {
FixedLen(length) => length,
VarLen(prefix, suffix) => prefix + suffix,
}
}
/// Whether this pattern includes patterns of length `other_len`.
fn covers_length(self, other_len: u64) -> bool {
match self.value_kind() {
FixedLen(len) => len == other_len,
VarLen(prefix, suffix) => prefix + suffix <= other_len,
}
}
/// Returns a collection of slices that spans the values covered by `self`, subtracted by the
/// values covered by `other`: i.e., `self \ other` (in set notation).
fn subtract(self, other: Self) -> SmallVec<[Self; 1]> {
// Remember, `VarLen(i, j)` covers the union of `FixedLen` from `i + j` to infinity.
// Naming: we remove the "neg" constructors from the "pos" ones.
match self.value_kind() {
FixedLen(pos_len) => {
if other.covers_length(pos_len) {
smallvec![]
} else {
smallvec![self]
}
}
VarLen(pos_prefix, pos_suffix) => {
let pos_len = pos_prefix + pos_suffix;
match other.value_kind() {
FixedLen(neg_len) => {
if neg_len < pos_len {
smallvec![self]
} else {
(pos_len..neg_len)
.map(FixedLen)
// We know that `neg_len + 1 >= pos_len >= pos_suffix`.
.chain(Some(VarLen(neg_len + 1 - pos_suffix, pos_suffix)))
.map(|kind| Slice { array_len: None, kind })
.collect()
}
}
VarLen(neg_prefix, neg_suffix) => {
let neg_len = neg_prefix + neg_suffix;
if neg_len <= pos_len {
smallvec![]
} else {
(pos_len..neg_len)
.map(FixedLen)
.map(|kind| Slice { array_len: None, kind })
.collect()
}
}
}
}
}
}
}
#[derive(Clone, Debug, PartialEq)]
@ -614,11 +703,7 @@ enum Constructor<'tcx> {
/// Ranges of floating-point literal values (`2.0..=5.2`).
FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd),
/// Array and slice patterns.
Slice {
// The length of the type of the pattern, if fixed.
type_len: Option<u64>,
kind: SliceKind,
},
Slice(Slice),
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
NonExhaustive,
}
@ -626,7 +711,7 @@ enum Constructor<'tcx> {
impl<'tcx> Constructor<'tcx> {
fn is_slice(&self) -> bool {
match self {
Slice { .. } => true,
Slice(_) => true,
_ => false,
}
}
@ -655,104 +740,41 @@ impl<'tcx> Constructor<'tcx> {
Single | Variant(_) | ConstantValue(..) | FloatRange(..) => {
if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] }
}
&Slice { type_len: Some(self_len), .. } | &Slice { kind: FixedLen(self_len), .. } => {
let overlaps = |c: &Constructor<'_>| match *c {
Slice { type_len: Some(len), .. } | Slice { kind: FixedLen(len), .. } => {
len == self_len
}
Slice { type_len: None, kind: VarLen(prefix, suffix) } => {
prefix + suffix <= self_len
}
_ => false,
};
if other_ctors.iter().any(overlaps) { vec![] } else { vec![self.clone()] }
}
Slice { type_len: None, kind: VarLen(..) } => {
let mut remaining_ctors = vec![self.clone()];
// For each used ctor, subtract from the current set of constructors.
// Naming: we remove the "neg" constructors from the "pos" ones.
// Remember, `VarLen(i, j)` covers the union of `FixedLen` from
// `i + j` to infinity.
for neg_ctor in other_ctors {
remaining_ctors = remaining_ctors
.into_iter()
.flat_map(|pos_ctor| -> SmallVec<[Constructor<'tcx>; 1]> {
// Compute `pos_ctor \ neg_ctor`.
match pos_ctor {
Slice { type_len: Some(pos_len), .. }
| Slice { kind: FixedLen(pos_len), .. } => match *neg_ctor {
Slice { type_len: Some(neg_len), .. }
| Slice { kind: FixedLen(neg_len), .. }
if neg_len == pos_len =>
{
smallvec![]
}
Slice {
type_len: None,
kind: VarLen(neg_prefix, neg_suffix),
} if neg_prefix + neg_suffix <= pos_len => smallvec![],
_ => smallvec![pos_ctor],
},
Slice { type_len: None, kind: VarLen(pos_prefix, pos_suffix) } => {
let pos_len = pos_prefix + pos_suffix;
match *neg_ctor {
Slice { type_len: Some(neg_len), .. }
| Slice { kind: FixedLen(neg_len), .. }
if neg_len >= pos_len =>
{
(pos_len..neg_len)
.map(|l| Slice {
type_len: None,
kind: FixedLen(l),
})
// We know that `neg_len + 1 >= pos_len >=
// pos_suffix`.
.chain(Some(Slice {
type_len: None,
kind: VarLen(
neg_len + 1 - pos_suffix,
pos_suffix,
),
}))
.collect()
}
Slice {
type_len: None,
kind: VarLen(neg_prefix, neg_suffix),
} => {
let neg_len = neg_prefix + neg_suffix;
if neg_len <= pos_len {
smallvec![]
} else {
(pos_len..neg_len)
.map(|l| Slice {
type_len: None,
kind: FixedLen(l),
})
.collect()
}
}
_ => smallvec![pos_ctor],
}
}
_ => bug!(
"unexpected ctor while subtracting from VarLen: {:?}",
pos_ctor
),
}
})
.collect();
// If the constructors that have been considered so far already cover
// the entire range of `self`, no need to look at more constructors.
if remaining_ctors.is_empty() {
break;
}
&Slice(slice) => match slice.value_kind() {
FixedLen(self_len) => {
let overlaps = |c: &Constructor<'_>| match *c {
Slice(other_slice) => other_slice.covers_length(self_len),
_ => false,
};
if other_ctors.iter().any(overlaps) { vec![] } else { vec![Slice(slice)] }
}
VarLen(..) => {
let mut remaining_slices = vec![slice];
remaining_ctors
}
// For each used slice, subtract from the current set of slices.
// Naming: we remove the "neg" constructors from the "pos" ones.
for neg_ctor in other_ctors {
let neg_slice = match neg_ctor {
Slice(slice) => *slice,
// FIXME(#65413): If `neg_ctor` is not a slice, we assume it doesn't
// cover any value here.
_ => continue,
};
remaining_slices = remaining_slices
.into_iter()
.flat_map(|pos_slice| pos_slice.subtract(neg_slice))
.collect();
// If the constructors that have been considered so far already cover
// the entire range of `self`, no need to look at more constructors.
if remaining_slices.is_empty() {
break;
}
}
remaining_slices.into_iter().map(Slice).collect()
}
},
IntRange(self_range) => {
let mut remaining_ranges = vec![self_range.clone()];
for other_ctor in other_ctors {
@ -845,7 +867,7 @@ impl<'tcx> Constructor<'tcx> {
}
_ => vec![],
},
Slice { .. } => match ty.kind {
Slice(_) => match ty.kind {
ty::Slice(ty) | ty::Array(ty, _) => {
let arity = self.arity(cx, ty);
(0..arity).map(|_| Pat::wildcard_from_ty(ty)).collect()
@ -875,7 +897,7 @@ impl<'tcx> Constructor<'tcx> {
}
_ => 0,
},
Slice { kind, .. } => kind.arity(),
Slice(slice) => slice.arity(),
ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => 0,
}
}
@ -930,20 +952,17 @@ impl<'tcx> Constructor<'tcx> {
ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty),
_ => PatKind::Wild,
},
Slice { kind: FixedLen(_), .. } => {
PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
}
Slice { type_len: Some(len), kind: VarLen(prefix, suffix) }
if prefix + suffix == *len =>
{
PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
}
Slice { kind: VarLen(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 }
}
Slice(slice) => match slice.pattern_kind() {
FixedLen(_) => {
PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
}
VarLen(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 }
}
},
&ConstantValue(value) => PatKind::Constant { value },
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
IntRange(range) => return range.to_pat(cx.tcx),
@ -1159,16 +1178,13 @@ fn all_constructors<'a, 'tcx>(
if len != 0 && cx.is_uninhabited(sub_ty) {
vec![]
} else {
vec![Slice { type_len: Some(len), kind: VarLen(0, 0) }]
vec![Slice(Slice { array_len: Some(len), kind: VarLen(0, 0) })]
}
}
// Treat arrays of a constant but unknown length like slices.
ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => {
if cx.is_uninhabited(sub_ty) {
vec![Slice { type_len: None, kind: FixedLen(0) }]
} else {
vec![Slice { type_len: None, kind: VarLen(0, 0) }]
}
let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) };
vec![Slice(Slice { array_len: None, kind })]
}
ty::Adt(def, substs) if def.is_enum() => {
let ctors: Vec<_> = def
@ -1750,7 +1766,7 @@ fn pat_constructor<'tcx>(
}
PatKind::Array { ref prefix, ref slice, ref suffix }
| PatKind::Slice { ref prefix, ref slice, ref suffix } => {
let type_len = match pat.ty.kind {
let array_len = match pat.ty.kind {
ty::Array(_, length) => Some(length.eval_usize(tcx, param_env)),
ty::Slice(_) => None,
_ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
@ -1759,7 +1775,7 @@ fn pat_constructor<'tcx>(
let suffix = suffix.len() as u64;
let kind =
if slice.is_some() { VarLen(prefix, suffix) } else { FixedLen(prefix + suffix) };
Some(Slice { type_len, kind })
Some(Slice(Slice { array_len, kind }))
}
PatKind::Or { .. } => {
bug!("support for or-patterns has not been fully implemented yet.");
@ -1975,7 +1991,7 @@ fn split_grouped_constructors<'p, 'tcx>(
.map(IntRange),
);
}
Slice { kind: VarLen(self_prefix, self_suffix), type_len } => {
Slice(Slice { array_len, kind: VarLen(self_prefix, 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.
@ -2084,26 +2100,29 @@ fn split_grouped_constructors<'p, 'tcx>(
max_prefix_len = max_fixed_len + 1 - max_suffix_len;
}
match type_len {
match array_len {
Some(len) => {
let kind = if max_prefix_len + max_suffix_len < len {
VarLen(max_prefix_len, max_suffix_len)
} else {
FixedLen(len)
};
split_ctors.push(Slice { type_len, kind });
split_ctors.push(Slice(Slice { array_len, kind }));
}
None => {
// `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 VarLen constructor.
// `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 VarLen
// constructor.
split_ctors.extend(
(self_prefix + self_suffix..max_prefix_len + max_suffix_len)
.map(|len| Slice { type_len, kind: FixedLen(len) }),
.map(|len| Slice(Slice { array_len, kind: FixedLen(len) })),
);
split_ctors
.push(Slice { type_len, kind: VarLen(max_prefix_len, max_suffix_len) });
split_ctors.push(Slice(Slice {
array_len,
kind: VarLen(max_prefix_len, max_suffix_len),
}));
}
}
}
@ -2324,7 +2343,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 {
Slice { .. } => {
Slice(_) => {
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() {