diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 555772f8b58d..fdee2ffee983 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -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> { 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() { diff --git a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr index 6e52072e3bfe..63ed49094fc5 100644 --- a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr +++ b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr @@ -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 diff --git a/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs b/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs index 11ca06e36adc..5881c35d356b 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs +++ b/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs @@ -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] => {} } diff --git a/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr b/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr index 7bf283ec741b..9a711f2a441d 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr +++ b/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr @@ -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