@@ -466,9 +466,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
466
466
let normal_exit_block = f ( self ) ;
467
467
let breakable_scope = self . scopes . breakable_scopes . pop ( ) . unwrap ( ) ;
468
468
assert ! ( breakable_scope. region_scope == region_scope) ;
469
- let break_block = self . build_exit_tree ( breakable_scope. break_drops , None ) ;
469
+ let break_block =
470
+ self . build_exit_tree ( breakable_scope. break_drops , region_scope, span, None ) ;
470
471
if let Some ( drops) = breakable_scope. continue_drops {
471
- self . build_exit_tree ( drops, loop_block) ;
472
+ self . build_exit_tree ( drops, region_scope , span , loop_block) ;
472
473
}
473
474
match ( normal_exit_block, break_block) {
474
475
( Some ( block) , None ) | ( None , Some ( block) ) => block,
@@ -510,6 +511,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
510
511
pub ( crate ) fn in_if_then_scope < F > (
511
512
& mut self ,
512
513
region_scope : region:: Scope ,
514
+ span : Span ,
513
515
f : F ,
514
516
) -> ( BasicBlock , BasicBlock )
515
517
where
@@ -524,7 +526,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
524
526
assert ! ( if_then_scope. region_scope == region_scope) ;
525
527
526
528
let else_block = self
527
- . build_exit_tree ( if_then_scope. else_drops , None )
529
+ . build_exit_tree ( if_then_scope. else_drops , region_scope , span , None )
528
530
. map_or_else ( || self . cfg . start_new_block ( ) , |else_block_and| unpack ! ( else_block_and) ) ;
529
531
530
532
( then_block, else_block)
@@ -1021,6 +1023,38 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1021
1023
cached_drop
1022
1024
}
1023
1025
1026
+ /// This is similar to [diverge_cleanup_target] except its target is set to
1027
+ /// some ancestor scope instead of the current scope.
1028
+ /// It is possible to unwind to some ancestor scope if some drop panics as
1029
+ /// the program breaks out of a if-then scope.
1030
+ fn diverge_cleanup_target ( & mut self , target_scope : region:: Scope , span : Span ) -> DropIdx {
1031
+ let target = self . scopes . scope_index ( target_scope, span) ;
1032
+ let ( uncached_scope, mut cached_drop) = self . scopes . scopes [ ..=target]
1033
+ . iter ( )
1034
+ . enumerate ( )
1035
+ . rev ( )
1036
+ . find_map ( |( scope_idx, scope) | {
1037
+ scope. cached_unwind_block . map ( |cached_block| ( scope_idx + 1 , cached_block) )
1038
+ } )
1039
+ . unwrap_or ( ( 0 , ROOT_NODE ) ) ;
1040
+
1041
+ if uncached_scope > target {
1042
+ return cached_drop;
1043
+ }
1044
+
1045
+ let is_generator = self . generator_kind . is_some ( ) ;
1046
+ for scope in & mut self . scopes . scopes [ uncached_scope..=target] {
1047
+ for drop in & scope. drops {
1048
+ if is_generator || drop. kind == DropKind :: Value {
1049
+ cached_drop = self . scopes . unwind_drops . add_drop ( * drop, cached_drop) ;
1050
+ }
1051
+ }
1052
+ scope. cached_unwind_block = Some ( cached_drop) ;
1053
+ }
1054
+
1055
+ cached_drop
1056
+ }
1057
+
1024
1058
/// Prepares to create a path that performs all required cleanup for a
1025
1059
/// terminator that can unwind at the given basic block.
1026
1060
///
@@ -1222,21 +1256,24 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
1222
1256
fn build_exit_tree (
1223
1257
& mut self ,
1224
1258
mut drops : DropTree ,
1259
+ else_scope : region:: Scope ,
1260
+ span : Span ,
1225
1261
continue_block : Option < BasicBlock > ,
1226
1262
) -> Option < BlockAnd < ( ) > > {
1227
1263
let mut blocks = IndexVec :: from_elem ( None , & drops. drops ) ;
1228
1264
blocks[ ROOT_NODE ] = continue_block;
1229
1265
1230
1266
drops. build_mir :: < ExitScopes > ( & mut self . cfg , & mut blocks) ;
1267
+ let is_generator = self . generator_kind . is_some ( ) ;
1231
1268
1232
1269
// Link the exit drop tree to unwind drop tree.
1233
1270
if drops. drops . iter ( ) . any ( |( drop, _) | drop. kind == DropKind :: Value ) {
1234
- let unwind_target = self . diverge_cleanup ( ) ;
1271
+ let unwind_target = self . diverge_cleanup_target ( else_scope , span ) ;
1235
1272
let mut unwind_indices = IndexVec :: from_elem_n ( unwind_target, 1 ) ;
1236
1273
for ( drop_idx, drop_data) in drops. drops . iter_enumerated ( ) . skip ( 1 ) {
1237
1274
match drop_data. 0 . kind {
1238
1275
DropKind :: Storage => {
1239
- if self . generator_kind . is_some ( ) {
1276
+ if is_generator {
1240
1277
let unwind_drop = self
1241
1278
. scopes
1242
1279
. unwind_drops
0 commit comments