.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.
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,
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 },
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();
/// 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
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
);
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
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!(
= 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 {
}
}
- 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
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);
"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();
// 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
fake_borrows: &Vec<(&Place<'tcx>, Local)>,
scrutinee_span: Span,
region_scope: (region::Scope, SourceInfo),
- ) {
) -> BasicBlock {
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
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;
}