From d51b5cdd82ec0e36212fc75b83652372980d56ca Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 2 Feb 2019 10:56:55 +0000 Subject: [PATCH] Clean up MIR match lowering * Adjust fake borrows to only be live over guards. * Remove unused `slice_len_checked` field. * Split the methods on builder into those for matches and those for all kinds of pattern bindings. --- src/librustc_mir/build/matches/mod.rs | 846 ++++++++++++++---------- src/librustc_mir/build/matches/test.rs | 266 +++----- src/librustc_mir/build/matches/util.rs | 1 - src/test/mir-opt/issue-49232.rs | 36 +- src/test/mir-opt/match_false_edges.rs | 336 +++++----- src/test/mir-opt/match_test.rs | 59 +- src/test/mir-opt/remove_fake_borrows.rs | 98 ++- 7 files changed, 838 insertions(+), 804 deletions(-) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 96d2c903459..a287659ec8e 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -1,7 +1,9 @@ -//! 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}; @@ -23,12 +25,78 @@ 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>, @@ -38,6 +106,9 @@ pub fn match_expr( arms: Vec>, ) -> 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)); @@ -66,26 +137,7 @@ pub fn match_expr( ), }); - 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::(); @@ -93,70 +145,8 @@ pub fn match_expr( .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(), @@ -164,27 +154,72 @@ pub fn match_expr( 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::>(); // 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, @@ -197,29 +232,59 @@ pub fn match_expr( // // 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, @@ -227,6 +292,7 @@ pub fn match_expr( TerminatorKind::Goto { target: end_block }, ); } + self.source_scope = outer_source_info.scope; end_block.unit() @@ -359,11 +425,9 @@ pub fn place_into_pattern( 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, }; @@ -613,13 +677,7 @@ pub(super) fn visit_bindings( } } -/// List of blocks for each arm (and potentially other metadata in the -/// future). -struct ArmBlocks { - blocks: Vec, -} - -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Candidate<'pat, 'tcx: 'pat> { // span of the original pattern that gave rise to this candidate span: Span, @@ -630,21 +688,15 @@ pub struct Candidate<'pat, 'tcx: 'pat> { // ...these bindings established... bindings: Vec>, - // ...these types asserted... + // ...and these types asserted... ascriptions: Vec>, - // ...and the guard must be evaluated... - guard: Option>, - - // ...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, // ...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)] @@ -676,13 +728,6 @@ pub struct MatchPair<'pat, 'tcx: 'pat> { // ... 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)] @@ -722,6 +767,11 @@ pub struct Test<'tcx> { 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 @@ -732,7 +782,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// 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 @@ -747,13 +797,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// 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>, + candidates: &mut [&mut Candidate<'pat, 'tcx>], mut block: BasicBlock, fake_borrows: &mut Option, BorrowKind>>, ) -> Vec { @@ -762,17 +811,16 @@ fn match_candidates<'pat>( 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()) @@ -781,87 +829,172 @@ fn match_candidates<'pat>( "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, BorrowKind>>, + ) -> Option { + 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 { @@ -995,17 +1128,17 @@ fn join_otherwise_blocks(&mut self, span: Span, mut otherwise: Vec) /// 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, BorrowKind>>, - ) -> (Vec, usize) { + ) -> (Vec, &'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 @@ -1019,7 +1152,7 @@ fn test_candidates<'pat>( } => { for candidate in candidates.iter() { if !self.add_cases_to_switch( - &match_pair.place, + &match_place, candidate, switch_ty, options, @@ -1034,7 +1167,7 @@ fn test_candidates<'pat>( 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; } } @@ -1044,7 +1177,7 @@ fn test_candidates<'pat>( // 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 @@ -1055,25 +1188,29 @@ fn test_candidates<'pat>( "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![]; + 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 @@ -1082,59 +1219,99 @@ fn test_candidates<'pat>( 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, 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 { - debug!( - "bind_and_guard_matched_candidate(block={:?}, candidate={:?})", - block, candidate - ); + guard: Option>, + 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, @@ -1143,6 +1320,7 @@ fn bind_and_guard_matched_candidate<'pat>( 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. @@ -1226,14 +1404,13 @@ fn bind_and_guard_matched_candidate<'pat>( // 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 { @@ -1249,12 +1426,29 @@ fn bind_and_guard_matched_candidate<'pat>( 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(); @@ -1264,7 +1458,15 @@ fn bind_and_guard_matched_candidate<'pat>( ); } - 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 @@ -1296,7 +1498,12 @@ fn bind_and_guard_matched_candidate<'pat>( 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 { @@ -1308,19 +1515,8 @@ fn bind_and_guard_matched_candidate<'pat>( 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.) @@ -1330,7 +1526,6 @@ fn bind_and_guard_matched_candidate<'pat>( candidate_source_info, TerminatorKind::Goto { target: arm_block }, ); - None } } @@ -1397,8 +1592,9 @@ fn bind_matched_candidate_for_guard( 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 => { @@ -1585,86 +1781,4 @@ fn declare_binding( 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, 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(), - ), - }); - } - } - } - } } diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index a41d3895d6d..72b92444dec 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -69,8 +69,7 @@ pub fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { } } - 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 @@ -85,7 +84,6 @@ pub fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { PatternKind::AscribeUserType { .. } | PatternKind::Array { .. } | - PatternKind::Slice { .. } | PatternKind::Wild | PatternKind::Binding { .. } | PatternKind::Leaf { .. } | @@ -433,59 +431,49 @@ fn compare(&mut self, 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>]) - -> bool { + pub fn sort_candidate<'pat, 'cand>( + &mut self, + test_place: &Place<'tcx>, + test: &Test<'tcx>, + candidate: &mut Candidate<'pat, 'tcx>, + ) -> Option { // 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 @@ -493,17 +481,15 @@ pub fn sort_candidate<'pat>(&mut self, (&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. @@ -514,10 +500,8 @@ pub fn sort_candidate<'pat>(&mut self, &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 }, @@ -530,14 +514,13 @@ pub fn sort_candidate<'pat>(&mut self, // 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 }) => { @@ -546,32 +529,28 @@ pub fn sort_candidate<'pat>(&mut self, (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) } } } @@ -584,32 +563,28 @@ pub fn sort_candidate<'pat>(&mut self, (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 } } } @@ -617,12 +592,11 @@ pub fn sort_candidate<'pat>(&mut self, (&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 = (|| { @@ -649,10 +623,9 @@ pub fn sort_candidate<'pat>(&mut self, 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 } } @@ -660,15 +633,13 @@ pub fn sort_candidate<'pat>(&mut self, 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 { .. }, _) => { @@ -677,73 +648,53 @@ pub fn sort_candidate<'pat>(&mut self, // 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| { @@ -754,26 +705,7 @@ fn candidate_after_variant_switch<'pat>(&mut self, 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>) -> ! { diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index b583b184a41..3b90ff7884f 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -72,7 +72,6 @@ pub fn new(place: Place<'tcx>, pattern: &'pat Pattern<'tcx>) -> MatchPair<'pat, MatchPair { place, pattern, - slice_len_checked: false, } } } diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs index 8417c2cfd13..5b4ec483277 100644 --- a/src/test/mir-opt/issue-49232.rs +++ b/src/test/mir-opt/issue-49232.rs @@ -41,46 +41,46 @@ fn main() { // StorageLive(_3); // _3 = const true; // FakeRead(ForMatchedPlace, _3); -// switchInt(_3) -> [false: bb11, otherwise: bb10]; +// switchInt(_3) -> [false: bb9, otherwise: bb8]; // } // bb4: { // resume; // } // bb5: { -// _2 = const 4i32; -// goto -> bb14; +// falseEdges -> [real: bb12, imaginary: bb6]; // } // bb6: { -// _0 = (); -// goto -> bb15; +// falseEdges -> [real: bb14, imaginary: bb7]; // } // bb7: { -// falseEdges -> [real: bb12, imaginary: bb8]; +// unreachable; // } // bb8: { -// falseEdges -> [real: bb13, imaginary: bb9]; +// goto -> bb6; // } // bb9: { -// unreachable; +// goto -> bb5; // } // bb10: { -// goto -> bb8; +// FakeRead(ForLet, _2); +// StorageDead(_3); +// StorageLive(_6); +// _6 = &_2; +// _5 = const std::mem::drop(move _6) -> [return: bb19, unwind: bb4]; // } // bb11: { -// goto -> bb7; +// _2 = const 4i32; +// goto -> bb10; // } // bb12: { -// goto -> bb5; +// goto -> bb11; // } // bb13: { -// goto -> bb6; +// _0 = (); +// goto -> bb15; // } // bb14: { -// FakeRead(ForLet, _2); -// StorageDead(_3); -// StorageLive(_6); -// _6 = &_2; -// _5 = const std::mem::drop(move _6) -> [return: bb19, unwind: bb4]; +// goto -> bb13; // } // bb15: { // StorageDead(_3); @@ -96,7 +96,7 @@ fn main() { // } // bb18: { // StorageDead(_4); -// goto -> bb14; +// goto -> bb10; // } // bb19: { // StorageDead(_6); diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index b512172de64..9faecf5b7af 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -44,72 +44,68 @@ fn main() { // ... // _2 = std::option::Option::Some(const 42i32,); // FakeRead(ForMatchedPlace, _2); -// _7 = discriminant(_2); -// _9 = &shallow (promoted[2]: std::option::Option); -// _10 = &(((promoted[1]: std::option::Option) 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) 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) as Some).0: i32); +// _4 = &shallow (promoted[1]: std::option::Option); +// _5 = &(((promoted[0]: std::option::Option) 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 // @@ -118,164 +114,158 @@ fn main() { // ... // _2 = std::option::Option::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::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::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 diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs index 9bfb728e134..835a5ce1200 100644 --- a/src/test/mir-opt/match_test.rs +++ b/src/test/mir-opt/match_test.rs @@ -20,66 +20,67 @@ fn main() { // START rustc.main.SimplifyCfg-initial.after.mir // bb0: { // ... -// _4 = Le(const 0i32, _1); -// switchInt(move _4) -> [false: bb10, otherwise: bb11]; +// switchInt(move _4) -> [false: bb7, otherwise: bb8]; // } // bb1: { -// _3 = const 0i32; -// goto -> bb16; +// falseEdges -> [real: bb13, imaginary: bb2]; // } // bb2: { -// _3 = const 1i32; -// goto -> bb16; +// falseEdges -> [real: bb14, imaginary: bb3]; // } // bb3: { -// _3 = const 2i32; -// goto -> bb16; +// falseEdges -> [real: bb15, imaginary: bb4]; // } // bb4: { -// _3 = const 3i32; -// goto -> bb16; +// falseEdges -> [real: bb16, imaginary: bb5]; // } // bb5: { -// falseEdges -> [real: bb12, imaginary: bb6]; +// unreachable; // } // bb6: { -// falseEdges -> [real: bb2, imaginary: bb7]; +// falseEdges -> [real: bb4, imaginary: bb2]; // } // bb7: { -// falseEdges -> [real: bb3, imaginary: bb8]; +// _6 = Le(const 10i32, _1); +// switchInt(move _6) -> [false: bb9, otherwise: bb10]; // } // bb8: { -// falseEdges -> [real: bb4, imaginary: bb9]; +// _5 = Lt(_1, const 10i32); +// switchInt(move _5) -> [false: bb7, otherwise: bb1]; // } // bb9: { -// unreachable; +// switchInt(_1) -> [-1i32: bb3, otherwise: bb4]; // } // bb10: { -// _7 = Le(const 10i32, _1); -// switchInt(move _7) -> [false: bb14, otherwise: bb15]; +// _7 = Le(_1, const 20i32); +// switchInt(move _7) -> [false: bb9, otherwise: bb2]; // } // bb11: { -// _5 = Lt(_1, const 10i32); -// switchInt(move _5) -> [false: bb10, otherwise: bb5]; +// StorageDead(_8); +// _0 = (); +// StorageDead(_2); +// StorageDead(_1); +// return; // } // bb12: { -// StorageLive(_6); -// _6 = _2; -// switchInt(move _6) -> [false: bb13, otherwise: bb1]; +// _3 = const 0i32; +// goto -> bb11; // } // bb13: { -// falseEdges -> [real: bb8, imaginary: bb6]; +// StorageLive(_8); +// _8 = _2; +// switchInt(move _8) -> [false: bb6, otherwise: bb12]; // } // bb14: { -// switchInt(_1) -> [-1i32: bb7, otherwise: bb8]; +// _3 = const 1i32; +// goto -> bb11; // } // bb15: { -// _8 = Le(_1, const 20i32); -// switchInt(move _8) -> [false: bb14, otherwise: bb6]; +// _3 = const 2i32; +// goto -> bb11; // } // bb16: { -// StorageDead(_6); -// ... -// return; +// _3 = const 3i32; +// goto -> bb11; // } // END rustc.main.SimplifyCfg-initial.after.mir diff --git a/src/test/mir-opt/remove_fake_borrows.rs b/src/test/mir-opt/remove_fake_borrows.rs index 8411fba02e9..fab4d28a936 100644 --- a/src/test/mir-opt/remove_fake_borrows.rs +++ b/src/test/mir-opt/remove_fake_borrows.rs @@ -4,15 +4,15 @@ #![feature(nll)] -fn match_guard(x: Option<&&i32>) -> i32 { +fn match_guard(x: Option<&&i32>, c: bool) -> i32 { match x { - Some(0) if true => 0, + Some(0) if c => 0, _ => 1, } } fn main() { - match_guard(None); + match_guard(None, true); } // END RUST SOURCE @@ -20,49 +20,48 @@ fn main() { // START rustc.match_guard.CleanFakeReadsAndBorrows.before.mir // bb0: { // FakeRead(ForMatchedPlace, _1); -// _2 = discriminant(_1); -// _3 = &shallow _1; -// _4 = &shallow ((_1 as Some).0: &' &' i32); -// _5 = &shallow (*((_1 as Some).0: &' &' i32)); -// _6 = &shallow (*(*((_1 as Some).0: &' &' i32))); -// switchInt(move _2) -> [1isize: bb6, otherwise: bb4]; +// _3 = discriminant(_1); +// switchInt(move _3) -> [1isize: bb5, otherwise: bb2]; // } // bb1: { -// _0 = const 0i32; -// goto -> bb9; +// goto -> bb8; // } // bb2: { -// _0 = const 1i32; // goto -> bb9; // } // bb3: { -// FakeRead(ForMatchGuard, _3); -// FakeRead(ForMatchGuard, _4); -// FakeRead(ForMatchGuard, _5); -// FakeRead(ForMatchGuard, _6); -// goto -> bb7; +// unreachable; // } // bb4: { -// FakeRead(ForMatchGuard, _3); -// FakeRead(ForMatchGuard, _4); -// FakeRead(ForMatchGuard, _5); -// FakeRead(ForMatchGuard, _6); // goto -> bb2; // } // bb5: { -// unreachable; +// switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb1, otherwise: bb2]; // } // bb6: { -// switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb3, otherwise: bb4]; +// StorageDead(_8); +// return; // } // bb7: { -// goto -> bb1; +// _0 = const 0i32; +// goto -> bb6; // } // bb8: { -// goto -> bb4; +// _4 = &shallow _1; +// _5 = &shallow ((_1 as Some).0: &' &' i32); +// _6 = &shallow (*((_1 as Some).0: &' &' i32)); +// _7 = &shallow (*(*((_1 as Some).0: &' &' i32))); +// StorageLive(_8); +// _8 = _2; +// FakeRead(ForMatchGuard, _4); +// FakeRead(ForMatchGuard, _5); +// FakeRead(ForMatchGuard, _6); +// FakeRead(ForMatchGuard, _7); +// switchInt(move _8) -> [false: bb4, otherwise: bb7]; // } // bb9: { -// return; +// _0 = const 1i32; +// goto -> bb6; // } // bb10: { // resume; @@ -72,51 +71,50 @@ fn main() { // START rustc.match_guard.CleanFakeReadsAndBorrows.after.mir // bb0: { // nop; -// _2 = discriminant(_1); -// nop; -// nop; -// nop; -// nop; -// switchInt(move _2) -> [1isize: bb6, otherwise: bb4]; +// _3 = discriminant(_1); +// switchInt(move _3) -> [1isize: bb5, otherwise: bb2]; // } // bb1: { -// _0 = const 0i32; -// goto -> bb9; +// goto -> bb8; // } // bb2: { -// _0 = const 1i32; // goto -> bb9; // } // bb3: { -// nop; -// nop; -// nop; -// nop; -// goto -> bb7; +// unreachable; // } // bb4: { -// nop; -// nop; -// nop; -// nop; // goto -> bb2; // } // bb5: { -// unreachable; +// switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb1, otherwise: bb2]; // } // bb6: { -// switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb3, otherwise: bb4]; +// StorageDead(_8); +// return; // } // bb7: { -// goto -> bb1; +// _0 = const 0i32; +// goto -> bb6; // } // bb8: { -// goto -> bb4; +// nop; +// nop; +// nop; +// nop; +// StorageLive(_8); +// _8 = _2; +// nop; +// nop; +// nop; +// nop; +// switchInt(move _8) -> [false: bb4, otherwise: bb7]; // } // bb9: { -// return; +// _0 = const 1i32; +// goto -> bb6; // } // bb10: { // resume; -// } +// } // END rustc.match_guard.CleanFakeReadsAndBorrows.after.mir -- 2.44.0