]> git.lizzy.rs Git - rust.git/commitdiff
Implement general or-patterns in `match` expressions
authorMatthew Jasper <mjjasper1@gmail.com>
Fri, 27 Dec 2019 13:39:43 +0000 (13:39 +0000)
committerMatthew Jasper <mjjasper1@gmail.com>
Sat, 1 Feb 2020 22:10:03 +0000 (22:10 +0000)
src/librustc_mir_build/build/matches/mod.rs
src/librustc_mir_build/build/matches/simplify.rs
src/librustc_mir_build/build/matches/test.rs
src/librustc_mir_build/hair/mod.rs

index a060c8fd2a215a93136534944c208fb26ff57fee..4ac3ced17dbb1b08e04fd46d40e042cd47a65dbc 100644 (file)
@@ -26,7 +26,9 @@
 mod test;
 mod util;
 
+use std::borrow::Borrow;
 use std::convert::TryFrom;
+use std::mem;
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Generates MIR for a `match` expression.
@@ -95,7 +97,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
         let match_has_guard = arms.iter().any(|arm| arm.guard.is_some());
         let candidates =
-            arm_candidates.iter_mut().flat_map(|(_, candidates)| candidates).collect::<Vec<_>>();
+            arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::<Vec<_>>();
 
         let fake_borrow_temps =
             self.lower_match_tree(block, scrutinee_span, match_has_guard, candidates);
@@ -145,27 +147,25 @@ fn create_match_candidates<'pat>(
         &mut self,
         scrutinee: &Place<'tcx>,
         arms: &'pat [Arm<'tcx>],
-    ) -> Vec<(&'pat Arm<'tcx>, Vec<Candidate<'pat, 'tcx>>)> {
+    ) -> Vec<(&'pat Arm<'tcx>, Candidate<'pat, 'tcx>)> {
         // Assemble a list of candidates: there is one candidate per pattern,
         // which means there may be more than one candidate *per arm*.
         arms.iter()
             .map(|arm| {
                 let arm_has_guard = arm.guard.is_some();
-                let arm_candidates: Vec<_> = arm
-                    .top_pats_hack()
-                    .iter()
-                    .map(|pattern| Candidate {
-                        span: pattern.span,
-                        has_guard: arm_has_guard,
-                        match_pairs: smallvec![MatchPair::new(*scrutinee, pattern)],
-                        bindings: vec![],
-                        ascriptions: vec![],
-                        otherwise_block: None,
-                        pre_binding_block: None,
-                        next_candidate_pre_binding_block: None,
-                    })
-                    .collect();
-                (arm, arm_candidates)
+                let arm_candidate = Candidate {
+                    span: arm.pattern.span,
+                    match_pairs: smallvec![MatchPair::new(*scrutinee, &arm.pattern),],
+                    bindings: vec![],
+                    ascriptions: vec![],
+                    has_guard: arm_has_guard,
+                    needs_otherwise_block: arm_has_guard,
+                    otherwise_block: None,
+                    pre_binding_block: None,
+                    next_candidate_pre_binding_block: None,
+                    subcandidates: vec![],
+                };
+                (arm, arm_candidate)
             })
             .collect()
     }
@@ -205,11 +205,15 @@ fn lower_match_tree<'pat>(
             self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
         }
 
-        let mut next_prebinding_block = None;
+        let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
 
