@@ -726,30 +726,79 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
726
726
///
727
727
/// Unlike other kinds of early exits, tail calls do not go through the drop tree.
728
728
/// Instead, all scheduled drops are immediately added to the CFG.
729
- pub ( crate ) fn break_for_tail_call ( & mut self , mut block : BasicBlock ) -> BlockAnd < ( ) > {
729
+ pub ( crate ) fn break_for_tail_call (
730
+ & mut self ,
731
+ mut block : BasicBlock ,
732
+ args : & [ Operand < ' tcx > ] ,
733
+ source_info : SourceInfo ,
734
+ ) -> BlockAnd < ( ) > {
735
+ let arg_drops: Vec < _ > = args
736
+ . iter ( )
737
+ . rev ( )
738
+ . filter_map ( |arg| match arg {
739
+ Operand :: Copy ( _) => bug ! ( "copy op in tail call args" ) ,
740
+ Operand :: Move ( place) => {
741
+ let local =
742
+ place. as_local ( ) . unwrap_or_else ( || bug ! ( "projection in tail call args" ) ) ;
743
+
744
+ Some ( DropData { source_info, local, kind : DropKind :: Value } )
745
+ }
746
+ Operand :: Constant ( _) => None ,
747
+ } )
748
+ . collect ( ) ;
749
+
750
+ let mut unwind_to = self . diverge_cleanup_target (
751
+ self . scopes . scopes . iter ( ) . rev ( ) . nth ( 1 ) . unwrap ( ) . region_scope ,
752
+ DUMMY_SP ,
753
+ ) ;
754
+ let unwind_drops = & mut self . scopes . unwind_drops ;
755
+
730
756
// the innermost scope contains only the destructors for the tail call arguments
731
757
// we only want to drop these in case of a panic, so we skip it
732
758
for scope in self . scopes . scopes [ 1 ..] . iter ( ) . rev ( ) . skip ( 1 ) {
733
- for drop in scope. drops . iter ( ) . rev ( ) {
734
- match drop. kind {
759
+ // FIXME(explicit_tail_calls) code duplication with `build_scope_drops`
760
+ for drop_data in scope. drops . iter ( ) . rev ( ) {
761
+ let source_info = drop_data. source_info ;
762
+ let local = drop_data. local ;
763
+
764
+ match drop_data. kind {
735
765
DropKind :: Value => {
736
- let target = self . cfg . start_new_block ( ) ;
737
- let terminator = TerminatorKind :: Drop {
738
- target,
739
- // The caller will handle this if needed.
740
- unwind : UnwindAction :: Terminate ,
741
- place : drop. local . into ( ) ,
742
- replace : false ,
743
- } ;
744
- self . cfg . terminate ( block, drop. source_info , terminator) ;
745
- block = target;
766
+ // `unwind_to` should drop the value that we're about to
767
+ // schedule. If dropping this value panics, then we continue
768
+ // with the *next* value on the unwind path.
769
+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . 0 . local, drop_data. local) ;
770
+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . 0 . kind, drop_data. kind) ;
771
+ unwind_to = unwind_drops. drops [ unwind_to] . 1 ;
772
+
773
+ let mut unwind_entry_point = unwind_to;
774
+
775
+ // the tail call arguments must be dropped if any of these drops panic
776
+ for drop in arg_drops. iter ( ) . copied ( ) {
777
+ unwind_entry_point = unwind_drops. add_drop ( drop, unwind_entry_point) ;
778
+ }
779
+
780
+ unwind_drops. add_entry ( block, unwind_entry_point) ;
781
+
782
+ let next = self . cfg . start_new_block ( ) ;
783
+ self . cfg . terminate (
784
+ block,
785
+ source_info,
786
+ TerminatorKind :: Drop {
787
+ place : local. into ( ) ,
788
+ target : next,
789
+ unwind : UnwindAction :: Continue ,
790
+ replace : false ,
791
+ } ,
792
+ ) ;
793
+ block = next;
746
794
}
747
795
DropKind :: Storage => {
748
- let stmt = Statement {
749
- source_info : drop. source_info ,
750
- kind : StatementKind :: StorageDead ( drop. local ) ,
751
- } ;
752
- self . cfg . push ( block, stmt) ;
796
+ // Only temps and vars need their storage dead.
797
+ assert ! ( local. index( ) > self . arg_count) ;
798
+ self . cfg . push (
799
+ block,
800
+ Statement { source_info, kind : StatementKind :: StorageDead ( local) } ,
801
+ ) ;
753
802
}
754
803
}
755
804
}
0 commit comments