@@ -988,18 +988,101 @@ fn insert_panic_block<'tcx>(
988988 assert_block
989989}
990990
991+ fn can_return < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > ) -> bool {
992+ // Returning from a function with an uninhabited return type is undefined behavior.
993+ if body. return_ty ( ) . conservative_is_privately_uninhabited ( tcx) {
994+ return false ;
995+ }
996+
997+ // If there's a return terminator the function may return.
998+ for block in body. basic_blocks ( ) {
999+ if let TerminatorKind :: Return = block. terminator ( ) . kind {
1000+ return true ;
1001+ }
1002+ }
1003+
1004+ // Otherwise the function can't return.
1005+ false
1006+ }
1007+
1008+ fn can_unwind < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > ) -> bool {
1009+ // Nothing can unwind when landing pads are off.
1010+ if tcx. sess . no_landing_pads ( ) {
1011+ return false ;
1012+ }
1013+
1014+ // Unwinds can only start at certain terminators.
1015+ for block in body. basic_blocks ( ) {
1016+ match block. terminator ( ) . kind {
1017+ // These never unwind.
1018+ TerminatorKind :: Goto { .. }
1019+ | TerminatorKind :: SwitchInt { .. }
1020+ | TerminatorKind :: Abort
1021+ | TerminatorKind :: Return
1022+ | TerminatorKind :: Unreachable
1023+ | TerminatorKind :: GeneratorDrop
1024+ | TerminatorKind :: FalseEdges { .. }
1025+ | TerminatorKind :: FalseUnwind { .. } => { }
1026+
1027+ // Resume will *continue* unwinding, but if there's no other unwinding terminator it
1028+ // will never be reached.
1029+ TerminatorKind :: Resume => { }
1030+
1031+ TerminatorKind :: Yield { .. } => {
1032+ unreachable ! ( "`can_unwind` called before generator transform" )
1033+ }
1034+
1035+ // These may unwind.
1036+ TerminatorKind :: Drop { .. }
1037+ | TerminatorKind :: DropAndReplace { .. }
1038+ | TerminatorKind :: Call { .. }
1039+ | TerminatorKind :: Assert { .. } => return true ,
1040+ }
1041+ }
1042+
1043+ // If we didn't find an unwinding terminator, the function cannot unwind.
1044+ false
1045+ }
1046+
9911047fn create_generator_resume_function < ' tcx > (
9921048 tcx : TyCtxt < ' tcx > ,
9931049 transform : TransformVisitor < ' tcx > ,
9941050 def_id : DefId ,
9951051 source : MirSource < ' tcx > ,
9961052 body : & mut BodyAndCache < ' tcx > ,
1053+ can_return : bool ,
9971054) {
1055+ let can_unwind = can_unwind ( tcx, body) ;
1056+
9981057 // Poison the generator when it unwinds
999- for block in body. basic_blocks_mut ( ) {
1000- let source_info = block. terminator ( ) . source_info ;
1001- if let & TerminatorKind :: Resume = & block. terminator ( ) . kind {
1002- block. statements . push ( transform. set_discr ( VariantIdx :: new ( POISONED ) , source_info) ) ;
1058+ if can_unwind {
1059+ let poison_block = BasicBlock :: new ( body. basic_blocks ( ) . len ( ) ) ;
1060+ let source_info = source_info ( body) ;
1061+ body. basic_blocks_mut ( ) . push ( BasicBlockData {
1062+ statements : vec ! [ transform. set_discr( VariantIdx :: new( POISONED ) , source_info) ] ,
1063+ terminator : Some ( Terminator { source_info, kind : TerminatorKind :: Resume } ) ,
1064+ is_cleanup : true ,
1065+ } ) ;
1066+
1067+ for ( idx, block) in body. basic_blocks_mut ( ) . iter_enumerated_mut ( ) {
1068+ let source_info = block. terminator ( ) . source_info ;
1069+
1070+ if let TerminatorKind :: Resume = block. terminator ( ) . kind {
1071+ // An existing `Resume` terminator is redirected to jump to our dedicated
1072+ // "poisoning block" above.
1073+ if idx != poison_block {
1074+ * block. terminator_mut ( ) = Terminator {
1075+ source_info,
1076+ kind : TerminatorKind :: Goto { target : poison_block } ,
1077+ } ;
1078+ }
1079+ } else if !block. is_cleanup {
1080+ // Any terminators that *can* unwind but don't have an unwind target set are also
1081+ // pointed at our poisoning block (unless they're part of the cleanup path).
1082+ if let Some ( unwind @ None ) = block. terminator_mut ( ) . unwind_mut ( ) {
1083+ * unwind = Some ( poison_block) ;
1084+ }
1085+ }
10031086 }
10041087 }
10051088
@@ -1012,8 +1095,20 @@ fn create_generator_resume_function<'tcx>(
10121095
10131096 // Panic when resumed on the returned or poisoned state
10141097 let generator_kind = body. generator_kind . unwrap ( ) ;
1015- cases. insert ( 1 , ( RETURNED , insert_panic_block ( tcx, body, ResumedAfterReturn ( generator_kind) ) ) ) ;
1016- cases. insert ( 2 , ( POISONED , insert_panic_block ( tcx, body, ResumedAfterPanic ( generator_kind) ) ) ) ;
1098+
1099+ if can_unwind {
1100+ cases. insert (
1101+ 1 ,
1102+ ( POISONED , insert_panic_block ( tcx, body, ResumedAfterPanic ( generator_kind) ) ) ,
1103+ ) ;
1104+ }
1105+
1106+ if can_return {
1107+ cases. insert (
1108+ 1 ,
1109+ ( RETURNED , insert_panic_block ( tcx, body, ResumedAfterReturn ( generator_kind) ) ) ,
1110+ ) ;
1111+ }
10171112
10181113 insert_switch ( body, cases, & transform, TerminatorKind :: Unreachable ) ;
10191114
@@ -1197,6 +1292,8 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
11971292 let ( remap, layout, storage_liveness) =
11981293 compute_layout ( tcx, source, & upvars, interior, movable, body) ;
11991294
1295+ let can_return = can_return ( tcx, body) ;
1296+
12001297 // Run the transformation which converts Places from Local to generator struct
12011298 // accesses for locals in `remap`.
12021299 // It also rewrites `return x` and `yield y` as writing a new generator state and returning
@@ -1240,6 +1337,6 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
12401337 body. generator_drop = Some ( box drop_shim) ;
12411338
12421339 // Create the Generator::resume function
1243- create_generator_resume_function ( tcx, transform, def_id, source, body) ;
1340+ create_generator_resume_function ( tcx, transform, def_id, source, body, can_return ) ;
12441341 }
12451342}
0 commit comments