Skip to content

Commit f95e4f3

Browse files
committed
Improve code and documentation clarity
1 parent 7c98f6f commit f95e4f3

File tree

1 file changed

+75
-41
lines changed
  • compiler/rustc_mir_build/src/thir/pattern

1 file changed

+75
-41
lines changed

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

+75-41
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,10 @@
139139
//!
140140
//! It is computed as follows. We look at the pattern `p_1` on top of the stack,
141141
//! and we have three cases:
142-
//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
143-
//! 1.2. `p_1 = _`. We return the rest of the stack:
142+
//! 2.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
143+
//! 2.2. `p_1 = _`. We return the rest of the stack:
144144
//! p_2, .., p_n
145-
//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
145+
//! 2.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
146146
//! stack.
147147
//! D((r_1, p_2, .., p_n))
148148
//! D((r_2, p_2, .., p_n))
@@ -509,6 +509,14 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
509509
#[derive(Clone, Debug)]
510510
enum SpecializationCache {
511511
/// Patterns consist of only enum variants.
512+
/// Variant patterns does not intersect with each other (in contrast to range patterns),
513+
/// so it is possible to precompute the result of `Matrix::specialize_constructor` at a
514+
/// lower computational complexity.
515+
/// `lookup` is responsible for holding the precomputed result of
516+
/// `Matrix::specialize_constructor`, while `wilds` is used for two purposes: the first one is
517+
/// the precomputed result of `Matrix::specialize_wildcard`, and the second is to be used as a
518+
/// fallback for `Matrix::specialize_constructor` when it tries to apply a constructor that
519+
/// has not been seen in the `Matrix`. See `update_cache` for further explanations.
512520
Variants { lookup: FxHashMap<DefId, SmallVec<[usize; 1]>>, wilds: SmallVec<[usize; 1]> },
513521
/// Does not belong to the cases above, use the slow path.
514522
Incompatible,
@@ -523,7 +531,8 @@ crate struct Matrix<'p, 'tcx> {
523531

524532
impl<'p, 'tcx> Matrix<'p, 'tcx> {
525533
crate fn empty() -> Self {
526-
// Use SpecializationCache::Incompatible as a placeholder; the initialization is in push().
534+
// Use `SpecializationCache::Incompatible` as a placeholder; we will initialize it on the
535+
// first call to `push`. See the first half of `update_cache`.
527536
Matrix { patterns: vec![], cache: SpecializationCache::Incompatible }
528537
}
529538

@@ -536,47 +545,71 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
536545
self.push(row)
537546
}
538547
} else {
539-
if self.patterns.is_empty() {
540-
self.cache = if row.is_empty() {
541-
SpecializationCache::Incompatible
542-
} else {
543-
match *row.head().kind {
544-
PatKind::Variant { .. } => SpecializationCache::Variants {
545-
lookup: FxHashMap::default(),
546-
wilds: SmallVec::new(),
547-
},
548-
// Note: If the first pattern is a wildcard, then all patterns after that is not
549-
// useful. The check is simple enough so we treat it as the same as unsupported
550-
// patterns.
551-
_ => SpecializationCache::Incompatible,
552-
}
553-
};
554-
}
555-
let idx_to_insert = self.patterns.len();
556-
match &mut self.cache {
557-
SpecializationCache::Variants { ref mut lookup, ref mut wilds } => {
558-
let head = row.head();
559-
match *head.kind {
560-
_ if head.is_wildcard() => {
561-
for (_, v) in lookup.iter_mut() {
562-
v.push(idx_to_insert);
563-
}
564-
wilds.push(idx_to_insert);
565-
}
566-
PatKind::Variant { adt_def, variant_index, .. } => {
567-
lookup
568-
.entry(adt_def.variants[variant_index].def_id)
569-
.or_insert_with(|| wilds.clone())
570-
.push(idx_to_insert);
571-
}
572-
_ => {
573-
self.cache = SpecializationCache::Incompatible;
548+
self.patterns.push(row);
549+
self.update_cache(self.patterns.len() - 1);
550+
}
551+
}
552+
553+
fn update_cache(&mut self, idx: usize) {
554+
let row = &self.patterns[idx];
555+
// We don't know which kind of cache could be used until we see the first row; therefore an
556+
// empty `Matrix` is initialized with `SpecializationCache::Empty`, then the cache is
557+
// assigned the appropriate variant below on the first call to `push`.
558+
if self.patterns.is_empty() {
559+
self.cache = if row.is_empty() {
560+
SpecializationCache::Incompatible
561+
} else {
562+
match *row.head().kind {
563+
PatKind::Variant { .. } => SpecializationCache::Variants {
564+
lookup: FxHashMap::default(),
565+
wilds: SmallVec::new(),
566+
},
567+
// Note: If the first pattern is a wildcard, then all patterns after that is not
568+
// useful. The check is simple enough so we treat it as the same as unsupported
569+
// patterns.
570+
_ => SpecializationCache::Incompatible,
571+
}
572+
};
573+
}
574+
// Update the cache.
575+
match &mut self.cache {
576+
SpecializationCache::Variants { ref mut lookup, ref mut wilds } => {
577+
let head = row.head();
578+
match *head.kind {
579+
_ if head.is_wildcard() => {
580+
// Per rule 1.3 in the top-level comments, a wildcard pattern is included in
581+
// the result of `specialize_constructor` for *any* `Constructor`.
582+
// We push the wildcard pattern to the precomputed result for constructors
583+
// that we have seen before; results for constructors we have not yet seen
584+
// defaults to `wilds`, which is updated right below.
585+
for (_, v) in lookup.iter_mut() {
586+
v.push(idx);
574587
}
588+
// Per rule 2.1 and 2.2 in the top-level comments, only wildcard patterns
589+
// are included in the result of `specialize_wildcard`.
590+
// What we do here is to track the wildcards we have seen; so in addition to
591+
// acting as the precomputed result of `specialize_wildcard`, `wilds` also
592+
// serves as the default value of `specialize_constructor` for constructors
593+
// that are not in `lookup`.
594+
wilds.push(idx);
595+
}
596+
PatKind::Variant { adt_def, variant_index, .. } => {
597+
// Handle the cases of rule 1.1 and 1.2 in the top-level comments.
598+
// A variant pattern can only be included in the results of
599+
// `specialize_constructor` for a particular constructor, therefore we are
600+
// using a HashMap to track that.
601+
lookup
602+
.entry(adt_def.variants[variant_index].def_id)
603+
// Default to `wilds` for absent keys. See above for an explanation.
604+
.or_insert_with(|| wilds.clone())
605+
.push(idx);
606+
}
607+
_ => {
608+
self.cache = SpecializationCache::Incompatible;
575609
}
576610
}
577-
SpecializationCache::Incompatible => {}
578611
}
579-
self.patterns.push(row);
612+
SpecializationCache::Incompatible => {}
580613
}
581614
}
582615

@@ -609,6 +642,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
609642
if let Constructor::Variant(id) = constructor {
610643
lookup
611644
.get(id)
645+
// Default to `wilds` for absent keys. See `update_cache` for an explanation.
612646
.unwrap_or(&wilds)
613647
.iter()
614648
.filter_map(|&i| {

0 commit comments

Comments
 (0)