@@ -83,9 +83,10 @@ use rustc::middle::region;
8383use rustc:: ty:: Ty ;
8484use rustc:: hir;
8585use rustc:: mir:: * ;
86- use syntax_pos:: { Span } ;
86+ use syntax_pos:: { Span , DUMMY_SP } ;
8787use rustc_data_structures:: fx:: FxHashMap ;
8888use std:: collections:: hash_map:: Entry ;
89+ use std:: mem;
8990
9091#[ derive( Debug ) ]
9192pub struct Scope < ' tcx > {
@@ -98,16 +99,10 @@ pub struct Scope<'tcx> {
9899 /// the span of that region_scope
99100 region_scope_span : Span ,
100101
101- /// Whether there's anything to do for the cleanup path, that is,
102+ /// Whether we need landing pads for the cleanup path, that is,
102103 /// when unwinding through this scope. This includes destructors,
103- /// but not StorageDead statements, which don't get emitted at all
104- /// for unwinding, for several reasons:
105- /// * clang doesn't emit llvm.lifetime.end for C++ unwinding
106- /// * LLVM's memory dependency analysis can't handle it atm
107- /// * polluting the cleanup MIR with StorageDead creates
108- /// landing pads even though there's no actual destructors
109- /// * freeing up stack space has no effect during unwinding
110- needs_cleanup : bool ,
104+ /// but not StorageDead statements.
105+ cleanup_may_panic : bool ,
111106
112107 /// set of places to drop when exiting this scope. This starts
113108 /// out empty but grows as variables are declared during the
@@ -348,7 +343,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
348343 source_scope : vis_scope,
349344 region_scope : region_scope. 0 ,
350345 region_scope_span : region_scope. 1 . span ,
351- needs_cleanup : false ,
346+ cleanup_may_panic : false ,
352347 drops : vec ! [ ] ,
353348 cached_generator_drop : None ,
354349 cached_exits : Default :: default ( ) ,
@@ -411,7 +406,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
411406
412407 // If we are emitting a `drop` statement, we need to have the cached
413408 // diverge cleanup pads ready in case that drop panics.
414- let may_panic = self . scopes [ ( len - scope_count) ..] . iter ( ) . any ( |s| s. needs_cleanup ) ;
409+ let may_panic = self . scopes [ ( len - scope_count) ..] . iter ( ) . any ( |s| s. cleanup_may_panic ) ;
415410 if may_panic {
416411 self . diverge_cleanup ( ) ;
417412 }
@@ -466,10 +461,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
466461 /// This path terminates in GeneratorDrop. Returns the start of the path.
467462 /// None indicates there’s no cleanup to do at this point.
468463 pub fn generator_drop_cleanup ( & mut self ) -> Option < BasicBlock > {
469- if !self . scopes . iter ( ) . any ( |scope| scope. needs_cleanup ) {
470- return None ;
471- }
472-
473464 // Fill in the cache for unwinds
474465 self . diverge_cleanup_gen ( true ) ;
475466
@@ -480,10 +471,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
480471 let result = block;
481472
482473 while let Some ( scope) = scopes. next ( ) {
483- if !scope. needs_cleanup {
484- continue ;
485- }
486-
487474 block = if let Some ( b) = scope. cached_generator_drop {
488475 self . cfg . terminate ( block, src_info,
489476 TerminatorKind :: Goto { target : b } ) ;
@@ -623,7 +610,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
623610
624611 // Schedule an abort block - this is used for some ABIs that cannot unwind
625612 pub fn schedule_abort ( & mut self ) -> BasicBlock {
626- self . scopes [ 0 ] . needs_cleanup = true ;
613+ self . scopes [ 0 ] . cleanup_may_panic = true ;
627614 let abortblk = self . cfg . start_new_cleanup_block ( ) ;
628615 let source_info = self . scopes [ 0 ] . source_info ( self . fn_span ) ;
629616 self . cfg . terminate ( abortblk, source_info, TerminatorKind :: Abort ) ;
@@ -735,7 +722,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
735722 scope. invalidate_cache ( !needs_drop, this_scope) ;
736723 if this_scope {
737724 if let DropKind :: Value { .. } = drop_kind {
738- scope. needs_cleanup = true ;
725+ scope. cleanup_may_panic = true ;
739726 }
740727
741728 let region_scope_span = region_scope. span ( self . hir . tcx ( ) ,
@@ -900,12 +887,6 @@ fn build_scope_drops<'tcx>(
900887 // drops panic (panicking while unwinding will abort, so there's no need for
901888 // another set of arrows). The drops for the unwind path should have already
902889 // been generated by `diverge_cleanup_gen`.
903- //
904- // The code in this function reads from right to left.
905- // Storage dead drops have to be done left to right (since we can only push
906- // to the end of a Vec). So, we find the next drop and then call
907- // push_storage_deads which will iterate backwards through them so that
908- // they are added in the correct order.
909890
910891 let mut unwind_blocks = scope. drops . iter ( ) . rev ( ) . filter_map ( |drop_data| {
911892 if let DropKind :: Value { cached_block } = drop_data. kind {
@@ -936,11 +917,6 @@ fn build_scope_drops<'tcx>(
936917 block = next;
937918 }
938919 DropKind :: Storage => {
939- // We do not need to emit StorageDead for generator drops
940- if generator_drop {
941- continue
942- }
943-
944920 // Drop the storage for both value and storage drops.
945921 // Only temps and vars need their storage dead.
946922 match drop_data. location {
@@ -958,12 +934,12 @@ fn build_scope_drops<'tcx>(
958934 block. unit ( )
959935}
960936
961- fn build_diverge_scope < ' tcx > ( cfg : & mut CFG < ' tcx > ,
962- span : Span ,
963- scope : & mut Scope < ' tcx > ,
964- mut target : BasicBlock ,
965- generator_drop : bool )
966- -> BasicBlock
937+ fn build_diverge_scope ( cfg : & mut CFG < ' tcx > ,
938+ span : Span ,
939+ scope : & mut Scope < ' tcx > ,
940+ mut target : BasicBlock ,
941+ generator_drop : bool )
942+ -> BasicBlock
967943{
968944 // Build up the drops in **reverse** order. The end result will
969945 // look like:
@@ -981,7 +957,13 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
981957 scope : source_scope
982958 } ;
983959
984- // Next, build up the drops. Here we iterate the vector in
960+ // We keep track of StorageDead statements to prepend to our current block
961+ // and store them here, in reverse order.
962+ let mut storage_deads = vec ! [ ] ;
963+
964+ let mut target_built_by_us = false ;
965+
966+ // Build up the drops. Here we iterate the vector in
985967 // *forward* order, so that we generate drops[0] first (right to
986968 // left in diagram above).
987969 for ( j, drop_data) in scope. drops . iter_mut ( ) . enumerate ( ) {
@@ -994,28 +976,70 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
994976 // frozen stack)? In particular, the intent may have been to
995977 // match the behavior of clang, but on inspection eddyb says
996978 // this is not what clang does.
997- let cached_block = match drop_data. kind {
998- DropKind :: Value { ref mut cached_block } => cached_block. ref_mut ( generator_drop) ,
999- DropKind :: Storage => continue
1000- } ;
1001- target = if let Some ( cached_block) = * cached_block {
1002- cached_block
1003- } else {
1004- let block = cfg. start_new_cleanup_block ( ) ;
1005- cfg. terminate ( block, source_info ( drop_data. span ) ,
1006- TerminatorKind :: Drop {
1007- location : drop_data. location . clone ( ) ,
1008- target,
1009- unwind : None
1010- } ) ;
1011- * cached_block = Some ( block) ;
1012- block
979+ match drop_data. kind {
980+ DropKind :: Storage => {
981+ // Only temps and vars need their storage dead.
982+ match drop_data. location {
983+ Place :: Base ( PlaceBase :: Local ( index) ) => {
984+ storage_deads. push ( Statement {
985+ source_info : source_info ( drop_data. span ) ,
986+ kind : StatementKind :: StorageDead ( index)
987+ } ) ;
988+ }
989+ _ => unreachable ! ( ) ,
990+ } ;
991+ }
992+ DropKind :: Value { ref mut cached_block } => {
993+ let cached_block = cached_block. ref_mut ( generator_drop) ;
994+ target = if let Some ( cached_block) = * cached_block {
995+ storage_deads. clear ( ) ;
996+ target_built_by_us = false ;
997+ cached_block
998+ } else {
999+ push_storage_deads (
1000+ cfg, & mut target, & mut storage_deads, target_built_by_us, source_scope) ;
1001+ let block = cfg. start_new_cleanup_block ( ) ;
1002+ cfg. terminate ( block, source_info ( drop_data. span ) ,
1003+ TerminatorKind :: Drop {
1004+ location : drop_data. location . clone ( ) ,
1005+ target,
1006+ unwind : None
1007+ } ) ;
1008+ * cached_block = Some ( block) ;
1009+ target_built_by_us = true ;
1010+ block
1011+ } ;
1012+ }
10131013 } ;
10141014 }
1015-
1015+ push_storage_deads ( cfg , & mut target , & mut storage_deads , target_built_by_us , source_scope ) ;
10161016 * scope. cached_unwind . ref_mut ( generator_drop) = Some ( target) ;
10171017
1018+ assert ! ( storage_deads. is_empty( ) ) ;
10181019 debug ! ( "build_diverge_scope({:?}, {:?}) = {:?}" , scope, span, target) ;
10191020
10201021 target
10211022}
1023+
1024+ fn push_storage_deads ( cfg : & mut CFG < ' tcx > ,
1025+ target : & mut BasicBlock ,
1026+ storage_deads : & mut Vec < Statement < ' tcx > > ,
1027+ target_built_by_us : bool ,
1028+ source_scope : SourceScope ) {
1029+ if storage_deads. is_empty ( ) { return ; }
1030+ if !target_built_by_us {
1031+ // We cannot add statements to an existing block, so we create a new
1032+ // block for our StorageDead statements.
1033+ let block = cfg. start_new_cleanup_block ( ) ;
1034+ let source_info = SourceInfo { span : DUMMY_SP , scope : source_scope } ;
1035+ cfg. terminate ( block, source_info, TerminatorKind :: Goto { target : * target } ) ;
1036+ * target = block;
1037+ }
1038+ let statements = & mut cfg. block_data_mut ( * target) . statements ;
1039+ storage_deads. reverse ( ) ;
1040+ debug ! ( "push_storage_deads({:?}), storage_deads={:?}, statements={:?}" ,
1041+ * target, storage_deads, statements) ;
1042+ storage_deads. append ( statements) ;
1043+ mem:: swap ( statements, storage_deads) ;
1044+ assert ! ( storage_deads. is_empty( ) ) ;
1045+ }
0 commit comments