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
2 changed files with 44 additions and 30 deletions
|
@ -5,6 +5,7 @@
|
||||||
// tidy-alphabetical-start
|
// tidy-alphabetical-start
|
||||||
#![allow(rustc::diagnostic_outside_of_impl)]
|
#![allow(rustc::diagnostic_outside_of_impl)]
|
||||||
#![allow(rustc::untranslatable_diagnostic)]
|
#![allow(rustc::untranslatable_diagnostic)]
|
||||||
|
#![cfg_attr(feature = "rustc", feature(let_chains))]
|
||||||
// tidy-alphabetical-end
|
// tidy-alphabetical-end
|
||||||
|
|
||||||
pub mod constructor;
|
pub mod constructor;
|
||||||
|
|
|
@ -827,7 +827,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
||||||
fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
|
fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
|
||||||
use print::{FieldPat, Pat, PatKind};
|
use print::{FieldPat, Pat, PatKind};
|
||||||
let cx = self;
|
let cx = self;
|
||||||
let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild);
|
|
||||||
let hoist = |p| Box::new(cx.hoist_witness_pat(p));
|
let hoist = |p| Box::new(cx.hoist_witness_pat(p));
|
||||||
let mut subpatterns = pat.iter_fields().map(hoist);
|
let mut subpatterns = pat.iter_fields().map(hoist);
|
||||||
let kind = match pat.ctor() {
|
let kind = match pat.ctor() {
|
||||||
|
@ -862,37 +861,35 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
||||||
// ignore this issue.
|
// ignore this issue.
|
||||||
Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
|
Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
|
||||||
Slice(slice) => {
|
Slice(slice) => {
|
||||||
match slice.kind {
|
let (prefix_len, has_dot_dot) = match slice.kind {
|
||||||
SliceKind::FixedLen(_) => PatKind::Slice {
|
SliceKind::FixedLen(len) => (len, false),
|
||||||
prefix: subpatterns.collect(),
|
SliceKind::VarLen(prefix_len, _) => (prefix_len, true),
|
||||||
has_dot_dot: false,
|
};
|
||||||
suffix: Box::new([]),
|
|
||||||
},
|
let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len);
|
||||||
SliceKind::VarLen(prefix, _) => {
|
|
||||||
let mut subpatterns = subpatterns.peekable();
|
// If the pattern contains a `..`, but is applied to values of statically-known
|
||||||
let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
|
// length (arrays), then we can slightly simplify diagnostics by merging any
|
||||||
if slice.array_len.is_some() {
|
// adjacent wildcard patterns into the `..`: `[x, _, .., _, y]` => `[x, .., y]`.
|
||||||
// Improves diagnostics a bit: if the type is a known-size array, instead
|
// (This simplification isn't allowed for slice values, because in that case
|
||||||
// of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
|
// `[x, .., y]` would match some slices that `[x, _, .., _, y]` would not.)
|
||||||
// This is incorrect if the size is not known, since `[_, ..]` captures
|
if has_dot_dot && slice.array_len.is_some() {
|
||||||
// arrays of lengths `>= 1` whereas `[..]` captures any length.
|
while let [rest @ .., last] = prefix
|
||||||
while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
|
&& would_print_as_wildcard(cx.tcx, last)
|
||||||
prefix.pop();
|
|
||||||
}
|
|
||||||
while subpatterns.peek().is_some()
|
|
||||||
&& is_wildcard(subpatterns.peek().unwrap())
|
|
||||||
{
|
{
|
||||||
subpatterns.next();
|
prefix = rest;
|
||||||
}
|
}
|
||||||
}
|
while let [first, rest @ ..] = suffix
|
||||||
let suffix: Box<[_]> = subpatterns.collect();
|
&& would_print_as_wildcard(cx.tcx, first)
|
||||||
PatKind::Slice {
|
{
|
||||||
prefix: prefix.into_boxed_slice(),
|
suffix = rest;
|
||||||
has_dot_dot: true,
|
|
||||||
suffix,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 },
|
&Str(value) => PatKind::Constant { value },
|
||||||
Never if self.tcx.features().never_patterns => PatKind::Never,
|
Never if self.tcx.features().never_patterns => PatKind::Never,
|
||||||
|
@ -910,6 +907,22 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, '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> {
|
impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
|
||||||
type Ty = RevealedTy<'tcx>;
|
type Ty = RevealedTy<'tcx>;
|
||||||
type Error = ErrorGuaranteed;
|
type Error = ErrorGuaranteed;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue