|
| 1 | +//! Validates the MIR to ensure that invariants are upheld. |
| 2 | +
|
| 3 | +use super::{MirPass, MirSource}; |
| 4 | +use rustc_middle::mir::visit::Visitor; |
| 5 | +use rustc_middle::{ |
| 6 | + mir::{Body, Location, Operand, Rvalue, Statement, StatementKind}, |
| 7 | + ty::{ParamEnv, TyCtxt}, |
| 8 | +}; |
| 9 | +use rustc_span::{def_id::DefId, Span, DUMMY_SP}; |
| 10 | + |
| 11 | +pub struct Validator { |
| 12 | + /// Describes at which point in the pipeline this validation is happening. |
| 13 | + pub when: String, |
| 14 | +} |
| 15 | + |
| 16 | +impl<'tcx> MirPass<'tcx> for Validator { |
| 17 | + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { |
| 18 | + let def_id = source.def_id(); |
| 19 | + let param_env = tcx.param_env(def_id); |
| 20 | + TypeChecker { when: &self.when, def_id, body, tcx, param_env }.visit_body(body); |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | +struct TypeChecker<'a, 'tcx> { |
| 25 | + when: &'a str, |
| 26 | + def_id: DefId, |
| 27 | + body: &'a Body<'tcx>, |
| 28 | + tcx: TyCtxt<'tcx>, |
| 29 | + param_env: ParamEnv<'tcx>, |
| 30 | +} |
| 31 | + |
| 32 | +impl<'a, 'tcx> TypeChecker<'a, 'tcx> { |
| 33 | + fn fail(&self, span: Span, msg: impl AsRef<str>) { |
| 34 | + // We use `delay_span_bug` as we might see broken MIR when other errors have already |
| 35 | + // occurred. |
| 36 | + self.tcx.sess.diagnostic().delay_span_bug( |
| 37 | + span, |
| 38 | + &format!("broken MIR in {:?} ({}): {}", self.def_id, self.when, msg.as_ref()), |
| 39 | + ); |
| 40 | + } |
| 41 | +} |
| 42 | + |
| 43 | +impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { |
| 44 | + fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { |
| 45 | + // `Operand::Copy` is only supposed to be used with `Copy` types. |
| 46 | + if let Operand::Copy(place) = operand { |
| 47 | + let ty = place.ty(&self.body.local_decls, self.tcx).ty; |
| 48 | + |
| 49 | + if !ty.is_copy_modulo_regions(self.tcx, self.param_env, DUMMY_SP) { |
| 50 | + self.fail( |
| 51 | + DUMMY_SP, |
| 52 | + format!("`Operand::Copy` with non-`Copy` type {} at {:?}", ty, location), |
| 53 | + ); |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + self.super_operand(operand, location); |
| 58 | + } |
| 59 | + |
| 60 | + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { |
| 61 | + // The sides of an assignment must not alias. Currently this just checks whether the places |
| 62 | + // are identical. |
| 63 | + if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind { |
| 64 | + match rvalue { |
| 65 | + Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => { |
| 66 | + if dest == src { |
| 67 | + self.fail( |
| 68 | + DUMMY_SP, |
| 69 | + format!( |
| 70 | + "encountered `Assign` statement with overlapping memory at {:?}", |
| 71 | + location |
| 72 | + ), |
| 73 | + ); |
| 74 | + } |
| 75 | + } |
| 76 | + _ => {} |
| 77 | + } |
| 78 | + } |
| 79 | + } |
| 80 | +} |
0 commit comments