diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 2e54165be1f1b..ef0d4be268eaf 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -419,7 +419,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { for arm in arms { // Add an exit node for when we've visited all the // patterns and the guard (if there is one) in the arm. - let arm_exit = self.add_dummy_node(&[]); + let bindings_exit = self.add_dummy_node(&[]); for pat in &arm.pats { // Visit the pattern, coming from the discriminant exit @@ -453,14 +453,16 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { // Add an edge from the exit of this pattern to the // exit of the arm - self.add_contained_edge(pat_exit, arm_exit); + self.add_contained_edge(pat_exit, bindings_exit); } // Visit the body of this arm - let body_exit = self.expr(&arm.body, arm_exit); + let body_exit = self.expr(&arm.body, bindings_exit); + + let arm_exit = self.add_ast_node(arm.hir_id.local_id, &[body_exit]); // Link the body to the exit of the expression - self.add_contained_edge(body_exit, expr_exit); + self.add_contained_edge(arm_exit, expr_exit); } expr_exit diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 38d6d710868c0..517c99f99efea 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -1102,6 +1102,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { } pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm) { + visitor.visit_id(arm.hir_id); walk_list!(visitor, visit_pat, &arm.pats); if let Some(ref g) = arm.guard { match g { diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 3ec4d4e8cc8f6..591f713f81f79 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1314,6 +1314,7 @@ impl<'a> LoweringContext<'a> { fn lower_arm(&mut self, arm: &Arm) -> hir::Arm { hir::Arm { + hir_id: self.next_id(), attrs: self.lower_attrs(&arm.attrs), pats: arm.pats.iter().map(|x| self.lower_pat(x)).collect(), guard: match arm.guard { @@ -1321,6 +1322,7 @@ impl<'a> LoweringContext<'a> { _ => None, }, body: P(self.lower_expr(&arm.body)), + span: arm.span, } } @@ -5024,9 +5026,11 @@ impl<'a> LoweringContext<'a> { fn arm(&mut self, pats: hir::HirVec
>, expr: P >,
@@ -2656,6 +2659,7 @@ pub enum Node<'hir> {
TraitRef(&'hir TraitRef),
Binding(&'hir Pat),
Pat(&'hir Pat),
+ Arm(&'hir Arm),
Block(&'hir Block),
Local(&'hir Local),
MacroDef(&'hir MacroDef),
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index 8a9028e544391..ef9fee5cab694 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -1862,7 +1862,7 @@ impl<'a> State<'a> {
self.ann.post(self, AnnNode::Pat(pat))
}
- fn print_arm(&mut self, arm: &hir::Arm) -> io::Result<()> {
+ pub fn print_arm(&mut self, arm: &hir::Arm) -> io::Result<()> {
// I have no idea why this check is necessary, but here it
// is :(
if arm.attrs.is_empty() {
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index 9c4683e094634..512e4d434434c 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -852,6 +852,12 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'a, 'tcx> {
})
}
+ fn visit_arm(&mut self, a: &'tcx hir::Arm) {
+ self.with_lint_attrs(a.hir_id, &a.attrs, |builder| {
+ intravisit::walk_arm(builder, a);
+ })
+ }
+
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
self.with_lint_attrs(trait_item.hir_id, &trait_item.attrs, |builder| {
intravisit::walk_trait_item(builder, trait_item);
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index 37681ad7fcdd2..fa4e8e3d4769d 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -119,18 +119,18 @@ impl fmt::Debug for Scope {
pub enum ScopeData {
Node,
- // Scope of the call-site for a function or closure
- // (outlives the arguments as well as the body).
+ /// Scope of the call-site for a function or closure
+ /// (outlives the arguments as well as the body).
CallSite,
- // Scope of arguments passed to a function or closure
- // (they outlive its body).
+ /// Scope of arguments passed to a function or closure
+ /// (they outlive its body).
Arguments,
- // Scope of destructors for temporaries of node-id.
+ /// Scope of destructors for temporaries of node-id.
Destruction,
- // Scope following a `let id = expr;` binding in a block.
+ /// Scope following a `let id = expr;` binding in a block.
Remainder(FirstStatementIndex)
}
@@ -152,11 +152,11 @@ newtype_index! {
///
/// * The subscope with `first_statement_index == 1` is scope of `c`,
/// and thus does not include EXPR_2, but covers the `...`.
- pub struct FirstStatementIndex { .. }
+ pub struct FirstStatementIndex {
+ derive [HashStable]
+ }
}
-impl_stable_hash_for!(struct crate::middle::region::FirstStatementIndex { private });
-
// compilation error if size of `ScopeData` is not the same as a `u32`
static_assert_size!(ScopeData, 4);
@@ -814,6 +814,16 @@ fn resolve_block<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, blk:
}
fn resolve_arm<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, arm: &'tcx hir::Arm) {
+ let prev_cx = visitor.cx;
+
+ visitor.enter_scope(
+ Scope {
+ id: arm.hir_id.local_id,
+ data: ScopeData::Node,
+ }
+ );
+ visitor.cx.var_parent = visitor.cx.parent;
+
visitor.terminating_scopes.insert(arm.body.hir_id.local_id);
if let Some(hir::Guard::If(ref expr)) = arm.guard {
@@ -821,6 +831,8 @@ fn resolve_arm<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, arm: &
}
intravisit::walk_arm(visitor, arm);
+
+ visitor.cx = prev_cx;
}
fn resolve_pat<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, pat: &'tcx hir::Pat) {
@@ -893,10 +905,6 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:
terminating(body.hir_id.local_id);
}
- hir::ExprKind::Match(..) => {
- visitor.cx.var_parent = visitor.cx.parent;
- }
-
hir::ExprKind::DropTemps(ref expr) => {
// `DropTemps(expr)` does not denote a conditional scope.
// Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`.
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index 7469aceee3a9e..b5bab1585342a 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -23,8 +23,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
safety_mode
} =
self.hir.mirror(ast_block);
- self.in_opt_scope(opt_destruction_scope.map(|de|(de, source_info)), block, move |this| {
- this.in_scope((region_scope, source_info), LintLevel::Inherited, block, move |this| {
+ self.in_opt_scope(opt_destruction_scope.map(|de|(de, source_info)), move |this| {
+ this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
if targeted_by_break {
// This is a `break`-able block
let exit_block = this.cfg.start_new_block();
@@ -83,9 +83,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
StmtKind::Expr { scope, expr } => {
this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
unpack!(block = this.in_opt_scope(
- opt_destruction_scope.map(|de|(de, source_info)), block, |this| {
+ opt_destruction_scope.map(|de|(de, source_info)), |this| {
let si = (scope, source_info);
- this.in_scope(si, LintLevel::Inherited, block, |this| {
+ this.in_scope(si, LintLevel::Inherited, |this| {
let expr = this.hir.mirror(expr);
this.stmt_expr(block, expr, Some(stmt_span))
})
@@ -113,31 +113,39 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let remainder_span = remainder_scope.span(this.hir.tcx(),
&this.hir.region_scope_tree);
- let scope;
+ let visibility_scope =
+ Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
// Evaluate the initializer, if present.
if let Some(init) = initializer {
let initializer_span = init.span();
- scope = this.declare_bindings(
- None,
- remainder_span,
- lint_level,
- &pattern,
- ArmHasGuard(false),
- Some((None, initializer_span)),
- );
unpack!(block = this.in_opt_scope(
- opt_destruction_scope.map(|de|(de, source_info)), block, |this| {
+ opt_destruction_scope.map(|de|(de, source_info)), |this| {
let scope = (init_scope, source_info);
- this.in_scope(scope, lint_level, block, |this| {
+ this.in_scope(scope, lint_level, |this| {
+ this.declare_bindings(
+ visibility_scope,
+ remainder_span,
+ &pattern,
+ ArmHasGuard(false),
+ Some((None, initializer_span)),
+ );
this.expr_into_pattern(block, pattern, init)
})
}));
} else {
- scope = this.declare_bindings(
- None, remainder_span, lint_level, &pattern,
- ArmHasGuard(false), None);
+ let scope = (init_scope, source_info);
+ unpack!(this.in_scope(scope, lint_level, |this| {
+ this.declare_bindings(
+ visibility_scope,
+ remainder_span,
+ &pattern,
+ ArmHasGuard(false),
+ None,
+ );
+ block.unit()
+ }));
debug!("ast_block_stmts: pattern={:?}", pattern);
this.visit_bindings(
@@ -149,8 +157,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
})
}
- // Enter the source scope, after evaluating the initializer.
- if let Some(source_scope) = scope {
+ // Enter the visibility scope, after evaluating the initializer.
+ if let Some(source_scope) = visibility_scope {
this.source_scope = source_scope;
}
}
diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs
index e354a2ee8160b..ed80cb1a16369 100644
--- a/src/librustc_mir/build/expr/as_operand.rs
+++ b/src/librustc_mir/build/expr/as_operand.rs
@@ -57,7 +57,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
{
let source_info = this.source_info(expr.span);
let region_scope = (region_scope, source_info);
- return this.in_scope(region_scope, lint_level, block, |this| {
+ return this.in_scope(region_scope, lint_level, |this| {
this.as_operand(block, scope, value)
});
}
diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs
index 5f444d4ceeb88..a956eacb0699f 100644
--- a/src/librustc_mir/build/expr/as_place.rs
+++ b/src/librustc_mir/build/expr/as_place.rs
@@ -52,7 +52,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
region_scope,
lint_level,
value,
- } => this.in_scope((region_scope, source_info), lint_level, block, |this| {
+ } => this.in_scope((region_scope, source_info), lint_level, |this| {
if mutability == Mutability::Not {
this.as_read_only_place(block, value)
} else {
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index fbc4835a6557b..a0b504a99de9a 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -58,7 +58,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
value,
} => {
let region_scope = (region_scope, source_info);
- this.in_scope(region_scope, lint_level, block, |this| {
+ this.in_scope(region_scope, lint_level, |this| {
this.as_rvalue(block, scope, value)
})
}
diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs
index cba771f27065d..cffd8fb2892f5 100644
--- a/src/librustc_mir/build/expr/as_temp.rs
+++ b/src/librustc_mir/build/expr/as_temp.rs
@@ -1,6 +1,7 @@
//! See docs in build/expr/mod.rs
use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use crate::build::scope::{CachedBlock, DropKind};
use crate::hair::*;
use rustc::middle::region;
use rustc::mir::*;
@@ -43,7 +44,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
value,
} = expr.kind
{
- return this.in_scope((region_scope, source_info), lint_level, block, |this| {
+ return this.in_scope((region_scope, source_info), lint_level, |this| {
this.as_temp(block, temp_lifetime, value, mutability)
});
}
@@ -63,6 +64,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
this.local_decls.push(local_decl)
};
+ let temp_place = &Place::Base(PlaceBase::Local(temp));
+
if !expr_ty.is_never() {
this.cfg.push(
block,
@@ -71,25 +74,38 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
kind: StatementKind::StorageLive(temp),
},
);
+
+ // In constants, `temp_lifetime` is `None` for temporaries that live for the
+ // `'static` lifetime. Thus we do not drop these temporaries and simply leak them.
+ // This is equivalent to what `let x = &foo();` does in functions. The temporary
+ // is lifted to their surrounding scope. In a function that means the temporary lives
+ // until just before the function returns. In constants that means it outlives the
+ // constant's initialization value computation. Anything outliving a constant
+ // must have the `'static` lifetime and live forever.
+ // Anything with a shorter lifetime (e.g the `&foo()` in `bar(&foo())` or anything
+ // within a block will keep the regular drops just like runtime code.
+ if let Some(temp_lifetime) = temp_lifetime {
+ this.schedule_drop(
+ expr_span,
+ temp_lifetime,
+ temp_place,
+ expr_ty,
+ DropKind::Storage,
+ );
+ }
}
- unpack!(block = this.into(&Place::Base(PlaceBase::Local(temp)), block, expr));
+ unpack!(block = this.into(temp_place, block, expr));
- // In constants, temp_lifetime is None for temporaries that live for the
- // 'static lifetime. Thus we do not drop these temporaries and simply leak them.
- // This is equivalent to what `let x = &foo();` does in functions. The temporary
- // is lifted to their surrounding scope. In a function that means the temporary lives
- // until just before the function returns. In constants that means it outlives the
- // constant's initialization value computation. Anything outliving a constant
- // must have the `'static` lifetime and live forever.
- // Anything with a shorter lifetime (e.g the `&foo()` in `bar(&foo())` or anything
- // within a block will keep the regular drops just like runtime code.
if let Some(temp_lifetime) = temp_lifetime {
- this.schedule_drop_storage_and_value(
+ this.schedule_drop(
expr_span,
temp_lifetime,
- &Place::Base(PlaceBase::Local(temp)),
+ temp_place,
expr_ty,
+ DropKind::Value {
+ cached_block: CachedBlock::default(),
+ },
);
}
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index 15795a64e3b7d..7bdfdf0b0895f 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -46,7 +46,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
value,
} => {
let region_scope = (region_scope, source_info);
- this.in_scope(region_scope, lint_level, block, |this| {
+ this.in_scope(region_scope, lint_level, |this| {
this.into(destination, block, value)
})
}
diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs
index b58914b017fd4..ac690f89264bf 100644
--- a/src/librustc_mir/build/expr/stmt.rs
+++ b/src/librustc_mir/build/expr/stmt.rs
@@ -29,7 +29,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
value,
} => {
let value = this.hir.mirror(value);
- this.in_scope((region_scope, source_info), lint_level, block, |this| {
+ this.in_scope((region_scope, source_info), lint_level, |this| {
this.stmt_expr(block, value, opt_stmt_span)
})
}
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 51d5c96083d8f..58ca35abcb123 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -12,6 +12,7 @@ use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
use crate::hair::{self, *};
use rustc::hir::HirId;
use rustc::mir::*;
+use rustc::middle::region;
use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty};
use rustc::ty::layout::VariantIdx;
use rustc_data_structures::bit_set::BitSet;
@@ -251,37 +252,39 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// Step 5. Create everything else: the guards and the arms.
- let outer_source_info = self.source_info(span);
let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(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[0],
- ArmHasGuard(arm.guard.is_some()),
- Some((Some(&scrutinee_place), scrutinee_span)),
- );
-
- for candidate in candidates {
- self.bind_and_guard_matched_candidate(
- candidate,
- arm.guard.clone(),
- arm_block,
- &fake_borrow_temps,
- scrutinee_span,
+ let arm_source_info = self.source_info(arm.span);
+ let region_scope = (arm.scope, arm_source_info);
+ self.in_scope(region_scope, arm.lint_level, |this| {
+ let arm_block = this.cfg.start_new_block();
+
+ let body = this.hir.mirror(arm.body.clone());
+ let scope = this.declare_bindings(
+ None,
+ arm.span,
+ &arm.patterns[0],
+ ArmHasGuard(arm.guard.is_some()),
+ Some((Some(&scrutinee_place), scrutinee_span)),
);
- }
- if let Some(source_scope) = scope {
- self.source_scope = source_scope;
- }
+ if let Some(source_scope) = scope {
+ this.source_scope = source_scope;
+ }
- unpack!(arm_block = self.into(destination, arm_block, body));
+ for candidate in candidates {
+ this.clear_top_scope(arm.scope);
+ this.bind_and_guard_matched_candidate(
+ candidate,
+ arm.guard.clone(),
+ arm_block,
+ &fake_borrow_temps,
+ scrutinee_span,
+ region_scope,
+ );
+ }
- arm_block
+ this.into(destination, arm_block, body)
+ })
}).collect();
// all the arm blocks will rejoin here
@@ -289,7 +292,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
for arm_block in arm_end_blocks {
self.cfg.terminate(
- arm_block,
+ unpack!(arm_block),
outer_source_info,
TerminatorKind::Goto { target: end_block },
);
@@ -489,33 +492,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
&mut self,
mut visibility_scope: Option >,
pub guard: Option >, expr: P >, expr: P;
// let mut _2: std::boxed::Box;
// let mut _3: ();
// let mut _4: std::boxed::Box;
// scope 1 {
-// let _1: std::boxed::Box;
-// }
-// scope 2 {
// }
// bb0: {
// StorageLive(_1);
diff --git a/src/test/mir-opt/issue-41110.rs b/src/test/mir-opt/issue-41110.rs
index 023440af0eb10..0b678be2ab319 100644
--- a/src/test/mir-opt/issue-41110.rs
+++ b/src/test/mir-opt/issue-41110.rs
@@ -29,27 +29,24 @@ impl S {
// END RUST SOURCE
// START rustc.main.ElaborateDrops.after.mir
// let mut _0: ();
+// let _1: ();
// let mut _2: S;
// let mut _3: S;
// let mut _4: S;
// let mut _5: bool;
// scope 1 {
-// let _1: ();
-// }
-// scope 2 {
// }
// ...
// bb0: {
// END rustc.main.ElaborateDrops.after.mir
// START rustc.test.ElaborateDrops.after.mir
// let mut _0: ();
+// let _1: S;
// let mut _3: ();
// let mut _4: S;
// let mut _5: S;
// let mut _6: bool;
// ...
-// let _1: S;
-// ...
// let mut _2: S;
// ...
// bb0: {
diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs
index 29446d2ecc23e..bf22f00b50552 100644
--- a/src/test/mir-opt/issue-49232.rs
+++ b/src/test/mir-opt/issue-49232.rs
@@ -18,14 +18,12 @@ fn main() {
// fn main() -> (){
// let mut _0: ();
// let mut _1: ();
+// let _2: i32;
// let mut _3: bool;
// let mut _4: !;
// let mut _5: ();
// let mut _6: &i32;
// scope 1 {
-// let _2: i32;
-// }
-// scope 2 {
// }
// bb0: {
// goto -> bb1;
@@ -88,7 +86,6 @@ fn main() {
// unreachable;
// }
// bb17: {
-// StorageDead(_4);
// goto -> bb18;
// }
// bb18: {
diff --git a/src/test/mir-opt/match-arm-scopes.rs b/src/test/mir-opt/match-arm-scopes.rs
new file mode 100644
index 0000000000000..0f026b8a08dfa
--- /dev/null
+++ b/src/test/mir-opt/match-arm-scopes.rs
@@ -0,0 +1,245 @@
+// Test that StorageDead and Drops are generated properly for bindings in
+// matches:
+// * The MIR should only contain a single drop of `s` and `t`: at the end
+// of their respective arms.
+// * StorageDead and StorageLive statements are correctly matched up on
+// non-unwind paths.
+// * The visibility scopes of the match arms should be disjoint, and contain.
+// all of the bindings for that scope.
+// * No drop flags are used.
+
+#![feature(nll, bind_by_move_pattern_guards)]
+
+fn complicated_match(cond: bool, items: (bool, bool, String)) -> i32 {
+ match items {
+ (false, a, s) | (a, false, s) if if cond { return 3 } else { a } => 1,
+ (true, b, t) | (false, b, t) => 2,
+ }
+}
+
+const CASES: &[(bool, bool, bool, i32)] = &[
+ (false, false, false, 2),
+ (false, false, true, 1),
+ (false, true, false, 1),
+ (false, true, true, 2),
+ (true, false, false, 3),
+ (true, false, true, 3),
+ (true, true, false, 3),
+ (true, true, true, 2),
+];
+
+fn main() {
+ for &(cond, items_1, items_2, result) in CASES {
+ assert_eq!(
+ complicated_match(cond, (items_1, items_2, String::new())),
+ result,
+ );
+ }
+}
+
+// END RUST SOURCE
+// START rustc.complicated_match.SimplifyCfg-initial.after.mir
+// let mut _0: i32;
+// let mut _3: &bool; // Temp for fake borrow of `items.0`
+// let mut _4: &bool; // Temp for fake borrow of `items.1`
+// let _5: bool; // `a` in arm
+// let _6: &bool; // `a` in guard
+// let _7: std::string::String; // `s` in arm
+// let _8: &std::string::String; // `s` in guard
+// let mut _9: bool; // `if cond { return 3 } else { a }`
+// let mut _10: bool; // `cond`
+// let mut _11: !; // `return 3`
+// let mut _12: bool; // `if cond { return 3 } else { a }`
+// let mut _13: bool; // `cond`
+// let mut _14: !; // `return 3`
+// let _15: bool; // `b`
+// let _16: std::string::String; // `t`
+// scope 1 {
+// }
+// scope 2 {
+// }
+// bb0: {
+// FakeRead(ForMatchedPlace, _2);
+// switchInt((_2.0: bool)) -> [false: bb2, otherwise: bb7];
+// }
+// bb1 (cleanup): {
+// resume;
+// }
+// bb2: {
+// falseEdges -> [real: bb10, imaginary: bb3];
+// }
+// bb3: {
+// falseEdges -> [real: bb21, imaginary: bb4];
+// }
+// bb4: {
+// falseEdges -> [real: bb31, imaginary: bb5];
+// }
+// bb5: {
+// falseEdges -> [real: bb32, imaginary: bb6];
+// }
+// bb6: {
+// unreachable;
+// }
+// bb7: {
+// switchInt((_2.1: bool)) -> [false: bb3, otherwise: bb8];
+// }
+// bb8: {
+// switchInt((_2.0: bool)) -> [false: bb5, otherwise: bb4];
+// }
+// bb9: { // arm 1
+// _0 = const 1i32;
+// drop(_7) -> [return: bb29, unwind: bb16];
+// }
+// bb10: { // guard - first time
+// StorageLive(_6);
+// _6 = &(_2.1: bool);
+// StorageLive(_8);
+// _8 = &(_2.2: std::string::String);
+// _3 = &shallow (_2.0: bool);
+// _4 = &shallow (_2.1: bool);
+// StorageLive(_9);
+// StorageLive(_10);
+// _10 = _1;
+// FakeRead(ForMatchedPlace, _10);
+// switchInt(_10) -> [false: bb12, otherwise: bb11];
+// }
+// bb11: {
+// falseEdges -> [real: bb14, imaginary: bb12];
+// }
+// bb12: {
+// falseEdges -> [real: bb18, imaginary: bb13];
+// }
+// bb13: {
+// unreachable;
+// }
+// bb14: { // `return 3` - first time
+// _0 = const 3i32;
+// StorageDead(_10);
+// StorageDead(_9);
+// StorageDead(_8);
+// StorageDead(_6);
+// goto -> bb17;
+// }
+// bb15: {
+// return;
+// }
+// bb16 (cleanup): {
+// drop(_2) -> bb1;
+// }
+// bb17: {
+// drop(_2) -> [return: bb15, unwind: bb1];
+// }
+// bb18: { // `else` block - first time
+// _9 = (*_6);
+// StorageDead(_10);
+// FakeRead(ForMatchGuard, _3);
+// FakeRead(ForMatchGuard, _4);
+// FakeRead(ForGuardBinding, _6);
+// FakeRead(ForGuardBinding, _8);
+// switchInt(move _9) -> [false: bb20, otherwise: bb19];
+// }
+// bb19: {
+// StorageDead(_9);
+// StorageLive(_5);
+// _5 = (_2.1: bool);
+// StorageLive(_7);
+// _7 = move (_2.2: std::string::String);
+// goto -> bb9;
+// }
+// bb20: { // guard otherwise case - first time
+// StorageDead(_9);
+// StorageDead(_8);
+// StorageDead(_6);
+// falseEdges -> [real: bb7, imaginary: bb3];
+// }
+// bb21: { // guard - second time
+// StorageLive(_6);
+// _6 = &(_2.0: bool);
+// StorageLive(_8);
+// _8 = &(_2.2: std::string::String);
+// _3 = &shallow (_2.0: bool);
+// _4 = &shallow (_2.1: bool);
+// StorageLive(_12);
+// StorageLive(_13);
+// _13 = _1;
+// FakeRead(ForMatchedPlace, _13);
+// switchInt(_13) -> [false: bb23, otherwise: bb22];
+// }
+// bb22: {
+// falseEdges -> [real: bb25, imaginary: bb23];
+// }
+// bb23: {
+// falseEdges -> [real: bb26, imaginary: bb24];
+// }
+// bb24: {
+// unreachable;
+// }
+// bb25: { // `return 3` - second time
+// _0 = const 3i32;
+// StorageDead(_13);
+// StorageDead(_12);
+// StorageDead(_8);
+// StorageDead(_6);
+// goto -> bb17;
+// }
+// bb26: { // `else` block - second time
+// _12 = (*_6);
+// StorageDead(_13);
+// FakeRead(ForMatchGuard, _3);
+// FakeRead(ForMatchGuard, _4);
+// FakeRead(ForGuardBinding, _6);
+// FakeRead(ForGuardBinding, _8);
+// switchInt(move _12) -> [false: bb28, otherwise: bb27];
+// }
+// bb27: { // Guard otherwise case - second time
+// StorageDead(_12);
+// StorageLive(_5);
+// _5 = (_2.0: bool);
+// StorageLive(_7);
+// _7 = move (_2.2: std::string::String);
+// goto -> bb9;
+// }
+// bb28: { // rest of arm 1
+// StorageDead(_12);
+// StorageDead(_8);
+// StorageDead(_6);
+// falseEdges -> [real: bb8, imaginary: bb4];
+// }
+// bb29: {
+// StorageDead(_7);
+// StorageDead(_5);
+// StorageDead(_8);
+// StorageDead(_6);
+// goto -> bb34;
+// }
+// bb30: { // arm 2
+// _0 = const 2i32;
+// drop(_16) -> [return: bb33, unwind: bb16];
+// }
+// bb31: { // bindings for arm 2 - first pattern
+// StorageLive(_15);
+// _15 = (_2.1: bool);
+// StorageLive(_16);
+// _16 = move (_2.2: std::string::String);
+// goto -> bb30;
+// }
+// bb32: { // bindings for arm 2 - first pattern
+// StorageLive(_15);
+// _15 = (_2.1: bool);
+// StorageLive(_16);
+// _16 = move (_2.2: std::string::String);
+// goto -> bb30;
+// }
+// bb33: { // rest of arm 2
+// StorageDead(_16);
+// StorageDead(_15);
+// goto -> bb34;
+// }
+// bb34: { // end of match
+// drop(_2) -> [return: bb15, unwind: bb1];
+// }
+// END rustc.complicated_match.SimplifyCfg-initial.after.mir
+// START rustc.complicated_match.ElaborateDrops.after.mir
+// let _16: std::string::String; // No drop flags, which would come after this.
+// scope 1 {
+// END rustc.complicated_match.ElaborateDrops.after.mir
diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs
index 7ac36a22274f3..6979924c8cd90 100644
--- a/src/test/mir-opt/match_false_edges.rs
+++ b/src/test/mir-opt/match_false_edges.rs
@@ -45,13 +45,13 @@ fn main() {
// _2 = std::option::Option::