diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index f46b590d2dc59..e78c2ad7c88b7 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -201,6 +201,10 @@ for mir::TerminatorKind<'gcx> { target.hash_stable(hcx, hasher); } } + mir::TerminatorKind::FalseUnwind { ref real_target, ref unwind } => { + real_target.hash_stable(hcx, hasher); + unwind.hash_stable(hcx, hasher); + } } } } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index d82691f882c73..e7284a2716fd1 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -803,9 +803,28 @@ pub enum TerminatorKind<'tcx> { /// Indicates the end of the dropping of a generator GeneratorDrop, + /// A block where control flow only ever takes one real path, but borrowck + /// needs to be more conservative. FalseEdges { + /// The target normal control flow will take real_target: BasicBlock, - imaginary_targets: Vec + /// The list of blocks control flow could conceptually take, but won't + /// in practice + imaginary_targets: Vec, + }, + /// A terminator for blocks that only take one path in reality, but where we + /// reserve the right to unwind in borrowck, even if it won't happen in practice. + /// This can arise in infinite loops with no function calls for example. + FalseUnwind { + /// The target normal control flow will take + real_target: BasicBlock, + /// The imaginary cleanup block link. This particular path will never be taken + /// in practice, but in order to avoid fragility we want to always + /// consider it in borrowck. We don't want to accept programs which + /// pass borrowck only when panic=abort or some assertions are disabled + /// due to release vs. debug mode builds. This needs to be an Option because + /// of the remove_noop_landing_pads and no_landing_pads passes + unwind: Option, }, } @@ -865,6 +884,8 @@ impl<'tcx> TerminatorKind<'tcx> { s.extend_from_slice(imaginary_targets); s.into_cow() } + FalseUnwind { real_target: t, unwind: Some(u) } => vec![t, u].into_cow(), + FalseUnwind { real_target: ref t, unwind: None } => slice::from_ref(t).into_cow(), } } @@ -897,6 +918,8 @@ impl<'tcx> TerminatorKind<'tcx> { s.extend(imaginary_targets.iter_mut()); s } + FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => vec![t, u], + FalseUnwind { ref mut real_target, unwind: None } => vec![real_target], } } @@ -916,7 +939,8 @@ impl<'tcx> TerminatorKind<'tcx> { TerminatorKind::Call { cleanup: ref mut unwind, .. } | TerminatorKind::Assert { cleanup: ref mut unwind, .. } | TerminatorKind::DropAndReplace { ref mut unwind, .. } | - TerminatorKind::Drop { ref mut unwind, .. } => { + TerminatorKind::Drop { ref mut unwind, .. } | + TerminatorKind::FalseUnwind { ref mut unwind, .. } => { Some(unwind) } } @@ -1045,7 +1069,8 @@ impl<'tcx> TerminatorKind<'tcx> { write!(fmt, ")") }, - FalseEdges { .. } => write!(fmt, "falseEdges") + FalseEdges { .. } => write!(fmt, "falseEdges"), + FalseUnwind { .. } => write!(fmt, "falseUnwind"), } } @@ -1087,6 +1112,8 @@ impl<'tcx> TerminatorKind<'tcx> { l.resize(imaginary_targets.len() + 1, "imaginary".into()); l } + FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()], + FalseUnwind { unwind: None, .. } => vec!["real".into()], } } } @@ -2189,7 +2216,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { Return => Return, Unreachable => Unreachable, FalseEdges { real_target, ref imaginary_targets } => - FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() } + FalseEdges { real_target, imaginary_targets: imaginary_targets.clone() }, + FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind }, }; Terminator { source_info: self.source_info, @@ -2231,7 +2259,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { Return | GeneratorDrop | Unreachable | - FalseEdges { .. } => false + FalseEdges { .. } | + FalseUnwind { .. } => false } } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 57ed41f2f06e6..b26e1854d97fd 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -495,15 +495,21 @@ 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 } => { + TerminatorKind::FalseEdges { real_target, ref imaginary_targets} => { self.visit_branch(block, real_target); for target in imaginary_targets { self.visit_branch(block, *target); } } + + TerminatorKind::FalseUnwind { real_target, unwind } => { + self.visit_branch(block, real_target); + if let Some(unwind) = unwind { + self.visit_branch(block, unwind); + } + } } } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 9a6d83b8eb759..6a19392a6e88b 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -576,7 +576,8 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx TerminatorKind::Goto { target: _ } | TerminatorKind::Abort | TerminatorKind::Unreachable - | TerminatorKind::FalseEdges { .. } => { + | TerminatorKind::FalseEdges { real_target: _, imaginary_targets: _ } + | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => { // no data used, thus irrelevant to borrowck } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index c0c680a4ddcbc..7ca8d0bdd500b 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -796,7 +796,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } - | TerminatorKind::FalseEdges { .. } => { + | TerminatorKind::FalseEdges { .. } + | TerminatorKind::FalseUnwind { .. } => { // no checks needed for these } @@ -1152,6 +1153,18 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.assert_iscleanup(mir, block_data, *target, is_cleanup); } } + TerminatorKind::FalseUnwind { + real_target, + unwind + } => { + self.assert_iscleanup(mir, block_data, real_target, is_cleanup); + if let Some(unwind) = unwind { + if is_cleanup { + span_mirbug!(self, block_data, "cleanup in cleanup block via false unwind"); + } + self.assert_iscleanup(mir, block_data, unwind, true); + } + } } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 68b23d1ae17e8..28dc329e4fe7c 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -104,8 +104,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Or: // // [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block] - // | | (false) - // +----------true------------+-------------------> [false_block] + // | (true) | (false) + // [true_block] [false_block] let (true_block, false_block, mut else_block, join_block) = (this.cfg.start_new_block(), this.cfg.start_new_block(), @@ -147,20 +147,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { join_block.unit() } ExprKind::Loop { condition: opt_cond_expr, body } => { - // [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block] - // ^ | - // | 0 - // | | - // | v - // [body_block_end] <~~~ [body_block] + // [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block] + // ^ | + // | 0 + // | | + // | v + // [body_block_end] <-/eval. body/-- [body_block] // // If `opt_cond_expr` is `None`, then the graph is somewhat simplified: // - // [block] --> [loop_block / body_block ] ~~> [body_block_end] [exit_block] - // ^ | - // | | - // +--------------------------+ - // + // [block] + // | + // [loop_block] -> [body_block] -/eval. body/-> [body_block_end] + // | ^ | + // false link | | + // | +-----------------------------------------+ + // +-> [diverge_cleanup] + // The false link is required to make sure borrowck considers unwinds through the + // body, even when the exact code in the body cannot unwind let loop_block = this.cfg.start_new_block(); let exit_block = this.cfg.start_new_block(); @@ -188,7 +192,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // always `()` anyway this.cfg.push_assign_unit(exit_block, source_info, destination); } else { - body_block = loop_block; + body_block = this.cfg.start_new_block(); + let diverge_cleanup = this.diverge_cleanup(); + this.cfg.terminate(loop_block, source_info, + TerminatorKind::FalseUnwind { + real_target: body_block, + unwind: Some(diverge_cleanup) + }) } // The “return” value of the loop body must always be an unit. We therefore diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 6cb9217776648..e2096bf5356c1 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -728,7 +728,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { TerminatorKind::FalseEdges { real_target: block, imaginary_targets: - vec![candidate.next_candidate_pre_binding_block]}); + vec![candidate.next_candidate_pre_binding_block], + }); self.bind_matched_candidate(block, candidate.bindings); @@ -749,7 +750,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { TerminatorKind::FalseEdges { real_target: otherwise, imaginary_targets: - vec![candidate.next_candidate_pre_binding_block] }); + vec![candidate.next_candidate_pre_binding_block], + }); Some(otherwise) } else { self.cfg.terminate(block, candidate_source_info, diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 80990bcc08089..ad6ac6876ce66 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -517,6 +517,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { mir::TerminatorKind::Yield {..} | mir::TerminatorKind::Goto {..} | mir::TerminatorKind::FalseEdges {..} | + mir::TerminatorKind::FalseUnwind {..} | mir::TerminatorKind::Unreachable => {} } } diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index 291c22b5e1ed0..9c7d9b398cc56 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -864,6 +864,14 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation self.propagate_bits_into_entry_set_for(in_out, changed, target); } } + mir::TerminatorKind::FalseUnwind { ref real_target, unwind } => { + self.propagate_bits_into_entry_set_for(in_out, changed, real_target); + if let Some(ref unwind) = unwind { + if !self.dead_unwinds.contains(&bb) { + self.propagate_bits_into_entry_set_for(in_out, changed, unwind); + } + } + } } } diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index cd36282eca0a6..635d99e7737a9 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -346,6 +346,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> { TerminatorKind::Abort | TerminatorKind::GeneratorDrop | TerminatorKind::FalseEdges { .. } | + TerminatorKind::FalseUnwind { .. } | TerminatorKind::Unreachable => { } TerminatorKind::Return => { diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index c8a0dbdd90308..606bda51edb1f 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -165,6 +165,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Resume => unimplemented!(), Abort => unimplemented!(), FalseEdges { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"), + FalseUnwind { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"), Unreachable => return err!(Unreachable), } diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index f16187797d4e5..a80dfaef0dab1 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -636,7 +636,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { mir::TerminatorKind::Assert { .. } => {} mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Yield { .. } | - mir::TerminatorKind::FalseEdges { .. } => bug!(), + mir::TerminatorKind::FalseEdges { .. } | + mir::TerminatorKind::FalseUnwind { .. } => bug!(), } self.super_terminator_kind(block, kind, location); diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index ae27f54e618a1..bbc7803b84d8e 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -76,7 +76,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::Unreachable | - TerminatorKind::FalseEdges { .. } => { + TerminatorKind::FalseEdges { .. } | + TerminatorKind::FalseUnwind { .. } => { // safe (at least as emitted during MIR construction) } diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index ceea97e3ed3b0..2d861921c9c8d 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -813,6 +813,9 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { *target = self.update_target(*target); } } + TerminatorKind::FalseUnwind { real_target: _ , unwind: _ } => + // see the ordering of passes in the optimized_mir query. + bug!("False unwinds should have been removed before inlining") } } diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index da76adfd48f3f..741e39fe06880 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -327,7 +327,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } | TerminatorKind::Unreachable | - TerminatorKind::FalseEdges { .. } => None, + TerminatorKind::FalseEdges { .. } | + TerminatorKind::FalseUnwind { .. } => None, TerminatorKind::Return => { // Check for unused values. This usually means diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs index e7cab469bc222..cd80d25c410f1 100644 --- a/src/librustc_mir/transform/remove_noop_landing_pads.rs +++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs @@ -75,7 +75,8 @@ impl RemoveNoopLandingPads { TerminatorKind::Goto { .. } | TerminatorKind::Resume | TerminatorKind::SwitchInt { .. } | - TerminatorKind::FalseEdges { .. } => { + TerminatorKind::FalseEdges { .. } | + TerminatorKind::FalseUnwind { .. } => { terminator.successors().iter().all(|succ| { nop_landing_pads.contains(succ.index()) }) diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs index 20c33bab1aacb..41089f567bd71 100644 --- a/src/librustc_mir/transform/simplify_branches.rs +++ b/src/librustc_mir/transform/simplify_branches.rs @@ -64,6 +64,9 @@ impl MirPass for SimplifyBranches { TerminatorKind::FalseEdges { real_target, .. } => { TerminatorKind::Goto { target: real_target } }, + TerminatorKind::FalseUnwind { real_target, .. } => { + TerminatorKind::Goto { target: real_target } + }, _ => continue }; } diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index b379a174b23f6..e4705674e2292 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -123,6 +123,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { TerminatorKind::GeneratorDrop => "TerminatorKind::GeneratorDrop", TerminatorKind::Yield { .. } => "TerminatorKind::Yield", TerminatorKind::FalseEdges { .. } => "TerminatorKind::FalseEdges", + TerminatorKind::FalseUnwind { .. } => "TerminatorKind::FalseUnwind", }, kind); self.super_terminator_kind(block, kind, location); } diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index bf82e1d50c473..f683703ce6d53 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -242,7 +242,8 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec { + TerminatorKind::FalseEdges { .. } | + TerminatorKind::FalseUnwind { .. } => { /* nothing to do */ } TerminatorKind::Call { cleanup: unwind, .. } | diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index af1e30a4b19a6..bb2a7840faee7 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -608,8 +608,9 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { cleanup); } mir::TerminatorKind::GeneratorDrop | - mir::TerminatorKind::Yield { .. } | - mir::TerminatorKind::FalseEdges { .. } => bug!("generator ops in trans"), + mir::TerminatorKind::Yield { .. } => bug!("generator ops in trans"), + mir::TerminatorKind::FalseEdges { .. } | + mir::TerminatorKind::FalseUnwind { .. } => bug!("borrowck false edges in trans"), } } diff --git a/src/test/compile-fail/issue-46036.rs b/src/test/compile-fail/issue-46036.rs new file mode 100644 index 0000000000000..b5cdded4d304a --- /dev/null +++ b/src/test/compile-fail/issue-46036.rs @@ -0,0 +1,23 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Issue 46036: [NLL] false edges on infinite loops +// Infinite loops should create false edges to the cleanup block. +#![feature(nll)] + +struct Foo { x: &'static u32 } + +fn foo() { + let a = 3; + let foo = Foo { x: &a }; //~ ERROR E0597 + loop { } +} + +fn main() { } diff --git a/src/test/incremental/hashes/loop_expressions.rs b/src/test/incremental/hashes/loop_expressions.rs index dcb937fd867ab..8599f8d7f9a00 100644 --- a/src/test/incremental/hashes/loop_expressions.rs +++ b/src/test/incremental/hashes/loop_expressions.rs @@ -179,7 +179,7 @@ pub fn change_continue_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, TypeckTables")] +#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, TypeckTables")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_label() { let mut _x = 0; diff --git a/src/test/mir-opt/end_region_2.rs b/src/test/mir-opt/end_region_2.rs index 56c3e2a38a0ed..d6084d5a6da92 100644 --- a/src/test/mir-opt/end_region_2.rs +++ b/src/test/mir-opt/end_region_2.rs @@ -40,15 +40,21 @@ fn main() { // goto -> bb1; // } // bb1: { +// falseUnwind -> [real: bb2, cleanup: bb3]; +// } +// bb2: { // StorageLive(_2); // _2 = const true; // StorageLive(_3); // _3 = &'23_1rs _2; // StorageLive(_5); // _5 = _2; -// switchInt(move _5) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _5) -> [0u8: bb5, otherwise: bb4]; // } -// bb2: { +// bb3: { +// ... +// } +// bb4: { // _0 = (); // StorageDead(_5); // EndRegion('23_1rs); @@ -56,7 +62,7 @@ fn main() { // StorageDead(_2); // return; // } -// bb3: { +// bb5: { // _4 = (); // StorageDead(_5); // StorageLive(_7); diff --git a/src/test/mir-opt/end_region_3.rs b/src/test/mir-opt/end_region_3.rs index 8c0d56eba7828..46548f1cce978 100644 --- a/src/test/mir-opt/end_region_3.rs +++ b/src/test/mir-opt/end_region_3.rs @@ -43,14 +43,20 @@ fn main() { // goto -> bb1; // } // bb1: { +// falseUnwind -> [real: bb2, cleanup: bb3]; +// } +// bb2: { // _1 = const true; // StorageLive(_3); // _3 = &'26_1rs _1; // StorageLive(_5); // _5 = _1; -// switchInt(move _5) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _5) -> [0u8: bb5, otherwise: bb4]; // } -// bb2: { +// bb3: { +// ... +// } +// bb4: { // _0 = (); // StorageDead(_5); // EndRegion('26_1rs); @@ -58,7 +64,7 @@ fn main() { // StorageDead(_1); // return; // } -// bb3: { +// bb5: { // _4 = (); // StorageDead(_5); // StorageLive(_7); diff --git a/src/test/mir-opt/end_region_9.rs b/src/test/mir-opt/end_region_9.rs index b313e296ac99c..0f1d714cc6fd2 100644 --- a/src/test/mir-opt/end_region_9.rs +++ b/src/test/mir-opt/end_region_9.rs @@ -57,16 +57,24 @@ fn main() { // _1 = const false; // StorageLive(_2); // _2 = const 3i32; -// StorageLive(_4); -// goto -> bb1; +// falseUnwind -> [real: bb2, cleanup: bb1]; // } -// // bb1: { +// ... +// } +// bb2: { +// StorageLive(_4); +// goto -> bb3; +// } +// bb3: { +// falseUnwind -> [real: bb4, cleanup: bb1]; +// } +// bb4: { // StorageLive(_7); // _7 = _1; -// switchInt(move _7) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _7) -> [0u8: bb6, otherwise: bb5]; // } -// bb2: { +// bb5: { // _0 = (); // StorageDead(_7); // EndRegion('33_0rs); @@ -75,13 +83,13 @@ fn main() { // StorageDead(_1); // return; // } -// bb3: { +// bb6: { // _4 = &'33_0rs _2; // _6 = (); // StorageDead(_7); // _1 = const true; // _3 = (); -// goto -> bb1; +// goto -> bb3; // } // } // END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/end_region_cyclic.rs b/src/test/mir-opt/end_region_cyclic.rs index 37a6229febabb..2a82e2675b67d 100644 --- a/src/test/mir-opt/end_region_cyclic.rs +++ b/src/test/mir-opt/end_region_cyclic.rs @@ -67,16 +67,19 @@ fn query() -> bool { true } // goto -> bb1; // } // bb1: { +// falseUnwind -> [real: bb2, cleanup: bb3]; +// } +// bb2: { // StorageLive(_2); // StorageLive(_3); // StorageLive(_4); // _4 = std::option::Option<&'35_0rs S<'35_0rs>>::None; -// _3 = const >::new(move _4) -> [return: bb3, unwind: bb2]; +// _3 = const >::new(move _4) -> [return: bb4, unwind: bb3]; // } -// bb2: { +// bb3: { // resume; // } -// bb3: { +// bb4: { // StorageDead(_4); // _2 = S<'35_0rs> { r: move _3 }; // StorageDead(_3); @@ -89,27 +92,27 @@ fn query() -> bool { true } // _8 = &'35_0rs (*_9); // _7 = std::option::Option<&'35_0rs S<'35_0rs>>::Some(move _8,); // StorageDead(_8); -// _5 = const >::set(move _6, move _7) -> [return: bb4, unwind: bb2]; +// _5 = const >::set(move _6, move _7) -> [return: bb5, unwind: bb3]; // } -// bb4: { +// bb5: { // EndRegion('16s); // StorageDead(_7); // StorageDead(_6); // StorageDead(_9); // StorageLive(_11); -// _11 = const query() -> [return: bb5, unwind: bb2]; -// } -// bb5: { -// switchInt(move _11) -> [0u8: bb7, otherwise: bb6]; +// _11 = const query() -> [return: bb6, unwind: bb3]; // } // bb6: { +// switchInt(move _11) -> [0u8: bb8, otherwise: bb7]; +// } +// bb7: { // _0 = (); // StorageDead(_11); // EndRegion('35_0rs); // StorageDead(_2); // return; // } -// bb7: { +// bb8: { // _10 = (); // StorageDead(_11); // StorageLive(_14); @@ -121,9 +124,9 @@ fn query() -> bool { true } // _16 = &'35_0rs (*_17); // _15 = std::option::Option<&'35_0rs S<'35_0rs>>::Some(move _16,); // StorageDead(_16); -// _13 = const >::set(move _14, move _15) -> [return: bb8, unwind: bb2]; +// _13 = const >::set(move _14, move _15) -> [return: bb9, unwind: bb3]; // } -// bb8: { +// bb9: { // EndRegion('33s); // StorageDead(_15); // StorageDead(_14); diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs index b5c188cf834a9..3151c0643079c 100644 --- a/src/test/mir-opt/issue-38669.rs +++ b/src/test/mir-opt/issue-38669.rs @@ -25,27 +25,30 @@ fn main() { // bb0: { // StorageLive(_1); // _1 = const false; -// goto -> bb1; +// goto -> bb2; // } -// // bb1: { +// resume; +// } +// bb2: { +// falseUnwind -> [real: bb3, cleanup: bb1]; +// } +// bb3: { // StorageLive(_4); // _4 = _1; -// switchInt(move _4) -> [0u8: bb3, otherwise: bb2]; +// switchInt(move _4) -> [0u8: bb5, otherwise: bb4]; // } -// -// bb2: { +// bb4: { // _0 = (); // StorageDead(_4); // StorageDead(_1); // return; // } -// -// bb3: { +// bb5: { // _3 = (); // StorageDead(_4); // _1 = const true; // _2 = (); -// goto -> bb1; +// goto -> bb2; // } // END rustc.main.SimplifyCfg-initial.after.mir diff --git a/src/test/mir-opt/loop_test.rs b/src/test/mir-opt/loop_test.rs new file mode 100644 index 0000000000000..d36d890809497 --- /dev/null +++ b/src/test/mir-opt/loop_test.rs @@ -0,0 +1,49 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z identify_regions -Z emit-end-regions + +// Tests to make sure we correctly generate falseUnwind edges in loops + +fn main() { + // Exit early at runtime. Since only care about the generated MIR + // and not the runtime behavior (which is exercised by other tests) + // we just bail early. Without this the test just loops infinitely. + if true { + return; + } + loop { + let x = 1; + continue; + } +} + +// END RUST SOURCE +// START rustc.main.SimplifyCfg-qualify-consts.after.mir +// ... +// bb1: { // The cleanup block +// resume; +// } +// ... +// bb3: { // Entry into the loop +// _1 = (); +// goto -> bb4; +// } +// bb4: { // The loop_block +// falseUnwind -> [real: bb5, cleanup: bb1]; +// } +// bb5: { // The loop body (body_block) +// StorageLive(_5); +// _5 = const 1i32; +// StorageDead(_5); +// goto -> bb4; +// } +// ... +// END rustc.main.SimplifyCfg-qualify-consts.after.mir diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs index b060222a95f17..64ffc7446062c 100644 --- a/src/test/mir-opt/nll/liveness-drop-intra-block.rs +++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs @@ -25,17 +25,17 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | Live variables on entry to bb2: [] -// bb2: { -// | Live variables on entry to bb2[0]: [] +// | Live variables on entry to bb3: [] +// bb3: { +// | Live variables on entry to bb3[0]: [] // _1 = const 55usize; -// | Live variables on entry to bb2[1]: [_1] +// | Live variables on entry to bb3[1]: [_1] // StorageLive(_3); -// | Live variables on entry to bb2[2]: [_1] +// | Live variables on entry to bb3[2]: [_1] // StorageLive(_4); -// | Live variables on entry to bb2[3]: [_1] +// | Live variables on entry to bb3[3]: [_1] // _4 = _1; -// | Live variables on entry to bb2[4]: [_4] -// _3 = const use_x(move _4) -> [return: bb3, unwind: bb1]; +// | Live variables on entry to bb3[4]: [_4] +// _3 = const use_x(move _4) -> [return: bb4, unwind: bb1]; // } // END rustc.main.nll.0.mir