Skip to content

Commit a245bfa

Browse files
committed
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.
1 parent c764bea commit a245bfa

File tree

2 files changed

+44
-30
lines changed

2 files changed

+44
-30
lines changed

compiler/rustc_pattern_analysis/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// tidy-alphabetical-start
66
#![allow(rustc::diagnostic_outside_of_impl)]
77
#![allow(rustc::untranslatable_diagnostic)]
8+
#![cfg_attr(feature = "rustc", feature(let_chains))]
89
// tidy-alphabetical-end
910

1011
pub mod constructor;

compiler/rustc_pattern_analysis/src/rustc.rs

+43-30
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
827827
fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
828828
use print::{FieldPat, Pat, PatKind};
829829
let cx = self;
830-
let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild);
831830
let hoist = |p| Box::new(cx.hoist_witness_pat(p));
832831
let mut subpatterns = pat.iter_fields().map(hoist);
833832
let kind = match pat.ctor() {
@@ -862,37 +861,35 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
862861
// ignore this issue.
863862
Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
864863
Slice(slice) => {
865-
match slice.kind {
866-
SliceKind::FixedLen(_) => PatKind::Slice {
867-
prefix: subpatterns.collect(),
868-
has_dot_dot: false,
869-
suffix: Box::new([]),
870-
},
871-
SliceKind::VarLen(prefix, _) => {
872-
let mut subpatterns = subpatterns.peekable();
873-
let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
874-
if slice.array_len.is_some() {
875-
// Improves diagnostics a bit: if the type is a known-size array, instead
876-
// of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
877-
// This is incorrect if the size is not known, since `[_, ..]` captures
878-
// arrays of lengths `>= 1` whereas `[..]` captures any length.
879-
while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
880-
prefix.pop();
881-
}
882-
while subpatterns.peek().is_some()
883-
&& is_wildcard(subpatterns.peek().unwrap())
884-
{
885-
subpatterns.next();
886-
}
887-
}
888-
let suffix: Box<[_]> = subpatterns.collect();
889-
PatKind::Slice {
890-
prefix: prefix.into_boxed_slice(),
891-
has_dot_dot: true,
892-
suffix,
893-
}
864+
let (prefix_len, has_dot_dot) = match slice.kind {
865+
SliceKind::FixedLen(len) => (len, false),
866+
SliceKind::VarLen(prefix_len, _) => (prefix_len, true),
867+
};
868+
869+
let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len);
870+
871+
// If the pattern contains a `..`, but is applied to values of statically-known
872+
// length (arrays), then we can slightly simplify diagnostics by merging any
873+
// adjacent wildcard patterns into the `..`: `[x, _, .., _, y]` => `[x, .., y]`.
874+
// (This simplification isn't allowed for slice values, because in that case
875+
// `[x, .., y]` would match some slices that `[x, _, .., _, y]` would not.)
876+
if has_dot_dot && slice.array_len.is_some() {
877+
while let [rest @ .., last] = prefix
878+
&& would_print_as_wildcard(cx.tcx, last)
879+
{
880+
prefix = rest;
881+
}
882+
while let [first, rest @ ..] = suffix
883+
&& would_print_as_wildcard(cx.tcx, first)
884+
{
885+
suffix = rest;
894886
}
895887
}
888+
889+
let prefix = prefix.iter().map(hoist).collect();
890+
let suffix = suffix.iter().map(hoist).collect();
891+
892+
PatKind::Slice { prefix, has_dot_dot, suffix }
896893
}
897894
&Str(value) => PatKind::Constant { value },
898895
Never if self.tcx.features().never_patterns => PatKind::Never,
@@ -910,6 +907,22 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
910907
}
911908
}
912909

910+
/// Returns `true` if the given pattern would be printed as a wildcard (`_`).
911+
fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool {
912+
match p.ctor() {
913+
Constructor::IntRange(IntRange {
914+
lo: MaybeInfiniteInt::NegInfinity,
915+
hi: MaybeInfiniteInt::PosInfinity,
916+
})
917+
| Constructor::Wildcard
918+
| Constructor::NonExhaustive
919+
| Constructor::Hidden
920+
| Constructor::PrivateUninhabited => true,
921+
Constructor::Never if !tcx.features().never_patterns => true,
922+
_ => false,
923+
}
924+
}
925+
913926
impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
914927
type Ty = RevealedTy<'tcx>;
915928
type Error = ErrorGuaranteed;

0 commit comments

Comments
 (0)