mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-14 21:16:50 +00:00
Auto merge of #47802 - bobtwinkles:loop_false_edge, r=nikomatsakis
[NLL] Add false edges out of infinite loops Resolves #46036 by adding a `cleanup` member to the `FalseEdges` terminator kind. There's also a small doc fix to one of the other comments in `into.rs` which I can pull out in to another PR if desired =) This PR should pass CI but the test suite has been relatively unstable on my system so I'm not 100% sure. r? @nikomatsakis
This commit is contained in:
commit
3bcda48a30
@ -219,6 +219,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -816,9 +816,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<BasicBlock>
|
||||
/// The list of blocks control flow could conceptually take, but won't
|
||||
/// in practice
|
||||
imaginary_targets: Vec<BasicBlock>,
|
||||
},
|
||||
/// 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<BasicBlock>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -878,6 +897,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(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -910,6 +931,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],
|
||||
}
|
||||
}
|
||||
|
||||
@ -929,7 +952,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)
|
||||
}
|
||||
}
|
||||
@ -1058,7 +1082,8 @@ impl<'tcx> TerminatorKind<'tcx> {
|
||||
|
||||
write!(fmt, ")")
|
||||
},
|
||||
FalseEdges { .. } => write!(fmt, "falseEdges")
|
||||
FalseEdges { .. } => write!(fmt, "falseEdges"),
|
||||
FalseUnwind { .. } => write!(fmt, "falseUnwind"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1100,6 +1125,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()],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2202,7 +2229,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,
|
||||
@ -2244,7 +2272,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
|
||||
Return |
|
||||
GeneratorDrop |
|
||||
Unreachable |
|
||||
FalseEdges { .. } => false
|
||||
FalseEdges { .. } |
|
||||
FalseUnwind { .. } => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -575,7 +575,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
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 => {}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 => {
|
||||
|
@ -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),
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,7 +329,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
|
||||
|
@ -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())
|
||||
})
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -242,7 +242,8 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::SwitchInt { .. } |
|
||||
TerminatorKind::Yield { .. } |
|
||||
TerminatorKind::FalseEdges { .. } => {
|
||||
TerminatorKind::FalseEdges { .. } |
|
||||
TerminatorKind::FalseUnwind { .. } => {
|
||||
/* nothing to do */
|
||||
}
|
||||
TerminatorKind::Call { cleanup: unwind, .. } |
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
23
src/test/compile-fail/issue-46036.rs
Normal file
23
src/test/compile-fail/issue-46036.rs
Normal file
@ -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 <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.
|
||||
|
||||
// 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() { }
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -57,16 +57,24 @@ fn main() {
|
||||
// _1 = const false;
|
||||
// StorageLive(_2);
|
||||
// _2 = const 3i32;
|
||||
// StorageLive(_4);
|
||||
// goto -> bb1;
|
||||
// falseUnwind -> [real: bb2, cleanup: bb1];
|
||||
// }
|
||||
//
|
||||
// bb1: {
|
||||
// StorageLive(_7);
|
||||
// _7 = _1;
|
||||
// switchInt(move _7) -> [0u8: bb3, otherwise: bb2];
|
||||
// ...
|
||||
// }
|
||||
// bb2: {
|
||||
// StorageLive(_4);
|
||||
// goto -> bb3;
|
||||
// }
|
||||
// bb3: {
|
||||
// falseUnwind -> [real: bb4, cleanup: bb1];
|
||||
// }
|
||||
// bb4: {
|
||||
// StorageLive(_7);
|
||||
// _7 = _1;
|
||||
// switchInt(move _7) -> [0u8: bb6, otherwise: bb5];
|
||||
// }
|
||||
// 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
|
||||
|
@ -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 <std::cell::Cell<T>>::new(move _4) -> [return: bb3, unwind: bb2];
|
||||
// }
|
||||
// bb2: {
|
||||
// resume;
|
||||
// _3 = const <std::cell::Cell<T>>::new(move _4) -> [return: bb4, unwind: bb3];
|
||||
// }
|
||||
// bb3: {
|
||||
// resume;
|
||||
// }
|
||||
// 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 <std::cell::Cell<T>>::set(move _6, move _7) -> [return: bb4, unwind: bb2];
|
||||
// _5 = const <std::cell::Cell<T>>::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 <std::cell::Cell<T>>::set(move _14, move _15) -> [return: bb8, unwind: bb2];
|
||||
// _13 = const <std::cell::Cell<T>>::set(move _14, move _15) -> [return: bb9, unwind: bb3];
|
||||
// }
|
||||
// bb8: {
|
||||
// bb9: {
|
||||
// EndRegion('33s);
|
||||
// StorageDead(_15);
|
||||
// StorageDead(_14);
|
||||
|
@ -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
|
||||
|
49
src/test/mir-opt/loop_test.rs
Normal file
49
src/test/mir-opt/loop_test.rs
Normal file
@ -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 <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 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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user