@@ -285,6 +285,12 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
285
285
UnwindAction :: Unreachable | UnwindAction :: Terminate ( UnwindTerminateReason :: Abi ) => ( ) ,
286
286
}
287
287
}
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
+ }
288
294
}
289
295
290
296
impl < ' a , ' tcx > Visitor < ' tcx > for CfgChecker < ' a , ' tcx > {
@@ -425,6 +431,22 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
425
431
}
426
432
self . check_unwind_edge ( location, * unwind) ;
427
433
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
+
428
450
// The call destination place and Operand::Move place used as an argument might be
429
451
// passed by a reference to the callee. Consequently they must be non-overlapping
430
452
// and cannot be packed. Currently this simply checks for duplicate places.
0 commit comments