about summary refs log tree commit diff
diff options
context:
space:
mode:
authorIshi Tatsuyuki <ishitatsuyuki@gmail.com>2020-09-21 20:29:12 +0900
committerIshi Tatsuyuki <ishitatsuyuki@gmail.com>2020-09-21 20:29:12 +0900
commitf95e4f3ca9860bfef68ca279017db8c035966dbe (patch)
treec466c7833100b4a19b3b0554b9f88ce2c8433e15
parent7c98f6f58440d80f6116e4d060778ab0a9b98131 (diff)
downloadrust-f95e4f3ca9860bfef68ca279017db8c035966dbe.tar.gz
rust-f95e4f3ca9860bfef68ca279017db8c035966dbe.zip
Improve code and documentation clarity
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/_match.rs116
1 files changed, 75 insertions, 41 deletions
diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs
index 1cbfc73a9c6..ff14ffc2a11 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs
@@ -139,10 +139,10 @@
 //!
 //!    It is computed as follows. We look at the pattern `p_1` on top of the stack,
 //!    and we have three cases:
-//!         1.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.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
+//!         2.2. `p_1 = _`. We return the rest of the stack:
 //!                 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.
 //!                 D((r_1, 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)]
 enum SpecializationCache {
     /// 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]> },
     /// Does not belong to the cases above, use the slow path.
     Incompatible,
@@ -523,7 +531,8 @@ crate struct Matrix<'p, 'tcx> {
 
 impl<'p, 'tcx> Matrix<'p, 'tcx> {
     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 }
     }
 
@@ -536,47 +545,71 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
                 self.push(row)
             }
         } else {
-            if self.patterns.is_empty() {
-                self.cache = if row.is_empty() {
-                    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,
-                    }
-                };
-            }
-            let idx_to_insert = self.patterns.len();
-            match &mut self.cache {
-                SpecializationCache::Variants { ref mut lookup, ref mut wilds } => {
-                    let head = row.head();
-                    match *head.kind {
-                        _ if head.is_wildcard() => {
-                            for (_, v) in lookup.iter_mut() {
-                                v.push(idx_to_insert);
-                            }
-                            wilds.push(idx_to_insert);
-                        }
-                        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;
+            self.patterns.push(row);
+            self.update_cache(self.patterns.len() - 1);
+        }
+    }
+
+    fn update_cache(&mut self, idx: usize) {
+        let row = &self.patterns[idx];
+        // 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
+        // assigned the appropriate variant below on the first call to `push`.
+        if self.patterns.is_empty() {
+            self.cache = if row.is_empty() {
+                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.
+                        // A variant pattern can only be included in the results of
+                        // `specialize_constructor` for a particular constructor, therefore we are
+                        // using a HashMap to track that.
+                        lookup
+                            .entry(adt_def.variants[variant_index].def_id)
+                            // Default to `wilds` for absent keys. See above for an explanation.
+                            .or_insert_with(|| wilds.clone())
+                            .push(idx);
+                    }
+                    _ => {
+                        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 {
                     lookup
                         .get(id)
+                        // Default to `wilds` for absent keys. See `update_cache` for an explanation.
                         .unwrap_or(&wilds)
                         .iter()
                         .filter_map(|&i| {