-        for candidate in candidates.iter_mut().rev() {
-            candidate.next_candidate_pre_binding_block = next_prebinding_block;
-            next_prebinding_block = candidate.pre_binding_block;
+        for candidate in candidates.into_iter() {
+            candidate.visit_leaves(|leaf_candidate| {
+                if let Some(ref mut prev) = previous_candidate {
+                    prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block;
+                }
+                previous_candidate = Some(leaf_candidate);
+            });
         }
 
         if let Some(ref borrows) = fake_borrows {
@@ -230,7 +234,7 @@ fn lower_match_arms(
         destination: &Place<'tcx>,
         scrutinee_place: Place<'tcx>,
         scrutinee_span: Span,
-        arm_candidates: Vec<(&'_ Arm<'tcx>, Vec<Candidate<'_, 'tcx>>)>,
+        arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>,
         outer_source_info: SourceInfo,
         fake_borrow_temps: Vec<(Place<'tcx>, Local)>,
     ) -> BlockAnd<()> {
@@ -238,8 +242,8 @@ fn lower_match_arms(
 
         let arm_end_blocks: Vec<_> = arm_candidates
             .into_iter()
-            .map(|(arm, candidates)| {
-                debug!("lowering arm {:?}\ncanidates = {:?}", arm, candidates);
+            .map(|(arm, candidate)| {
+                debug!("lowering arm {:?}\ncanidate = {:?}", arm, candidate);
 
                 let arm_source_info = self.source_info(arm.span);
                 let arm_scope = (arm.scope, arm_source_info);
@@ -248,14 +252,14 @@ fn lower_match_arms(
                     let scope = this.declare_bindings(
                         None,
                         arm.span,
-                        &arm.top_pats_hack()[0],
+                        &arm.pattern,
                         ArmHasGuard(arm.guard.is_some()),
                         Some((Some(&scrutinee_place), scrutinee_span)),
                     );
 
                     let arm_block = this.bind_pattern(
                         outer_source_info,
-                        candidates,
+                        candidate,
                         arm.guard.as_ref().map(|g| (g, match_scope)),
                         &fake_borrow_temps,
                         scrutinee_span,
@@ -289,35 +293,52 @@ fn lower_match_arms(
     fn bind_pattern(
         &mut self,
         outer_source_info: SourceInfo,
-        mut candidates: Vec<Candidate<'_, 'tcx>>,
+        candidate: Candidate<'_, 'tcx>,
         guard: Option<(&Guard<'tcx>, region::Scope)>,
         fake_borrow_temps: &Vec<(Place<'tcx>, Local)>,
         scrutinee_span: Span,
         arm_scope: region::Scope,
     ) -> BasicBlock {
-        if candidates.len() == 1 {
+        if candidate.subcandidates.is_empty() {
             // Avoid generating another `BasicBlock` when we only have one
             // candidate.
             self.bind_and_guard_matched_candidate(
-                candidates.pop().unwrap(),
+                candidate,
+                &[],
                 guard,
                 fake_borrow_temps,
                 scrutinee_span,
             )
         } else {
-            let arm_block = self.cfg.start_new_block();
-            for candidate in candidates {
-                // Avoid scheduling drops multiple times.
-                self.clear_top_scope(arm_scope);
-                let binding_end = self.bind_and_guard_matched_candidate(
-                    candidate,
-                    guard,
-                    fake_borrow_temps,
-                    scrutinee_span,
-                );
-                self.cfg.goto(binding_end, outer_source_info, arm_block);
-            }
-            arm_block
+            let target_block = self.cfg.start_new_block();
+
+            // We keep a stack of all of the bindings and type asciptions
+            // from the the parent candidates that we visit, that also need to
+            // be bound for each candidate.
+            traverse_candidate(
+                candidate,
+                &mut Vec::new(),
+                &mut |leaf_candidate, parent_bindings| {
+                    self.clear_top_scope(arm_scope);
+                    let binding_end = self.bind_and_guard_matched_candidate(
+                        leaf_candidate,
+                        parent_bindings,
+                        guard,
+                        &fake_borrow_temps,
+                        scrutinee_span,
+                    );
+                    self.cfg.goto(binding_end, outer_source_info, target_block);
+                },
+                |inner_candidate, parent_bindings| {
+                    parent_bindings.push((inner_candidate.bindings, inner_candidate.ascriptions));
+                    inner_candidate.subcandidates.into_iter()
+                },
+                |parent_bindings| {
+                    parent_bindings.pop();
+                },
+            );
+
+            target_block
         }
     }
 
@@ -427,6 +448,7 @@ pub(super) fn expr_into_pattern(
         let mut candidate = Candidate {
             span: irrefutable_pat.span,
             has_guard: false,
+            needs_otherwise_block: false,
             match_pairs: smallvec![MatchPair::new(*initializer, &irrefutable_pat)],
             bindings: vec![],
             ascriptions: vec![],
@@ -435,6 +457,7 @@ pub(super) fn expr_into_pattern(
             otherwise_block: None,
             pre_binding_block: None,
             next_candidate_pre_binding_block: None,
+            subcandidates: vec![],
         };
 
         // Simplify the candidate. Since the pattern is irrefutable, this should
@@ -632,9 +655,7 @@ pub(super) fn visit_bindings(
                 }
             }
             PatKind::Or { ref pats } => {
-                for pat in pats {
-                    self.visit_bindings(&pat, pattern_user_ty.clone(), f);
-                }
+                self.visit_bindings(&pats[0], pattern_user_ty.clone(), f);
             }
         }
     }
@@ -642,28 +663,72 @@ pub(super) fn visit_bindings(
 
 #[derive(Debug)]
 crate struct Candidate<'pat, 'tcx> {
-    // span of the original pattern that gave rise to this candidate
+    /// `Span` of the original pattern that gave rise to this candidate
     span: Span,
 
+    /// This `Candidate` has a guard.
     has_guard: bool,
 
-    // all of these must be satisfied...
+    /// This `Candidate` needs and otherwise block, either because it has a
+    /// guard or it has subcandidates.
+    needs_otherwise_block: bool,
+
+    /// All of these must be satisfied...
     match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
 
-    // ...these bindings established...
+    /// ...these bindings established...
     bindings: Vec<Binding<'tcx>>,
 
-    // ...and these types asserted...
+    /// ...and these types asserted...
     ascriptions: Vec<Ascription<'tcx>>,
 
-    // ...and the guard must be evaluated, if false branch to Block...
+    /// ... and if this is non-empty, one of these subcandidates also has to match ...
+    subcandidates: Vec<Candidate<'pat, 'tcx>>,
+
+    /// ...and the guard must be evaluated, if false branch to Block...
     otherwise_block: Option<BasicBlock>,
 
-    // ...and the blocks for add false edges between candidates
+    /// ...and the blocks for add false edges between candidates
     pre_binding_block: Option<BasicBlock>,
     next_candidate_pre_binding_block: Option<BasicBlock>,
 }
 
+impl Candidate<'_, '_> {
+    /// Visit the leaf candidates (those with no subcandidates) contained in
+    /// this candidate.
+    fn visit_leaves<'a>(&'a mut self, mut visit_leaf: impl FnMut(&'a mut Self)) {
+        traverse_candidate(
+            self,
+            &mut (),
+            &mut move |c, _| visit_leaf(c),
+            move |c, _| c.subcandidates.iter_mut(),
+            |_| {},
+        );
+    }
+}
+
+/// A depth-first traversal of the `Candidate` and all of its recursive
+/// subcandidates.
+fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>(
+    candidate: C,
+    context: &mut T,
+    visit_leaf: &mut impl FnMut(C, &mut T),
+    get_children: impl Copy + Fn(C, &mut T) -> I,
+    complete_children: impl Copy + Fn(&mut T),
+) where
+    C: Borrow<Candidate<'pat, 'tcx>>,
+    I: Iterator<Item = C>,
+{
+    if candidate.borrow().subcandidates.is_empty() {
+        visit_leaf(candidate, context)
+    } else {
+        for child in get_children(candidate, context) {
+            traverse_candidate(child, context, visit_leaf, get_children, complete_children);
+        }
+        complete_children(context)
+    }
+}
+
 #[derive(Clone, Debug)]
 struct Binding<'tcx> {
     span: Span,
@@ -793,10 +858,45 @@ fn match_candidates<'pat>(
         // Start by simplifying candidates. Once this process is complete, all
         // the match pairs which remain require some form of test, whether it
         // be a switch or pattern comparison.
+        let mut split_or_candidate = false;
         for candidate in &mut *candidates {
-            self.simplify_candidate(candidate);
+            split_or_candidate |= self.simplify_candidate(candidate);
         }
 
+        if split_or_candidate {
+            // At least one of the candidates has been split into subcandidates.
+            // We need to change the candidate list to include those.
+            let mut new_candidates = Vec::new();
+
+            for candidate in candidates {
+                candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate));
+            }
+            self.match_simplified_candidates(
+                span,
+                start_block,
+                otherwise_block,
+                &mut *new_candidates,
+                fake_borrows,
+            );
+        } else {
+            self.match_simplified_candidates(
+                span,
+                start_block,
+                otherwise_block,
+                candidates,
+                fake_borrows,
+            );
+        };
+    }
+
+    fn match_simplified_candidates(
+        &mut self,
+        span: Span,
+        start_block: BasicBlock,
+        otherwise_block: &mut Option<BasicBlock>,
+        candidates: &mut [&mut Candidate<_, 'tcx>],
+        fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
+    ) {
         // The candidates are sorted by priority. Check to see whether the
         // higher priority candidates (and hence at the front of the slice)
         // have satisfied all their match pairs.
@@ -835,7 +935,13 @@ fn match_candidates<'pat>(
         }
 
         // Test for the remaining candidates.
-        self.test_candidates(span, unmatched_candidates, block, otherwise_block, fake_borrows);
+        self.test_candidates_with_or(
+            span,
+            unmatched_candidates,
+            block,
+            otherwise_block,
+            fake_borrows,
+        );
     }
 
     /// Link up matched candidates. For example, if we have something like
@@ -866,6 +972,10 @@ fn select_matched_candidates(
             !matched_candidates.is_empty(),
             "select_matched_candidates called with no candidates",
         );
+        debug_assert!(
+            matched_candidates.iter().all(|c| c.subcandidates.is_empty()),
+            "subcandidates should be empty in select_matched_candidates",
+        );
 
         // Insert a borrows of prefixes of places that are bound and are
         // behind a dereference projection.
@@ -902,7 +1012,7 @@ fn select_matched_candidates(
 
         let fully_matched_with_guard = matched_candidates
             .iter()
-            .position(|c| !c.has_guard)
+            .position(|c| !c.needs_otherwise_block)
             .unwrap_or(matched_candidates.len() - 1);
 
         let (reachable_candidates, unreachable_candidates) =
@@ -914,7 +1024,7 @@ fn select_matched_candidates(
             assert!(candidate.otherwise_block.is_none());
             assert!(candidate.pre_binding_block.is_none());
             candidate.pre_binding_block = Some(next_prebinding);
-            if candidate.has_guard {
+            if candidate.needs_otherwise_block {
                 next_prebinding = self.cfg.start_new_block();
                 candidate.otherwise_block = Some(next_prebinding);
             }
@@ -932,6 +1042,120 @@ fn select_matched_candidates(
         reachable_candidates.last_mut().unwrap().otherwise_block
     }
 
+    fn test_candidates_with_or(
+        &mut self,
+        span: Span,
+        candidates: &mut [&mut Candidate<'_, 'tcx>],
+        block: BasicBlock,
+        otherwise_block: &mut Option<BasicBlock>,
+        fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
+    ) {
+        let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap();
+
+        if let PatKind::Or { .. } = *first_candidate.match_pairs[0].pattern.kind {
+            let match_pairs = mem::take(&mut first_candidate.match_pairs);
+            first_candidate.needs_otherwise_block = true;
+            first_candidate.pre_binding_block = Some(block);
+
+            // We sort or-patterns to the end in `simplify_candidate`, so all
+            // the remaining match pairs are or-patterns.
+            for match_pair in match_pairs {
+                if let PatKind::Or { ref pats } = *match_pair.pattern.kind {
+                    let or_span = match_pair.pattern.span;
+                    let place = &match_pair.place;
+
+                    first_candidate.visit_leaves(|leaf_candidate| {
+                        self.test_or_pattern(leaf_candidate, pats, or_span, place, fake_borrows);
+                    });
+                } else {
+                    bug!("Or patterns should have been sorted to the end");
+                }
+            }
+            let remainder_start =
+                first_candidate.otherwise_block.unwrap_or_else(|| self.cfg.start_new_block());
+            self.match_candidates(
+                span,
+                remainder_start,
+                otherwise_block,
+                remaining_candidates,
+                fake_borrows,
+            )
+        } else {
+            self.test_candidates(span, candidates, block, otherwise_block, fake_borrows)
+        }
+    }
+
+    fn test_or_pattern<'pat>(
+        &mut self,
+        candidate: &mut Candidate<'pat, 'tcx>,
+        pats: &'pat [Pat<'tcx>],
+        or_span: Span,
+        place: &Place<'tcx>,
+        fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
+    ) {
+        debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats);
+        let mut or_candidates: Vec<_> = pats
+            .iter()
+            .map(|pat| {
+                let new_match_pair = smallvec![MatchPair { pattern: pat, place: place.clone() }];
+                Candidate {
+                    span: pat.span,
+                    has_guard: candidate.has_guard,
+                    needs_otherwise_block: candidate.needs_otherwise_block,
+                    match_pairs: new_match_pair,
+                    bindings: Vec::new(),
+                    ascriptions: Vec::new(),
+                    otherwise_block: None,
+                    pre_binding_block: None,
+                    next_candidate_pre_binding_block: None,
+                    subcandidates: Vec::new(),
+                }
+            })
+            .collect();
+        let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect();
+        self.match_candidates(
+            or_span,
+            candidate.pre_binding_block.unwrap(),
+            &mut candidate.otherwise_block,
+            &mut or_candidate_refs,
+            fake_borrows,
+        );
+        candidate.subcandidates = or_candidates;
+        self.merge_trivial_subcandidates(candidate, self.source_info(or_span));
+    }
+
+    /// Try to merge all of the subcandidates of the given candidate into one.
+    /// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`.
+    fn merge_trivial_subcandidates(
+        &mut self,
+        candidate: &mut Candidate<'_, 'tcx>,
+        source_info: SourceInfo,
+    ) {
+        if candidate.subcandidates.is_empty() {
+            return;
+        }
+        let mut can_merge = !candidate.has_guard;
+
+        // Not `Iterator::all` because we don't want to short-circuit.
+        for subcandidate in &mut candidate.subcandidates {
+            self.merge_trivial_subcandidates(subcandidate, source_info);
+
+            // FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
+            can_merge &= subcandidate.subcandidates.is_empty()
+                && subcandidate.bindings.is_empty()
+                && subcandidate.ascriptions.is_empty();
+        }
+
+        if can_merge {
+            let any_matches = self.cfg.start_new_block();
+            for subcandidate in mem::take(&mut candidate.subcandidates) {
+                let or_block = subcandidate.pre_binding_block.unwrap();
+                self.cfg.goto(or_block, source_info, any_matches);
+            }
+            candidate.pre_binding_block = Some(any_matches);
+        }
+    }
+
     /// This is the most subtle part of the matching algorithm. At
     /// this point, the input candidates have been fully simplified,
     /// and so we know that all remaining match-pairs require some
@@ -1258,6 +1482,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     fn bind_and_guard_matched_candidate<'pat>(
         &mut self,
         candidate: Candidate<'pat, 'tcx>,
+        parent_bindings: &[(Vec<Binding<'tcx>>, Vec<Ascription<'tcx>>)],
         guard: Option<(&Guard<'tcx>, region::Scope)>,
         fake_borrows: &Vec<(Place<'tcx>, Local)>,
         scrutinee_span: Span,
@@ -1281,7 +1506,13 @@ fn bind_and_guard_matched_candidate<'pat>(
             block = fresh_block;
         }
 
-        self.ascribe_types(block, &candidate.ascriptions);
+        self.ascribe_types(
+            block,
+            parent_bindings
+                .iter()
+                .flat_map(|(_, ascriptions)| ascriptions)
+                .chain(&candidate.ascriptions),
+        );
 
         // rust-lang/rust#27282: The `autoref` business deserves some
         // explanation here.
@@ -1365,14 +1596,14 @@ fn bind_and_guard_matched_candidate<'pat>(
         //      reference to that.
         if let Some((guard, region_scope)) = guard {
             let tcx = self.hir.tcx();
+            let bindings = parent_bindings
+                .iter()
+                .flat_map(|(bindings, _)| bindings)
+                .chain(&candidate.bindings);
 
-            self.bind_matched_candidate_for_guard(block, &candidate.bindings);
+            self.bind_matched_candidate_for_guard(block, bindings.clone());
             let guard_frame = GuardFrame {
-                locals: candidate
-                    .bindings
-                    .iter()
-                    .map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode))
-                    .collect(),
+                locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(),
             };
             debug!("entering guard building context: {:?}", guard_frame);
             self.guard_context.push(guard_frame);
@@ -1446,9 +1677,14 @@ fn bind_and_guard_matched_candidate<'pat>(
             // ```
             //
             // and that is clearly not correct.
-            let by_value_bindings = candidate.bindings.iter().filter(|binding| {
-                if let BindingMode::ByValue = binding.binding_mode { true } else { false }
-            });
+            let by_value_bindings =
+                parent_bindings
+                    .iter()
+                    .flat_map(|(bindings, _)| bindings)
+                    .chain(&candidate.bindings)
+                    .filter(|binding| {
+                        if let BindingMode::ByValue = binding.binding_mode { true } else { false }
+                    });
             // Read all of the by reference bindings to ensure that the
             // place they refer to can't be modified by the guard.
             for binding in by_value_bindings.clone() {
@@ -1460,18 +1696,29 @@ fn bind_and_guard_matched_candidate<'pat>(
 
             post_guard_block
         } else {
-            assert!(candidate.otherwise_block.is_none());
             // (Here, it is not too early to bind the matched
             // candidate on `block`, because there is no guard result
             // that we have to inspect before we bind them.)
-            self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
+            self.bind_matched_candidate_for_arm_body(
+                block,
+                parent_bindings
+                    .iter()
+                    .flat_map(|(bindings, _)| bindings)
+                    .chain(&candidate.bindings),
+            );
             block
         }
     }
 
     /// Append `AscribeUserType` statements onto the end of `block`
     /// for each ascription
-    fn ascribe_types(&mut self, block: BasicBlock, ascriptions: &[Ascription<'tcx>]) {
+    fn ascribe_types<'b>(
+        &mut self,
+        block: BasicBlock,
+        ascriptions: impl IntoIterator<Item = &'b Ascription<'tcx>>,
+    ) where
+        'tcx: 'b,
+    {
         for ascription in ascriptions {
             let source_info = self.source_info(ascription.span);
 
@@ -1498,14 +1745,21 @@ fn ascribe_types(&mut self, block: BasicBlock, ascriptions: &[Ascription<'tcx>])
         }
     }
 
-    fn bind_matched_candidate_for_guard(&mut self, block: BasicBlock, bindings: &[Binding<'tcx>]) {
-        debug!("bind_matched_candidate_for_guard(block={:?}, bindings={:?})", block, bindings);
+    fn bind_matched_candidate_for_guard<'b>(
+        &mut self,
+        block: BasicBlock,
+        bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
+    ) where
+        'tcx: 'b,
+    {
+        debug!("bind_matched_candidate_for_guard(block={:?})", block);
 
         // Assign each of the bindings. Since we are binding for a
         // guard expression, this will never trigger moves out of the
         // candidate.
         let re_erased = self.hir.tcx().lifetimes.re_erased;
         for binding in bindings {
+            debug!("bind_matched_candidate_for_guard(binding={:?})", binding);
             let source_info = self.source_info(binding.span);
 
             // For each pattern ident P of type T, `ref_for_guard` is
index 77bbce2d37aa9f03d2884fb229d4b80e77eddf8a..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 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
index 1f97f5f1b728199d672d7772428bbd888028e5a0..ff95f162257517a6288f0d2acfb890cf116b1712 100644 (file)
@@ -70,11 +70,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 }
             }
 
-            PatKind::Or { .. } => self
-                .hir
-                .tcx()
-                .sess
-                .span_fatal(match_pair.pattern.span, "or-patterns are not fully implemented yet"),
+            PatKind::Or { .. } => bug!("or-patterns should have already been handled"),
 
             PatKind::AscribeUserType { .. }
             | PatKind::Array { .. }
index 0f2c76152edae5a42e18c1585d1e283a41f2460c..cb93ba7c9250f3b31c216b6f48f3d71f972aaca5 100644 (file)
     crate span: Span,
 }
 
-impl<'tcx> Arm<'tcx> {
-    // HACK(or_patterns; Centril | dlrobertson): Remove this and
-    // correctly handle each case in which this method is used.
-    crate fn top_pats_hack(&self) -> &[Pat<'tcx>] {
-        match &*self.pattern.kind {
-            PatKind::Or { pats } => pats,
-            _ => std::slice::from_ref(&self.pattern),
-        }
-    }
-}
-
 #[derive(Clone, Debug)]
 crate enum Guard<'tcx> {
     If(ExprRef<'tcx>),