Skip to content

Commit 1190e72

Browse files
committed
Cache head constructor in PatStack
Since the constructor is recomputed a lot, caching is worth it.
1 parent 833089f commit 1190e72

File tree

2 files changed

+63
-68
lines changed

2 files changed

+63
-68
lines changed

compiler/rustc_mir_build/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#![feature(control_flow_enum)]
1010
#![feature(crate_visibility_modifier)]
1111
#![feature(bool_to_option)]
12+
#![feature(once_cell)]
1213
#![feature(or_patterns)]
1314
#![recursion_limit = "256"]
1415

compiler/rustc_mir_build/src/thir/pattern/_match.rs

Lines changed: 62 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ use self::WitnessPreference::*;
295295

296296
use rustc_data_structures::captures::Captures;
297297
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
298+
use rustc_data_structures::sync::OnceCell;
298299
use rustc_index::vec::Idx;
299300

300301
use super::{compare_const_vals, PatternFoldable, PatternFolder};
@@ -346,32 +347,40 @@ impl<'tcx> Pat<'tcx> {
346347

347348
/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
348349
/// works well.
349-
#[derive(Debug, Clone, PartialEq)]
350-
crate struct PatStack<'p, 'tcx>(SmallVec<[&'p Pat<'tcx>; 2]>);
350+
#[derive(Debug, Clone)]
351+
crate struct PatStack<'p, 'tcx> {
352+
pats: SmallVec<[&'p Pat<'tcx>; 2]>,
353+
/// Cache for the constructor of the head
354+
head_ctor: OnceCell<Constructor<'tcx>>,
355+
}
351356

352357
impl<'p, 'tcx> PatStack<'p, 'tcx> {
353358
crate fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
354-
PatStack(smallvec![pat])
359+
Self::from_vec(smallvec![pat])
355360
}
356361

357362
fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self {
358-
PatStack(vec)
363+
PatStack { pats: vec, head_ctor: OnceCell::new() }
359364
}
360365

361366
fn is_empty(&self) -> bool {
362-
self.0.is_empty()
367+
self.pats.is_empty()
363368
}
364369

365370
fn len(&self) -> usize {
366-
self.0.len()
371+
self.pats.len()
367372
}
368373

369374
fn head(&self) -> &'p Pat<'tcx> {
370-
self.0[0]
375+
self.pats[0]
376+
}
377+
378+
fn head_ctor<'a>(&'a self, cx: &MatchCheckCtxt<'p, 'tcx>) -> &'a Constructor<'tcx> {
379+
self.head_ctor.get_or_init(|| pat_constructor(cx, self.head()))
371380
}
372381

373382
fn iter(&self) -> impl Iterator<Item = &Pat<'tcx>> {
374-
self.0.iter().copied()
383+
self.pats.iter().copied()
375384
}
376385

377386
// If the first pattern is an or-pattern, expand this pattern. Otherwise, return `None`.
@@ -383,7 +392,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
383392
pats.iter()
384393
.map(|pat| {
385394
let mut new_patstack = PatStack::from_pattern(pat);
386-
new_patstack.0.extend_from_slice(&self.0[1..]);
395+
new_patstack.pats.extend_from_slice(&self.pats[1..]);
387396
new_patstack
388397
})
389398
.collect(),
@@ -414,16 +423,13 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
414423
is_my_head_ctor: bool,
415424
) -> Option<PatStack<'p, 'tcx>> {
416425
// We return `None` if `ctor` is not covered by `self.head()`. If `ctor` is known to be
417-
// derived from `self.head()`, then we don't need to check; otherwise, we compute the
418-
// constructor of `self.head()` and check for constructor inclusion.
426+
// derived from `self.head()`, then we don't need to check; otherwise, we check for
427+
// constructor inclusion.
419428
// Note that this shortcut is also necessary for correctness: a pattern should always be
420429
// specializable with its own constructor, even in cases where we refuse to inspect values like
421430
// opaque constants.
422-
if !is_my_head_ctor {
423-
let head_ctor = pat_constructor(cx.tcx, cx.param_env, self.head());
424-
if !ctor.is_covered_by(cx, &head_ctor, self.head().ty) {
425-
return None;
426-
}
431+
if !is_my_head_ctor && !ctor.is_covered_by(cx, self.head_ctor(cx), self.head().ty) {
432+
return None;
427433
}
428434
let new_fields = ctor_wild_subpatterns.replace_with_pattern_arguments(self.head());
429435

@@ -437,13 +443,19 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
437443

438444
// We pop the head pattern and push the new fields extracted from the arguments of
439445
// `self.head()`.
440-
Some(new_fields.push_on_patstack(&self.0[1..]))
446+
Some(new_fields.push_on_patstack(&self.pats[1..]))
441447
}
442448
}
443449

