Auto merge of #45384 - mikhail-m1:mir_add_false_edges_terminator_kind, r=arielb1

add TerminatorKind::FalseEdges and use it in matches

impl #45184 and fixes #45043 right way.

False edges unexpectedly affects uninitialized variables analysis in MIR borrowck.
This commit is contained in:
bors 2017-11-04 00:09:14 +00:00
commit 95a401609f
21 changed files with 447 additions and 48 deletions

View File

@ -62,7 +62,8 @@ for mir::Terminator<'gcx> {
mir::TerminatorKind::Drop { .. } |
mir::TerminatorKind::DropAndReplace { .. } |
mir::TerminatorKind::Yield { .. } |
mir::TerminatorKind::Call { .. } => false,
mir::TerminatorKind::Call { .. } |
mir::TerminatorKind::FalseEdges { .. } => false,
};
if hash_spans_unconditionally {
@ -210,6 +211,12 @@ for mir::TerminatorKind<'gcx> {
target.hash_stable(hcx, hasher);
cleanup.hash_stable(hcx, hasher);
}
mir::TerminatorKind::FalseEdges { ref real_target, ref imaginary_targets } => {
real_target.hash_stable(hcx, hasher);
for target in imaginary_targets {
target.hash_stable(hcx, hasher);
}
}
}
}
}

View File

@ -682,6 +682,11 @@ pub enum TerminatorKind<'tcx> {
/// Indicates the end of the dropping of a generator
GeneratorDrop,
FalseEdges {
real_target: BasicBlock,
imaginary_targets: Vec<BasicBlock>
},
}
impl<'tcx> Terminator<'tcx> {
@ -731,6 +736,11 @@ impl<'tcx> TerminatorKind<'tcx> {
}
Assert { target, cleanup: Some(unwind), .. } => vec![target, unwind].into_cow(),
Assert { ref target, .. } => slice::ref_slice(target).into_cow(),
FalseEdges { ref real_target, ref imaginary_targets } => {
let mut s = vec![*real_target];
s.extend_from_slice(imaginary_targets);
s.into_cow()
}
}
}
@ -757,7 +767,12 @@ impl<'tcx> TerminatorKind<'tcx> {
vec![target]
}
Assert { ref mut target, cleanup: Some(ref mut unwind), .. } => vec![target, unwind],
Assert { ref mut target, .. } => vec![target]
Assert { ref mut target, .. } => vec![target],
FalseEdges { ref mut real_target, ref mut imaginary_targets } => {
let mut s = vec![real_target];
s.extend(imaginary_targets.iter_mut());
s
}
}
}
}
@ -874,7 +889,8 @@ impl<'tcx> TerminatorKind<'tcx> {
}
write!(fmt, ")")
}
},
FalseEdges { .. } => write!(fmt, "falseEdges")
}
}
@ -910,7 +926,12 @@ impl<'tcx> TerminatorKind<'tcx> {
}
Assert { cleanup: None, .. } => vec!["".into()],
Assert { .. } =>
vec!["success".into_cow(), "unwind".into_cow()]
vec!["success".into_cow(), "unwind".into_cow()],
FalseEdges { ref imaginary_targets, .. } => {
let mut l = vec!["real".into()];
l.resize(imaginary_targets.len() + 1, "imaginary".into());
l
}
}
}
}
@ -1878,6 +1899,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
Resume => Resume,
Return => Return,
Unreachable => Unreachable,
FalseEdges { real_target, ref imaginary_targets } =>
FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() }
};
Terminator {
source_info: self.source_info,
@ -1917,7 +1940,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
Resume |
Return |
GeneratorDrop |
Unreachable => false
Unreachable |
FalseEdges { .. } => false
}
}
}

View File

@ -486,8 +486,15 @@ macro_rules! make_mir_visitor {
self.visit_operand(value, source_location);
self.visit_branch(block, resume);
drop.map(|t| self.visit_branch(block, t));
}
TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => {
self.visit_branch(block, real_target);
for target in imaginary_targets {
self.visit_branch(block, *target);
}
}
}
}

View File

@ -364,7 +364,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx>
TerminatorKind::Resume |
TerminatorKind::Return |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable => {
TerminatorKind::Unreachable |
TerminatorKind::FalseEdges { .. } => {
// no data used, thus irrelevant to borrowck
}
}

View File

