Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove print::Pat from the printing of WitnessPat #128965

Merged
merged 11 commits into from
Aug 16, 2024
83 changes: 45 additions & 38 deletions compiler/rustc_pattern_analysis/src/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -774,17 +774,16 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
}
}

/// Convert to a [`print::Pat`] for diagnostic purposes.
fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> print::Pat<'tcx> {
use print::{Pat, PatKind};
/// Prints an [`IntRange`] to a string for diagnostic purposes.
fn print_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> String {
use MaybeInfiniteInt::*;
let cx = self;
let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
PatKind::Wild
if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
"_".to_string()
} else if range.is_singleton() {
let lo = cx.hoist_pat_range_bdy(range.lo, ty);
let value = lo.as_finite().unwrap();
PatKind::Constant { value }
value.to_string()
} else {
// We convert to an inclusive range for diagnostics.
let mut end = rustc_hir::RangeEnd::Included;
Expand All @@ -807,32 +806,24 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
range.hi
};
let hi = cx.hoist_pat_range_bdy(hi, ty);
PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() }))
};

Pat { ty: ty.inner(), kind }
PatRange { lo, hi, end, ty: ty.inner() }.to_string()
}
}

/// Prints a [`WitnessPat`] to an owned string, for diagnostic purposes.
///
/// This panics for patterns that don't appear in diagnostics, like float ranges.
pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
// This works by converting the witness pattern to a `print::Pat`
// and then printing that, but callers don't need to know that.
self.hoist_witness_pat(pat).to_string()
}

/// Convert to a [`print::Pat`] for diagnostic purposes. This panics for patterns that don't
/// appear in diagnostics, like float ranges.
fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
use print::{FieldPat, Pat, PatKind};
let cx = self;
let hoist = |p| Box::new(cx.hoist_witness_pat(p));
let kind = match pat.ctor() {
Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) },
IntRange(range) => return self.hoist_pat_range(range, *pat.ty()),
let print = |p| cx.print_witness_pat(p);
match pat.ctor() {
Bool(b) => b.to_string(),
Str(s) => s.to_string(),
IntRange(range) => return self.print_pat_range(range, *pat.ty()),
Struct if pat.ty().is_box() => {
// Outside of the `alloc` crate, the only way to create a struct pattern
// of type `Box` is to use a `box` pattern via #[feature(box_patterns)].
PatKind::Box { subpattern: hoist(&pat.fields[0]) }
format!("box {}", print(&pat.fields[0]))
}
Struct | Variant(_) | UnionField => {
let enum_info = match *pat.ty().kind() {
Expand All @@ -847,12 +838,29 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
let subpatterns = pat
.iter_fields()
.enumerate()
.map(|(i, pat)| FieldPat { field: FieldIdx::new(i), pattern: hoist(pat) })
.map(|(i, pat)| print::FieldPat {
field: FieldIdx::new(i),
pattern: print(pat),
is_wildcard: would_print_as_wildcard(cx.tcx, pat),
})
.collect::<Vec<_>>();

PatKind::StructLike { enum_info, subpatterns }
let mut s = String::new();
print::write_struct_like(
&mut s,
self.tcx,
pat.ty().inner(),
&enum_info,
&subpatterns,
)
.unwrap();
s
}
Ref => {
let mut s = String::new();
print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
s
}
Ref => PatKind::Deref { subpattern: hoist(&pat.fields[0]) },
Slice(slice) => {
let (prefix_len, has_dot_dot) = match slice.kind {
SliceKind::FixedLen(len) => (len, false),
Expand All @@ -879,24 +887,23 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
}
}

let prefix = prefix.iter().map(hoist).collect();
let suffix = suffix.iter().map(hoist).collect();
let prefix = prefix.iter().map(print).collect::<Vec<_>>();
let suffix = suffix.iter().map(print).collect::<Vec<_>>();

PatKind::Slice { prefix, has_dot_dot, suffix }
let mut s = String::new();
print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap();
s
}
&Str(value) => PatKind::Constant { value },
Never if self.tcx.features().never_patterns => PatKind::Never,
Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
Never if self.tcx.features().never_patterns => "!".to_string(),
Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => "_".to_string(),
Missing { .. } => bug!(
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
`Missing` should have been processed in `apply_constructors`"
),
F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => {
bug!("can't convert to pattern: {:?}", pat)
}
};

Pat { ty: pat.ty().inner(), kind }
}
}
}

