Skip to content

Commit 853255e

Browse files
authored
Rollup merge of #128536 - Zalathar:print-cleanup, r=Nadrieril
Preliminary cleanup of `WitnessPat` hoisting/printing Follow-up to #128430. The eventual goal is to remove `print::Pat` entirely, but in the course of working towards that I made so many small improvements that it seems wise to let those be reviewed/merged on their own first. Best reviewed commit-by-commit, most of which should be pretty simple and straightforward. r? ``@Nadrieril``
2 parents e8f6819 + 482412c commit 853255e

File tree

3 files changed

+216
-185
lines changed

3 files changed

+216
-185
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

+68-64
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::constructor::{
2323
};
2424
use crate::lints::lint_nonexhaustive_missing_variants;
2525
use crate::pat_column::PatternColumn;
26+
use crate::rustc::print::EnumInfo;
2627
use crate::usefulness::{compute_match_usefulness, PlaceValidity};
2728
use crate::{errors, Captures, PatCx, PrivateUninhabitedField};
2829

@@ -824,77 +825,64 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
824825
fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
825826
use print::{FieldPat, Pat, PatKind};
826827
let cx = self;
827-
let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild);
828-
let mut subpatterns = pat.iter_fields().map(|p| Box::new(cx.hoist_witness_pat(p)));
828+
let hoist = |p| Box::new(cx.hoist_witness_pat(p));
829829
let kind = match pat.ctor() {
830830
Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) },
831831
IntRange(range) => return self.hoist_pat_range(range, *pat.ty()),
832-
Struct | Variant(_) | UnionField => match pat.ty().kind() {
833-
ty::Tuple(..) => PatKind::Leaf {
834-
subpatterns: subpatterns
835-
.enumerate()
836-
.map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern })
837-
.collect(),
838-
},
839-
ty::Adt(adt_def, _) if adt_def.is_box() => {
840-
// Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
841-
// of `std`). So this branch is only reachable when the feature is enabled and
842-
// the pattern is a box pattern.
843-
PatKind::Deref { subpattern: subpatterns.next().unwrap() }
844-
}
845-
ty::Adt(adt_def, _args) => {
846-
let variant_index = RustcPatCtxt::variant_index_for_adt(&pat.ctor(), *adt_def);
847-
let subpatterns = subpatterns
848-
.enumerate()
849-
.map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern })
850-
.collect();
832+
Struct if pat.ty().is_box() => {
833+
// Outside of the `alloc` crate, the only way to create a struct pattern
834+
// of type `Box` is to use a `box` pattern via #[feature(box_patterns)].
835+
PatKind::Box { subpattern: hoist(&pat.fields[0]) }
836+
}
837+
Struct | Variant(_) | UnionField => {
838+
let enum_info = match *pat.ty().kind() {
839+
ty::Adt(adt_def, _) if adt_def.is_enum() => EnumInfo::Enum {
840+
adt_def,
841+
variant_index: RustcPatCtxt::variant_index_for_adt(pat.ctor(), adt_def),
842+
},
843+
ty::Adt(..) | ty::Tuple(..) => EnumInfo::NotEnum,
844+
_ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()),
845+
};
851846

852-
if adt_def.is_enum() {
853-
PatKind::Variant { adt_def: *adt_def, variant_index, subpatterns }
854-
} else {
855-
PatKind::Leaf { subpatterns }
856-
}
857-
}
858-
_ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()),
859-
},
860-
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
861-
// be careful to reconstruct the correct constant pattern here. However a string
862-
// literal pattern will never be reported as a non-exhaustiveness witness, so we
863-
// ignore this issue.
864-
Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
847+
let subpatterns = pat
848+
.iter_fields()
849+
.enumerate()
850+
.map(|(i, pat)| FieldPat { field: FieldIdx::new(i), pattern: hoist(pat) })
851+
.collect::<Vec<_>>();
852+
853+
PatKind::StructLike { enum_info, subpatterns }
854+
}
855+
Ref => PatKind::Deref { subpattern: hoist(&pat.fields[0]) },
865856
Slice(slice) => {
866-
match slice.kind {
867-
SliceKind::FixedLen(_) => PatKind::Slice {
868-
prefix: subpatterns.collect(),
869-
slice: None,
870-
suffix: Box::new([]),
871-
},
872-
SliceKind::VarLen(prefix, _) => {
873-
let mut subpatterns = subpatterns.peekable();
874-
let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
875-
if slice.array_len.is_some() {
876-
// Improves diagnostics a bit: if the type is a known-size array, instead
877-
// of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
878-
// This is incorrect if the size is not known, since `[_, ..]` captures
879-
// arrays of lengths `>= 1` whereas `[..]` captures any length.
880-
while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
881-
prefix.pop();
882-
}
883-
while subpatterns.peek().is_some()
884-
&& is_wildcard(subpatterns.peek().unwrap())
885-
{
886-
subpatterns.next();
887-
}
888-
}
889-
let suffix: Box<[_]> = subpatterns.collect();
890-
let wild = Pat { ty: pat.ty().inner(), kind: PatKind::Wild };
891-
PatKind::Slice {
892-
prefix: prefix.into_boxed_slice(),
893-
slice: Some(Box::new(wild)),
894-
suffix,
895-
}
857+
let (prefix_len, has_dot_dot) = match slice.kind {
858+
SliceKind::FixedLen(len) => (len, false),
859+
SliceKind::VarLen(prefix_len, _) => (prefix_len, true),
860+
};
861+
862+
let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len);
863+
864+
// If the pattern contains a `..`, but is applied to values of statically-known
865+
// length (arrays), then we can slightly simplify diagnostics by merging any
866+
// adjacent wildcard patterns into the `..`: `[x, _, .., _, y]` => `[x, .., y]`.
867+
// (This simplification isn't allowed for slice values, because in that case
868+
// `[x, .., y]` would match some slices that `[x, _, .., _, y]` would not.)
869+
if has_dot_dot && slice.array_len.is_some() {
870+
while let [rest @ .., last] = prefix
871+
&& would_print_as_wildcard(cx.tcx, last)
872+
{
873+
prefix = rest;
874+
}
875+
while let [first, rest @ ..] = suffix
876+
&& would_print_as_wildcard(cx.tcx, first)
877+
{
878+
suffix = rest;
896879
}
897880
}
881+
882+
let prefix = prefix.iter().map(hoist).collect();
883+
let suffix = suffix.iter().map(hoist).collect();
884+
885+
PatKind::Slice { prefix, has_dot_dot, suffix }
898886
}
899887
&Str(value) => PatKind::Constant { value },
900888
Never if self.tcx.features().never_patterns => PatKind::Never,
@@ -912,6 +900,22 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
912900
}
913901
}
914902

903+
/// Returns `true` if the given pattern would be printed as a wildcard (`_`).
904+
fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool {
905+
match p.ctor() {
906+
Constructor::IntRange(IntRange {
907+
lo: MaybeInfiniteInt::NegInfinity,
908+
hi: MaybeInfiniteInt::PosInfinity,
909+
})
910+
| Constructor::Wildcard
911+
| Constructor::NonExhaustive
912+
| Constructor::Hidden
913+
| Constructor::PrivateUninhabited => true,
914+
Constructor::Never if !tcx.features().never_patterns => true,
915+
_ => false,
916+
}
917+
}
918+
915919
impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
916920
type Ty = RevealedTy<'tcx>;
917921
type Error = ErrorGuaranteed;

0 commit comments

Comments
 (0)