@ -21,7 +21,7 @@ use rustc::mir::*;
use rustc::hir;
use hair::*;
use syntax::ast::{Name, NodeId};
use syntax_pos::{DUMMY_SP, Span};
use syntax_pos::Span;
// helper functions, broken out by category:
mod simplify;
@ -54,11 +54,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
(body, scope.unwrap_or(self.visibility_scope))
}).collect();
// create binding start block for link them by false edges
let candidate_count = arms.iter().fold(0, |ac, c| ac + c.patterns.len());
let pre_binding_blocks: Vec<_> = (0..candidate_count + 1)
.map(|_| self.cfg.start_new_block()).collect();
// 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()
@ -66,17 +72,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
arm.patterns.iter()
.map(move |pat| (arm_index, pat, arm.guard.clone()))
})
.map(|(arm_index, pattern, guard)| {
.zip(pre_binding_blocks.iter().zip(pre_binding_blocks.iter().skip(1)))
.map(|((arm_index, pattern, guard),
(pre_binding_block, next_candidate_pre_binding_block))| {
Candidate {
span: pattern.span,
match_pairs: vec![MatchPair::new(discriminant_lvalue.clone(), pattern)],
bindings: vec![],
guard,
arm_index,
pre_binding_block: *pre_binding_block,
next_candidate_pre_binding_block: *next_candidate_pre_binding_block,
}
})
.collect();
let outer_source_info = self.source_info(span);
self.cfg.terminate(*pre_binding_blocks.last().unwrap(),
outer_source_info, TerminatorKind::Unreachable);
// this will generate code to test discriminant_lvalue and
// branch to the appropriate arm block
let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block);
@ -148,7 +162,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
match_pairs: vec![MatchPair::new(initializer.clone(), &irrefutable_pat)],
bindings: vec![],
guard: None,
arm_index: 0, // since we don't call `match_candidates`, this field is unused
// since we don't call `match_candidates`, next fields is unused
arm_index: 0,
pre_binding_block: block,
next_candidate_pre_binding_block: block
};
// Simplify the candidate. Since the pattern is irrefutable, this should
@ -278,6 +296,10 @@ pub struct Candidate<'pat, 'tcx:'pat> {
// ...and then we branch to arm with this index.
arm_index: usize,
// ...and the blocks for add false edges between candidates
pre_binding_block: BasicBlock,
next_candidate_pre_binding_block: BasicBlock,
}
#[derive(Clone, Debug)]
@ -398,17 +420,43 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
candidates.iter().take_while(|c| c.match_pairs.is_empty()).count();
debug!("match_candidates: {:?} candidates fully matched", fully_matched);
let mut unmatched_candidates = candidates.split_off(fully_matched);
for (index, candidate) in candidates.into_iter().enumerate() {
let fully_matched_with_guard =
candidates.iter().take_while(|c| c.guard.is_some()).count();
let unreachable_candidates = if fully_matched_with_guard + 1 < candidates.len() {
candidates.split_off(fully_matched_with_guard + 1)
} else {
vec![]
};
for candidate in candidates {
// If so, apply any bindings, test the guard (if any), and
// branch to the arm.
let is_last = index == fully_matched - 1;
if let Some(b) = self.bind_and_guard_matched_candidate(block, arm_blocks,
candidate, is_last) {
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).
return vec![];
// 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);
}
}
if unmatched_candidates.is_empty() {
return vec![]
} else {
let target = self.cfg.start_new_block();
return self.match_candidates(span, arm_blocks, unmatched_candidates, target);
}
}
}
@ -423,9 +471,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
self.test_candidates(span, arm_blocks, &unmatched_candidates, block);
// If the target candidates were exhaustive, then we are done.
if otherwise.is_empty() {
return vec![];
}
// But for borrowck continue build decision tree.
// If all candidates were sorted into `target_candidates` somewhere, then
// the initial set was inexhaustive.
@ -666,17 +712,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
fn bind_and_guard_matched_candidate<'pat>(&mut self,
mut block: BasicBlock,
arm_blocks: &mut ArmBlocks,
candidate: Candidate<'pat, 'tcx>,
is_last_arm: bool)
candidate: Candidate<'pat, 'tcx>)
-> Option<BasicBlock> {
debug!("bind_and_guard_matched_candidate(block={:?}, candidate={:?})",
block, candidate);
debug_assert!(candidate.match_pairs.is_empty());
self.bind_matched_candidate(block, candidate.bindings);
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();
self.cfg.terminate(candidate.pre_binding_block, candidate_source_info,
TerminatorKind::FalseEdges {
real_target: block,
imaginary_targets:
vec![candidate.next_candidate_pre_binding_block]});
self.bind_matched_candidate(block, candidate.bindings);
if let Some(guard) = candidate.guard {
// the block to branch to if the guard fails; if there is no
@ -684,29 +740,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let guard = self.hir.mirror(guard);
let source_info = self.source_info(guard.span);
let cond = unpack!(block = self.as_local_operand(block, guard));
let otherwise = self.cfg.start_new_block();
let false_edge_block = self.cfg.start_new_block();
self.cfg.terminate(block, source_info,
TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise));
Some(otherwise)
} else if !is_last_arm {
// Add always true guard in case of more than one arm
// it creates false edges and allow MIR borrowck detects errors
// FIXME(#45184) -- permit "false edges"
let source_info = self.source_info(candidate.span);
let true_expr = Expr {
temp_lifetime: None,
ty: self.hir.tcx().types.bool,
span: DUMMY_SP,
kind: ExprKind::Literal{literal: self.hir.true_literal()},
};
let cond = unpack!(block = self.as_local_operand(block, true_expr));
TerminatorKind::if_(self.hir.tcx(), cond, arm_block,
false_edge_block));
let otherwise = self.cfg.start_new_block();
self.cfg.terminate(block, source_info,
TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise));
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 {
let source_info = self.source_info(candidate.span);
self.cfg.terminate(block, source_info,
self.cfg.terminate(block, candidate_source_info,
TerminatorKind::Goto { target: arm_block });
None
}

View File

@ -598,6 +598,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
bindings: candidate.bindings.clone(),
guard: candidate.guard.clone(),
arm_index: candidate.arm_index,
pre_binding_block: candidate.pre_binding_block,
next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
}
}
@ -659,6 +661,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
bindings: candidate.bindings.clone(),
guard: candidate.guard.clone(),
arm_index: candidate.arm_index,
pre_binding_block: candidate.pre_binding_block,
next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
}
}