444450
impl<'p, 'tcx> Default for PatStack<'p, 'tcx> {
445451
fn default() -> Self {
446-
PatStack(smallvec![])
452+
Self::from_vec(smallvec![])
453+
}
454+
}
455+
456+
impl<'p, 'tcx> PartialEq for PatStack<'p, 'tcx> {
457+
fn eq(&self, other: &Self) -> bool {
458+
self.pats == other.pats
447459
}
448460
}
449461

@@ -452,7 +464,7 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
452464
where
453465
T: IntoIterator<Item = &'p Pat<'tcx>>,
454466
{
455-
PatStack(iter.into_iter().collect())
467+
Self::from_vec(iter.into_iter().collect())
456468
}
457469
}
458470

@@ -570,6 +582,14 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
570582
self.patterns.iter().map(|r| r.head())
571583
}
572584

585+
/// Iterate over the first constructor of each row
586+
fn head_ctors<'a>(
587+
&'a self,
588+
cx: &'a MatchCheckCtxt<'p, 'tcx>,
589+
) -> impl Iterator<Item = &'a Constructor<'tcx>> + Captures<'a> + Captures<'p> {
590+
self.patterns.iter().map(move |r| r.head_ctor(cx))
591+
}
592+
573593
/// This computes `S(constructor, self)`. See top of the file for explanations.
574594
fn specialize_constructor(
575595
&self,
@@ -906,10 +926,7 @@ impl Slice {
906926
_ => return smallvec![Slice(self)],
907927
};
908928

909-
let head_ctors = matrix
910-
.heads()
911-
.map(|p| pat_constructor(cx.tcx, cx.param_env, p))
912-
.filter(|c| !c.is_wildcard());
929+
let head_ctors = matrix.head_ctors(cx).filter(|c| !c.is_wildcard());
913930

914931
let mut max_prefix_len = self_prefix;
915932
let mut max_suffix_len = self_suffix;
@@ -1120,7 +1137,7 @@ impl<'tcx> Constructor<'tcx> {
11201137
/// `hir_id` is `None` when we're evaluating the wildcard pattern. In that case we do not want
11211138
/// to lint for overlapping ranges.
11221139
fn split<'p>(
1123-
self,
1140+
&self,
11241141
cx: &MatchCheckCtxt<'p, 'tcx>,
11251142
pcx: PatCtxt<'tcx>,
11261143
matrix: &Matrix<'p, 'tcx>,
@@ -1138,7 +1155,7 @@ impl<'tcx> Constructor<'tcx> {
11381155
}
11391156
Slice(slice @ Slice { kind: VarLen(..), .. }) => slice.split(cx, matrix),
11401157
// Any other constructor can be used unchanged.
1141-
_ => smallvec![self],
1158+
_ => smallvec![self.clone()],
11421159
}
11431160
}
11441161

@@ -1991,25 +2008,9 @@ impl<'tcx> IntRange<'tcx> {
19912008
}
19922009
}
19932010

