@@ -60,6 +60,9 @@ pub struct Frame<'mir, 'tcx, Tag=(), Extra=()> {
60
60
/// The span of the call site.
61
61
pub span : source_map:: Span ,
62
62
63
+ /// Extra data for the machine.
64
+ pub extra : Extra ,
65
+
63
66
////////////////////////////////////////////////////////////////////////////////
64
67
// Return place and locals
65
68
////////////////////////////////////////////////////////////////////////////////
@@ -82,13 +85,12 @@ pub struct Frame<'mir, 'tcx, Tag=(), Extra=()> {
82
85
////////////////////////////////////////////////////////////////////////////////
83
86
/// The block that is currently executed (or will be executed after the above call stacks
84
87
/// return).
85
- pub block : mir:: BasicBlock ,
88
+ /// If this is `None`, we are unwinding and this function doesn't need any clean-up.
89
+ /// Just continue the same as with
90
+ pub block : Option < mir:: BasicBlock > ,
86
91
87
92
/// The index of the currently evaluated statement.
88
93
pub stmt : usize ,
89
-
90
- /// Extra data for the machine.
91
- pub extra : Extra ,
92
94
}
93
95
94
96
#[ derive( Clone , Eq , PartialEq , Debug ) ] // Miri debug-prints these
@@ -491,7 +493,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
491
493
let extra = M :: stack_push ( self ) ?;
492
494
self . stack . push ( Frame {
493
495
body,
494
- block : mir:: START_BLOCK ,
496
+ block : Some ( mir:: START_BLOCK ) ,
495
497
return_to_block,
496
498
return_place,
497
499
// empty local array, we fill it in below, after we are inside the stack frame and
@@ -549,51 +551,75 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
549
551
}
550
552
}
551
553
552
- pub ( super ) fn pop_stack_frame_internal (
554
+ pub ( super ) fn pop_stack_frame (
553
555
& mut self ,
554
556
unwinding : bool
555
- ) -> InterpResult < ' tcx , ( StackPopCleanup , StackPopInfo ) > {
557
+ ) -> InterpResult < ' tcx > {
556
558
info ! ( "LEAVING({}) {} (unwinding = {})" ,
557
559
self . cur_frame( ) , self . frame( ) . instance, unwinding) ;
558
560
561
+ // Sanity check `unwinding`.
562
+ assert_eq ! (
563
+ unwinding,
564
+ match self . frame( ) . block {
565
+ None => true ,
566
+ Some ( block) => self . body( ) . basic_blocks( ) [ block] . is_cleanup
567
+ }
568
+ ) ;
569
+
559
570
:: log_settings:: settings ( ) . indentation -= 1 ;
560
571
let frame = self . stack . pop ( ) . expect (
561
572
"tried to pop a stack frame, but there were none" ,
562
573
) ;
563
574
let stack_pop_info = M :: stack_pop ( self , frame. extra ) ?;
575
+ match ( unwinding, stack_pop_info) {
576
+ ( true , StackPopInfo :: StartUnwinding ) =>
577
+ bug ! ( "Attempted to start unwinding while already unwinding!" ) ,
578
+ ( false , StackPopInfo :: StopUnwinding ) =>
579
+ bug ! ( "Attempted to stop unwinding while there is no unwinding!" ) ,
580
+ _ => { }
581
+ }
564
582
565
- // Abort early if we do not want to clean up: We also avoid validation in that case,
583
+ // Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
584
+ // In that case, we return early. We also avoid validation in that case,
566
585
// because this is CTFE and the final value will be thoroughly validated anyway.
567
- match frame. return_to_block {
568
- StackPopCleanup :: Goto { .. } => { } ,
586
+ let cleanup = unwinding || match frame. return_to_block {
587
+ StackPopCleanup :: Goto { .. } => true ,
569
588
StackPopCleanup :: None { cleanup, .. } => {
570
- assert ! ( !unwinding, "Encountered StackPopCleanup::None while unwinding" ) ;
571
-
572
- if !cleanup {
573
- assert ! ( self . stack. is_empty( ) , "only the topmost frame should ever be leaked" ) ;
574
- // Leak the locals, skip validation.
575
- return Ok ( ( frame. return_to_block , stack_pop_info) ) ;
576
- }
589
+ cleanup
577
590
}
591
+ } ;
592
+ if !cleanup {
593
+ assert ! ( self . stack. is_empty( ) , "only the topmost frame should ever be leaked" ) ;
594
+ // Leak the locals, skip validation.
595
+ return Ok ( ( ) ) ;
578
596
}
579
- // Deallocate all locals that are backed by an allocation.
597
+
598
+ // Cleanup: deallocate all locals that are backed by an allocation.
580
599
for local in frame. locals {
581
600
self . deallocate_local ( local. value ) ?;
582
601
}
583
602
584
- // If we're popping frames due to unwinding, and we didn't just exit
585
- // unwinding, we skip a bunch of validation and cleanup logic (including
586
- // jumping to the regular return block specified in the StackPopCleanup)
587
- let cur_unwinding = unwinding && stack_pop_info != StackPopInfo :: StopUnwinding ;
603
+ // Now where do we jump next?
588
604
589
- info ! ( "StackPopCleanup: {:?} StackPopInfo: {:?} cur_unwinding = {:?}" ,
605
+ // Determine if we leave this function normally or via unwinding.
606
+ let cur_unwinding = unwinding && stack_pop_info != StackPopInfo :: StopUnwinding ;
607
+ trace ! ( "StackPopCleanup: {:?} StackPopInfo: {:?} cur_unwinding = {:?}" ,
590
608
frame. return_to_block, stack_pop_info, cur_unwinding) ;
591
-
592
-
593
- // When we're popping a stack frame for unwinding purposes,
594
- // we don't care at all about returning-related stuff (i.e. return_place
595
- // and return_to_block), because we're not performing a return from this frame.
596
- if !cur_unwinding {
609
+ if cur_unwinding {
610
+ // Follow the unwind edge.
611
+ match frame. return_to_block {
612
+ StackPopCleanup :: Goto { unwind, .. } => {
613
+ let next_frame = self . frame_mut ( ) ;
614
+ // If `unwind` is `None`, we'll leave that function immediately again.
615
+ next_frame. block = unwind;
616
+ next_frame. stmt = 0 ;
617
+ } ,
618
+ StackPopCleanup :: None { .. } =>
619
+ bug ! ( "Encountered StackPopCleanup::None while unwinding" ) ,
620
+ }
621
+ } else {
622
+ // Follow the normal return edge.
597
623
// Validate the return value. Do this after deallocating so that we catch dangling
598
624
// references.
599
625
if let Some ( return_place) = frame. return_place {
@@ -625,70 +651,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
625
651
}
626
652
}
627
653
628
-
629
- Ok ( ( frame. return_to_block , stack_pop_info) )
630
- }
631
-
632
- pub ( super ) fn pop_stack_frame ( & mut self , unwinding : bool ) -> InterpResult < ' tcx > {
633
- let ( mut cleanup, mut stack_pop_info) = self . pop_stack_frame_internal ( unwinding) ?;
634
-
635
- // There are two cases where we want to unwind the stack:
636
- // * The caller explicitly told us (i.e. we hit a Resume terminator)
637
- // * The machine indicated that we've just started unwinding (i.e.
638
- // a panic has just occured)
639
- if unwinding || stack_pop_info == StackPopInfo :: StartUnwinding {
640
- trace ! ( "unwinding: starting stack unwind..." ) ;
641
- // Overwrite our current stack_pop_info, so that the check
642
- // below doesn't fail.
643
- stack_pop_info = StackPopInfo :: Normal ;
644
- // There are three posible ways that we can exit the loop:
645
- // 1) We find an unwind block - we jump to it to allow cleanup
646
- // to occur for that frame
647
- // 2) pop_stack_frame_internal reports that we're no longer unwinding
648
- // - this means that the panic has been caught, and that execution
649
- // should continue as normal
650
- // 3) We pop all of our frames off the stack - this should never happen.
651
- while !self . stack . is_empty ( ) {
652
- match stack_pop_info {
653
- // We tried to start unwinding while we were already
654
- // unwinding. Note that this **is not** the same thing
655
- // as a double panic, which will be intercepted by
656
- // libcore/libstd before we actually attempt to unwind.
657
- StackPopInfo :: StartUnwinding => {
658
- throw_ub_format ! ( "Attempted to start unwinding while already unwinding!" ) ;
659
- } ,
660
- StackPopInfo :: StopUnwinding => {
661
- trace ! ( "unwinding: no longer unwinding!" ) ;
662
- break ;
663
- }
664
- StackPopInfo :: Normal => { }
665
- }
666
-
667
- match cleanup {
668
- StackPopCleanup :: Goto { unwind, .. } if unwind. is_some ( ) => {
669
-
670
- info ! ( "unwind: found cleanup block {:?}" , unwind) ;
671
- self . goto_block ( unwind) ?;
672
- break ;
673
- } ,
674
- _ => { }
675
- }
676
-
677
- info ! ( "unwinding: popping frame!" ) ;
678
- let res = self . pop_stack_frame_internal ( true ) ?;
679
- cleanup = res. 0 ;
680
- stack_pop_info = res. 1 ;
681
- }
682
- if self . stack . is_empty ( ) {
683
- // We should never get here:
684
- // The 'start_fn' lang item should always install a panic handler
685
- throw_ub ! ( Unreachable ) ;
686
- }
687
-
688
- }
689
-
690
654
if self . stack . len ( ) > 0 {
691
- info ! ( "CONTINUING({}) {}" , self . cur_frame( ) , self . frame( ) . instance) ;
655
+ info ! ( "CONTINUING({}) {} (unwinding = {})" ,
656
+ self . cur_frame( ) , self . frame( ) . instance, cur_unwinding) ;
692
657
}
693
658
694
659
Ok ( ( ) )
@@ -833,16 +798,20 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
833
798
} else {
834
799
last_span = Some ( span) ;
835
800
}
836
- let block = & body. basic_blocks ( ) [ block] ;
837
- let source_info = if stmt < block. statements . len ( ) {
838
- block. statements [ stmt] . source_info
839
- } else {
840
- block. terminator ( ) . source_info
841
- } ;
842
- let lint_root = match body. source_scope_local_data {
843
- mir:: ClearCrossCrate :: Set ( ref ivs) => Some ( ivs[ source_info. scope ] . lint_root ) ,
844
- mir:: ClearCrossCrate :: Clear => None ,
845
- } ;
801
+
802
+ let lint_root = block. and_then ( |block| {
803
+ let block = & body. basic_blocks ( ) [ block] ;
804
+ let source_info = if stmt < block. statements . len ( ) {
805
+ block. statements [ stmt] . source_info
806
+ } else {
807
+ block. terminator ( ) . source_info
808
+ } ;
809
+ match body. source_scope_local_data {
810
+ mir:: ClearCrossCrate :: Set ( ref ivs) => Some ( ivs[ source_info. scope ] . lint_root ) ,
811
+ mir:: ClearCrossCrate :: Clear => None ,
812
+ }
813
+ } ) ;
814
+
846
815
frames. push ( FrameInfo { call_site : span, instance, lint_root } ) ;
847
816
}
848
817
trace ! ( "generate stacktrace: {:#?}, {:?}" , frames, explicit_span) ;
0 commit comments