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