1
Fork 0

Improve code and documentation clarity

This commit is contained in:
Ishi Tatsuyuki 2020-09-21 20:29:12 +09:00
parent 7c98f6f584
commit f95e4f3ca9

View file

@ -139,10 +139,10 @@
//! //!
//! It is computed as follows. We look at the pattern `p_1` on top of the stack, //! It is computed as follows. We look at the pattern `p_1` on top of the stack,
//! and we have three cases: //! and we have three cases:
//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing. //! 2.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
//! 1.2. `p_1 = _`. We return the rest of the stack: //! 2.2. `p_1 = _`. We return the rest of the stack:
//! p_2, .., p_n //! p_2, .., p_n
//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting //! 2.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
//! stack. //! stack.
//! D((r_1, p_2, .., p_n)) //! D((r_1, p_2, .., p_n))
//! D((r_2, p_2, .., p_n)) //! D((r_2, p_2, .., p_n))
@ -509,6 +509,14 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum SpecializationCache { enum SpecializationCache {
/// Patterns consist of only enum variants. /// Patterns consist of only enum variants.
/// Variant patterns does not intersect with each other (in contrast to range patterns),
/// so it is possible to precompute the result of `Matrix::specialize_constructor` at a
/// lower computational complexity.
/// `lookup` is responsible for holding the precomputed result of
/// `Matrix::specialize_constructor`, while `wilds` is used for two purposes: the first one is
/// the precomputed result of `Matrix::specialize_wildcard`, and the second is to be used as a
/// fallback for `Matrix::specialize_constructor` when it tries to apply a constructor that
/// has not been seen in the `Matrix`. See `update_cache` for further explanations.
Variants { lookup: FxHashMap<DefId, SmallVec<[usize; 1]>>, wilds: SmallVec<[usize; 1]> }, Variants { lookup: FxHashMap<DefId, SmallVec<[usize; 1]>>, wilds: SmallVec<[usize; 1]> },
/// Does not belong to the cases above, use the slow path. /// Does not belong to the cases above, use the slow path.
Incompatible, Incompatible,
@ -523,7 +531,8 @@ crate struct Matrix<'p, 'tcx> {
impl<'p, 'tcx> Matrix<'p, 'tcx> { impl<'p, 'tcx> Matrix<'p, 'tcx> {
crate fn empty() -> Self { crate fn empty() -> Self {
// Use SpecializationCache::Incompatible as a placeholder; the initialization is in push(). // Use `SpecializationCache::Incompatible` as a placeholder; we will initialize it on the
// first call to `push`. See the first half of `update_cache`.
Matrix { patterns: vec![], cache: SpecializationCache::Incompatible } Matrix { patterns: vec![], cache: SpecializationCache::Incompatible }
} }
@ -536,47 +545,71 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
self.push(row) self.push(row)
} }
} else { } else {
if self.patterns.is_empty() { self.patterns.push(row);
self.cache = if row.is_empty() { self.update_cache(self.patterns.len() - 1);
SpecializationCache::Incompatible }
} else { }
match *row.head().kind {
PatKind::Variant { .. } => SpecializationCache::Variants { fn update_cache(&mut self, idx: usize) {
lookup: FxHashMap::default(), let row = &self.patterns[idx];
wilds: SmallVec::new(), // We don't know which kind of cache could be used until we see the first row; therefore an
}, // empty `Matrix` is initialized with `SpecializationCache::Empty`, then the cache is
// Note: If the first pattern is a wildcard, then all patterns after that is not // assigned the appropriate variant below on the first call to `push`.
// useful. The check is simple enough so we treat it as the same as unsupported if self.patterns.is_empty() {
// patterns. self.cache = if row.is_empty() {
_ => SpecializationCache::Incompatible, SpecializationCache::Incompatible
} else {
match *row.head().kind {
PatKind::Variant { .. } => SpecializationCache::Variants {
lookup: FxHashMap::default(),
wilds: SmallVec::new(),
},
// Note: If the first pattern is a wildcard, then all patterns after that is not
// useful. The check is simple enough so we treat it as the same as unsupported
// patterns.
_ => SpecializationCache::Incompatible,
}
};
}
// Update the cache.
match &mut self.cache {
SpecializationCache::Variants { ref mut lookup, ref mut wilds } => {
let head = row.head();
match *head.kind {
_ if head.is_wildcard() => {
// Per rule 1.3 in the top-level comments, a wildcard pattern is included in
// the result of `specialize_constructor` for *any* `Constructor`.
// We push the wildcard pattern to the precomputed result for constructors
// that we have seen before; results for constructors we have not yet seen
// defaults to `wilds`, which is updated right below.
for (_, v) in lookup.iter_mut() {
v.push(idx);
}
// Per rule 2.1 and 2.2 in the top-level comments, only wildcard patterns
// are included in the result of `specialize_wildcard`.
// What we do here is to track the wildcards we have seen; so in addition to
// acting as the precomputed result of `specialize_wildcard`, `wilds` also
// serves as the default value of `specialize_constructor` for constructors
// that are not in `lookup`.
wilds.push(idx);
} }
}; PatKind::Variant { adt_def, variant_index, .. } => {
} // Handle the cases of rule 1.1 and 1.2 in the top-level comments.
let idx_to_insert = self.patterns.len(); // A variant pattern can only be included in the results of
match &mut self.cache { // `specialize_constructor` for a particular constructor, therefore we are
SpecializationCache::Variants { ref mut lookup, ref mut wilds } => { // using a HashMap to track that.
let head = row.head(); lookup
match *head.kind { .entry(adt_def.variants[variant_index].def_id)
_ if head.is_wildcard() => { // Default to `wilds` for absent keys. See above for an explanation.
for (_, v) in lookup.iter_mut() { .or_insert_with(|| wilds.clone())
v.push(idx_to_insert); .push(idx);
} }
wilds.push(idx_to_insert); _ => {
} self.cache = SpecializationCache::Incompatible;
PatKind::Variant { adt_def, variant_index, .. } => {
lookup
.entry(adt_def.variants[variant_index].def_id)
.or_insert_with(|| wilds.clone())
.push(idx_to_insert);
}
_ => {
self.cache = SpecializationCache::Incompatible;
}
} }
} }
SpecializationCache::Incompatible => {}
} }
self.patterns.push(row); SpecializationCache::Incompatible => {}
} }
} }
@ -609,6 +642,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
if let Constructor::Variant(id) = constructor { if let Constructor::Variant(id) = constructor {
lookup lookup
.get(id) .get(id)
// Default to `wilds` for absent keys. See `update_cache` for an explanation.
.unwrap_or(&wilds) .unwrap_or(&wilds)
.iter() .iter()
.filter_map(|&i| { .filter_map(|&i| {