@@ -86,7 +86,7 @@ should go to.
8686
8787*/
8888
89- use build:: { BlockAnd , BlockAndExtension , Builder , CFG } ;
89+ use build:: { BlockAnd , BlockAndExtension , Builder } ;
9090use rustc:: middle:: region:: CodeExtent ;
9191use rustc:: middle:: ty:: Ty ;
9292use rustc:: mir:: repr:: * ;
@@ -227,16 +227,44 @@ impl<'a,'tcx> Builder<'a,'tcx> {
227227 self . cfg . terminate ( block, Terminator :: Goto { target : target } ) ;
228228 }
229229
230- /// Creates a path that performs all required cleanup for
231- /// unwinding. This path terminates in DIVERGE. Returns the start
232- /// of the path. See module comment for more details.
233- pub fn diverge_cleanup ( & mut self ) -> BasicBlock {
234- diverge_cleanup_helper ( & mut self . cfg , & mut self . scopes )
230+ /// Creates a path that performs all required cleanup for unwinding.
231+ ///
232+ /// This path terminates in Resume. Returns the start of the path.
233+ /// See module comment for more details. None indicates there’s no
234+ /// cleanup to do at this point.
235+ pub fn diverge_cleanup ( & mut self ) -> Option < BasicBlock > {
236+ if self . scopes . is_empty ( ) {
237+ return None ;
238+ }
239+
240+ let mut terminator = Terminator :: Resume ;
241+ // Given an array of scopes, we generate these from the outermost scope to the innermost
242+ // one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will
243+ // generate B0 <- B1 <- B2 in left-to-right order. The outermost scope (B0) will always
244+ // terminate with a Resume terminator.
245+ for scope in self . scopes . iter_mut ( ) . filter ( |s| !s. drops . is_empty ( ) ) {
246+ if let Some ( b) = scope. cached_block {
247+ terminator = Terminator :: Goto { target : b } ;
248+ continue ;
249+ } else {
250+ let new_block = self . cfg . start_new_block ( ) ;
251+ self . cfg . terminate ( new_block, terminator) ;
252+ terminator = Terminator :: Goto { target : new_block } ;
253+ for & ( kind, span, ref lvalue) in scope. drops . iter ( ) . rev ( ) {
254+ self . cfg . push_drop ( new_block, span, kind, lvalue) ;
255+ }
256+ scope. cached_block = Some ( new_block) ;
257+ }
258+ }
259+ // Return the innermost cached block, most likely the one we just generated.
260+ // Note that if there are no cleanups in scope we return None.
261+ self . scopes . iter ( ) . rev ( ) . flat_map ( |b| b. cached_block ) . next ( )
235262 }
236263
237264 /// Create diverge cleanup and branch to it from `block`.
238265 pub fn panic ( & mut self , block : BasicBlock ) {
239- let cleanup = self . diverge_cleanup ( ) ;
266+ // FIXME: panic terminator should also have conditional cleanup?
267+ let cleanup = self . diverge_cleanup ( ) . unwrap_or ( DIVERGE_BLOCK ) ;
240268 self . cfg . terminate ( block, Terminator :: Panic { target : cleanup } ) ;
241269 }
242270
@@ -249,14 +277,18 @@ impl<'a,'tcx> Builder<'a,'tcx> {
249277 lvalue : & Lvalue < ' tcx > ,
250278 lvalue_ty : Ty < ' tcx > ) {
251279 if self . hir . needs_drop ( lvalue_ty) {
252- match self . scopes . iter_mut ( ) . rev ( ) . find ( |s| s. extent == extent) {
253- Some ( scope) => {
280+ for scope in self . scopes . iter_mut ( ) . rev ( ) {
281+ // We must invalidate all the cached_blocks leading up to the scope we’re looking
282+ // for, because otherwise some/most of the blocks in the chain might become
283+ // incorrect (i.e. they still are pointing at old cached_block).
284+ scope. cached_block = None ;
285+ if scope. extent == extent {
254286 scope. drops . push ( ( kind, span, lvalue. clone ( ) ) ) ;
255- scope . cached_block = None ;
287+ return ;
256288 }
257- None => self . hir . span_bug ( span, & format ! ( "extent {:?} not in scope to drop {:?}" ,
258- extent, lvalue) ) ,
259289 }
290+ self . hir . span_bug ( span,
291+ & format ! ( "extent {:?} not in scope to drop {:?}" , extent, lvalue) ) ;
260292 }
261293 }
262294
@@ -268,28 +300,3 @@ impl<'a,'tcx> Builder<'a,'tcx> {
268300 self . scopes . first ( ) . map ( |scope| scope. extent ) . unwrap ( )
269301 }
270302}
271-
272- fn diverge_cleanup_helper < ' tcx > ( cfg : & mut CFG < ' tcx > , scopes : & mut [ Scope < ' tcx > ] ) -> BasicBlock {
273- let len = scopes. len ( ) ;
274-
275- if len == 0 {
276- return DIVERGE_BLOCK ;
277- }
278-
279- let ( remaining, scope) = scopes. split_at_mut ( len - 1 ) ;
280- let scope = & mut scope[ 0 ] ;
281-
282- if let Some ( b) = scope. cached_block {
283- return b;
284- }
285-
286- let block = cfg. start_new_block ( ) ;
287- for & ( kind, span, ref lvalue) in & scope. drops {
288- cfg. push_drop ( block, span, kind, lvalue) ;
289- }
290- scope. cached_block = Some ( block) ;
291-
292- let remaining_cleanup_block = diverge_cleanup_helper ( cfg, remaining) ;
293- cfg. terminate ( block, Terminator :: Goto { target : remaining_cleanup_block } ) ;
294- block
295- }
0 commit comments