about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_mir/interpret/terminator.rs8
-rw-r--r--src/librustc_mir/transform/validate.rs103
2 files changed, 108 insertions, 3 deletions
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..046889193da 100644
--- a/src/librustc_mir/transform/validate.rs
+++ b/src/librustc_mir/transform/validate.rs
@@ -3,8 +3,11 @@
 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};
 
@@ -38,6 +41,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             &format!("broken MIR in {:?} ({}): {}", self.def_id, self.when, msg.as_ref()),
         );
     }
+
+    fn check_bb(&self, span: Span, bb: BasicBlock) {
+        if self.body.basic_blocks().get(bb).is_none() {
+            self.fail(span, format!("encountered jump to invalid basic block {:?}", bb))
+        }
+    }
 }
 
 impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
@@ -77,4 +86,94 @@ 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(terminator.source_info.span, *target);
+            }
+            TerminatorKind::SwitchInt { targets, .. } => {
+                if targets.is_empty() {
+                    self.fail(
+                        terminator.source_info.span,
+                        "encountered `SwitchInt` terminator with no target to jump to",
+                    );
+                }
+                for target in targets {
+                    self.check_bb(terminator.source_info.span, *target);
+                }
+            }
+            TerminatorKind::Drop { target, unwind, .. } => {
+                self.check_bb(terminator.source_info.span, *target);
+                if let Some(unwind) = unwind {
+                    self.check_bb(terminator.source_info.span, *unwind);
+                }
+            }
+            TerminatorKind::DropAndReplace { target, unwind, .. } => {
+                self.check_bb(terminator.source_info.span, *target);
+                if let Some(unwind) = unwind {
+                    self.check_bb(terminator.source_info.span, *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(
+                        terminator.source_info.span,
+                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
+                    ),
+                }
+                if let Some((_, target)) = destination {
+                    self.check_bb(terminator.source_info.span, *target);
+                }
+                if let Some(cleanup) = cleanup {
+                    self.check_bb(terminator.source_info.span, *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(
+                        terminator.source_info.span,
+                        format!(
+                            "encountered non-boolean condition of type {} in `Assert` terminator",
+                            cond_ty
+                        ),
+                    );
+                }
+                self.check_bb(terminator.source_info.span, *target);
+                if let Some(cleanup) = cleanup {
+                    self.check_bb(terminator.source_info.span, *cleanup);
+                }
+            }
+            TerminatorKind::Yield { resume, drop, .. } => {
+                self.check_bb(terminator.source_info.span, *resume);
+                if let Some(drop) = drop {
+                    self.check_bb(terminator.source_info.span, *drop);
+                }
+            }
+            TerminatorKind::FalseEdges { real_target, imaginary_target } => {
+                self.check_bb(terminator.source_info.span, *real_target);
+                self.check_bb(terminator.source_info.span, *imaginary_target);
+            }
+            TerminatorKind::FalseUnwind { real_target, unwind } => {
+                self.check_bb(terminator.source_info.span, *real_target);
+                if let Some(unwind) = unwind {
+                    self.check_bb(terminator.source_info.span, *unwind);
+                }
+            }
+            TerminatorKind::InlineAsm { destination, .. } => {
+                if let Some(destination) = destination {
+                    self.check_bb(terminator.source_info.span, *destination);
+                }
+            }
+            // Nothing to validate for these.
+            TerminatorKind::Resume
+            | TerminatorKind::Abort
+            | TerminatorKind::Return
+            | TerminatorKind::Unreachable
+            | TerminatorKind::GeneratorDrop => {}
+        }
+    }
 }