]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir_build/build/matches/simplify.rs
Implement general or-patterns in `match` expressions
[rust.git] / src / librustc_mir_build / build / matches / simplify.rs
index a5f691add65c12102f50d0fb4b09ffafd3f680d9..3a806ab6bff0ae1cfb3abab36156fa4592f08413 100644 (file)
 use crate::build::Builder;
 use crate::hair::{self, *};
 use rustc::mir::interpret::truncate;
+use rustc::mir::Place;
 use rustc::ty;
 use rustc::ty::layout::{Integer, IntegerExt, Size};
+use rustc_attr::{SignedInt, UnsignedInt};
 use rustc_hir::RangeEnd;
-use syntax::attr::{SignedInt, UnsignedInt};
 
+use smallvec::smallvec;
 use std::mem;
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
-    crate fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) {
+    /// Simplify a candidate so that all match pairs require a test.
+    ///
+    /// This method will also split a candidate where the only match-pair is an
+    /// or-pattern into multiple candidates. This is so that
+    ///
+    /// match x {
+    ///     0 | 1 => { ... },
+    ///     2 | 3 => { ... },
+    /// }
+    ///
+    /// only generates a single switch. If this happens this method returns
+    /// `true`.
+    crate fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) -> bool {
         // repeatedly simplify match pairs until fixed point is reached
         loop {
             let match_pairs = mem::take(&mut candidate.match_pairs);
+
+            if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, ref place }] =
+                *match_pairs
+            {
+                candidate.subcandidates = self.create_or_subcanidates(candidate, place, pats);
+                return true;
+            }
+
             let mut changed = false;
             for match_pair in match_pairs {
                 match self.simplify_match_pair(match_pair, candidate) {
@@ -40,11 +62,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 }
             }
             if !changed {
-                return; // if we were not able to simplify any, done.
+                // Move or-patterns to the end, because they can result in us
+                // creating additional candidates, so we want to test them as
+                // late as possible.
+                candidate
+                    .match_pairs
+                    .sort_by_key(|pair| matches!(*pair.pattern.kind, PatKind::Or { .. }));
+                return false; // if we were not able to simplify any, done.
             }
         }
     }
 
+    fn create_or_subcanidates<'pat>(
+        &mut self,
+        candidate: &Candidate<'pat, 'tcx>,
+        place: &Place<'tcx>,
+        pats: &'pat [Pat<'tcx>],
+    ) -> Vec<Candidate<'pat, 'tcx>> {
+        pats.iter()
+            .map(|pat| {
+                let mut candidate = Candidate {
+                    span: pat.span,
+                    has_guard: candidate.has_guard,
+                    needs_otherwise_block: candidate.needs_otherwise_block,
+                    match_pairs: smallvec![MatchPair { place: place.clone(), pattern: pat }],
+                    bindings: vec![],
+                    ascriptions: vec![],
+                    subcandidates: vec![],
+                    otherwise_block: None,
+                    pre_binding_block: None,
+                    next_candidate_pre_binding_block: None,
+                };
+                self.simplify_candidate(&mut candidate);
+                candidate
+            })
+            .collect()
+    }
+
     /// Tries to simplify `match_pair`, returning `Ok(())` if
     /// successful. If successful, new match pairs and bindings will
     /// have been pushed into the candidate. If no simplification is
@@ -59,14 +113,14 @@ fn simplify_match_pair<'pat>(
         match *match_pair.pattern.kind {
             PatKind::AscribeUserType {
                 ref subpattern,
-                ascription: hair::pattern::Ascription { variance, ref user_ty, user_ty_span },
+                ascription: hair::pattern::Ascription { variance, user_ty, user_ty_span },
             } => {
                 // Apply the type ascription to the value at `match_pair.place`, which is the
                 // value being matched, taking the variance field into account.
                 candidate.ascriptions.push(Ascription {
                     span: user_ty_span,
-                    user_ty: user_ty.clone(),
-                    source: match_pair.place.clone(),
+                    user_ty: user_ty,
+                    source: match_pair.place,
                     variance,
                 });
 
@@ -85,7 +139,7 @@ fn simplify_match_pair<'pat>(
                     name,
                     mutability,
                     span: match_pair.pattern.span,
-                    source: match_pair.place.clone(),
+                    source: match_pair.place,
                     var_id: var,
                     var_ty: ty,
                     binding_mode: mode,