-//! Code related to match expressions. These are sufficiently complex
-//! to warrant their own module and submodules. :) This main module
-//! includes the high-level algorithm, the submodules contain the
-//! details.
+//! Code related to match expressions. These are sufficiently complex to
+//! warrant their own module and submodules. :) This main module includes the
+//! high-level algorithm, the submodules contain the details.
+//!
+//! This also includes code for pattern bindings in `let` statements and
+//! function parameters.
use crate::build::scope::{CachedBlock, DropKind};
use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard};
use std::convert::TryFrom;
-/// ArmHasGuard is isomorphic to a boolean flag. It indicates whether
-/// a match arm has a guard expression attached to it.
-#[derive(Copy, Clone, Debug)]
-pub(crate) struct ArmHasGuard(pub bool);
-
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
+ /// Generates MIR for a `match` expression.
+ ///
+ /// The MIR that we generate for a match looks like this.
+ ///
+ /// ```text
+ /// [ 0. Pre-match ]
+ /// |
+ /// [ 1. Evaluate Scrutinee]
+ /// [ (fake read of scrutinee) ]
+ /// |
+ /// [ 2. Decision tree -- check discriminants ] <--------+
+ /// | |
+ /// | (once a specific arm is chosen) |
+ /// | |
+ /// [pre_binding_block] [otherwise_block]
+ /// | |
+ /// [ 3. Create "guard bindings" for arm ] |
+ /// [ (create fake borrows) ] |
+ /// | |
+ /// [ 4. Execute guard code ] |
+ /// [ (read fake borrows) ] --(guard is false)-----------+
+ /// |
+ /// | (guard results in true)
+ /// |
+ /// [ 5. Create real bindings and execute arm ]
+ /// |
+ /// [ Exit match ]
+ /// ```
+ ///
+ /// All of the different arms have been stacked on top of each other to
+ /// simplify the diagram. For an arm with no guard the blocks marked 3 and
+ /// 4 and the fake borrows are omitted.
+ ///
+ /// We generate MIR in the following steps:
+ ///
+ /// 1. Evaluate the scrutinee and add the fake read of it.
+ /// 2. Create the prebinding and otherwise blocks.
+ /// 3. Create the decision tree and record the places that we bind or test.
+ /// 4. Determine the fake borrows that are needed from the above places.
+ /// Create the required temporaries for them.
+ /// 5. Create everything else: Create everything else: the guards and the
+ /// arms.
+ ///
+ /// ## Fake Reads and borrows
+ ///
+ /// Match exhaustiveness checking is no able to handle the case where the
+ /// place being matched on is mutated in the guards. There is an AST check
+ /// that tries to stop this but it is buggy and overly restrictive. Instead
+ /// we add "fake borrows" to the guards that prevent any mutation of the
+ /// place being matched. There are a some subtleties:
+ ///
+ /// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared
+ /// refence, the borrow isn't even tracked. As such we have to add fake
+ /// borrows of any prefixes of a place
+ /// 2. We don't want `match x { _ => (), }` to conflict with mutable
+ /// borrows of `x`, so we only add fake borrows for places which are
+ /// bound or tested by the match.
+ /// 3. We don't want the fake borrows to conflict with `ref mut` bindings,
+ /// so we lower `ref mut` bindings as two-phase borrows for the guard.
+ /// 4. The fake borrows may be of places in inactive variants, so it would
+ /// be UB to generate code for them. They therefore have to be removed
+ /// by a MIR pass run after borrow checking.
+ ///
+ /// ## False edges
+ ///
+ /// We don't want to have the exact structure of the decision tree be
+ /// visible through borrow checking. False edges ensure that the CFG as
+ /// seen by borrow checking doesn't encode this. False edges are added:
+ ///
+ /// * From each prebinding block to the next prebinding block.
+ /// * From each otherwise block to the next prebinding block.
pub fn match_expr(
&mut self,
destination: &Place<'tcx>,
arms: Vec<Arm<'tcx>>,
) -> BlockAnd<()> {
let tcx = self.hir.tcx();
+
+ // Step 1. Evaluate the scrutinee and add the fake read of it.
+
let discriminant_span = discriminant.span();
let discriminant_place = unpack!(block = self.as_place(block, discriminant));
),
});
- let mut arm_blocks = ArmBlocks {
- blocks: arms.iter().map(|_| self.cfg.start_new_block()).collect(),
- };
-
- // Get the arm bodies and their scopes, while declaring bindings.
- let arm_bodies: Vec<_> = arms.iter()
- .map(|arm| {
- // BUG: use arm lint level
- let body = self.hir.mirror(arm.body.clone());
- let scope = self.declare_bindings(
- None,
- body.span,
- LintLevel::Inherited,
- &arm.patterns[..],
- ArmHasGuard(arm.guard.is_some()),
- Some((Some(&discriminant_place), discriminant_span)),
- );
- (body, scope.unwrap_or(self.source_scope))
- })
- .collect();
+ // Step 2. Create the otherwise and prebinding blocks.
// create binding start block for link them by false edges
let candidate_count = arms.iter().map(|c| c.patterns.len()).sum::<usize>();
.map(|_| self.cfg.start_new_block())
.collect();
- let mut has_guard = false;
-
- // assemble a list of candidates: there is one candidate per
- // pattern, which means there may be more than one candidate
- // *per arm*. These candidates are kept sorted such that the
- // highest priority candidate comes first in the list.
- // (i.e., same order as in source)
-
- let candidates: Vec<_> = arms.iter()
- .enumerate()
- .flat_map(|(arm_index, arm)| {
- arm.patterns
- .iter()
- .enumerate()
- .map(move |(pat_index, pat)| (arm_index, pat_index, pat, arm.guard.clone()))
- })
- .zip(
- pre_binding_blocks
- .iter()
- .zip(pre_binding_blocks.iter().skip(1)),
- )
- .map(
- |(
- (arm_index, pat_index, pattern, guard),
- (pre_binding_block, next_candidate_pre_binding_block)
- )| {
- has_guard |= guard.is_some();
-
- // One might ask: why not build up the match pair such that it
- // matches via `borrowed_input_temp.deref()` instead of
- // using the `discriminant_place` directly, as it is doing here?
- //
- // The basic answer is that if you do that, then you end up with
- // accceses to a shared borrow of the input and that conflicts with
- // any arms that look like e.g.
- //
- // match Some(&4) {
- // ref mut foo => {
- // ... /* mutate `foo` in arm body */ ...
- // }
- // }
- //
- // (Perhaps we could further revise the MIR
- // construction here so that it only does a
- // shared borrow at the outset and delays doing
- // the mutable borrow until after the pattern is
- // matched *and* the guard (if any) for the arm
- // has been run.)
-
- Candidate {
- span: pattern.span,
- match_pairs: vec![MatchPair::new(discriminant_place.clone(), pattern)],
- bindings: vec![],
- ascriptions: vec![],
- guard,
- arm_index,
- pat_index,
- pre_binding_block: *pre_binding_block,
- next_candidate_pre_binding_block: *next_candidate_pre_binding_block,
- }
- },
- )
- .collect();
-
+ // There's one move pre_binding block that there are candidates so that
+ // every candidate has a next prebinding_block.
let outer_source_info = self.source_info(span);
self.cfg.terminate(
*pre_binding_blocks.last().unwrap(),
TerminatorKind::Unreachable,
);
+ let mut match_has_guard = false;
+
+ let mut candidate_pre_binding_blocks = pre_binding_blocks.iter();
+ let mut next_candidate_pre_binding_blocks = pre_binding_blocks.iter().skip(1);
+
+ // Assemble a list of candidates: there is one candidate per pattern,
+ // which means there may be more than one candidate *per arm*.
+ let mut arm_candidates: Vec<_> = arms
+ .iter()
+ .map(|arm| {
+ let arm_has_guard = arm.guard.is_some();
+ match_has_guard |= arm_has_guard;
+ let arm_candidates: Vec<_> = arm.patterns
+ .iter()
+ .zip(candidate_pre_binding_blocks.by_ref())
+ .zip(next_candidate_pre_binding_blocks.by_ref())
+ .map(
+ |((pattern, pre_binding_block), next_candidate_pre_binding_block)| {
+ Candidate {
+ span: pattern.span,
+ match_pairs: vec![
+ MatchPair::new(discriminant_place.clone(), pattern),
+ ],
+ bindings: vec![],
+ ascriptions: vec![],
+ otherwise_block: if arm_has_guard {
+ Some(self.cfg.start_new_block())
+ } else {
+ None
+ },
+ pre_binding_block: *pre_binding_block,
+ next_candidate_pre_binding_block:
+ *next_candidate_pre_binding_block,
+ }
+ },
+ )
+ .collect();
+ (arm, arm_candidates)
+ })
+ .collect();
+
+ // Step 3. Create the decision tree and record the places that we bind or test.
+
// Maps a place to the kind of Fake borrow that we want to perform on
// it: either Shallow or Shared, depending on whether the place is
// bound in the match, or just switched on.
// If there are no match guards then we don't need any fake borrows,
// so don't track them.
- let mut fake_borrows = if has_guard && tcx.generate_borrow_of_any_match_input() {
+ let mut fake_borrows = if match_has_guard && tcx.generate_borrow_of_any_match_input() {
Some(FxHashMap::default())
} else {
None
};
- let pre_binding_blocks: Vec<_> = candidates
- .iter()
- .map(|cand| (cand.pre_binding_block, cand.span))
- .collect();
+ // These candidates are kept sorted such that the highest priority
+ // candidate comes first in the list. (i.e., same order as in source)
+ // As we gnerate the decision tree,
+ let candidates = &mut arm_candidates
+ .iter_mut()
+ .flat_map(|(_, candidates)| candidates)
+ .collect::<Vec<_>>();
// this will generate code to test discriminant_place and
// branch to the appropriate arm block
let otherwise = self.match_candidates(
discriminant_span,
- &mut arm_blocks,
candidates,
block,
&mut fake_borrows,
//
// In that case, the inexhaustive tips of the decision tree
// can't be reached - terminate them with an `unreachable`.
- let source_info = self.source_info(span);
-
let mut otherwise = otherwise;
otherwise.sort();
otherwise.dedup(); // variant switches can introduce duplicate target blocks
for block in otherwise {
self.cfg
- .terminate(block, source_info, TerminatorKind::Unreachable);
+ .terminate(block, outer_source_info, TerminatorKind::Unreachable);
}
}
- if let Some(fake_borrows) = fake_borrows {
- self.add_fake_borrows(&pre_binding_blocks, fake_borrows, source_info, block);
- }
+ // Step 4. Determine the fake borrows that are needed from the above
+ // places. Create the required temporaries for them.
+
+ let fake_borrow_temps = if let Some(ref borrows) = fake_borrows {
+ self.calculate_fake_borrows(borrows, discriminant_span)
+ } else {
+ Vec::new()
+ };
+
+ // Step 5. Create everything else: the guards and the arms.
// all the arm blocks will rejoin here
let end_block = self.cfg.start_new_block();
let outer_source_info = self.source_info(span);
- for (arm_index, (body, source_scope)) in arm_bodies.into_iter().enumerate() {
- let mut arm_block = arm_blocks.blocks[arm_index];
- // Re-enter the source scope we created the bindings in.
- self.source_scope = source_scope;
+
+ for (arm, candidates) in arm_candidates {
+ let mut arm_block = self.cfg.start_new_block();
+
+ let body = self.hir.mirror(arm.body.clone());
+ let scope = self.declare_bindings(
+ None,
+ body.span,
+ LintLevel::Inherited,
+ &arm.patterns[..],
+ ArmHasGuard(arm.guard.is_some()),
+ Some((Some(&discriminant_place), discriminant_span)),
+ );
+
+ for (pat_index, candidate) in candidates.into_iter().enumerate() {
+ self.bind_and_guard_matched_candidate(
+ candidate,
+ arm.guard.clone(),
+ arm_block,
+ &fake_borrow_temps,
+ discriminant_span,
+ pat_index,
+ );
+ }
+
+ if let Some(source_scope) = scope {
+ self.source_scope = source_scope;
+ }
+
unpack!(arm_block = self.into(destination, arm_block, body));
self.cfg.terminate(
arm_block,
TerminatorKind::Goto { target: end_block },
);
}
+
self.source_scope = outer_source_info.scope;
end_block.unit()
match_pairs: vec![MatchPair::new(initializer.clone(), &irrefutable_pat)],
bindings: vec![],
ascriptions: vec![],
- guard: None,
- // since we don't call `match_candidates`, next fields is unused
- arm_index: 0,
- pat_index: 0,
+ // since we don't call `match_candidates`, next fields are unused
+ otherwise_block: None,
pre_binding_block: block,
next_candidate_pre_binding_block: block,
};
}
}
-/// List of blocks for each arm (and potentially other metadata in the
-/// future).
-struct ArmBlocks {
- blocks: Vec<BasicBlock>,
-}
-
-#[derive(Clone, Debug)]
+#[derive(Debug)]
pub struct Candidate<'pat, 'tcx: 'pat> {
// span of the original pattern that gave rise to this candidate
span: Span,
// ...these bindings established...
bindings: Vec<Binding<'tcx>>,
- // ...these types asserted...
+ // ...and these types asserted...
ascriptions: Vec<Ascription<'tcx>>,
- // ...and the guard must be evaluated...
- guard: Option<Guard<'tcx>>,
-
- // ...and then we branch to arm with this index.
- arm_index: usize,
+ // ...and the guard must be evaluated, if false branch to Block...
+ otherwise_block: Option<BasicBlock>,
// ...and the blocks for add false edges between candidates
pre_binding_block: BasicBlock,
next_candidate_pre_binding_block: BasicBlock,
-
- // This uniquely identifies this candidate *within* the arm.
- pat_index: usize,
}
#[derive(Clone, Debug)]
// ... must match this pattern.
pattern: &'pat Pattern<'tcx>,
-
- // HACK(eddyb) This is used to toggle whether a Slice pattern
- // has had its length checked. This is only necessary because
- // the "rest" part of the pattern right now has type &[T] and
- // as such, it requires an Rvalue::Slice to be generated.
- // See RFC 495 / issue #23121 for the eventual (proper) solution.
- slice_len_checked: bool,
}
#[derive(Clone, Debug, PartialEq)]
kind: TestKind<'tcx>,
}
+/// ArmHasGuard is isomorphic to a boolean flag. It indicates whether
+/// a match arm has a guard expression attached to it.
+#[derive(Copy, Clone, Debug)]
+pub(crate) struct ArmHasGuard(pub bool);
+
///////////////////////////////////////////////////////////////////////////
// Main matching algorithm
/// candidates are sorted such that the first item in the list
/// has the highest priority. When a candidate is found to match
/// the value, we will generate a branch to the appropriate
- /// block found in `arm_blocks`.
+ /// prebinding block.
///
/// The return value is a list of "otherwise" blocks. These are
/// points in execution where we found that *NONE* of the
/// list. This is important to keep the size of the generated code
/// under control. See `test_candidates` for more details.
///
- /// If `add_fake_borrows` is true, then places which need fake borrows
+ /// If `fake_borrows` is Some, then places which need fake borrows
/// will be added to it.
fn match_candidates<'pat>(
&mut self,
span: Span,
- arm_blocks: &mut ArmBlocks,
- mut candidates: Vec<Candidate<'pat, 'tcx>>,
+ candidates: &mut [&mut Candidate<'pat, 'tcx>],
mut block: BasicBlock,
fake_borrows: &mut Option<FxHashMap<Place<'tcx>, BorrowKind>>,
) -> Vec<BasicBlock> {
span, block, candidates
);
- // 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.
- for candidate in &mut candidates {
+ // 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.
+ for candidate in &mut *candidates {
self.simplify_candidate(candidate);
}
- // The candidates are sorted by priority. Check to see
- // whether the higher priority candidates (and hence at
- // the front of the vec) have satisfied all their match
- // pairs.
+ // 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.
let fully_matched = candidates
.iter()
.take_while(|c| c.match_pairs.is_empty())
"match_candidates: {:?} candidates fully matched",
fully_matched
);
- let mut unmatched_candidates = candidates.split_off(fully_matched);
+ let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
+
+ if !matched_candidates.is_empty() {
+ block = if let Some(last_otherwise_block) = self.select_matched_candidates(
+ matched_candidates,
+ block,
+ fake_borrows,
+ ) {
+ last_otherwise_block
+ } else {
+ // Any remaining candidates are unreachable.
+ if unmatched_candidates.is_empty() {
+ return Vec::new();
+ } else {
+ 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];
+ }
+
+ // Test candidates where possible.
+ let (otherwise, untested_candidates) = self.test_candidates(
+ span,
+ unmatched_candidates,
+ 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,
+ &mut None,
+ )
+ }
+
+ /// Link up matched candidates. For example, if we have something like
+ /// this:
+ ///
+ /// ...
+ /// Some(x) if cond => ...
+ /// Some(x) => ...
+ /// Some(x) if cond => ...
+ /// ...
+ ///
+ /// We generate real edges from:
+ /// * `block` to the prebinding_block of the first pattern,
+ /// * the otherwise block of the first pattern to the second pattern,
+ /// * the otherwise block of the third pattern to the a block with an
+ /// Unreachable terminator.
+ ///
+ /// As well as that we add fake edges from the otherwise blocks to the
+ /// prebinding block of the next candidate in the original set of
+ /// candidates.
+ fn select_matched_candidates(
+ &mut self,
+ matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
+ block: BasicBlock,
+ fake_borrows: &mut Option<FxHashMap<Place<'tcx>, BorrowKind>>,
+ ) -> Option<BasicBlock> {
+ debug_assert!(
+ !matched_candidates.is_empty(),
+ "select_matched_candidates called with no candidates",
+ );
// Insert a *Shared* borrow of any places that are bound.
if let Some(fake_borrows) = fake_borrows {
for Binding { source, .. }
- in candidates.iter().flat_map(|candidate| &candidate.bindings)
+ in matched_candidates.iter().flat_map(|candidate| &candidate.bindings)
{
fake_borrows.insert(source.clone(), BorrowKind::Shared);
}
}
- let fully_matched_with_guard = candidates.iter().take_while(|c| c.guard.is_some()).count();
+ let fully_matched_with_guard = matched_candidates
+ .iter()
+ .position(|c| c.otherwise_block.is_none())
+ .unwrap_or(matched_candidates.len() - 1);
- let unreachable_candidates = if fully_matched_with_guard + 1 < candidates.len() {
- candidates.split_off(fully_matched_with_guard + 1)
- } else {
- vec![]
- };
+ let (reachable_candidates, unreachable_candidates)
+ = matched_candidates.split_at_mut(fully_matched_with_guard + 1);
- for candidate in candidates {
- // If so, apply any bindings, test the guard (if any), and
- // branch to the arm.
- if let Some(b) = self.bind_and_guard_matched_candidate(block, arm_blocks, candidate) {
- block = b;
- } else {
- // if None is returned, then any remaining candidates
- // are unreachable (at least not through this path).
- // Link them with false edges.
- debug!(
- "match_candidates: add false edges for unreachable {:?} and unmatched {:?}",
- unreachable_candidates, unmatched_candidates
- );
- for candidate in unreachable_candidates {
- let source_info = self.source_info(candidate.span);
- let target = self.cfg.start_new_block();
- if let Some(otherwise) =
- self.bind_and_guard_matched_candidate(target, arm_blocks, candidate)
- {
- self.cfg
- .terminate(otherwise, source_info, TerminatorKind::Unreachable);
- }
- }
+ let first_candidate = &reachable_candidates[0];
- if unmatched_candidates.is_empty() {
- return vec![];
+ 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,
+ },
+ );
+
+ for window in reachable_candidates.windows(2) {
+ if let [first_candidate, second_candidate] = window {
+ let source_info = self.source_info(first_candidate.span);
+ if let Some(otherwise_block) = first_candidate.otherwise_block {
+ self.cfg.terminate(
+ otherwise_block,
+ source_info,
+ TerminatorKind::FalseEdges {
+ real_target: second_candidate.pre_binding_block,
+ imaginary_targets: vec![
+ first_candidate.next_candidate_pre_binding_block
+ ],
+ }
+ )
} else {
- let target = self.cfg.start_new_block();
- return self.match_candidates(
- span,
- arm_blocks,
- unmatched_candidates,
- target,
- &mut None,
- );
+ bug!("candidate other than the last has no guard");
}
+ } else {
+ bug!("<[_]>::windows returned incorrectly sized window");
}
}
- // 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];
- }
- // Test candidates where possible.
- let (otherwise, tested_candidates) =
- self.test_candidates(span, arm_blocks, &unmatched_candidates, block, fake_borrows);
+ // if None is returned, then
+ debug!("match_candidates: add false edges for unreachable {:?}", unreachable_candidates);
+ for candidate in unreachable_candidates {
+ if let Some(otherwise) = candidate.otherwise_block {
+ let source_info = self.source_info(candidate.span);
+ let unreachable = self.cfg.start_new_block();
+ self.cfg.terminate(
+ otherwise,
+ source_info,
+ TerminatorKind::FalseEdges {
+ real_target: unreachable,
+ imaginary_targets: vec![candidate.next_candidate_pre_binding_block],
+ }
+ );
+ self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
+ }
+ }
- // If the target candidates were exhaustive, then we are done.
- // But for borrowck continue build decision tree.
+ let last_candidate = reachable_candidates.last().unwrap();
- // If all candidates were sorted into `target_candidates` somewhere, then
- // the initial set was inexhaustive.
- let untested_candidates = unmatched_candidates.split_off(tested_candidates);
- if untested_candidates.len() == 0 {
- return otherwise;
+ if let Some(otherwise) = last_candidate.otherwise_block {
+ let source_info = self.source_info(last_candidate.span);
+ let block = self.cfg.start_new_block();
+ self.cfg.terminate(
+ otherwise,
+ source_info,
+ TerminatorKind::FalseEdges {
+ real_target: block,
+ imaginary_targets: vec![last_candidate.next_candidate_pre_binding_block]
+ }
+ );
+ Some(block)
+ } else {
+ None
}
-
- // Otherwise, let's process those remaining candidates.
- let join_block = self.join_otherwise_blocks(span, otherwise);
- self.match_candidates(span, arm_blocks, untested_candidates, join_block, &mut None)
}
fn join_otherwise_blocks(&mut self, span: Span, mut otherwise: Vec<BasicBlock>) -> BasicBlock {
/// In addition to avoiding exponential-time blowups, this algorithm
/// also has nice property that each guard and arm is only generated
/// once.
- fn test_candidates<'pat>(
+ fn test_candidates<'pat, 'b, 'c>(
&mut self,
span: Span,
- arm_blocks: &mut ArmBlocks,
- candidates: &[Candidate<'pat, 'tcx>],
+ mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
block: BasicBlock,
fake_borrows: &mut Option<FxHashMap<Place<'tcx>, BorrowKind>>,
- ) -> (Vec<BasicBlock>, usize) {
+ ) -> (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);
+ let match_place = match_pair.place.clone();
// most of the time, the test to perform is simply a function
// of the main candidate; but for a test like SwitchInt, we
} => {
for candidate in candidates.iter() {
if !self.add_cases_to_switch(
- &match_pair.place,
+ &match_place,
candidate,
switch_ty,
options,
ref mut variants,
} => {
for candidate in candidates.iter() {
- if !self.add_variants_to_switch(&match_pair.place, candidate, variants) {
+ if !self.add_variants_to_switch(&match_place, candidate, variants) {
break;
}
}
// Insert a Shallow borrow of any places that is switched on.
fake_borrows.as_mut().map(|fb| {
- fb.entry(match_pair.place.clone()).or_insert(BorrowKind::Shallow)
+ fb.entry(match_place.clone()).or_insert(BorrowKind::Shallow)
});
// perform the test, branching to one of N blocks. For each of
"match_candidates: test={:?} match_pair={:?}",
test, match_pair
);
- let target_blocks = self.perform_test(block, &match_pair.place, &test);
- let mut target_candidates = vec![vec![]; target_blocks.len()];
+ 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);
+
+ let total_candidate_count = candidates.len();
// Sort the candidates into the appropriate vector in
// `target_candidates`. Note that at some point we may
// encounter a candidate where the test is not relevant; at
// that point, we stop sorting.
- let tested_candidates = candidates
- .iter()
- .take_while(|c| {
- self.sort_candidate(&match_pair.place, &test, c, &mut target_candidates)
- })
- .count();
- assert!(tested_candidates > 0); // at least the last candidate ought to be tested
- debug!("tested_candidates: {}", tested_candidates);
- debug!(
- "untested_candidates: {}",
- candidates.len() - tested_candidates
- );
+ while let Some(candidate) = candidates.first_mut() {
+ if let Some(idx) = self.sort_candidate(&match_place, &test, candidate) {
+ let (candidate, rest) = candidates.split_first_mut().unwrap();
+ target_candidates[idx].push(candidate);
+ candidates = rest;
+ } else {
+ break;
+ }
+ }
+ // at least the first candidate ought to be tested
+ assert!(total_candidate_count > candidates.len());
+ debug!("tested_candidates: {}", total_candidate_count - candidates.len());
+ debug!("untested_candidates: {}", candidates.len());
// For each outcome of test, process the candidates that still
// apply. Collect a list of blocks where control flow will
let otherwise: Vec<_> = target_blocks
.into_iter()
.zip(target_candidates)
- .flat_map(|(target_block, target_candidates)| {
+ .flat_map(|(target_block, mut target_candidates)| {
self.match_candidates(
span,
- arm_blocks,
- target_candidates,
+ &mut *target_candidates,
target_block,
fake_borrows,
)
})
.collect();
- (otherwise, tested_candidates)
+ (otherwise, candidates)
+ }
+
+ // Determine the fake borrows that are needed to ensure that the place
+ // will evaluate to the same thing until an arm has been chosen.
+ fn calculate_fake_borrows<'b>(
+ &mut self,
+ fake_borrows: &'b FxHashMap<Place<'tcx>, BorrowKind>,
+ temp_span: Span,
+ ) -> Vec<(&'b Place<'tcx>, BorrowKind, Local)> {
+ let tcx = self.hir.tcx();
+
+ debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows);
+
+ let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len());
+
+ // Insert a Shallow borrow of the prefixes of any fake borrows.
+ for (place, borrow_kind) in fake_borrows
+ {
+ let mut prefix_cursor = place;
+ while let Place::Projection(box Projection { base, elem }) = prefix_cursor {
+ if let ProjectionElem::Deref = elem {
+ // Insert a shallow borrow after a deref. For other
+ // projections the borrow of prefix_cursor will
+ // conflict with any mutation of base.
+ all_fake_borrows.push((base, BorrowKind::Shallow));
+ }
+ prefix_cursor = base;
+ }
+
+ all_fake_borrows.push((place, *borrow_kind));
+ }
+
+ // Deduplicate and ensure a deterministic order.
+ all_fake_borrows.sort();
+ all_fake_borrows.dedup();
+
+ debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows);
+
+ all_fake_borrows.into_iter().map(|(matched_place, borrow_kind)| {
+ let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).to_ty(tcx);
+ let fake_borrow_ty = tcx.mk_imm_ref(tcx.types.re_erased, fake_borrow_deref_ty);
+ let fake_borrow_temp = self.local_decls.push(
+ LocalDecl::new_temp(fake_borrow_ty, temp_span)
+ );
+
+ (matched_place, borrow_kind, fake_borrow_temp)
+ }).collect()
}
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Pattern binding - used for `let` and function parameters as well.
+impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// Initializes each of the bindings from the candidate by
- /// moving/copying/ref'ing the source as appropriate. Tests the
- /// guard, if any, and then branches to the arm. Returns the block
- /// for the case where the guard fails.
+ /// moving/copying/ref'ing the source as appropriate. Tests the guard, if
+ /// any, and then branches to the arm. Returns the block for the case where
+ /// the guard fails.
///
- /// Note: we check earlier that if there is a guard, there cannot
- /// be move bindings. This isn't really important for the
- /// self-consistency of this fn, but the reason for it should be
- /// clear: after we've done the assignments, if there were move
- /// bindings, further tests would be a use-after-move (which would
- /// in turn be detected by the borrowck code that runs on the
- /// MIR).
+ /// Note: we check earlier that if there is a guard, there cannot be move
+ /// bindings (unless feature(bind_by_move_pattern_guards) is used). This
+ /// isn't really important for the self-consistency of this fn, but the
+ /// reason for it should be clear: after we've done the assignments, if
+ /// there were move bindings, further tests would be a use-after-move.
+ /// bind_by_move_pattern_guards avoids this by only moving the binding once
+ /// the guard has evaluated to true (see below).
fn bind_and_guard_matched_candidate<'pat>(
&mut self,
- mut block: BasicBlock,
- arm_blocks: &mut ArmBlocks,
candidate: Candidate<'pat, 'tcx>,
- ) -> Option<BasicBlock> {
- debug!(
- "bind_and_guard_matched_candidate(block={:?}, candidate={:?})",
- block, candidate
- );
+ guard: Option<Guard<'tcx>>,
+ arm_block: BasicBlock,
+ fake_borrows: &Vec<(&Place<'tcx>, BorrowKind, Local)>,
+ discriminant_span: Span,
+ pat_index: usize,
+ ) {
+ debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
debug_assert!(candidate.match_pairs.is_empty());
- self.ascribe_types(block, &candidate.ascriptions);
-
- let arm_block = arm_blocks.blocks[candidate.arm_index];
let candidate_source_info = self.source_info(candidate.span);
- self.cfg.terminate(
- block,
- candidate_source_info,
- TerminatorKind::Goto {
- target: candidate.pre_binding_block,
- },
- );
-
- block = self.cfg.start_new_block();
+ let mut block = self.cfg.start_new_block();
self.cfg.terminate(
candidate.pre_binding_block,
candidate_source_info,
imaginary_targets: vec![candidate.next_candidate_pre_binding_block],
},
);
+ self.ascribe_types(block, &candidate.ascriptions);
// rust-lang/rust#27282: The `autoref` business deserves some
// explanation here.
// match input itself; it is up to us to create a place
// holding a `&` or `&mut` that we can then borrow).
- let autoref = self.hir
- .tcx()
- .all_pat_vars_are_implicit_refs_within_guards();
- if let Some(guard) = candidate.guard {
+ let tcx = self.hir.tcx();
+ let autoref = tcx.all_pat_vars_are_implicit_refs_within_guards();
+ if let Some(guard) = guard {
if autoref {
self.bind_matched_candidate_for_guard(
block,
- candidate.pat_index,
+ pat_index,
&candidate.bindings,
);
let guard_frame = GuardFrame {
self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
}
+ let re_erased = tcx.types.re_erased;
+ let discriminant_source_info = self.source_info(discriminant_span);
+ for &(place, borrow_kind, temp) in fake_borrows {
+ let borrow = Rvalue::Ref(
+ re_erased,
+ borrow_kind,
+ place.clone(),
+ );
+ self.cfg.push_assign(
+ block,
+ discriminant_source_info,
+ &Place::Local(temp),
+ borrow,
+ );
+ }
+
// the block to branch to if the guard fails; if there is no
// guard, this block is simply unreachable
let guard = match guard {
Guard::If(e) => self.hir.mirror(e),
};
let source_info = self.source_info(guard.span);
+ let guard_end = self.source_info(tcx.sess.source_map().end_point(guard.span));
let cond = unpack!(block = self.as_local_operand(block, guard));
if autoref {
let guard_frame = self.guard_context.pop().unwrap();
);
}
- let false_edge_block = self.cfg.start_new_block();
+ for &(_, _, temp) in fake_borrows {
+ self.cfg.push(block, Statement {
+ source_info: guard_end,
+ kind: StatementKind::FakeRead(
+ FakeReadCause::ForMatchGuard,
+ Place::Local(temp),
+ ),
+ });
+ }
// We want to ensure that the matched candidates are bound
// after we have confirmed this candidate *and* any
self.cfg.terminate(
block,
source_info,
- TerminatorKind::if_(self.hir.tcx(), cond, post_guard_block, false_edge_block),
+ TerminatorKind::if_(
+ self.hir.tcx(),
+ cond,
+ post_guard_block,
+ candidate.otherwise_block.unwrap()
+ ),
);
if autoref {
source_info,
TerminatorKind::Goto { target: arm_block },
);
-
- let otherwise = self.cfg.start_new_block();
-
- self.cfg.terminate(
- false_edge_block,
- source_info,
- TerminatorKind::FalseEdges {
- real_target: otherwise,
- imaginary_targets: vec![candidate.next_candidate_pre_binding_block],
- },
- );
- Some(otherwise)
} 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.)
candidate_source_info,
TerminatorKind::Goto { target: arm_block },
);
- None
}
}
let ref_for_guard =
self.storage_live_binding(block, binding.var_id, binding.span, RefWithinGuard);
// Question: Why schedule drops if bindings are all
- // shared-&'s? Answer: Because schedule_drop_for_binding
- // also emits StorageDead's for those locals.
+ // shared-&'s?
+ // Answer: Because schedule_drop_for_binding also emits
+ // StorageDead's for those locals.
self.schedule_drop_for_binding(binding.var_id, binding.span, RefWithinGuard);
match binding.binding_mode {
BindingMode::ByValue => {
debug!("declare_binding: vars={:?}", locals);
self.var_indices.insert(var_id, locals);
}
-
- // Determine the fake borrows that are needed to ensure that the place
- // will evaluate to the same thing until an arm has been chosen.
- fn add_fake_borrows<'pat>(
- &mut self,
- pre_binding_blocks: &[(BasicBlock, Span)],
- fake_borrows: FxHashMap<Place<'tcx>, BorrowKind>,
- source_info: SourceInfo,
- start_block: BasicBlock,
- ) {
- let tcx = self.hir.tcx();
-
- debug!("add_fake_borrows pre_binding_blocks = {:?}, fake_borrows = {:?}",
- pre_binding_blocks, fake_borrows);
-
- let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len());
-
- // Insert a Shallow borrow of the prefixes of any fake borrows.
- for (place, borrow_kind) in fake_borrows
- {
- {
- let mut prefix_cursor = &place;
- while let Place::Projection(box Projection { base, elem }) = prefix_cursor {
- if let ProjectionElem::Deref = elem {
- // Insert a shallow borrow after a deref. For other
- // projections the borrow of prefix_cursor will
- // conflict with any mutation of base.
- all_fake_borrows.push((base.clone(), BorrowKind::Shallow));
- }
- prefix_cursor = base;
- }
- }
-
- all_fake_borrows.push((place, borrow_kind));
- }
-
- // Deduplicate and ensure a deterministic order.
- all_fake_borrows.sort();
- all_fake_borrows.dedup();
-
- debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows);
-
- // Add fake borrows to the start of the match and reads of them before
- // the start of each arm.
- let mut borrowed_input_temps = Vec::with_capacity(all_fake_borrows.len());
-
- for (matched_place, borrow_kind) in all_fake_borrows {
- let borrowed_input =
- Rvalue::Ref(tcx.types.re_erased, borrow_kind, matched_place.clone());
- let borrowed_input_ty = borrowed_input.ty(&self.local_decls, tcx);
- let borrowed_input_temp = self.temp(borrowed_input_ty, source_info.span);
- self.cfg.push_assign(
- start_block,
- source_info,
- &borrowed_input_temp,
- borrowed_input
- );
- borrowed_input_temps.push(borrowed_input_temp);
- }
-
- // FIXME: This could be a lot of reads (#fake borrows * #patterns).
- // The false edges that we currently generate would allow us to only do
- // this on the last Candidate, but it's possible that there might not be
- // so many false edges in the future, so we read for all Candidates for
- // now.
- // Another option would be to make our own block and add our own false
- // edges to it.
- if tcx.emit_read_for_match() {
- for &(pre_binding_block, span) in pre_binding_blocks {
- let pattern_source_info = self.source_info(span);
- for temp in &borrowed_input_temps {
- self.cfg.push(pre_binding_block, Statement {
- source_info: pattern_source_info,
- kind: StatementKind::FakeRead(
- FakeReadCause::ForMatchGuard,
- temp.clone(),
- ),
- });
- }
- }
- }
- }
}
}
}
- PatternKind::Slice { ref prefix, ref slice, ref suffix }
- if !match_pair.slice_len_checked => {
+ PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
let len = prefix.len() + suffix.len();
let op = if slice.is_some() {
BinOp::Ge
PatternKind::AscribeUserType { .. } |
PatternKind::Array { .. } |
- PatternKind::Slice { .. } |
PatternKind::Wild |
PatternKind::Binding { .. } |
PatternKind::Leaf { .. } |
target_block
}
- /// Given that we are performing `test` against `test_place`,
- /// this job sorts out what the status of `candidate` will be
- /// after the test. The `resulting_candidates` vector stores, for
- /// each possible outcome of `test`, a vector of the candidates
- /// that will result. This fn should add a (possibly modified)
- /// clone of candidate into `resulting_candidates` wherever
- /// appropriate.
+ /// Given that we are performing `test` against `test_place`, this job
+ /// sorts out what the status of `candidate` will be after the test. See
+ /// `test_candidates` for the usage of this function. The returned index is
+ /// the index that this candiate should be placed in the
+ /// `target_candidates` vec. The candidate may be modified to update its
+ /// `match_pairs`.
///
- /// So, for example, if this candidate is `x @ Some(P0)` and the
- /// Tests is a variant test, then we would add `(x as Option).0 @
- /// P0` to the `resulting_candidates` entry corresponding to the
- /// variant `Some`.
+ /// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is
+ /// a variant test, then we would modify the candidate to be `(x as
+ /// Option).0 @ P0` and return the index corresponding to the variant
+ /// `Some`.
///
- /// However, in some cases, the test may just not be relevant to
- /// candidate. For example, suppose we are testing whether `foo.x == 22`,
- /// but in one match arm we have `Foo { x: _, ... }`... in that case,
- /// the test for what value `x` has has no particular relevance
- /// to this candidate. In such cases, this function just returns false
- /// without doing anything. This is used by the overall `match_candidates`
- /// algorithm to structure the match as a whole. See `match_candidates` for
- /// more details.
+ /// However, in some cases, the test may just not be relevant to candidate.
+ /// For example, suppose we are testing whether `foo.x == 22`, but in one
+ /// match arm we have `Foo { x: _, ... }`... in that case, the test for
+ /// what value `x` has has no particular relevance to this candidate. In
+ /// such cases, this function just returns None without doing anything.
+ /// This is used by the overall `match_candidates` algorithm to structure
+ /// the match as a whole. See `match_candidates` for more details.
///
- /// FIXME(#29623). In some cases, we have some tricky choices to
- /// make. for example, if we are testing that `x == 22`, but the
- /// candidate is `x @ 13..55`, what should we do? In the event
- /// that the test is true, we know that the candidate applies, but
- /// in the event of false, we don't know that it *doesn't*
- /// apply. For now, we return false, indicate that the test does
- /// not apply to this candidate, but it might be we can get
+ /// FIXME(#29623). In some cases, we have some tricky choices to make. for
+ /// example, if we are testing that `x == 22`, but the candidate is `x @
+ /// 13..55`, what should we do? In the event that the test is true, we know
+ /// that the candidate applies, but in the event of false, we don't know
+ /// that it *doesn't* apply. For now, we return false, indicate that the
+ /// test does not apply to this candidate, but it might be we can get
/// tighter match code if we do something a bit different.
- pub fn sort_candidate<'pat>(&mut self,
- test_place: &Place<'tcx>,
- test: &Test<'tcx>,
- candidate: &Candidate<'pat, 'tcx>,
- resulting_candidates: &mut [Vec<Candidate<'pat, 'tcx>>])
- -> bool {
+ pub fn sort_candidate<'pat, 'cand>(
+ &mut self,
+ test_place: &Place<'tcx>,
+ test: &Test<'tcx>,
+ candidate: &mut Candidate<'pat, 'tcx>,
+ ) -> Option<usize> {
// Find the match_pair for this place (if any). At present,
// afaik, there can be at most one. (In the future, if we
// adopted a more general `@` operator, there might be more
// than one, but it'd be very unusual to have two sides that
// both require tests; you'd expect one side to be simplified
// away.)
- let tested_match_pair = candidate.match_pairs.iter()
- .enumerate()
- .find(|&(_, mp)| mp.place == *test_place);
- let (match_pair_index, match_pair) = match tested_match_pair {
- Some(pair) => pair,
- None => {
- // We are not testing this place. Therefore, this
- // candidate applies to ALL outcomes.
- return false;
- }
- };
+ let (match_pair_index, match_pair) = candidate.match_pairs
+ .iter()
+ .enumerate()
+ .find(|&(_, mp)| mp.place == *test_place)?;
match (&test.kind, &*match_pair.pattern.kind) {
// If we are performing a variant switch, then this
(&TestKind::Switch { adt_def: tested_adt_def, .. },
&PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. }) => {
assert_eq!(adt_def, tested_adt_def);
- let new_candidate =
- self.candidate_after_variant_switch(match_pair_index,
- adt_def,
- variant_index,
- subpatterns,
- candidate);
- resulting_candidates[variant_index.as_usize()].push(new_candidate);
- true
+ self.candidate_after_variant_switch(match_pair_index,
+ adt_def,
+ variant_index,
+ subpatterns,
+ candidate);
+ Some(variant_index.as_usize())
}
- (&TestKind::Switch { .. }, _) => false,
+ (&TestKind::Switch { .. }, _) => None,
// If we are performing a switch over integers, then this informs integer
// equality, but nothing else.
&PatternKind::Constant { ref value })
if is_switch_ty(match_pair.pattern.ty) => {
let index = indices[value];
- let new_candidate = self.candidate_without_match_pair(match_pair_index,
- candidate);
- resulting_candidates[index].push(new_candidate);
- true
+ self.candidate_without_match_pair(match_pair_index, candidate);
+ Some(index)
}
(&TestKind::SwitchInt { switch_ty: _, ref options, ref indices },
// No switch values are contained in the pattern range,
// so the pattern can be matched only if this test fails.
let otherwise = options.len();
- resulting_candidates[otherwise].push(candidate.clone());
- true
+ Some(otherwise)
} else {
- false
+ None
}
}
- (&TestKind::SwitchInt { .. }, _) => false,
+ (&TestKind::SwitchInt { .. }, _) => None,
(&TestKind::Len { len: test_len, op: BinOp::Eq },
&PatternKind::Slice { ref prefix, ref slice, ref suffix }) => {
(Ordering::Equal, &None) => {
// on true, min_len = len = $actual_length,
// on false, len != $actual_length
- resulting_candidates[0].push(
- self.candidate_after_slice_test(match_pair_index,
- candidate,
- prefix,
- slice.as_ref(),
- suffix)
- );
- true
+ self.candidate_after_slice_test(match_pair_index,
+ candidate,
+ prefix,
+ slice.as_ref(),
+ suffix);
+ Some(0)
}
(Ordering::Less, _) => {
// test_len < pat_len. If $actual_len = test_len,
// then $actual_len < pat_len and we don't have
// enough elements.
- resulting_candidates[1].push(candidate.clone());
- true
+ Some(1)
}
(Ordering::Equal, &Some(_)) | (Ordering::Greater, &Some(_)) => {
// This can match both if $actual_len = test_len >= pat_len,
// and if $actual_len > test_len. We can't advance.
- false
+ None
}
(Ordering::Greater, &None) => {
// test_len != pat_len, so if $actual_len = test_len, then
// $actual_len != pat_len.
- resulting_candidates[1].push(candidate.clone());
- true
+ Some(1)
}
}
}
(Ordering::Equal, &Some(_)) => {
// $actual_len >= test_len = pat_len,
// so we can match.
- resulting_candidates[0].push(
- self.candidate_after_slice_test(match_pair_index,
- candidate,
- prefix,
- slice.as_ref(),
- suffix)
- );
- true
+ self.candidate_after_slice_test(match_pair_index,
+ candidate,
+ prefix,
+ slice.as_ref(),
+ suffix);
+ Some(0)
}
(Ordering::Less, _) | (Ordering::Equal, &None) => {
// test_len <= pat_len. If $actual_len < test_len,
// then it is also < pat_len, so the test passing is
// necessary (but insufficient).
- resulting_candidates[0].push(candidate.clone());
- true
+ Some(0)
}
(Ordering::Greater, &None) => {
// test_len > pat_len. If $actual_len >= test_len > pat_len,
// then we know we won't have a match.
- resulting_candidates[1].push(candidate.clone());
- true
+ Some(1)
}
(Ordering::Greater, &Some(_)) => {
// test_len < pat_len, and is therefore less
// strict. This can still go both ways.
- false
+ None
}
}
}
(&TestKind::Range(test),
&PatternKind::Range(pat)) => {
if test == pat {
- resulting_candidates[0]
- .push(self.candidate_without_match_pair(
- match_pair_index,
- candidate,
- ));
- return true;
+ self.candidate_without_match_pair(
+ match_pair_index,
+ candidate,
+ );
+ return Some(0);
}
let no_overlap = (|| {
if no_overlap == Some(true) {
// Testing range does not overlap with pattern range,
// so the pattern can be matched only if this test fails.
- resulting_candidates[1].push(candidate.clone());
- true
+ Some(1)
} else {
- false
+ None
}
}
if self.const_range_contains(range, value) == Some(false) {
// `value` is not contained in the testing range,
// so `value` can be matched only if this test fails.
- resulting_candidates[1].push(candidate.clone());
- true
+ Some(1)
} else {
- false
+ None
}
}
- (&TestKind::Range { .. }, _) => false,
-
+ (&TestKind::Range { .. }, _) => None,
(&TestKind::Eq { .. }, _) |
(&TestKind::Len { .. }, _) => {
// FIXME(#29623) we can be more clever here
let pattern_test = self.test(&match_pair);
if pattern_test.kind == test.kind {
- let new_candidate = self.candidate_without_match_pair(match_pair_index,
- candidate);
- resulting_candidates[0].push(new_candidate);
- true
+ self.candidate_without_match_pair(match_pair_index, candidate);
+ Some(0)
} else {
- false
+ None
}
}
}
}
- fn candidate_without_match_pair<'pat>(&mut self,
- match_pair_index: usize,
- candidate: &Candidate<'pat, 'tcx>)
- -> Candidate<'pat, 'tcx> {
- let other_match_pairs =
- candidate.match_pairs.iter()
- .enumerate()
- .filter(|&(index, _)| index != match_pair_index)
- .map(|(_, mp)| mp.clone())
- .collect();
- Candidate {
- span: candidate.span,
- match_pairs: other_match_pairs,
- bindings: candidate.bindings.clone(),
- ascriptions: candidate.ascriptions.clone(),
- guard: candidate.guard.clone(),
- arm_index: candidate.arm_index,
- pat_index: candidate.pat_index,
- pre_binding_block: candidate.pre_binding_block,
- next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
- }
+ fn candidate_without_match_pair(
+ &mut self,
+ match_pair_index: usize,
+ candidate: &mut Candidate<'_, 'tcx>,
+ ) {
+ candidate.match_pairs.remove(match_pair_index);
}
fn candidate_after_slice_test<'pat>(&mut self,
match_pair_index: usize,
- candidate: &Candidate<'pat, 'tcx>,
+ candidate: &mut Candidate<'pat, 'tcx>,
prefix: &'pat [Pattern<'tcx>],
opt_slice: Option<&'pat Pattern<'tcx>>,
- suffix: &'pat [Pattern<'tcx>])
- -> Candidate<'pat, 'tcx> {
- let mut new_candidate =
- self.candidate_without_match_pair(match_pair_index, candidate);
+ suffix: &'pat [Pattern<'tcx>]) {
+ let removed_place = candidate.match_pairs.remove(match_pair_index).place;
self.prefix_slice_suffix(
- &mut new_candidate.match_pairs,
- &candidate.match_pairs[match_pair_index].place,
+ &mut candidate.match_pairs,
+ &removed_place,
prefix,
opt_slice,
suffix);
-
- new_candidate
}
- fn candidate_after_variant_switch<'pat>(&mut self,
- match_pair_index: usize,
- adt_def: &'tcx ty::AdtDef,
- variant_index: VariantIdx,
- subpatterns: &'pat [FieldPattern<'tcx>],
- candidate: &Candidate<'pat, 'tcx>)
- -> Candidate<'pat, 'tcx> {
- let match_pair = &candidate.match_pairs[match_pair_index];
+ fn candidate_after_variant_switch<'pat>(
+ &mut self,
+ match_pair_index: usize,
+ adt_def: &'tcx ty::AdtDef,
+ variant_index: VariantIdx,
+ subpatterns: &'pat [FieldPattern<'tcx>],
+ candidate: &mut Candidate<'pat, 'tcx>,
+ ) {
+ let match_pair = candidate.match_pairs.remove(match_pair_index);
// So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
// we want to create a set of derived match-patterns like
// `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
let elem = ProjectionElem::Downcast(adt_def, variant_index);
- let downcast_place = match_pair.place.clone().elem(elem); // `(x as Variant)`
+ let downcast_place = match_pair.place.elem(elem); // `(x as Variant)`
let consequent_match_pairs =
subpatterns.iter()
.map(|subpattern| {
MatchPair::new(place, &subpattern.pattern)
});
- // In addition, we need all the other match pairs from the old candidate.
- let other_match_pairs =
- candidate.match_pairs.iter()
- .enumerate()
- .filter(|&(index, _)| index != match_pair_index)
- .map(|(_, mp)| mp.clone());
-
- let all_match_pairs = consequent_match_pairs.chain(other_match_pairs).collect();
-
- Candidate {
- span: candidate.span,
- match_pairs: all_match_pairs,
- bindings: candidate.bindings.clone(),
- ascriptions: candidate.ascriptions.clone(),
- guard: candidate.guard.clone(),
- arm_index: candidate.arm_index,
- pat_index: candidate.pat_index,
- pre_binding_block: candidate.pre_binding_block,
- next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
- }
+ candidate.match_pairs.extend(consequent_match_pairs);
}
fn error_simplifyable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! {
// ...
// _2 = std::option::Option<i32>::Some(const 42i32,);
// FakeRead(ForMatchedPlace, _2);
-// _7 = discriminant(_2);
-// _9 = &shallow (promoted[2]: std::option::Option<i32>);
-// _10 = &(((promoted[1]: std::option::Option<i32>) as Some).0: i32);
-// switchInt(move _7) -> [0isize: bb5, 1isize: bb3, otherwise: bb7];
+// _3 = discriminant(_2);
+// switchInt(move _3) -> [0isize: bb4, 1isize: bb2, otherwise: bb7];
// }
// bb1: {
// resume;
// }
-// bb2: { // arm1
-// _1 = (const 3i32, const 3i32);
-// goto -> bb13;
+// bb2: {
+// falseEdges -> [real: bb9, imaginary: bb3]; //pre_binding1
// }
-// bb3: { // binding3(empty) and arm3
-// FakeRead(ForMatchGuard, _9);
-// FakeRead(ForMatchGuard, _10);
-// falseEdges -> [real: bb8, imaginary: bb4]; //pre_binding1
+// bb3: {
+// falseEdges -> [real: bb12, imaginary: bb4]; //pre_binding2
// }
// bb4: {
-// FakeRead(ForMatchGuard, _9);
-// FakeRead(ForMatchGuard, _10);
-// falseEdges -> [real: bb12, imaginary: bb5]; //pre_binding2
+// falseEdges -> [real: bb13, imaginary: bb5]; //pre_binding3
// }
// bb5: {
-// FakeRead(ForMatchGuard, _9);
-// FakeRead(ForMatchGuard, _10);
-// falseEdges -> [real: bb2, imaginary: bb6]; //pre_binding3
-// }
-// bb6: {
// unreachable;
// }
+// bb6: { // to pre_binding2
+// falseEdges -> [real: bb3, imaginary: bb3];
+// }
// bb7: {
// unreachable;
// }
-// bb8: { // binding1 and guard
-// StorageLive(_5);
-// _5 = &(((promoted[0]: std::option::Option<i32>) as Some).0: i32);
-// StorageLive(_8);
-// _8 = const guard() -> [return: bb9, unwind: bb1];
-// }
-// bb9: {
-// switchInt(move _8) -> [false: bb10, otherwise: bb11];
+// bb8: {
+// ...
+// return;
// }
-// bb10: { // to pre_binding2
-// falseEdges -> [real: bb4, imaginary: bb4];
+// bb9: { // binding1 and guard
+// StorageLive(_8);
+// _8 = &(((promoted[2]: std::option::Option<i32>) as Some).0: i32);
+// _4 = &shallow (promoted[1]: std::option::Option<i32>);
+// _5 = &(((promoted[0]: std::option::Option<i32>) as Some).0: i32);
+// StorageLive(_9);
+// _9 = const guard() -> [return: bb10, unwind: bb1];
// }
-// bb11: { // bindingNoLandingPads.before.mir2 and arm2
-// StorageLive(_3);
-// _3 = ((_2 as Some).0: i32);
-// StorageLive(_11);
-// _11 = _3;
-// _1 = (const 1i32, move _11);
-// StorageDead(_11);
-// goto -> bb13;
+// bb10: {
+// FakeRead(ForMatchGuard, _4);
+// FakeRead(ForMatchGuard, _5);
+// switchInt(move _9) -> [false: bb6, otherwise: bb11];
// }
-// bb12: {
+// bb11: {
// StorageLive(_6);
// _6 = ((_2 as Some).0: i32);
+// StorageLive(_10);
+// _10 = _6;
+// _1 = (const 1i32, move _10);
+// StorageDead(_10);
+// goto -> bb8;
+// }
+// bb12: {
+// StorageLive(_11);
+// _11 = ((_2 as Some).0: i32);
// StorageLive(_12);
-// _12 = _6;
-// _1 = (const 2i32, move_12);
+// _12 = _11;
+// _1 = (const 2i32, move _12);
// StorageDead(_12);
-// goto -> bb13;
+// goto -> bb8;
// }
// bb13: {
-// ...
-// return;
+// _1 = (const 3i32, const 3i32);
+// goto -> bb8;
// }
// END rustc.full_tested_match.QualifyAndPromoteConstants.after.mir
//
// ...
// _2 = std::option::Option<i32>::Some(const 42i32,);
// FakeRead(ForMatchedPlace, _2);
-// _7 = discriminant(_2);
-// _9 = &shallow _2;
-// _10 = &((_2 as Some).0: i32);
-// switchInt(move _7) -> [0isize: bb4, 1isize: bb3, otherwise: bb7];
+// _3 = discriminant(_2);
+// switchInt(move _3) -> [0isize: bb3, 1isize: bb2, otherwise: bb7];
// }
// bb1: {
// resume;
// }
-// bb2: { // arm2
-// _1 = (const 3i32, const 3i32);
-// goto -> bb13;
+// bb2: {
+// falseEdges -> [real: bb9, imaginary: bb3];
// }
// bb3: {
-// FakeRead(ForMatchGuard, _9);
-// FakeRead(ForMatchGuard, _10);
-// falseEdges -> [real: bb8, imaginary: bb4]; //pre_binding1
+// falseEdges -> [real: bb12, imaginary: bb4];
// }
// bb4: {
-// FakeRead(ForMatchGuard, _9);
-// FakeRead(ForMatchGuard, _10);
-// falseEdges -> [real: bb2, imaginary: bb5]; //pre_binding2
+// falseEdges -> [real: bb13, imaginary: bb5];
// }
// bb5: {
-// FakeRead(ForMatchGuard, _9);
-// FakeRead(ForMatchGuard, _10);
-// falseEdges -> [real: bb12, imaginary: bb6]; //pre_binding3
-// }
-// bb6: {
// unreachable;
// }
+// bb6: { // to pre_binding3 (can skip 2 since this is `Some`)
+// falseEdges -> [real: bb4, imaginary: bb3];
+// }
// bb7: {
// unreachable;
// }
-// bb8: { // binding1 and guard
-// StorageLive(_5);
-// _5 = &((_2 as Some).0: i32);
-// StorageLive(_8);
-// _8 = const guard() -> [return: bb9, unwind: bb1];
+// bb8: {
+// ...
+// return;
// }
-// bb9: { // end of guard
-// switchInt(move _8) -> [false: bb10, otherwise: bb11];
+// bb9: { // binding1 and guard
+// StorageLive(_8);
+// _8 = &((_2 as Some).0: i32);
+// _4 = &shallow _2;
+// _5 = &((_2 as Some).0: i32);
+// StorageLive(_9);
+// _9 = const guard() -> [return: bb10, unwind: bb1];
// }
-// bb10: { // to pre_binding3 (can skip 2 since this is `Some`)
-// falseEdges -> [real: bb5, imaginary: bb4];
+// bb10: { // end of guard
+// FakeRead(ForMatchGuard, _4);
+// FakeRead(ForMatchGuard, _5);
+// switchInt(move _9) -> [false: bb6, otherwise: bb11];
// }
// bb11: { // arm1
-// StorageLive(_3);
-// _3 = ((_2 as Some).0: i32);
-// StorageLive(_11);
-// _11 = _3;
-// _1 = (const 1i32, move _11);
-// StorageDead(_11);
-// goto -> bb13;
-// }
-// bb12: { // binding3 and arm3
// StorageLive(_6);
// _6 = ((_2 as Some).0: i32);
+// StorageLive(_10);
+// _10 = _6;
+// _1 = (const 1i32, move _10);
+// StorageDead(_10);
+// goto -> bb8;
+// }
+// bb12: { // arm2
+// _1 = (const 3i32, const 3i32);
+// goto -> bb8;
+// }
+// bb13: { // binding3 and arm3
+// StorageLive(_11);
+// _11 = ((_2 as Some).0: i32);
// StorageLive(_12);
-// _12 = _6;
+// _12 = _11;
// _1 = (const 2i32, move _12);
// StorageDead(_12);
-// goto -> bb13;
-// }
-// bb13: {
-// ...
-// return;
+// goto -> bb8;
// }
// END rustc.full_tested_match2.QualifyAndPromoteConstants.before.mir
//
// START rustc.main.QualifyAndPromoteConstants.before.mir
// bb0: {
// ...
-// _2 = std::option::Option<i32>::Some(const 1i32,);
-// FakeRead(ForMatchedPlace, _2);
-// _11 = discriminant(_2);
-// _16 = &shallow _2;
-// _17 = &((_2 as Some).0: i32);
-// switchInt(move _11) -> [1isize: bb2, otherwise: bb3];
-// }
-// bb1: {
-// resume;
-// }
-// bb2: {
-// FakeRead(ForMatchGuard, _16);
-// FakeRead(ForMatchGuard, _17);
-// falseEdges -> [real: bb7, imaginary: bb3]; //pre_binding1
-// }
-// bb3: {
-// FakeRead(ForMatchGuard, _16);
-// FakeRead(ForMatchGuard, _17);
-// falseEdges -> [real: bb11, imaginary: bb4]; //pre_binding2
-// }
-// bb4: {
-// FakeRead(ForMatchGuard, _16);
-// FakeRead(ForMatchGuard, _17);
-// falseEdges -> [real: bb12, imaginary: bb5]; //pre_binding3
-// }
-// bb5: {
-// FakeRead(ForMatchGuard, _16);
-// FakeRead(ForMatchGuard, _17);
-// falseEdges -> [real: bb16, imaginary: bb6]; //pre_binding4
-// }
-// bb6: {
-// unreachable;
-// }
-// bb7: { // binding1: Some(w) if guard()
-// StorageLive(_5);
-// _5 = &((_2 as Some).0: i32);
-// StorageLive(_12);
-// _12 = const guard() -> [return: bb8, unwind: bb1];
-// }
-// bb8: { //end of guard
-// switchInt(move _12) -> [false: bb9, otherwise: bb10];
-// }
-// bb9: { // to pre_binding2
-// falseEdges -> [real: bb3, imaginary: bb3];
-// }
-// bb10: { // set up bindings for arm1
-// StorageLive(_3);
-// _3 = ((_2 as Some).0: i32);
-// _1 = const 1i32;
-// goto -> bb17;
-// }
-// bb11: { // binding2 & arm2
-// StorageLive(_6);
-// _6 = _2;
-// _1 = const 2i32;
-// goto -> bb17;
-// }
-// bb12: { // binding3: Some(y) if guard2(y)
-// StorageLive(_9);
-// _9 = &((_2 as Some).0: i32);
-// StorageLive(_14);
-// StorageLive(_15);
-// _15 = (*_9);
-// _14 = const guard2(move _15) -> [return: bb13, unwind: bb1];
-// }
-// bb13: { // end of guard2
-// StorageDead(_15);
-// switchInt(move _14) -> [false: bb14, otherwise: bb15];
-// }
-// bb14: { // to pre_binding4
-// falseEdges -> [real: bb5, imaginary: bb5];
-// }
-// bb15: { // set up bindings for arm3
-// StorageLive(_7);
-// _7 = ((_2 as Some).0: i32);
-// _1 = const 3i32;
-// goto -> bb17;
-// }
-// bb16: { // binding4 & arm4
-// StorageLive(_10);
-// _10 = _2;
-// _1 = const 4i32;
-// goto -> bb17;
-// }
-// bb17: {
-// ...
-// return;
-// }
+// _2 = std::option::Option<i32>::Some(const 1i32,);
+// FakeRead(ForMatchedPlace, _2);
+// _3 = discriminant(_2);
+// switchInt(move _3) -> [1isize: bb2, otherwise: bb3];
+// }
+// bb1: {
+// resume;
+// }
+// bb2: {
+// falseEdges -> [real: bb10, imaginary: bb3]; //pre_binding1
+// }
+// bb3: {
+// falseEdges -> [real: bb13, imaginary: bb4]; //pre_binding2
+// }
+// bb4: {
+// falseEdges -> [real: bb14, imaginary: bb5]; //pre_binding3
+// }
+// bb5: {
+// falseEdges -> [real: bb17, imaginary: bb6]; //pre_binding4
+// }
+// bb6: {
+// unreachable;
+// }
+// bb7: { // to pre_binding2
+// falseEdges -> [real: bb3, imaginary: bb3];
+// }
+// bb8: { // to pre_binding4
+// falseEdges -> [real: bb5, imaginary: bb5];
+// }
+// bb9: {
+// ...
+// return;
+// }
+// bb10: { // binding1: Some(w) if guard()
+// StorageLive(_9);
+// _9 = &((_2 as Some).0: i32);
+// _5 = &shallow _2;
+// _6 = &((_2 as Some).0: i32);
+// StorageLive(_10);
+// _10 = const guard() -> [return: bb11, unwind: bb1];
+// }
+// bb11: { //end of guard
+// FakeRead(ForMatchGuard, _5);
+// FakeRead(ForMatchGuard, _6);
+// switchInt(move _10) -> [false: bb7, otherwise: bb12];
+// }
+// bb12: { // set up bindings for arm1
+// StorageLive(_7);
+// _7 = ((_2 as Some).0: i32);
+// _1 = const 1i32;
+// goto -> bb9;
+// }
+// bb13: { // binding2 & arm2
+// StorageLive(_11);
+// _11 = _2;
+// _1 = const 2i32;
+// goto -> bb9;
+// }
+// bb14: { // binding3: Some(y) if guard2(y)
+// StorageLive(_14);
+// _14 = &((_2 as Some).0: i32);
+// _5 = &shallow _2;
+// _6 = &((_2 as Some).0: i32);
+// StorageLive(_15);
+// StorageLive(_16);
+// _16 = (*_14);
+// _15 = const guard2(move _16) -> [return: bb15, unwind: bb1];
+// }
+// bb15: { // end of guard2
+// StorageDead(_16);
+// FakeRead(ForMatchGuard, _5);
+// FakeRead(ForMatchGuard, _6);
+// switchInt(move _15) -> [false: bb8, otherwise: bb16];
+// }
+// bb16: { // binding4 & arm4
+// StorageLive(_12);
+// _12 = ((_2 as Some).0: i32);
+// _1 = const 3i32;
+// goto -> bb9;
+// }
+// bb17: {
+// StorageLive(_17);
+// _17 = _2;
+// _1 = const 4i32;
+// goto -> bb9;
+// }
// END rustc.main.QualifyAndPromoteConstants.before.mir