mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Add a small MIR validation pass
This commit is contained in:
parent
4ca626258a
commit
e04318e0fa
@ -511,6 +511,7 @@ fn test_debugging_options_tracking_hash() {
|
||||
untracked!(ui_testing, true);
|
||||
untracked!(unpretty, Some("expanded".to_string()));
|
||||
untracked!(unstable_options, true);
|
||||
untracked!(validate_mir, true);
|
||||
untracked!(verbose, true);
|
||||
|
||||
macro_rules! tracked {
|
||||
|
@ -39,6 +39,7 @@ pub mod simplify_branches;
|
||||
pub mod simplify_try;
|
||||
pub mod uninhabited_enum_branching;
|
||||
pub mod unreachable_prop;
|
||||
pub mod validate;
|
||||
|
||||
pub(crate) fn provide(providers: &mut Providers<'_>) {
|
||||
self::check_unsafety::provide(providers);
|
||||
@ -147,12 +148,18 @@ pub fn run_passes(
|
||||
passes: &[&[&dyn MirPass<'tcx>]],
|
||||
) {
|
||||
let phase_index = mir_phase.phase_index();
|
||||
let source = MirSource { instance, promoted };
|
||||
let validate = tcx.sess.opts.debugging_opts.validate_mir;
|
||||
|
||||
if body.phase >= mir_phase {
|
||||
return;
|
||||
}
|
||||
|
||||
let source = MirSource { instance, promoted };
|
||||
if validate {
|
||||
validate::Validator { when: format!("input to phase {:?}", mir_phase) }
|
||||
.run_pass(tcx, source, body);
|
||||
}
|
||||
|
||||
let mut index = 0;
|
||||
let mut run_pass = |pass: &dyn MirPass<'tcx>| {
|
||||
let run_hooks = |body: &_, index, is_after| {
|
||||
@ -169,6 +176,11 @@ pub fn run_passes(
|
||||
pass.run_pass(tcx, source, body);
|
||||
run_hooks(body, index, true);
|
||||
|
||||
if validate {
|
||||
validate::Validator { when: format!("after {} in phase {:?}", pass.name(), mir_phase) }
|
||||
.run_pass(tcx, source, body);
|
||||
}
|
||||
|
||||
index += 1;
|
||||
};
|
||||
|
||||
|
80
src/librustc_mir/transform/validate.rs
Normal file
80
src/librustc_mir/transform/validate.rs
Normal file
@ -0,0 +1,80 @@
|
||||
//! Validates the MIR to ensure that invariants are upheld.
|
||||
|
||||
use super::{MirPass, MirSource};
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::{
|
||||
mir::{Body, Location, Operand, Rvalue, Statement, StatementKind},
|
||||
ty::{ParamEnv, TyCtxt},
|
||||
};
|
||||
use rustc_span::{def_id::DefId, Span, DUMMY_SP};
|
||||
|
||||
pub struct Validator {
|
||||
/// Describes at which point in the pipeline this validation is happening.
|
||||
pub when: String,
|
||||
}
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for Validator {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
|
||||
let def_id = source.def_id();
|
||||
let param_env = tcx.param_env(def_id);
|
||||
TypeChecker { when: &self.when, def_id, body, tcx, param_env }.visit_body(body);
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeChecker<'a, 'tcx> {
|
||||
when: &'a str,
|
||||
def_id: DefId,
|
||||
body: &'a Body<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
fn fail(&self, span: Span, msg: impl AsRef<str>) {
|
||||
// We use `delay_span_bug` as we might see broken MIR when other errors have already
|
||||
// occurred.
|
||||
self.tcx.sess.diagnostic().delay_span_bug(
|
||||
span,
|
||||
&format!("broken MIR in {:?} ({}): {}", self.def_id, self.when, msg.as_ref()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
|
||||
// `Operand::Copy` is only supposed to be used with `Copy` types.
|
||||
if let Operand::Copy(place) = operand {
|
||||
let ty = place.ty(&self.body.local_decls, self.tcx).ty;
|
||||
|
||||
if !ty.is_copy_modulo_regions(self.tcx, self.param_env, DUMMY_SP) {
|
||||
self.fail(
|
||||
DUMMY_SP,
|
||||
format!("`Operand::Copy` with non-`Copy` type {} at {:?}", ty, location),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.super_operand(operand, location);
|
||||
}
|
||||
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||
// The sides of an assignment must not alias. Currently this just checks whether the places
|
||||
// are identical.
|
||||
if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
|
||||
match rvalue {
|
||||
Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
|
||||
if dest == src {
|
||||
self.fail(
|
||||
DUMMY_SP,
|
||||
format!(
|
||||
"encountered `Assign` statement with overlapping memory at {:?}",
|
||||
location
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1045,6 +1045,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
||||
"adds unstable command line options to rustc interface (default: no)"),
|
||||
use_ctors_section: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||
"use legacy .ctors section for initializers rather than .init_array"),
|
||||
validate_mir: bool = (false, parse_bool, [UNTRACKED],
|
||||
"validate MIR after each transformation"),
|
||||
verbose: bool = (false, parse_bool, [UNTRACKED],
|
||||
"in general, enable more debug printouts (default: no)"),
|
||||
verify_llvm_ir: bool = (false, parse_bool, [TRACKED],
|
||||
|
Loading…
Reference in New Issue
Block a user