@@ -285,6 +285,12 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
285285 UnwindAction :: Unreachable | UnwindAction :: Terminate ( UnwindTerminateReason :: Abi ) => ( ) ,
286286 }
287287 }
288+
289+ fn is_critical_call_edge ( & self , target : Option < BasicBlock > , unwind : UnwindAction ) -> bool {
290+ let Some ( target) = target else { return false } ;
291+ matches ! ( unwind, UnwindAction :: Cleanup ( _) | UnwindAction :: Terminate ( _) )
292+ && self . body . basic_blocks . predecessors ( ) [ target] . len ( ) > 1
293+ }
288294}
289295
290296impl < ' a , ' tcx > Visitor < ' tcx > for CfgChecker < ' a , ' tcx > {
@@ -425,6 +431,22 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
425431 }
426432 self . check_unwind_edge ( location, * unwind) ;
427433
434+ // The code generation assumes that there are no critical call edges. The assumption
435+ // is used to simplify inserting code that should be executed along the return edge
436+ // from the call. FIXME(tmiasko): Since this is a strictly code generation concern,
437+ // the code generation should be responsible for handling it.
438+ if self . mir_phase >= MirPhase :: Runtime ( RuntimePhase :: Optimized )
439+ && self . is_critical_call_edge ( * target, * unwind)
440+ {
441+ self . fail (
442+ location,
443+ format ! (
444+ "encountered critical edge in `Call` terminator {:?}" ,
445+ terminator. kind,
446+ ) ,
447+ ) ;
448+ }
449+
428450 // The call destination place and Operand::Move place used as an argument might be
429451 // passed by a reference to the callee. Consequently they must be non-overlapping
430452 // and cannot be packed. Currently this simply checks for duplicate places.
0 commit comments