diff options
| -rw-r--r-- | src/librustc/mir/repr.rs | 17 | ||||
| -rw-r--r-- | src/librustc/mir/visit.rs | 2 | ||||
| -rw-r--r-- | src/librustc_mir/build/expr/into.rs | 4 | ||||
| -rw-r--r-- | src/librustc_mir/build/scope.rs | 81 | ||||
| -rw-r--r-- | src/librustc_mir/transform/erase_regions.rs | 1 | ||||
| -rw-r--r-- | src/librustc_trans/trans/mir/block.rs | 10 |
6 files changed, 70 insertions, 45 deletions
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 42c9b7b0f39..8cdb804c599 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -251,6 +251,10 @@ pub enum Terminator<'tcx> { /// well-known diverging block actually diverges. Diverge, + /// Indicates that the landing pad is finished and unwinding should + /// continue. Emitted by build::scope::diverge_cleanup. + Resume, + /// Indicates a normal return. The ReturnPointer lvalue should /// have been filled in by now. This should only occur in the /// `END_BLOCK`. @@ -288,6 +292,14 @@ pub enum CallTargets { } impl CallTargets { + pub fn new(ret: BasicBlock, cleanup: Option<BasicBlock>) -> CallTargets { + if let Some(c) = cleanup { + CallTargets::WithCleanup((ret, c)) + } else { + CallTargets::Return(ret) + } + } + pub fn as_slice(&self) -> &[BasicBlock] { match *self { CallTargets::Return(ref b) => slice::ref_slice(b), @@ -313,6 +325,7 @@ impl<'tcx> Terminator<'tcx> { Switch { targets: ref b, .. } => b, SwitchInt { targets: ref b, .. } => b, Diverge => &[], + Resume => &[], Return => &[], Call { targets: ref b, .. } => b.as_slice(), DivergingCall { cleanup: ref b, .. } => if let Some(b) = b.as_ref() { @@ -332,6 +345,7 @@ impl<'tcx> Terminator<'tcx> { Switch { targets: ref mut b, .. } => b, SwitchInt { targets: ref mut b, .. } => b, Diverge => &mut [], + Resume => &mut [], Return => &mut [], Call { targets: ref mut b, .. } => b.as_mut_slice(), DivergingCall { cleanup: ref mut b, .. } => if let Some(b) = b.as_mut() { @@ -393,6 +407,7 @@ impl<'tcx> Terminator<'tcx> { SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv), Diverge => write!(fmt, "diverge"), Return => write!(fmt, "return"), + Resume => write!(fmt, "resume"), Call { .. } => { // the author didn’t bother rebasing this unimplemented!() @@ -408,7 +423,7 @@ impl<'tcx> Terminator<'tcx> { pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> { use self::Terminator::*; match *self { - Diverge | Return => vec![], + Diverge | Return | Resume => vec![], Goto { .. } | Panic { .. } => vec!["".into_cow()], If { .. } => vec!["true".into_cow(), "false".into_cow()], Call { .. } => vec!["return".into_cow(), "unwind".into_cow()], diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index c3a335fdfaa..41fd5056823 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -134,6 +134,7 @@ pub trait Visitor<'tcx> { } Terminator::Diverge | + Terminator::Resume | Terminator::Return => { } @@ -431,6 +432,7 @@ pub trait MutVisitor<'tcx> { } Terminator::Diverge | + Terminator::Resume | Terminator::Return => { } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index a173018b74f..e23b5517cad 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -225,13 +225,13 @@ impl<'a,'tcx> Builder<'a,'tcx> { let success = this.cfg.start_new_block(); let cleanup = this.diverge_cleanup(); let term = if diverges { - Terminator::DivergingCall { func: fun, args: args, cleanup: Some(cleanup) } + Terminator::DivergingCall { func: fun, args: args, cleanup: cleanup } } else { Terminator::Call { func: fun, args: args, destination: destination.clone(), - targets: CallTargets::WithCleanup((success, cleanup)) + targets: CallTargets::new(success, cleanup) } }; this.cfg.terminate(block, term); diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index b5e029ed94b..e7f3794c043 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -86,7 +86,7 @@ should go to. */ -use build::{BlockAnd, BlockAndExtension, Builder, CFG}; +use build::{BlockAnd, BlockAndExtension, Builder}; use rustc::middle::region::CodeExtent; use rustc::middle::ty::Ty; use rustc::mir::repr::*; @@ -227,16 +227,44 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.cfg.terminate(block, Terminator::Goto { target: target }); } - /// Creates a path that performs all required cleanup for - /// unwinding. This path terminates in DIVERGE. Returns the start - /// of the path. See module comment for more details. - pub fn diverge_cleanup(&mut self) -> BasicBlock { - diverge_cleanup_helper(&mut self.cfg, &mut self.scopes) + /// Creates a path that performs all required cleanup for unwinding. + /// + /// This path terminates in Resume. Returns the start of the path. + /// See module comment for more details. None indicates there’s no + /// cleanup to do at this point. + pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> { + if self.scopes.is_empty() { + return None; + } + + let mut terminator = Terminator::Resume; + // Given an array of scopes, we generate these from the outermost scope to the innermost + // one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will + // generate B0 <- B1 <- B2 in left-to-right order. The outermost scope (B0) will always + // terminate with a Resume terminator. + for scope in self.scopes.iter_mut().filter(|s| !s.drops.is_empty()) { + if let Some(b) = scope.cached_block { + terminator = Terminator::Goto { target: b }; + continue; + } else { + let new_block = self.cfg.start_new_block(); + self.cfg.terminate(new_block, terminator); + terminator = Terminator::Goto { target: new_block }; + for &(kind, span, ref lvalue) in scope.drops.iter().rev() { + self.cfg.push_drop(new_block, span, kind, lvalue); + } + scope.cached_block = Some(new_block); + } + } + // Return the innermost cached block, most likely the one we just generated. + // Note that if there are no cleanups in scope we return None. + self.scopes.iter().rev().flat_map(|b| b.cached_block).next() } /// Create diverge cleanup and branch to it from `block`. pub fn panic(&mut self, block: BasicBlock) { - let cleanup = self.diverge_cleanup(); + // FIXME: panic terminator should also have conditional cleanup? + let cleanup = self.diverge_cleanup().unwrap_or(DIVERGE_BLOCK); self.cfg.terminate(block, Terminator::Panic { target: cleanup }); } @@ -249,14 +277,18 @@ impl<'a,'tcx> Builder<'a,'tcx> { lvalue: &Lvalue<'tcx>, lvalue_ty: Ty<'tcx>) { if self.hir.needs_drop(lvalue_ty) { - match self.scopes.iter_mut().rev().find(|s| s.extent == extent) { - Some(scope) => { + for scope in self.scopes.iter_mut().rev() { + // We must invalidate all the cached_blocks leading up to the scope we’re looking + // for, because otherwise some/most of the blocks in the chain might become + // incorrect (i.e. they still are pointing at old cached_block). + scope.cached_block = None; + if scope.extent == extent { scope.drops.push((kind, span, lvalue.clone())); - scope.cached_block = None; + return; } - None => self.hir.span_bug(span, &format!("extent {:?} not in scope to drop {:?}", - extent, lvalue)), } + self.hir.span_bug(span, + &format!("extent {:?} not in scope to drop {:?}", extent, lvalue)); } } @@ -268,28 +300,3 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.scopes.first().map(|scope| scope.extent).unwrap() } } - -fn diverge_cleanup_helper<'tcx>(cfg: &mut CFG<'tcx>, scopes: &mut [Scope<'tcx>]) -> BasicBlock { - let len = scopes.len(); - - if len == 0 { - return DIVERGE_BLOCK; - } - - let (remaining, scope) = scopes.split_at_mut(len - 1); - let scope = &mut scope[0]; - - if let Some(b) = scope.cached_block { - return b; - } - - let block = cfg.start_new_block(); - for &(kind, span, ref lvalue) in &scope.drops { - cfg.push_drop(block, span, kind, lvalue); - } - scope.cached_block = Some(block); - - let remaining_cleanup_block = diverge_cleanup_helper(cfg, remaining); - cfg.terminate(block, Terminator::Goto { target: remaining_cleanup_block }); - block -} diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 1e7bb305cdb..3f5e18b7e79 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -80,6 +80,7 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { match *terminator { Terminator::Goto { .. } | Terminator::Diverge | + Terminator::Resume | Terminator::Return | Terminator::Panic { .. } => { /* nothing to do */ diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 00508ed21e5..ddd3ed34e8f 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -87,16 +87,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::Terminator::Diverge => { + build::Unreachable(bcx); + } + + mir::Terminator::Resume => { if let Some(llpersonalityslot) = self.llpersonalityslot { let lp = build::Load(bcx, llpersonalityslot); // FIXME(lifetime) base::call_lifetime_end(bcx, self.personality); build::Resume(bcx, lp); } else { - // This fn never encountered anything fallible, so - // a Diverge cannot actually happen. Note that we - // do a total hack to ensure that we visit the - // DIVERGE block last. - build::Unreachable(bcx); + panic!("resume terminator without personality slot") } } |
