diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index b048240ca8d..3db16a71bab 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -50,7 +50,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.go_to_block(target_block); } - Call { ref func, ref args, destination, ref cleanup, .. } => { + Call { + ref func, + ref args, + destination, + ref cleanup, + from_hir_call: _from_hir_call, + } => { let old_stack = self.frame_idx(); let old_loc = self.frame().loc; let func = self.eval_operand(func, None)?; diff --git a/src/librustc_mir/transform/validate.rs b/src/librustc_mir/transform/validate.rs index a25edd131ba..7d301b2f496 100644 --- a/src/librustc_mir/transform/validate.rs +++ b/src/librustc_mir/transform/validate.rs @@ -3,10 +3,13 @@ use super::{MirPass, MirSource}; use rustc_middle::mir::visit::Visitor; use rustc_middle::{ - mir::{Body, Location, Operand, Rvalue, Statement, StatementKind}, - ty::{ParamEnv, TyCtxt}, + mir::{ + BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator, + TerminatorKind, + }, + ty::{self, ParamEnv, TyCtxt}, }; -use rustc_span::{def_id::DefId, Span, DUMMY_SP}; +use rustc_span::def_id::DefId; pub struct Validator { /// Describes at which point in the pipeline this validation is happening. @@ -30,14 +33,27 @@ struct TypeChecker<'a, 'tcx> { } impl<'a, 'tcx> TypeChecker<'a, 'tcx> { - fn fail(&self, span: Span, msg: impl AsRef) { + fn fail(&self, location: Location, msg: impl AsRef) { + let span = self.body.source_info(location).span; // 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()), + &format!( + "broken MIR in {:?} ({}) at {:?}:\n{}", + self.def_id, + self.when, + location, + msg.as_ref() + ), ); } + + fn check_bb(&self, location: Location, bb: BasicBlock) { + if self.body.basic_blocks().get(bb).is_none() { + self.fail(location, format!("encountered jump to invalid basic block {:?}", bb)) + } + } } impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { @@ -45,12 +61,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // `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; + let span = self.body.source_info(location).span; - 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), - ); + if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) { + self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty)); } } @@ -65,11 +79,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => { if dest == src { self.fail( - DUMMY_SP, - format!( - "encountered `Assign` statement with overlapping memory at {:?}", - location - ), + location, + "encountered `Assign` statement with overlapping memory", ); } } @@ -77,4 +88,98 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + match &terminator.kind { + TerminatorKind::Goto { target } => { + self.check_bb(location, *target); + } + TerminatorKind::SwitchInt { targets, values, .. } => { + if targets.len() != values.len() + 1 { + self.fail( + location, + format!( + "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)", + values.len(), + targets.len(), + ), + ); + } + for target in targets { + self.check_bb(location, *target); + } + } + TerminatorKind::Drop { target, unwind, .. } => { + self.check_bb(location, *target); + if let Some(unwind) = unwind { + self.check_bb(location, *unwind); + } + } + TerminatorKind::DropAndReplace { target, unwind, .. } => { + self.check_bb(location, *target); + if let Some(unwind) = unwind { + self.check_bb(location, *unwind); + } + } + TerminatorKind::Call { func, destination, cleanup, .. } => { + let func_ty = func.ty(&self.body.local_decls, self.tcx); + match func_ty.kind { + ty::FnPtr(..) | ty::FnDef(..) => {} + _ => self.fail( + location, + format!("encountered non-callable type {} in `Call` terminator", func_ty), + ), + } + if let Some((_, target)) = destination { + self.check_bb(location, *target); + } + if let Some(cleanup) = cleanup { + self.check_bb(location, *cleanup); + } + } + TerminatorKind::Assert { cond, target, cleanup, .. } => { + let cond_ty = cond.ty(&self.body.local_decls, self.tcx); + if cond_ty != self.tcx.types.bool { + self.fail( + location, + format!( + "encountered non-boolean condition of type {} in `Assert` terminator", + cond_ty + ), + ); + } + self.check_bb(location, *target); + if let Some(cleanup) = cleanup { + self.check_bb(location, *cleanup); + } + } + TerminatorKind::Yield { resume, drop, .. } => { + self.check_bb(location, *resume); + if let Some(drop) = drop { + self.check_bb(location, *drop); + } + } + TerminatorKind::FalseEdges { real_target, imaginary_target } => { + self.check_bb(location, *real_target); + self.check_bb(location, *imaginary_target); + } + TerminatorKind::FalseUnwind { real_target, unwind } => { + self.check_bb(location, *real_target); + if let Some(unwind) = unwind { + self.check_bb(location, *unwind); + } + } + TerminatorKind::InlineAsm { destination, .. } => { + if let Some(destination) = destination { + self.check_bb(location, *destination); + } + } + // Nothing to validate for these. + TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::GeneratorDrop => {} + } + } }