Skip to content

Commit

Permalink
Add MIR validation for unwind out from nounwind functions
Browse files Browse the repository at this point in the history
  • Loading branch information
nbdd0121 committed Aug 18, 2023
1 parent cec8e09 commit 56b9337
Showing 1 changed file with 41 additions and 7 deletions.
48 changes: 41 additions & 7 deletions compiler/rustc_const_eval/src/transform/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use rustc_mir_dataflow::impls::MaybeStorageLive;
use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_mir_dataflow::{Analysis, ResultsCursor};
use rustc_target::abi::{Size, FIRST_VARIANT};
use rustc_target::spec::abi::Abi;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum EdgeKind {
Expand Down Expand Up @@ -58,6 +59,25 @@ impl<'tcx> MirPass<'tcx> for Validator {
.iterate_to_fixpoint()
.into_results_cursor(body);

let can_unwind = if mir_phase <= MirPhase::Runtime(RuntimePhase::Initial) {
// In this case `AbortUnwindingCalls` haven't yet been executed.
true
} else if !tcx.def_kind(def_id).is_fn_like() {
true
} else {
let body_ty = tcx.type_of(def_id).skip_binder();
let body_abi = match body_ty.kind() {
ty::FnDef(..) => body_ty.fn_sig(tcx).abi(),
ty::Closure(..) => Abi::RustCall,
ty::Generator(..) => Abi::Rust,
_ => {
span_bug!(body.span, "unexpected body ty: {:?} phase {:?}", body_ty, mir_phase)
}
};

ty::layout::fn_can_unwind(tcx, Some(def_id), body_abi)
};

let mut cfg_checker = CfgChecker {
when: &self.when,
body,
Expand All @@ -68,6 +88,7 @@ impl<'tcx> MirPass<'tcx> for Validator {
storage_liveness,
place_cache: FxHashSet::default(),
value_cache: FxHashSet::default(),
can_unwind,
};
cfg_checker.visit_body(body);
cfg_checker.check_cleanup_control_flow();
Expand Down Expand Up @@ -99,6 +120,9 @@ struct CfgChecker<'a, 'tcx> {
storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>,
place_cache: FxHashSet<PlaceRef<'tcx>>,
value_cache: FxHashSet<u128>,
// If `false`, then the MIR must not contain `UnwindAction::Continue` or
// `TerminatorKind::Resume`.
can_unwind: bool,
}

impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
Expand Down Expand Up @@ -237,13 +261,17 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
match unwind {
UnwindAction::Cleanup(unwind) => {
if is_cleanup {
self.fail(location, "unwind on cleanup block");
self.fail(location, "`UnwindAction::Cleanup` in cleanup block");
}
self.check_edge(location, unwind, EdgeKind::Unwind);
}
UnwindAction::Continue => {
if is_cleanup {
self.fail(location, "unwind on cleanup block");
self.fail(location, "`UnwindAction::Continue` in cleanup block");
}

if !self.can_unwind {
self.fail(location, "`UnwindAction::Continue` in no-unwind function");
}
}
UnwindAction::Unreachable | UnwindAction::Terminate => (),
Expand Down Expand Up @@ -464,13 +492,19 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
);
}
}
TerminatorKind::Resume | TerminatorKind::Terminate => {
TerminatorKind::Resume => {
let bb = location.block;
if !self.body.basic_blocks[bb].is_cleanup {
self.fail(
location,
"Cannot `Resume` or `Terminate` from non-cleanup basic block",
)
self.fail(location, "Cannot `Resume` from non-cleanup basic block")
}
if !self.can_unwind {
self.fail(location, "Cannot `Resume` in a function that cannot unwind")
}
}
TerminatorKind::Terminate => {
let bb = location.block;
if !self.body.basic_blocks[bb].is_cleanup {
self.fail(location, "Cannot `Terminate` from non-cleanup basic block")
}
}
TerminatorKind::Return => {
Expand Down

0 comments on commit 56b9337

Please sign in to comment.