]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/build/matches/mod.rs
Create fewer basic blocks in match MIR lowering
[rust.git] / src / librustc_mir / build / matches / mod.rs
index 38c9a177c388ef6e6d6621736a9f9e8f5e1e07a5..521aca56108a606477f214180d24a3c1b949511c 100644 (file)
@@ -206,33 +206,18 @@ pub fn match_expr(
             .flat_map(|(_, candidates)| candidates)
             .collect::<Vec<_>>();
 
+        let outer_source_info = self.source_info(span);
+
         // this will generate code to test scrutinee_place and
         // branch to the appropriate arm block
-        let otherwise = self.match_candidates(
+        self.match_candidates(
             scrutinee_span,
+            &mut Some(block),
+            None,
             candidates,
-            block,
             &mut fake_borrows,
         );
 
-        let outer_source_info = self.source_info(span);
-
-        if !otherwise.is_empty() {
-            // All matches are exhaustive. However, because some matches
-            // only have exponentially-large exhaustive decision trees, we
-            // sometimes generate an inexhaustive decision tree.
-            //
-            // In that case, the inexhaustive tips of the decision tree
-            // can't be reached - terminate them with an `unreachable`.
-            let mut otherwise = otherwise;
-            otherwise.sort();
-            otherwise.dedup(); // variant switches can introduce duplicate target blocks
-            for block in otherwise {
-                self.cfg
-                    .terminate(block, outer_source_info, TerminatorKind::Unreachable);
-            }
-        }
-
         // Step 4. Determine the fake borrows that are needed from the above
         // places. Create the required temporaries for them.
 
@@ -247,8 +232,6 @@ pub fn match_expr(
             let arm_source_info = self.source_info(arm.span);
             let region_scope = (arm.scope, arm_source_info);
             self.in_scope(region_scope, arm.lint_level, |this| {
-                let mut arm_block = this.cfg.start_new_block();
-
                 let body = this.hir.mirror(arm.body.clone());
                 let scope = this.declare_bindings(
                     None,
@@ -258,23 +241,27 @@ pub fn match_expr(
                     Some((Some(&scrutinee_place), scrutinee_span)),
                 );
 
+                let arm_block;
                 if candidates.len() == 1 {
-                    arm_block = self.bind_and_guard_matched_candidate(
+                    arm_block = this.bind_and_guard_matched_candidate(
                         candidates.pop().unwrap(),
                         arm.guard.clone(),
                         &fake_borrow_temps,
                         scrutinee_span,
+                        region_scope,
                     );
                 } else {
-                    arm_block = self.cfg.start_new_block();
+                    arm_block = this.cfg.start_new_block();
                     for candidate in candidates {
-                        let binding_end = self.bind_and_guard_matched_candidate(
+                        this.clear_top_scope(arm.scope);
+                        let binding_end = this.bind_and_guard_matched_candidate(
                             candidate,
                             arm.guard.clone(),
                             &fake_borrow_temps,
                             scrutinee_span,
+                            region_scope,
                         );
-                        self.cfg.terminate(
+                        this.cfg.terminate(
                             binding_end,
                             source_info,
                             TerminatorKind::Goto { target: arm_block },
@@ -286,18 +273,6 @@ pub fn match_expr(
                     this.source_scope = source_scope;
                 }
 
-                for candidate in candidates {
-                    this.clear_top_scope(arm.scope);
-                    this.bind_and_guard_matched_candidate(
-                        candidate,
-                        arm.guard.clone(),
-                        arm_block,
-                        &fake_borrow_temps,
-                        scrutinee_span,
-                        region_scope,
-                    );
-                }
-
                 this.into(destination, arm_block, body)
             })
         }).collect();
@@ -792,11 +767,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// the value, we will generate a branch to the appropriate
     /// prebinding block.
     ///
-    /// The return value is a list of "otherwise" blocks. These are
-    /// points in execution where we found that *NONE* of the
-    /// candidates apply. In principle, this means that the input
-    /// list was not exhaustive, though at present we sometimes are
-    /// not smart enough to recognize all exhaustive inputs.
+    /// If we find that *NONE* of the candidates apply, we branch to the
+    /// `otherwise_block`. In principle, this means that the input list was not
+    /// exhaustive, though at present we sometimes are not smart enough to
+    /// recognize all exhaustive inputs.
     ///
     /// It might be surprising that the input can be inexhaustive.
     /// Indeed, initially, it is not, because all matches are
@@ -810,13 +784,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     fn match_candidates<'pat>(
         &mut self,
         span: Span,
+        start_block: &mut Option<BasicBlock>,
+        otherwise_block: Option<BasicBlock>,
         candidates: &mut [&mut Candidate<'pat, 'tcx>],
-        mut block: BasicBlock,
         fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
-    ) -> Vec<BasicBlock> {
+    ) {
         debug!(
-            "matched_candidate(span={:?}, block={:?}, candidates={:?})",
-            span, block, candidates
+            "matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
+            span,
+            candidates,
+            start_block,
+            otherwise_block,
         );
 
         // Start by simplifying candidates. Once this process is complete, all
@@ -839,52 +817,57 @@ fn match_candidates<'pat>(
         );
         let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
 
+        let block: BasicBlock;
+
         if !matched_candidates.is_empty() {
-            block = if let Some(last_otherwise_block) = self.select_matched_candidates(
+            let otherwise_block = self.select_matched_candidates(
                 matched_candidates,
-                block,
+                start_block,
                 fake_borrows,
-            ) {
-                last_otherwise_block
+            );
+
+            if let Some(last_otherwise_block) = otherwise_block {
+                block = last_otherwise_block
             } else {
                 // Any remaining candidates are unreachable.
                 if unmatched_candidates.is_empty() {
-                    return Vec::new();
-                } else {
-                    self.cfg.start_new_block()
+                    return;
                 }
+                block = self.cfg.start_new_block();
             };
+        } else {
+            block = *start_block.get_or_insert_with(|| self.cfg.start_new_block());
         }
 
         // If there are no candidates that still need testing, we're
         // done. Since all matches are exhaustive, execution should
         // never reach this point.
         if unmatched_candidates.is_empty() {
-            return vec![block];
+            let source_info = self.source_info(span);
+            if let Some(otherwise) = otherwise_block {
+                self.cfg.terminate(
+                    block,
+                    source_info,
+                    TerminatorKind::Goto { target: otherwise },
+                );
+            } else {
+                self.cfg.terminate(
+                    block,
+                    source_info,
+                    TerminatorKind::Unreachable,
+                )
+            }
+            return;
         }
 
-        // Test candidates where possible.
-        let (otherwise, untested_candidates) = self.test_candidates(
+        // Test for the remaining candidates.
+        self.test_candidates(
             span,
             unmatched_candidates,
             block,
+            otherwise_block,
             fake_borrows,
         );
-
-        // If the target candidates were exhaustive, then we are done.
-        // But for borrowck continue build decision tree.
-        if untested_candidates.is_empty() {
-            return otherwise;
-        }
-
-        // Otherwise, let's process those remaining candidates.
-        let join_block = self.join_otherwise_blocks(span, otherwise);
-        self.match_candidates(
-            span,
-            untested_candidates,
-            join_block,
-            fake_borrows,
-        )
     }
 
     /// Link up matched candidates. For example, if we have something like
@@ -908,7 +891,7 @@ fn match_candidates<'pat>(
     fn select_matched_candidates(
         &mut self,
         matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
-        block: BasicBlock,
+        start_block: &mut Option<BasicBlock>,
         fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
     ) -> Option<BasicBlock> {
         debug_assert!(
@@ -956,16 +939,18 @@ fn select_matched_candidates(
             = matched_candidates.split_at_mut(fully_matched_with_guard + 1);
 
         let first_candidate = &reachable_candidates[0];
+        let first_prebinding_block = first_candidate.pre_binding_block;
 
-        let candidate_source_info = self.source_info(first_candidate.span);
-
-        self.cfg.terminate(
-            block,
-            candidate_source_info,
-            TerminatorKind::Goto {
-                target: first_candidate.pre_binding_block,
-            },
-        );
+        if let Some(start_block) = *start_block {
+            let source_info = self.source_info(first_candidate.span);
+            self.cfg.terminate(
+                start_block,
+                source_info,
+                TerminatorKind::Goto { target: first_prebinding_block },
+            );
+        } else {
+            *start_block = Some(first_prebinding_block);
+        }
 
         for window in reachable_candidates.windows(2) {
             if let [first_candidate, second_candidate] = window {
@@ -1017,25 +1002,6 @@ fn select_matched_candidates(
         }
     }
 
-    fn join_otherwise_blocks(&mut self, span: Span, mut otherwise: Vec<BasicBlock>) -> BasicBlock {
-        let source_info = self.source_info(span);
-        otherwise.sort();
-        otherwise.dedup(); // variant switches can introduce duplicate target blocks
-        if otherwise.len() == 1 {
-            otherwise[0]
-        } else {
-            let join_block = self.cfg.start_new_block();
-            for block in otherwise {
-                self.cfg.terminate(
-                    block,
-                    source_info,
-                    TerminatorKind::Goto { target: join_block },
-                );
-            }
-            join_block
-        }
-    }
-
     /// 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
@@ -1153,8 +1119,9 @@ fn test_candidates<'pat, 'b, 'c>(
         span: Span,
         mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
         block: BasicBlock,
+        mut otherwise_block: Option<BasicBlock>,
         fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
-    ) -> (Vec<BasicBlock>, &'b mut [&'c mut Candidate<'pat, 'tcx>]) {
+    ) {
         // extract the match-pair from the highest priority candidate
         let match_pair = &candidates.first().unwrap().match_pairs[0];
         let mut test = self.test(match_pair);
@@ -1208,9 +1175,8 @@ fn test_candidates<'pat, 'b, 'c>(
             "match_candidates: test={:?} match_pair={:?}",
             test, match_pair
         );
-        let target_blocks = self.perform_test(block, &match_place, &test);
         let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
-        target_candidates.resize_with(target_blocks.len(), Default::default);
+        target_candidates.resize_with(test.targets(), Default::default);
 
         let total_candidate_count = candidates.len();
 
@@ -1236,20 +1202,48 @@ fn test_candidates<'pat, 'b, 'c>(
         // apply. Collect a list of blocks where control flow will
         // branch if one of the `target_candidate` sets is not
         // exhaustive.
-        let otherwise: Vec<_> = target_blocks
-            .into_iter()
-            .zip(target_candidates)
-            .flat_map(|(target_block, mut target_candidates)| {
+        if !candidates.is_empty() {
+            let remainder_start = &mut None;
+            self.match_candidates(
+                span,
+                remainder_start,
+                otherwise_block,
+                candidates,
+                fake_borrows,
+            );
+            otherwise_block = Some(remainder_start.unwrap());
+        };
+        let target_blocks: Vec<_> = target_candidates.into_iter().map(|mut candidates| {
+            if candidates.len() != 0 {
+                let candidate_start = &mut None;
                 self.match_candidates(
                     span,
-                    &mut *target_candidates,
-                    target_block,
+                    candidate_start,
+                    otherwise_block,
+                    &mut *candidates,
                     fake_borrows,
-                )
-            })
-            .collect();
+                );
+                candidate_start.unwrap()
+            } else {
+                *otherwise_block.get_or_insert_with(|| {
+                    let unreachable = self.cfg.start_new_block();
+                    let source_info = self.source_info(span);
+                    self.cfg.terminate(
+                        unreachable,
+                        source_info,
+                        TerminatorKind::Unreachable,
+                    );
+                    unreachable
+                })
+            }
+        }).collect();
 
-        (otherwise, candidates)
+        self.perform_test(
+            block,
+            &match_place,
+            &test,
+            target_blocks,
+        );
     }
 
     // Determine the fake borrows that are needed to ensure that the place
@@ -1323,7 +1317,6 @@ fn bind_and_guard_matched_candidate<'pat>(
         fake_borrows: &Vec<(&Place<'tcx>, Local)>,
         scrutinee_span: Span,
         region_scope: (region::Scope, SourceInfo),
-    ) {
     ) -> BasicBlock {
         debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
 
@@ -1345,10 +1338,10 @@ fn bind_and_guard_matched_candidate<'pat>(
                 block,
                 fresh_block,
                 candidate.next_candidate_pre_binding_block,
-            candidate_source_info,
-        );
+                candidate_source_info,
+            );
             block = fresh_block;
-        self.ascribe_types(block, &candidate.ascriptions);
+            self.ascribe_types(block, &candidate.ascriptions);
         } else {
             return block;
         }