1994-
fn from_pat(
1995-
tcx: TyCtxt<'tcx>,
1996-
param_env: ty::ParamEnv<'tcx>,
1997-
pat: &Pat<'tcx>,
1998-
) -> Option<IntRange<'tcx>> {
1999-
// This MUST be kept in sync with `pat_constructor`.
2000-
match *pat.kind {
2001-
PatKind::Constant { value } => Self::from_const(tcx, param_env, value, pat.span),
2002-
PatKind::Range(PatRange { lo, hi, end }) => {
2003-
let ty = lo.ty;
2004-
Self::from_range(
2005-
tcx,
2006-
lo.eval_bits(tcx, param_env, lo.ty),
2007-
hi.eval_bits(tcx, param_env, hi.ty),
2008-
ty,
2009-
&end,
2010-
pat.span,
2011-
)
2012-
}
2011+
fn from_ctor<'a>(ctor: &'a Constructor<'tcx>) -> Option<&'a IntRange<'tcx>> {
2012+
match ctor {
2013+
IntRange(range) => Some(range),
20132014
_ => None,
20142015
}
20152016
}
@@ -2145,7 +2146,7 @@ impl<'tcx> IntRange<'tcx> {
21452146
/// between every pair of boundary points. (This essentially sums up to performing the intuitive
21462147
/// merging operation depicted above.)
21472148
fn split<'p>(
2148-
self,
2149+
&self,
21492150
cx: &MatchCheckCtxt<'p, 'tcx>,
21502151
pcx: PatCtxt<'tcx>,
21512152
matrix: &Matrix<'p, 'tcx>,
@@ -2176,15 +2177,13 @@ impl<'tcx> IntRange<'tcx> {
21762177
// Collect the span and range of all the intersecting ranges to lint on likely
21772178
// incorrect range patterns. (#63987)
21782179
let mut overlaps = vec![];
2180+
let row_len = matrix.patterns.get(0).map(|r| r.len()).unwrap_or(0);
21792181
// `borders` is the set of borders between equivalence classes: each equivalence
21802182
// class lies between 2 borders.
21812183
let row_borders = matrix
2182-
.patterns
2183-
.iter()
2184-
.flat_map(|row| {
2185-
IntRange::from_pat(cx.tcx, cx.param_env, row.head()).map(|r| (r, row.len()))
2186-
})
2187-
.flat_map(|(range, row_len)| {
2184+
.head_ctors(cx)
2185+
.filter_map(|ctor| IntRange::from_ctor(ctor))
2186+
.filter_map(|range| {
21882187
let intersection = self.intersection(cx.tcx, &range);
21892188
let should_lint = self.suspicious_intersection(&range);
21902189
if let (Some(range), 1, true) = (&intersection, row_len, should_lint) {
@@ -2229,7 +2228,7 @@ impl<'tcx> IntRange<'tcx> {
22292228
}
22302229

22312230
fn lint_overlapping_patterns(
2232-
self,
2231+
&self,
22332232
tcx: TyCtxt<'tcx>,
22342233
hir_id: Option<HirId>,
22352234
ty: Ty<'tcx>,
@@ -2412,7 +2411,7 @@ crate fn is_useful<'p, 'tcx>(
24122411

24132412
debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head());
24142413

2415-
let constructor = pat_constructor(cx.tcx, cx.param_env, v.head());
2414+
let constructor = v.head_ctor(cx);
24162415
let ret = if !constructor.is_wildcard() {
24172416
debug!("is_useful - expanding constructor: {:#?}", constructor);
24182417
constructor
@@ -2435,11 +2434,8 @@ crate fn is_useful<'p, 'tcx>(
24352434
} else {
24362435
debug!("is_useful - expanding wildcard");
24372436

2438-
let used_ctors: Vec<Constructor<'_>> = matrix
2439-
.heads()
2440-
.map(|p| pat_constructor(cx.tcx, cx.param_env, p))
2441-
.filter(|c| !c.is_wildcard())
2442-
.collect();
2437+
let used_ctors: Vec<Constructor<'_>> =
2438+
matrix.head_ctors(cx).cloned().filter(|c| !c.is_wildcard()).collect();
24432439
debug!("is_useful_used_ctors = {:#?}", used_ctors);
24442440
// `all_ctors` are all the constructors for the given type, which
24452441
// should all be represented (or caught with the wild pattern `_`).
@@ -2563,12 +2559,10 @@ fn is_useful_specialized<'p, 'tcx>(
25632559

25642560
/// Determines the constructor that the given pattern can be specialized to.
25652561
/// Returns `None` in case of a catch-all, which can't be specialized.
2566-
fn pat_constructor<'tcx>(
2567-
tcx: TyCtxt<'tcx>,
2568-
param_env: ty::ParamEnv<'tcx>,
2569-
pat: &Pat<'tcx>,
2562+
fn pat_constructor<'p, 'tcx>(
2563+
cx: &MatchCheckCtxt<'p, 'tcx>,
2564+
pat: &'p Pat<'tcx>,
25702565
) -> Constructor<'tcx> {
2571-
// This MUST be kept in sync with `IntRange::from_pat`.
25722566
match *pat.kind {
25732567
PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
25742568
PatKind::Binding { .. } | PatKind::Wild => Wildcard,
@@ -2577,7 +2571,7 @@ fn pat_constructor<'tcx>(
25772571
Variant(adt_def.variants[variant_index].def_id)
25782572
}
25792573
PatKind::Constant { value } => {
2580-
if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) {
2574+
if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value, pat.span) {
25812575
IntRange(int_range)
25822576
} else {
25832577
match value.ty.kind() {
@@ -2593,9 +2587,9 @@ fn pat_constructor<'tcx>(
25932587
PatKind::Range(PatRange { lo, hi, end }) => {
25942588
let ty = lo.ty;
25952589
if let Some(int_range) = IntRange::from_range(
2596-
tcx,
2597-
lo.eval_bits(tcx, param_env, lo.ty),
2598-
hi.eval_bits(tcx, param_env, hi.ty),
2590+
cx.tcx,
2591+
lo.eval_bits(cx.tcx, cx.param_env, lo.ty),
2592+
hi.eval_bits(cx.tcx, cx.param_env, hi.ty),
25992593
ty,
26002594
&end,
26012595
pat.span,
@@ -2608,7 +2602,7 @@ fn pat_constructor<'tcx>(
26082602
PatKind::Array { ref prefix, ref slice, ref suffix }
26092603
| PatKind::Slice { ref prefix, ref slice, ref suffix } => {
26102604
let array_len = match pat.ty.kind() {
2611-
ty::Array(_, length) => Some(length.eval_usize(tcx, param_env)),
2605+
ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env)),
26122606
ty::Slice(_) => None,
26132607
_ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
26142608
};

0 commit comments

Comments
 (0)