@@ -107,6 +107,9 @@ pub struct Scope<'tcx> {
107107 /// the extent of this scope within source code.
108108 extent : CodeExtent ,
109109
110+ /// the span of that extent
111+ extent_span : Span ,
112+
110113 /// Whether there's anything to do for the cleanup path, that is,
111114 /// when unwinding through this scope. This includes destructors,
112115 /// but not StorageDead statements, which don't get emitted at all
@@ -116,7 +119,7 @@ pub struct Scope<'tcx> {
116119 /// * pollutting the cleanup MIR with StorageDead creates
117120 /// landing pads even though there's no actual destructors
118121 /// * freeing up stack space has no effect during unwinding
119- pub ( super ) needs_cleanup : bool ,
122+ needs_cleanup : bool ,
120123
121124 /// set of lvalues to drop when exiting this scope. This starts
122125 /// out empty but grows as variables are declared during the
@@ -197,6 +200,15 @@ pub struct BreakableScope<'tcx> {
197200 pub break_destination : Lvalue < ' tcx > ,
198201}
199202
203+ impl DropKind {
204+ fn may_panic ( & self ) -> bool {
205+ match * self {
206+ DropKind :: Value { .. } => true ,
207+ DropKind :: Storage => false
208+ }
209+ }
210+ }
211+
200212impl < ' tcx > Scope < ' tcx > {
201213 /// Invalidate all the cached blocks in the scope.
202214 ///
@@ -282,7 +294,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
282294 where F : FnOnce ( & mut Builder < ' a , ' gcx , ' tcx > ) -> BlockAnd < R >
283295 {
284296 debug ! ( "in_opt_scope(opt_extent={:?}, block={:?})" , opt_extent, block) ;
285- if let Some ( extent) = opt_extent { self . push_scope ( extent. 0 ) ; }
297+ if let Some ( extent) = opt_extent { self . push_scope ( extent) ; }
286298 let rv = unpack ! ( block = f( self ) ) ;
287299 if let Some ( extent) = opt_extent {
288300 unpack ! ( block = self . pop_scope( extent, block) ) ;
@@ -301,7 +313,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
301313 where F : FnOnce ( & mut Builder < ' a , ' gcx , ' tcx > ) -> BlockAnd < R >
302314 {
303315 debug ! ( "in_scope(extent={:?}, block={:?})" , extent, block) ;
304- self . push_scope ( extent. 0 ) ;
316+ self . push_scope ( extent) ;
305317 let rv = unpack ! ( block = f( self ) ) ;
306318 unpack ! ( block = self . pop_scope( extent, block) ) ;
307319 debug ! ( "in_scope: exiting extent={:?} block={:?}" , extent, block) ;
@@ -312,12 +324,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
312324 /// scope and call `pop_scope` afterwards. Note that these two
313325 /// calls must be paired; using `in_scope` as a convenience
314326 /// wrapper maybe preferable.
315- pub fn push_scope ( & mut self , extent : CodeExtent ) {
327+ pub fn push_scope ( & mut self , extent : ( CodeExtent , SourceInfo ) ) {
316328 debug ! ( "push_scope({:?})" , extent) ;
317329 let vis_scope = self . visibility_scope ;
318330 self . scopes . push ( Scope {
319331 visibility_scope : vis_scope,
320- extent : extent,
332+ extent : extent. 0 ,
333+ extent_span : extent. 1 . span ,
321334 needs_cleanup : false ,
322335 drops : vec ! [ ] ,
323336 free : None ,
@@ -333,9 +346,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
333346 mut block : BasicBlock )
334347 -> BlockAnd < ( ) > {
335348 debug ! ( "pop_scope({:?}, {:?})" , extent, block) ;
336- // We need to have `cached_block`s available for all the drops, so we call diverge_cleanup
337- // to make sure all the `cached_block`s are filled in.
338- self . diverge_cleanup ( extent. 1 . span ) ;
349+ // If we are emitting a `drop` statement, we need to have the cached
350+ // diverge cleanup pads ready in case that drop panics.
351+ let may_panic =
352+ self . scopes . last ( ) . unwrap ( ) . drops . iter ( ) . any ( |s| s. kind . may_panic ( ) ) ;
353+ if may_panic {
354+ self . diverge_cleanup ( ) ;
355+ }
339356 let scope = self . scopes . pop ( ) . unwrap ( ) ;
340357 assert_eq ! ( scope. extent, extent. 0 ) ;
341358 unpack ! ( block = build_scope_drops( & mut self . cfg,
@@ -366,6 +383,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
366383 let len = self . scopes . len ( ) ;
367384 assert ! ( scope_count < len, "should not use `exit_scope` to pop ALL scopes" ) ;
368385 let tmp = self . get_unit_temp ( ) ;
386+
387+ // If we are emitting a `drop` statement, we need to have the cached
388+ // diverge cleanup pads ready in case that drop panics.
389+ let may_panic = self . scopes [ ( len - scope_count) ..] . iter ( )
390+ . any ( |s| s. drops . iter ( ) . any ( |s| s. kind . may_panic ( ) ) ) ;
391+ if may_panic {
392+ self . diverge_cleanup ( ) ;
393+ }
394+
369395 {
370396 let mut rest = & mut self . scopes [ ( len - scope_count) ..] ;
371397 while let Some ( ( scope, rest_) ) = { rest} . split_last_mut ( ) {
@@ -618,7 +644,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
618644 /// This path terminates in Resume. Returns the start of the path.
619645 /// See module comment for more details. None indicates there’s no
620646 /// cleanup to do at this point.
621- pub fn diverge_cleanup ( & mut self , span : Span ) -> Option < BasicBlock > {
647+ pub fn diverge_cleanup ( & mut self ) -> Option < BasicBlock > {
622648 if !self . scopes . iter ( ) . any ( |scope| scope. needs_cleanup ) {
623649 return None ;
624650 }
@@ -652,7 +678,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
652678 } ;
653679
654680 for scope in scopes. iter_mut ( ) {
655- target = build_diverge_scope ( hir. tcx ( ) , cfg, & unit_temp, span, scope, target) ;
681+ target = build_diverge_scope (
682+ hir. tcx ( ) , cfg, & unit_temp, scope. extent_span , scope, target) ;
656683 }
657684 Some ( target)
658685 }
@@ -668,7 +695,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
668695 }
669696 let source_info = self . source_info ( span) ;
670697 let next_target = self . cfg . start_new_block ( ) ;
671- let diverge_target = self . diverge_cleanup ( span ) ;
698+ let diverge_target = self . diverge_cleanup ( ) ;
672699 self . cfg . terminate ( block, source_info,
673700 TerminatorKind :: Drop {
674701 location : location,
@@ -686,7 +713,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
686713 value : Operand < ' tcx > ) -> BlockAnd < ( ) > {
687714 let source_info = self . source_info ( span) ;
688715 let next_target = self . cfg . start_new_block ( ) ;
689- let diverge_target = self . diverge_cleanup ( span ) ;
716+ let diverge_target = self . diverge_cleanup ( ) ;
690717 self . cfg . terminate ( block, source_info,
691718 TerminatorKind :: DropAndReplace {
692719 location : location,
@@ -709,7 +736,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
709736 let source_info = self . source_info ( span) ;
710737
711738 let success_block = self . cfg . start_new_block ( ) ;
712- let cleanup = self . diverge_cleanup ( span ) ;
739+ let cleanup = self . diverge_cleanup ( ) ;
713740
714741 self . cfg . terminate ( block, source_info,
715742 TerminatorKind :: Assert {
@@ -731,45 +758,48 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
731758 mut block : BasicBlock ,
732759 arg_count : usize )
733760 -> BlockAnd < ( ) > {
761+ debug ! ( "build_scope_drops({:?} -> {:?})" , block, scope) ;
734762 let mut iter = scope. drops . iter ( ) . rev ( ) . peekable ( ) ;
735763 while let Some ( drop_data) = iter. next ( ) {
736764 let source_info = scope. source_info ( drop_data. span ) ;
737- if let DropKind :: Value { .. } = drop_data. kind {
738- // Try to find the next block with its cached block
739- // for us to diverge into in case the drop panics.
740- let on_diverge = iter. peek ( ) . iter ( ) . filter_map ( |dd| {
741- match dd. kind {
742- DropKind :: Value { cached_block } => cached_block,
743- DropKind :: Storage => None
744- }
745- } ) . next ( ) ;
746- // If there’s no `cached_block`s within current scope,
747- // we must look for one in the enclosing scope.
748- let on_diverge = on_diverge. or_else ( ||{
749- earlier_scopes. iter ( ) . rev ( ) . flat_map ( |s| s. cached_block ( ) ) . next ( )
750- } ) ;
751- let next = cfg. start_new_block ( ) ;
752- cfg. terminate ( block, source_info, TerminatorKind :: Drop {
753- location : drop_data. location . clone ( ) ,
754- target : next,
755- unwind : on_diverge
756- } ) ;
757- block = next;
758- }
759765 match drop_data. kind {
760- DropKind :: Value { .. } |
761- DropKind :: Storage => {
762- // Only temps and vars need their storage dead.
763- match drop_data. location {
764- Lvalue :: Local ( index) if index. index ( ) > arg_count => { }
765- _ => continue
766- }
766+ DropKind :: Value { .. } => {
767+ // Try to find the next block with its cached block
768+ // for us to diverge into in case the drop panics.
769+ let on_diverge = iter. peek ( ) . iter ( ) . filter_map ( |dd| {
770+ match dd. kind {
771+ DropKind :: Value { cached_block : None } =>
772+ span_bug ! ( drop_data. span, "cached block not present?" ) ,
773+ DropKind :: Value { cached_block } => cached_block,
774+ DropKind :: Storage => None
775+ }
776+ } ) . next ( ) ;
777+ // If there’s no `cached_block`s within current scope,
778+ // we must look for one in the enclosing scope.
779+ let on_diverge = on_diverge. or_else ( || {
780+ earlier_scopes. iter ( ) . rev ( ) . flat_map ( |s| s. cached_block ( ) ) . next ( )
781+ } ) ;
782+ let next = cfg. start_new_block ( ) ;
783+ cfg. terminate ( block, source_info, TerminatorKind :: Drop {
784+ location : drop_data. location . clone ( ) ,
785+ target : next,
786+ unwind : on_diverge
787+ } ) ;
788+ block = next;
789+ }
790+ DropKind :: Storage => { }
791+ }
767792
793+ // Drop the storage for both value and storage drops.
794+ // Only temps and vars need their storage dead.
795+ match drop_data. location {
796+ Lvalue :: Local ( index) if index. index ( ) > arg_count => {
768797 cfg. push ( block, Statement {
769798 source_info : source_info,
770799 kind : StatementKind :: StorageDead ( drop_data. location . clone ( ) )
771800 } ) ;
772801 }
802+ _ => continue
773803 }
774804 }
775805 block. unit ( )
0 commit comments