about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNadrieril <nadrieril+git@gmail.com>2019-11-21 18:45:28 +0000
committerNadrieril <nadrieril+git@gmail.com>2019-11-21 18:45:28 +0000
commit6e85f467c59e7ae943404a2aee9cc83ee475cf23 (patch)
tree50f7fda155477b593c16ed3b8976b690f2b3c154
parentf1b882b55805c342e46ee4ca3beeef1d1fa2044b (diff)
downloadrust-6e85f467c59e7ae943404a2aee9cc83ee475cf23.tar.gz
rust-6e85f467c59e7ae943404a2aee9cc83ee475cf23.zip
Initial implementation of or-pattern usefulness checking
-rw-r--r--src/librustc_mir/hair/pattern/_match.rs60
-rw-r--r--src/test/compile-fail/or-patterns.rs45
2 files changed, 91 insertions, 14 deletions
diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs
index 5e7a7f01e7a..7d3bbbc1ee4 100644
--- a/src/librustc_mir/hair/pattern/_match.rs
+++ b/src/librustc_mir/hair/pattern/_match.rs
@@ -399,6 +399,25 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
         self.0.iter().map(|p| *p)
     }
 
+    // If the first pattern is an or-pattern, expand this pattern. Otherwise, return `None`.
+    fn expand_or_pat(&self) -> Option<Vec<PatStack<'p, 'tcx>>> {
+        if self.is_empty() {
+            None
+        } else if let PatKind::Or { pats } = &*self.head().kind {
+            Some(
+                pats.iter()
+                    .map(|pat| {
+                        let mut new_patstack = PatStack::from_pattern(pat);
+                        new_patstack.0.extend_from_slice(&self.0[1..]);
+                        new_patstack
+                    })
+                    .collect(),
+            )
+        } else {
+            None
+        }
+    }
+
     /// This computes `D(self)`. See top of the file for explanations.
     fn specialize_wildcard(&self) -> Option<Self> {
         if self.head().is_wildcard() { Some(self.to_tail()) } else { None }
@@ -446,8 +465,13 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
         Matrix(vec![])
     }
 
+    /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it.
     pub fn push(&mut self, row: PatStack<'p, 'tcx>) {
-        self.0.push(row)
+        if let Some(rows) = row.expand_or_pat() {
+            self.0.extend(rows);
+        } else {
+            self.0.push(row);
+        }
     }
 
     /// Iterate over the first component of each row
@@ -471,12 +495,10 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
         'a: 'q,
         'p: 'q,
     {
-        Matrix(
-            self.0
-                .iter()
-                .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns))
-                .collect(),
-        )
+        self.0
+            .iter()
+            .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns))
+            .collect()
     }
 }
 
@@ -528,7 +550,12 @@ impl<'p, 'tcx> FromIterator<PatStack<'p, 'tcx>> for Matrix<'p, 'tcx> {
     where
         T: IntoIterator<Item = PatStack<'p, 'tcx>>,
     {
-        Matrix(iter.into_iter().collect())
+        let mut matrix = Matrix::empty();
+        for x in iter {
+            // Using `push` ensures we correctly expand or-patterns.
+            matrix.push(x);
+        }
+        matrix
     }
 }
 
@@ -1601,6 +1628,15 @@ pub fn is_useful<'p, 'a, 'tcx>(
 
     assert!(rows.iter().all(|r| r.len() == v.len()));
 
+    // If the first pattern is an or-pattern, expand it.
+    if let Some(vs) = v.expand_or_pat() {
+        return vs
+            .into_iter()
+            .map(|v| is_useful(cx, matrix, &v, witness_preference, hir_id))
+            .find(|result| result.is_useful())
+            .unwrap_or(NotUseful);
+    }
+
     let (ty, span) = matrix
         .heads()
         .map(|r| (r.ty, r.span))
@@ -1802,9 +1838,7 @@ fn pat_constructor<'tcx>(
                 if slice.is_some() { VarLen(prefix, suffix) } else { FixedLen(prefix + suffix) };
             Some(Slice(Slice { array_len, kind }))
         }
-        PatKind::Or { .. } => {
-            bug!("support for or-patterns has not been fully implemented yet.");
-        }
+        PatKind::Or { .. } => bug!(), // Should have been expanded earlier on.
     }
 }
 
@@ -2410,9 +2444,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>(
             _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor),
         },
 
-        PatKind::Or { .. } => {
-            bug!("support for or-patterns has not been fully implemented yet.");
-        }
+        PatKind::Or { .. } => bug!(), // Should have been expanded earlier on.
     };
     debug!("specialize({:#?}, {:#?}) = {:#?}", pat, ctor_wild_subpatterns, result);
 
diff --git a/src/test/compile-fail/or-patterns.rs b/src/test/compile-fail/or-patterns.rs
new file mode 100644
index 00000000000..e1b5dce852d
--- /dev/null
+++ b/src/test/compile-fail/or-patterns.rs
@@ -0,0 +1,45 @@
+// should-ice
+#![allow(incomplete_features)]
+#![feature(or_patterns)]
+#![deny(unreachable_patterns)]
+
+// The ice will get removed once or-patterns are correctly implemented
+fn main() {
+    // We wrap patterns in a tuple because top-level or-patterns are special-cased for now.
+    match (0u8,) {
+        (1 | 2,) => {}
+        //~^ ERROR simplifyable pattern found
+        // This above is the ICE error message
+        _ => {}
+    }
+
+    match (0u8,) {
+        (1 | 2,) => {}
+        (1,) => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+    match (0u8,) {
+        (1 | 2,) => {}
+        (2,) => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+    match (0u8,) {
+        (1,) => {}
+        (2,) => {}
+        (1 | 2,) => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+    match (0u8,) {
+        (1 | 1,) => {} // redundancy not detected for now
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (1 | 2, 3 | 4) => {}
+        (1, 2) => {}
+        (1, 3) => {} //~ ERROR unreachable pattern
+        (1, 4) => {} //~ ERROR unreachable pattern
+        (2, 4) => {} //~ ERROR unreachable pattern
+        (2 | 1, 4) => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+}