View File

@ -721,6 +721,12 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb);
}
}
mir::TerminatorKind::FalseEdges { ref real_target, ref imaginary_targets } => {
self.propagate_bits_into_entry_set_for(in_out, changed, real_target);
for target in imaginary_targets {
self.propagate_bits_into_entry_set_for(in_out, changed, target);
}
}
}
}

View File

@ -305,6 +305,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
TerminatorKind::Goto { target: _ } |
TerminatorKind::Resume |
TerminatorKind::GeneratorDrop |
TerminatorKind::FalseEdges { .. } |
TerminatorKind::Unreachable => { }
TerminatorKind::Return => {

View File

@ -73,7 +73,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
TerminatorKind::GeneratorDrop |
TerminatorKind::Resume |
TerminatorKind::Return |
TerminatorKind::Unreachable => {
TerminatorKind::Unreachable |
TerminatorKind::FalseEdges { .. } => {
// safe (at least as emitted during MIR construction)
}

View File

@ -720,6 +720,12 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
}
}
TerminatorKind::Unreachable => { }
TerminatorKind::FalseEdges { ref mut real_target, ref mut imaginary_targets } => {
*real_target = self.update_target(*real_target);
for target in imaginary_targets {
*target = self.update_target(*target);
}
}
}
}

View File

@ -45,7 +45,8 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
TerminatorKind::Unreachable |
TerminatorKind::GeneratorDrop |
TerminatorKind::Yield { .. } |
TerminatorKind::SwitchInt { .. } => {
TerminatorKind::SwitchInt { .. } |
TerminatorKind::FalseEdges { .. } => {
/* nothing to do */
},
TerminatorKind::Call { cleanup: ref mut unwind, .. } |

View File

@ -317,7 +317,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
TerminatorKind::Resume |
TerminatorKind::GeneratorDrop |
TerminatorKind::Yield { .. } |
TerminatorKind::Unreachable => None,
TerminatorKind::Unreachable |
TerminatorKind::FalseEdges { .. } => None,
TerminatorKind::Return => {
// Check for unused values. This usually means

View File

@ -61,6 +61,9 @@ impl MirPass for SimplifyBranches {
}), expected, .. } if cond == expected => {
TerminatorKind::Goto { target: target }
},
TerminatorKind::FalseEdges { real_target, .. } => {
TerminatorKind::Goto { target: real_target }
},
_ => continue
};
}

View File

@ -441,7 +441,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
TerminatorKind::Return |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable |
TerminatorKind::Drop { .. } => {
TerminatorKind::Drop { .. } |
TerminatorKind::FalseEdges { .. } => {
// no checks needed for these
}
@ -685,6 +686,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
self.assert_iscleanup(mir, block, cleanup, true);
}
}
TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => {
self.assert_iscleanup(mir, block, real_target, is_cleanup);
for target in imaginary_targets {
self.assert_iscleanup(mir, block, *target, is_cleanup);
}
}
}
}

