Skip to content

Commit 56b9337

Browse files
committed
Add MIR validation for unwind out from nounwind functions
1 parent cec8e09 commit 56b9337

File tree

1 file changed

+41
-7
lines changed

1 file changed

+41
-7
lines changed

compiler/rustc_const_eval/src/transform/validate.rs

+41-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use rustc_mir_dataflow::impls::MaybeStorageLive;
1818
use rustc_mir_dataflow::storage::always_storage_live_locals;
1919
use rustc_mir_dataflow::{Analysis, ResultsCursor};
2020
use rustc_target::abi::{Size, FIRST_VARIANT};
21+
use rustc_target::spec::abi::Abi;
2122

2223
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2324
enum 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

104128
impl<'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

Comments
 (0)