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