@@ -988,18 +988,101 @@ fn insert_panic_block<'tcx>(
988
988
assert_block
989
989
}
990
990
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
+
991
1047
fn create_generator_resume_function < ' tcx > (
992
1048
tcx : TyCtxt < ' tcx > ,
993
1049
transform : TransformVisitor < ' tcx > ,
994
1050
def_id : DefId ,
995
1051
source : MirSource < ' tcx > ,
996
1052
body : & mut BodyAndCache < ' tcx > ,
1053
+ can_return : bool ,
997
1054
) {
1055
+ let can_unwind = can_unwind ( tcx, body) ;
1056
+
998
1057
// 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
+ }
1003
1086
}
1004
1087
}
1005
1088
@@ -1012,8 +1095,20 @@ fn create_generator_resume_function<'tcx>(
1012
1095
1013
1096
// Panic when resumed on the returned or poisoned state
1014
1097
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
+ }
1017
1112
1018
1113
insert_switch ( body, cases, & transform, TerminatorKind :: Unreachable ) ;
1019
1114
@@ -1197,6 +1292,8 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
1197
1292
let ( remap, layout, storage_liveness) =
1198
1293
compute_layout ( tcx, source, & upvars, interior, movable, body) ;
1199
1294
1295
+ let can_return = can_return ( tcx, body) ;
1296
+
1200
1297
// Run the transformation which converts Places from Local to generator struct
1201
1298
// accesses for locals in `remap`.
1202
1299
// 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 {
1240
1337
body. generator_drop = Some ( box drop_shim) ;
1241
1338
1242
1339
// 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 ) ;
1244
1341
}
1245
1342
}
0 commit comments