@@ -86,7 +86,7 @@ should go to.
86
86
87
87
*/
88
88
89
- use build:: { BlockAnd , BlockAndExtension , Builder , CFG } ;
89
+ use build:: { BlockAnd , BlockAndExtension , Builder } ;
90
90
use rustc:: middle:: region:: CodeExtent ;
91
91
use rustc:: middle:: ty:: Ty ;
92
92
use rustc:: mir:: repr:: * ;
@@ -227,16 +227,44 @@ impl<'a,'tcx> Builder<'a,'tcx> {
227
227
self . cfg . terminate ( block, Terminator :: Goto { target : target } ) ;
228
228
}
229
229
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 ( )
235
262
}
236
263
237
264
/// Create diverge cleanup and branch to it from `block`.
238
265
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 ) ;
240
268
self . cfg . terminate ( block, Terminator :: Panic { target : cleanup } ) ;
241
269
}
242
270
@@ -249,14 +277,18 @@ impl<'a,'tcx> Builder<'a,'tcx> {
249
277
lvalue : & Lvalue < ' tcx > ,
250
278
lvalue_ty : Ty < ' tcx > ) {
251
279
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 {
254
286
scope. drops . push ( ( kind, span, lvalue. clone ( ) ) ) ;
255
- scope . cached_block = None ;
287
+ return ;
256
288
}
257
- None => self . hir . span_bug ( span, & format ! ( "extent {:?} not in scope to drop {:?}" ,
258
- extent, lvalue) ) ,
259
289
}
290
+ self . hir . span_bug ( span,
291
+ & format ! ( "extent {:?} not in scope to drop {:?}" , extent, lvalue) ) ;
260
292
}
261
293
}
262
294
@@ -268,28 +300,3 @@ impl<'a,'tcx> Builder<'a,'tcx> {
268
300
self . scopes . first ( ) . map ( |scope| scope. extent ) . unwrap ( )
269
301
}
270
302
}
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