Simplify hoisting of array/slice patterns
We can replace some tricky iterator-mutation code with a much simpler version that uses `while let` to shrink a slice. We also check whether a subpattern would be a wildcard _before_ hoisting it, which will be very useful when trying to get rid of `print::PatKind` later.
This commit is contained in:
parent
c764bea0c3
commit
a245bfa617
@ -5,6 +5,7 @@
|
||||
// tidy-alphabetical-start
|
||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||
#![allow(rustc::untranslatable_diagnostic)]
|
||||
#![cfg_attr(feature = "rustc", feature(let_chains))]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
pub mod constructor;
|
||||
|
@ -827,7 +827,6 @@ pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
|
||||
fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
|
||||
use print::{FieldPat, Pat, PatKind};
|
||||
let cx = self;
|
||||
let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild);
|
||||
let hoist = |p| Box::new(cx.hoist_witness_pat(p));
|
||||
let mut subpatterns = pat.iter_fields().map(hoist);
|
||||
let kind = match pat.ctor() {
|
||||
@ -862,37 +861,35 @@ fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
|
||||
// ignore this issue.
|
||||
Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
|
||||
Slice(slice) => {
|
||||
match slice.kind {
|
||||
SliceKind::FixedLen(_) => PatKind::Slice {
|
||||
prefix: subpatterns.collect(),
|
||||
has_dot_dot: false,
|
||||
suffix: Box::new([]),
|
||||
},
|
||||
SliceKind::VarLen(prefix, _) => {
|
||||
let mut subpatterns = subpatterns.peekable();
|
||||
let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
|
||||
if slice.array_len.is_some() {
|
||||
// Improves diagnostics a bit: if the type is a known-size array, instead
|
||||
// of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
|
||||
// This is incorrect if the size is not known, since `[_, ..]` captures
|
||||
// arrays of lengths `>= 1` whereas `[..]` captures any length.
|
||||
while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
|
||||
prefix.pop();
|
||||
}
|
||||
while subpatterns.peek().is_some()
|
||||
&& is_wildcard(subpatterns.peek().unwrap())
|
||||
{
|
||||
subpatterns.next();
|
||||
}
|
||||
}
|
||||
let suffix: Box<[_]> = subpatterns.collect();
|
||||
PatKind::Slice {
|
||||
prefix: prefix.into_boxed_slice(),
|
||||
has_dot_dot: true,
|
||||
suffix,
|
||||
}
|
||||
let (prefix_len, has_dot_dot) = match slice.kind {
|
||||
SliceKind::FixedLen(len) => (len, false),
|
||||
SliceKind::VarLen(prefix_len, _) => (prefix_len, true),
|
||||
};
|
||||
|
||||
let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len);
|
||||
|
||||
// If the pattern contains a `..`, but is applied to values of statically-known
|
||||
// length (arrays), then we can slightly simplify diagnostics by merging any
|
||||
// adjacent wildcard patterns into the `..`: `[x, _, .., _, y]` => `[x, .., y]`.
|
||||
// (This simplification isn't allowed for slice values, because in that case
|
||||
// `[x, .., y]` would match some slices that `[x, _, .., _, y]` would not.)
|
||||
if has_dot_dot && slice.array_len.is_some() {
|
||||
while let [rest @ .., last] = prefix
|
||||
&& would_print_as_wildcard(cx.tcx, last)
|
||||
{
|
||||
prefix = rest;
|
||||
}
|
||||
while let [first, rest @ ..] = suffix
|
||||
&& would_print_as_wildcard(cx.tcx, first)
|
||||
{
|
||||
suffix = rest;
|
||||
}
|
||||
}
|
||||
|
||||
let prefix = prefix.iter().map(hoist).collect();
|
||||
let suffix = suffix.iter().map(hoist).collect();
|
||||
|
||||
PatKind::Slice { prefix, has_dot_dot, suffix }
|
||||
}
|
||||
&Str(value) => PatKind::Constant { value },
|
||||
Never if self.tcx.features().never_patterns => PatKind::Never,
|
||||
@ -910,6 +907,22 @@ fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the given pattern would be printed as a wildcard (`_`).
|
||||
fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool {
|
||||
match p.ctor() {
|
||||
Constructor::IntRange(IntRange {
|
||||
lo: MaybeInfiniteInt::NegInfinity,
|
||||
hi: MaybeInfiniteInt::PosInfinity,
|
||||
})
|
||||
| Constructor::Wildcard
|
||||
| Constructor::NonExhaustive
|
||||
| Constructor::Hidden
|
||||
| Constructor::PrivateUninhabited => true,
|
||||
Constructor::Never if !tcx.features().never_patterns => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
|
||||
type Ty = RevealedTy<'tcx>;
|
||||
type Error = ErrorGuaranteed;
|
||||
|
Loading…
Reference in New Issue
Block a user