@@ -18,6 +18,7 @@ use rustc_mir_dataflow::impls::MaybeStorageLive;
1818use rustc_mir_dataflow:: storage:: always_storage_live_locals;
1919use rustc_mir_dataflow:: { Analysis , ResultsCursor } ;
2020use rustc_target:: abi:: { Size , FIRST_VARIANT } ;
21+ use rustc_target:: spec:: abi:: Abi ;
2122
2223#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
2324enum EdgeKind {
@@ -58,6 +59,25 @@ impl<'tcx> MirPass<'tcx> for Validator {
5859 . iterate_to_fixpoint ( )
5960 . into_results_cursor ( body) ;
6061
62+ let can_unwind = if mir_phase <= MirPhase :: Runtime ( RuntimePhase :: Initial ) {
63+ // In this case `AbortUnwindingCalls` haven't yet been executed.
64+ true
65+ } else if !tcx. def_kind ( def_id) . is_fn_like ( ) {
66+ true
67+ } else {
68+ let body_ty = tcx. type_of ( def_id) . skip_binder ( ) ;
69+ let body_abi = match body_ty. kind ( ) {
70+ ty:: FnDef ( ..) => body_ty. fn_sig ( tcx) . abi ( ) ,
71+ ty:: Closure ( ..) => Abi :: RustCall ,
72+ ty:: Generator ( ..) => Abi :: Rust ,
73+ _ => {
74+ span_bug ! ( body. span, "unexpected body ty: {:?} phase {:?}" , body_ty, mir_phase)
75+ }
76+ } ;
77+
78+ ty:: layout:: fn_can_unwind ( tcx, Some ( def_id) , body_abi)
79+ } ;
80+
6181 let mut cfg_checker = CfgChecker {
6282 when : & self . when ,
6383 body,
@@ -68,6 +88,7 @@ impl<'tcx> MirPass<'tcx> for Validator {
6888 storage_liveness,
6989 place_cache : FxHashSet :: default ( ) ,
7090 value_cache : FxHashSet :: default ( ) ,
91+ can_unwind,
7192 } ;
7293 cfg_checker. visit_body ( body) ;
7394 cfg_checker. check_cleanup_control_flow ( ) ;
@@ -99,6 +120,9 @@ struct CfgChecker<'a, 'tcx> {
99120 storage_liveness : ResultsCursor < ' a , ' tcx , MaybeStorageLive < ' static > > ,
100121 place_cache : FxHashSet < PlaceRef < ' tcx > > ,
101122 value_cache : FxHashSet < u128 > ,
123+ // If `false`, then the MIR must not contain `UnwindAction::Continue` or
124+ // `TerminatorKind::Resume`.
125+ can_unwind : bool ,
102126}
103127
104128impl < ' a , ' tcx > CfgChecker < ' a , ' tcx > {
@@ -237,13 +261,17 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
237261 match unwind {
238262 UnwindAction :: Cleanup ( unwind) => {
239263 if is_cleanup {
240- self . fail ( location, "unwind on cleanup block" ) ;
264+ self . fail ( location, "`UnwindAction::Cleanup` in cleanup block" ) ;
241265 }
242266 self . check_edge ( location, unwind, EdgeKind :: Unwind ) ;
243267 }
244268 UnwindAction :: Continue => {
245269 if is_cleanup {
246- self . fail ( location, "unwind on cleanup block" ) ;
270+ self . fail ( location, "`UnwindAction::Continue` in cleanup block" ) ;
271+ }
272+
273+ if !self . can_unwind {
274+ self . fail ( location, "`UnwindAction::Continue` in no-unwind function" ) ;
247275 }
248276 }
249277 UnwindAction :: Unreachable | UnwindAction :: Terminate => ( ) ,
@@ -464,13 +492,19 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
464492 ) ;
465493 }
466494 }
467- TerminatorKind :: Resume | TerminatorKind :: Terminate => {
495+ TerminatorKind :: Resume => {
468496 let bb = location. block ;
469497 if !self . body . basic_blocks [ bb] . is_cleanup {
470- self . fail (
471- location,
472- "Cannot `Resume` or `Terminate` from non-cleanup basic block" ,
473- )
498+ self . fail ( location, "Cannot `Resume` from non-cleanup basic block" )
499+ }
500+ if !self . can_unwind {
501+ self . fail ( location, "Cannot `Resume` in a function that cannot unwind" )
502+ }
503+ }
504+ TerminatorKind :: Terminate => {
505+ let bb = location. block ;
506+ if !self . body . basic_blocks [ bb] . is_cleanup {
507+ self . fail ( location, "Cannot `Terminate` from non-cleanup basic block" )
474508 }
475509 }
476510 TerminatorKind :: Return => {
0 commit comments