Expand Down Expand Up @@ -972,7 +979,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
overlaps_on: IntRange,
overlaps_with: &[&crate::pat::DeconstructedPat<Self>],
) {
let overlap_as_pat = self.hoist_pat_range(&overlaps_on, *pat.ty());
let overlap_as_pat = self.print_pat_range(&overlaps_on, *pat.ty());
let overlaps: Vec<_> = overlaps_with
.iter()
.map(|pat| pat.data().span)
Expand Down Expand Up @@ -1012,7 +1019,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
suggested_range.end = rustc_hir::RangeEnd::Included;
suggested_range.to_string()
};
let gap_as_pat = self.hoist_pat_range(&gap, *pat.ty());
let gap_as_pat = self.print_pat_range(&gap, *pat.ty());
if gapped_with.is_empty() {
// If `gapped_with` is empty, `gap == T::MAX`.
self.tcx.emit_node_span_lint(
Expand Down
89 changes: 15 additions & 74 deletions compiler/rustc_pattern_analysis/src/rustc/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,75 +11,16 @@

use std::fmt;

use rustc_middle::thir::PatRange;
use rustc_middle::bug;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_middle::{bug, mir};
use rustc_span::sym;
use rustc_target::abi::{FieldIdx, VariantIdx};

#[derive(Clone, Debug)]
pub(crate) struct FieldPat<'tcx> {
pub(crate) struct FieldPat {
pub(crate) field: FieldIdx,
pub(crate) pattern: Box<Pat<'tcx>>,
}

#[derive(Clone, Debug)]
pub(crate) struct Pat<'tcx> {
pub(crate) ty: Ty<'tcx>,
pub(crate) kind: PatKind<'tcx>,
}

#[derive(Clone, Debug)]
pub(crate) enum PatKind<'tcx> {
Wild,

StructLike {
enum_info: EnumInfo<'tcx>,
subpatterns: Vec<FieldPat<'tcx>>,
},

Box {
subpattern: Box<Pat<'tcx>>,
},

Deref {
subpattern: Box<Pat<'tcx>>,
},

Constant {
value: mir::Const<'tcx>,
},

Range(Box<PatRange<'tcx>>),

Slice {
prefix: Box<[Box<Pat<'tcx>>]>,
/// True if this slice-like pattern should include a `..` between the
/// prefix and suffix.
has_dot_dot: bool,
suffix: Box<[Box<Pat<'tcx>>]>,
},

Never,
}

impl<'tcx> fmt::Display for Pat<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
PatKind::Wild => write!(f, "_"),
PatKind::Never => write!(f, "!"),
PatKind::Box { ref subpattern } => write!(f, "box {subpattern}"),
PatKind::StructLike { ref enum_info, ref subpatterns } => {
ty::tls::with(|tcx| write_struct_like(f, tcx, self.ty, enum_info, subpatterns))
}
PatKind::Deref { ref subpattern } => write_ref_like(f, self.ty, subpattern),
PatKind::Constant { value } => write!(f, "{value}"),
PatKind::Range(ref range) => write!(f, "{range}"),
PatKind::Slice { ref prefix, has_dot_dot, ref suffix } => {
write_slice_like(f, prefix, has_dot_dot, suffix)
}
}
}
pub(crate) pattern: String,
pub(crate) is_wildcard: bool,
}

/// Returns a closure that will return `""` when called the first time,
Expand All @@ -103,12 +44,12 @@ pub(crate) enum EnumInfo<'tcx> {
NotEnum,
}

fn write_struct_like<'tcx>(
pub(crate) fn write_struct_like<'tcx>(
f: &mut impl fmt::Write,
tcx: TyCtxt<'_>,
ty: Ty<'tcx>,
enum_info: &EnumInfo<'tcx>,
subpatterns: &[FieldPat<'tcx>],
subpatterns: &[FieldPat],
) -> fmt::Result {
let variant_and_name = match *enum_info {
EnumInfo::Enum { adt_def, variant_index } => {
Expand Down Expand Up @@ -139,12 +80,12 @@ fn write_struct_like<'tcx>(
write!(f, " {{ ")?;

let mut printed = 0;
for p in subpatterns {
if let PatKind::Wild = p.pattern.kind {
for &FieldPat { field, ref pattern, is_wildcard } in subpatterns {
if is_wildcard {
continue;
}
let name = variant.fields[p.field].name;
write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?;
let field_name = variant.fields[field].name;
write!(f, "{}{field_name}: {pattern}", start_or_comma())?;
printed += 1;
}

Expand Down Expand Up @@ -184,10 +125,10 @@ fn write_struct_like<'tcx>(
Ok(())
}

fn write_ref_like<'tcx>(
pub(crate) fn write_ref_like<'tcx>(
f: &mut impl fmt::Write,
ty: Ty<'tcx>,
subpattern: &Pat<'tcx>,
subpattern: &str,
) -> fmt::Result {
match ty.kind() {
ty::Ref(_, _, mutbl) => {
Expand All @@ -198,11 +139,11 @@ fn write_ref_like<'tcx>(
write!(f, "{subpattern}")
}

fn write_slice_like<'tcx>(
pub(crate) fn write_slice_like(
f: &mut impl fmt::Write,
prefix: &[Box<Pat<'tcx>>],
prefix: &[String],
has_dot_dot: bool,
suffix: &[Box<Pat<'tcx>>],
suffix: &[String],
) -> fmt::Result {
let mut start_or_comma = start_or_comma();
write!(f, "[")?;
Expand Down
Loading