View File

@ -121,6 +121,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
TerminatorKind::Assert { .. } => "TerminatorKind::Assert",
TerminatorKind::GeneratorDrop => "TerminatorKind::GeneratorDrop",
TerminatorKind::Yield { .. } => "TerminatorKind::Yield",
TerminatorKind::FalseEdges { .. } => "TerminatorKind::FalseEdges",
}, kind);
self.super_terminator_kind(block, kind, location);
}

View File

@ -228,7 +228,8 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable |
TerminatorKind::SwitchInt { .. } |
TerminatorKind::Yield { .. } => {
TerminatorKind::Yield { .. } |
TerminatorKind::FalseEdges { .. } => {
/* nothing to do */
}
TerminatorKind::Call { cleanup: unwind, .. } |

View File

@ -583,7 +583,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
cleanup);
}
mir::TerminatorKind::GeneratorDrop |
mir::TerminatorKind::Yield { .. } => bug!("generator ops in trans"),
mir::TerminatorKind::Yield { .. } |
mir::TerminatorKind::FalseEdges { .. } => bug!("generator ops in trans"),
}
}

View File

@ -623,7 +623,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
mir::TerminatorKind::Unreachable |
mir::TerminatorKind::Assert { .. } => {}
mir::TerminatorKind::GeneratorDrop |
mir::TerminatorKind::Yield { .. } => bug!(),
mir::TerminatorKind::Yield { .. } |
mir::TerminatorKind::FalseEdges { .. } => bug!(),
}
self.super_terminator_kind(block, kind, location);

View File

@ -0,0 +1,24 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//compile-flags: -Z emit-end-regions -Z borrowck-mir
fn foo(_:String) {}
fn main()
{
let my_str = "hello".to_owned();
match Some(42) {
Some(_) if { drop(my_str); false } => {}
Some(_) => {}
None => { foo(my_str); } //~ ERROR (Mir) [E0381]
}
}

View File

@ -11,6 +11,24 @@
// revisions: ast mir
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
enum Foo {
A(i32),
B
}
fn match_enum() {
let mut foo = Foo::B;
let p = &mut foo;
let _ = match foo {
Foo::B => 1, //[mir]~ ERROR (Mir) [E0503]
_ => 2,
Foo::A(x) => x //[ast]~ ERROR [E0503]
//[mir]~^ ERROR (Ast) [E0503]
//[mir]~| ERROR (Mir) [E0503]
};
}
fn main() {
let mut x = 1;
let _x = &mut x;

View File

@ -0,0 +1,236 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z emit-end-regions -Z borrowck-mir
fn guard() -> bool {
false
}
fn guard2(_:i32) -> bool {
true
}
fn full_tested_match() {
let _ = match Some(42) {
Some(x) if guard() => (1, x),
Some(y) => (2, y),
None => (3, 3),
};
}
fn full_tested_match2() {
let _ = match Some(42) {
Some(x) if guard() => (1, x),
None => (3, 3),
Some(y) => (2, y),
};
}
fn main() {
let _ = match Some(1) {
Some(_w) if guard() => 1,
_x => 2,
Some(y) if guard2(y) => 3,
_z => 4,
};
}
// END RUST SOURCE
//
// START rustc.node17.SimplifyBranches-initial.before.mir
// bb0: {
// ...
// _2 = std::option::Option<i32>::Some(const 42i32,);
// _5 = discriminant(_2);
// switchInt(_5) -> [0isize: bb5, otherwise: bb3];
// }
// bb1: { // arm1
// StorageLive(_7);
// _7 = _3;
// _1 = (const 1i32, _7);
// StorageDead(_7);
// goto -> bb11;
// }
// bb2: { // binding3(empty) and arm3
// _1 = (const 3i32, const 3i32);
// goto -> bb11;
// }
// bb3: {
// falseEdges -> [real: bb7, imaginary: bb4]; //pre_binding1
// }
// bb4: {
// falseEdges -> [real: bb10, imaginary: bb5]; //pre_binding2
// }
// bb5: {
// falseEdges -> [real: bb2, imaginary: bb6]; //pre_binding3
// }
// bb6: {
// unreachable;
// }
// bb7: { // binding1 and guard
// StorageLive(_3);
// _3 = ((_2 as Some).0: i32);
// StorageLive(_6);
// _6 = const guard() -> bb8;
// }
// bb8: { // end of guard
// switchInt(_6) -> [0u8: bb9, otherwise: bb1];
// }
// bb9: { // to pre_binding2
// falseEdges -> [real: bb4, imaginary: bb4];
// }
// bb10: { // bindingNoLandingPads.before.mir2 and arm2
// StorageLive(_4);
// _4 = ((_2 as Some).0: i32);
// StorageLive(_8);
// _8 = _4;
// _1 = (const 2i32, _8);
// StorageDead(_8);
// goto -> bb11;
// }
// bb11: {
// ...
// return;
// }
// END rustc.node17.SimplifyBranches-initial.before.mir
//
// START rustc.node42.SimplifyBranches-initial.before.mir
// bb0: {
// ...
// _2 = std::option::Option<i32>::Some(const 42i32,);
// _5 = discriminant(_2);
// switchInt(_5) -> [0isize: bb4, otherwise: bb3];
// }
// bb1: { // arm1
// StorageLive(_7);
// _7 = _3;
// _1 = (const 1i32, _7);
// StorageDead(_7);
// goto -> bb11;
// }
// bb2: { // binding3(empty) and arm3
// _1 = (const 3i32, const 3i32);
// goto -> bb11;
// }
// bb3: {
// falseEdges -> [real: bb7, imaginary: bb4]; //pre_binding1
// }
// bb4: {
// falseEdges -> [real: bb2, imaginary: bb5]; //pre_binding2
// }
// bb5: {
// falseEdges -> [real: bb10, imaginary: bb6]; //pre_binding3
// }
// bb6: {
// unreachable;
// }
// bb7: { // binding1 and guard
// StorageLive(_3);
// _3 = ((_2 as Some).0: i32);
// StorageLive(_6);
// _6 = const guard() -> bb8;
// }
// bb8: { // end of guard
// switchInt(_6) -> [0u8: bb9, otherwise: bb1];
// }
// bb9: { // to pre_binding2
// falseEdges -> [real: bb5, imaginary: bb4];
// }
// bb10: { // binding2 and arm2
// StorageLive(_4);
// _4 = ((_2 as Some).0: i32);
// StorageLive(_8);
// _8 = _4;
// _1 = (const 2i32, _8);
// StorageDead(_8);
// goto -> bb11;
// }
// bb11: {
// ...
// return;
// }
// END rustc.node42.SimplifyBranches-initial.before.mir
//
// START rustc.node67.SimplifyBranches-initial.before.mir
// bb0: {
// ...
// _2 = std::option::Option<i32>::Some(const 1i32,);
// _7 = discriminant(_2);
// switchInt(_7) -> [1isize: bb3, otherwise: bb4];
// }
// bb1: { // arm1
// _1 = const 1i32;
// goto -> bb16;
// }
// bb2: { // arm3
// _1 = const 3i32;
// goto -> bb16;
// }
//
// bb3: {
// falseEdges -> [real: bb8, imaginary: bb4]; //pre_binding1
// }
// bb4: {
// falseEdges -> [real: bb11, imaginary: bb5]; //pre_binding2
// }
// bb5: {
// falseEdges -> [real: bb12, imaginary: bb6]; //pre_binding3
// }
// bb6: {
// falseEdges -> [real: bb15, imaginary: bb7]; //pre_binding4
// }
// bb7: {
// unreachable;
// }
// bb8: { // binding1: Some(w) if guard()
// StorageLive(_3);
// _3 = ((_2 as Some).0: i32);
// StorageLive(_8);
// _8 = const guard() -> bb9;
// }
// bb9: { //end of guard
// switchInt(_8) -> [0u8: bb10, otherwise: bb1];
// }
// bb10: { // to pre_binding2
// falseEdges -> [real: bb4, imaginary: bb4];
// }
// bb11: { // binding2 & arm2
// StorageLive(_4);
// _4 = _2;
// _1 = const 2i32;
// goto -> bb16;
// }
// bb12: { // binding3: Some(y) if guard2(y)
// StorageLive(_5);
// _5 = ((_2 as Some).0: i32);
// StorageLive(_10);
// StorageLive(_11);
// _11 = _5;
// _10 = const guard2(_11) -> bb13;
// }
// bb13: { // end of guard2
// StorageDead(_11);
// switchInt(_10) -> [0u8: bb14, otherwise: bb2];
// }
// bb14: { // to pre_binding4
// falseEdges -> [real: bb6, imaginary: bb6];
// }
// bb15: { // binding4 & arm4
// StorageLive(_6);
// _6 = _2;
// _1 = const 4i32;
// goto -> bb16;
// }
// bb16: {
// ...
// return;
// }
// END rustc.node67.SimplifyBranches-initial.before.mir