about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/mir/repr.rs9
-rw-r--r--src/librustc/mir/tcx.rs70
-rw-r--r--src/librustc_mir/build/mod.rs5
-rw-r--r--src/librustc_mir/mir_map.rs5
-rw-r--r--src/librustc_mir/transform/mod.rs1
-rw-r--r--src/librustc_mir/transform/type_check.rs341
6 files changed, 423 insertions, 8 deletions
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index 783c58469a1..2f24a1a8962 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -45,6 +45,9 @@ pub struct Mir<'tcx> {
     /// values in that it is possible to borrow them and mutate them
     /// through the resulting reference.
     pub temp_decls: Vec<TempDecl<'tcx>>,
+
+    /// A span representing this MIR, for error reporting
+    pub span: Span,
 }
 
 /// where execution begins
@@ -145,7 +148,7 @@ pub enum BorrowKind {
 
 /// A "variable" is a binding declared by the user as part of the fn
 /// decl, a let, etc.
-#[derive(Clone, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct VarDecl<'tcx> {
     pub mutability: Mutability,
     pub name: Name,
@@ -154,7 +157,7 @@ pub struct VarDecl<'tcx> {
 
 /// A "temp" is a temporary that we place on the stack. They are
 /// anonymous, always mutable, and have only a type.
-#[derive(Clone, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct TempDecl<'tcx> {
     pub ty: Ty<'tcx>,
 }
@@ -170,7 +173,7 @@ pub struct TempDecl<'tcx> {
 ///
 /// there is only one argument, of type `(i32, u32)`, but two bindings
 /// (`x` and `y`).
-#[derive(Clone, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct ArgDecl<'tcx> {
     pub ty: Ty<'tcx>,
 }
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index 45cc0b8b413..e8b83962b52 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -14,7 +14,8 @@
  */
 
 use mir::repr::*;
-use middle::subst::Substs;
+use middle::const_eval::ConstVal;
+use middle::subst::{Subst, Substs};
 use middle::ty::{self, AdtDef, Ty};
 use rustc_front::hir;
 
@@ -150,6 +151,73 @@ impl<'tcx> Mir<'tcx> {
                 self.lvalue_ty(tcx, &proj.base).projection_ty(tcx, &proj.elem)
         }
     }
+
+    pub fn rvalue_ty(&self,
+                     tcx: &ty::ctxt<'tcx>,
+                     rvalue: &Rvalue<'tcx>)
+                     -> Option<Ty<'tcx>>
+    {
+        match *rvalue {
+            Rvalue::Use(ref operand) => Some(self.operand_ty(tcx, operand)),
+            Rvalue::Repeat(ref operand, ref count) => {
+                if let ConstVal::Uint(u) = count.value {
+                    let op_ty = self.operand_ty(tcx, operand);
+                    Some(tcx.mk_array(op_ty, u as usize))
+                } else {
+                    None
+                }
+            }
+            Rvalue::Ref(reg, bk, ref lv) => {
+                let lv_ty = self.lvalue_ty(tcx, lv).to_ty(tcx);
+                Some(tcx.mk_ref(
+                    tcx.mk_region(reg),
+                    ty::TypeAndMut {
+                        ty: lv_ty,
+                        mutbl: bk.to_mutbl_lossy()
+                    }
+                ))
+            }
+            Rvalue::Len(..) => Some(tcx.types.usize),
+            Rvalue::Cast(_, _, ty) => Some(ty),
+            Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
+                let lhs_ty = self.operand_ty(tcx, lhs);
+                let rhs_ty = self.operand_ty(tcx, rhs);
+                Some(self.binop_ty(tcx, op, lhs_ty, rhs_ty))
+            }
+            Rvalue::UnaryOp(_, ref operand) => {
+                Some(self.operand_ty(tcx, operand))
+            }
+            Rvalue::Box(t) => {
+                Some(tcx.mk_box(t))
+            }
+            Rvalue::Aggregate(ref ak, ref ops) => {
+                match *ak {
+                    AggregateKind::Vec => {
+                        if let Some(operand) = ops.get(0) {
+                            let ty = self.operand_ty(tcx, operand);
+                            Some(tcx.mk_array(ty, ops.len()))
+                        } else {
+                            None
+                        }
+                    }
+                    AggregateKind::Tuple => {
+                        Some(tcx.mk_tup(
+                            ops.iter().map(|op| self.operand_ty(tcx, op)).collect()
+                        ))
+                    }
+                    AggregateKind::Adt(def, _, substs) => {
+                        Some(def.type_scheme(tcx).ty.subst(tcx, substs))
+                    }
+                    AggregateKind::Closure(did, substs) => {
+                        Some(tcx.mk_closure_from_closure_substs(
+                            did, Box::new(substs.clone())))
+                    }
+                }
+            }
+            Rvalue::Slice { .. } => None,
+            Rvalue::InlineAsm(..) => None
+        }
+    }
 }
 
 impl BorrowKind {
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 86b34858189..b83d0f6a980 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -80,7 +80,7 @@ macro_rules! unpack {
 /// the main entry point for building MIR for a function
 
 pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
-                          _span: Span,
+                          span: Span,
                           implicit_arguments: Vec<Ty<'tcx>>,
                           explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
                           argument_extent: CodeExtent,
@@ -97,7 +97,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
         temp_decls: vec![],
         var_decls: vec![],
         var_indices: FnvHashMap(),
-        unit_temp: None
+        unit_temp: None,
     };
 
     assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
@@ -119,6 +119,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
         arg_decls: arg_decls,
         temp_decls: builder.temp_decls,
         return_ty: return_ty,
+        span: span
     }
 }
 
diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs
index 5b887db7ec0..7a2b90fbbb3 100644
--- a/src/librustc_mir/mir_map.rs
+++ b/src/librustc_mir/mir_map.rs
@@ -22,7 +22,7 @@ extern crate rustc_front;
 use build;
 use graphviz;
 use pretty;
-use transform::{simplify_cfg, no_landing_pads};
+use transform::{simplify_cfg, type_check, no_landing_pads};
 use rustc::dep_graph::DepNode;
 use rustc::mir::repr::Mir;
 use hair::cx::Cx;
@@ -148,8 +148,9 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
 
         match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
             Ok(mut mir) => {
-                no_landing_pads::NoLandingPads.run_on_mir(&mut mir, self.tcx);
                 simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, self.tcx);
+                type_check::TypeckMir::new(&infcx).run_on_mir(&mut mir, self.tcx);
+                no_landing_pads::NoLandingPads.run_on_mir(&mut mir, self.tcx);
 
                 let meta_item_list = self.attr
                                          .iter()
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index 017cf360716..64e188a3b55 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -11,4 +11,5 @@
 pub mod simplify_cfg;
 pub mod erase_regions;
 pub mod no_landing_pads;
+pub mod type_check;
 mod util;
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
new file mode 100644
index 00000000000..3a891543e80
--- /dev/null
+++ b/src/librustc_mir/transform/type_check.rs
@@ -0,0 +1,341 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This pass type-checks the MIR to ensure it is not broken.
+
+use rustc::middle::infer;
+use rustc::middle::ty::{self, Ty};
+use rustc::middle::ty::fold::TypeFoldable;
+use rustc::mir::repr::*;
+use rustc::mir::transform::MirPass;
+use rustc::mir::visit::{self, Visitor};
+
+use syntax::codemap::{Span, DUMMY_SP};
+use std::fmt;
+
+macro_rules! span_mirbug {
+    ($context:expr, $elem:expr, $($message:tt)*) => ({
+        $context.tcx().sess.span_warn(
+            $context.last_span,
+            &format!("broken MIR ({:?}): {:?}", $elem, format!($($message)*))
+        )
+    })
+}
+
+/// Verifies that MIR types are sane to not crash further
+/// checks.
+struct TypeVerifier<'a, 'tcx: 'a> {
+    infcx: &'a infer::InferCtxt<'a, 'tcx>,
+    mir: &'a Mir<'tcx>,
+    last_span: Span,
+    errors_reported: bool
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> {
+    fn visit_span(&mut self, span: &Span) {
+        if *span != DUMMY_SP {
+            self.last_span = *span;
+        }
+    }
+
+    fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: visit::LvalueContext) {
+        self.super_lvalue(lvalue, context);
+        let lv_ty = self.mir.lvalue_ty(self.tcx(), lvalue).to_ty(self.tcx());
+        self.sanitize_type(lvalue, lv_ty);
+    }
+
+    fn visit_constant(&mut self, constant: &Constant<'tcx>) {
+        self.super_constant(constant);
+        self.sanitize_type(constant, constant.ty);
+    }
+
+    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
+        self.super_rvalue(rvalue);
+        if let Some(ty) = self.mir.rvalue_ty(self.tcx(), rvalue) {
+            self.sanitize_type(rvalue, ty);
+        }
+    }
+
+    fn visit_mir(&mut self, mir: &Mir<'tcx>) {
+        if let ty::FnConverging(t) = mir.return_ty {
+            self.sanitize_type(&"return type", t);
+        }
+        for var_decl in &mir.var_decls {
+            self.sanitize_type(var_decl, var_decl.ty);
+        }
+        for (n, arg_decl) in mir.arg_decls.iter().enumerate() {
+            self.sanitize_type(&(n, arg_decl), arg_decl.ty);
+        }
+        for (n, tmp_decl) in mir.temp_decls.iter().enumerate() {
+            self.sanitize_type(&(n, tmp_decl), tmp_decl.ty);
+        }
+        self.super_mir(mir);
+    }
+}
+
+impl<'a, 'tcx> TypeVerifier<'a, 'tcx> {
+    fn new(infcx: &'a infer::InferCtxt<'a, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
+        TypeVerifier {
+            infcx: infcx,
+            mir: mir,
+            last_span: mir.span,
+            errors_reported: false
+        }
+    }
+
+    fn tcx(&self) -> &'a ty::ctxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) {
+        if !(ty.needs_infer() || ty.has_escaping_regions()) {
+            return;
+        }
+        span_mirbug!(self, parent, "bad type {:?}", ty);
+        self.errors_reported = true;
+    }
+}
+
+pub struct TypeckMir<'a, 'tcx: 'a> {
+    infcx: &'a infer::InferCtxt<'a, 'tcx>,
+    last_span: Span
+}
+
+impl<'a, 'tcx> TypeckMir<'a, 'tcx> {
+    pub fn new(infcx: &'a infer::InferCtxt<'a, 'tcx>) -> Self {
+        TypeckMir {
+            infcx: infcx,
+            last_span: DUMMY_SP
+        }
+    }
+
+    fn tcx(&self) -> &'a ty::ctxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) {
+        debug!("check_stmt: {:?}", stmt);
+        let tcx = self.tcx();
+        match stmt.kind {
+            StatementKind::Assign(ref lv, ref rv) => {
+                match lv {
+                    &Lvalue::ReturnPointer if mir.return_ty == ty::FnDiverging => {
+                        // HACK: buggy writes
+                        return;
+                    }
+                    _ => {}
+                }
+
+                let lv_ty = mir.lvalue_ty(tcx, lv).to_ty(tcx);
+                let rv_ty = mir.rvalue_ty(tcx, rv);
+                if let Some(rv_ty) = rv_ty {
+                    if let Err(terr) = infer::can_mk_subty(self.infcx, rv_ty, lv_ty) {
+                        span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}",
+                                     lv_ty, rv_ty, terr);
+                    }
+                }
+
+                // FIXME: rvalue with undeterminable type - e.g. inline
+                // asm.
+            }
+        }
+    }
+
+    fn check_terminator(&self,
+                        mir: &Mir<'tcx>,
+                        term: &Terminator<'tcx>) {
+        debug!("check_terminator: {:?}", term);
+        let tcx = self.tcx();
+        match *term {
+            Terminator::Goto { .. } |
+            Terminator::Resume |
+            Terminator::Return |
+            Terminator::Drop { .. } => {}
+            Terminator::If { ref cond, .. } => {
+                let cond_ty = mir.operand_ty(tcx, cond);
+                match cond_ty.sty {
+                    ty::TyBool => {}
+                    _ => {
+                        span_mirbug!(self, term, "bad If ({:?}, not bool", cond_ty);
+                    }
+                }
+            }
+            Terminator::SwitchInt { ref discr, switch_ty, .. } => {
+                let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx);
+                if let Err(terr) = infer::can_mk_subty(self.infcx, discr_ty, switch_ty) {
+                    span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}",
+                                 switch_ty, discr_ty, terr);
+                }
+            }
+            Terminator::Switch { ref discr, adt_def, .. } => {
+                let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx);
+                match discr_ty.sty {
+                    ty::TyEnum(def, _) if def == adt_def => {},
+                    _ => {
+                        span_mirbug!(self, term, "bad Switch ({:?} on {:?})",
+                                     adt_def, discr_ty);
+                    }
+                }
+            }
+            Terminator::Call { ref func, ref args, ref destination, .. } => {
+                let func_ty = mir.operand_ty(tcx, func);
+                debug!("check_terminator: call, func_ty={:?}", func_ty);
+                let func_ty = match func_ty.sty {
+                    ty::TyBareFn(_, func_ty) => func_ty,
+                    _ => {
+                        span_mirbug!(self, term, "call to non-function {:?}", func_ty);
+                        return;
+                    }
+                };
+                let sig = tcx.erase_late_bound_regions(&func_ty.sig);
+                self.check_call_dest(mir, term, &sig, destination);
+
+                if self.is_box_free(func) {
+                    self.check_box_free_inputs(mir, term, &sig, args);
+                } else {
+                    self.check_call_inputs(mir, term, &sig, args);
+                }
+            }
+        }
+    }
+
+    fn check_call_dest(&self,
+                       mir: &Mir<'tcx>,
+                       term: &Terminator<'tcx>,
+                       sig: &ty::FnSig<'tcx>,
+                       destination: &Option<(Lvalue<'tcx>, BasicBlock)>) {
+        let tcx = self.tcx();
+        match (destination, sig.output) {
+            (&Some(..), ty::FnDiverging) => {
+                span_mirbug!(self, term, "call to diverging function {:?} with dest", sig);
+            }
+            (&Some((ref dest, _)), ty::FnConverging(ty)) => {
+                let dest_ty = mir.lvalue_ty(tcx, dest).to_ty(tcx);
+                if let Err(terr) = infer::can_mk_subty(self.infcx, ty, dest_ty) {
+                    span_mirbug!(self, term,
+                                 "call dest mismatch ({:?} <- {:?}): {:?}",
+                                 dest_ty, ty, terr);
+                }
+            }
+            (&None, ty::FnDiverging) => {}
+            (&None, ty::FnConverging(..)) => {
+                span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
+             }
+        }
+    }
+
+    fn check_call_inputs(&self,
+                         mir: &Mir<'tcx>,
+                         term: &Terminator<'tcx>,
+                         sig: &ty::FnSig<'tcx>,
+                         args: &[Operand<'tcx>])
+    {
+        debug!("check_call_inputs({:?}, {:?})", sig, args);
+        if sig.inputs.len() > args.len() ||
+           (sig.inputs.len() < args.len() && !sig.variadic) {
+            span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
+        }
+        for (n, (fn_arg, op_arg)) in sig.inputs.iter().zip(args).enumerate() {
+            let op_arg_ty = mir.operand_ty(self.tcx(), op_arg);
+            if let Err(terr) = infer::can_mk_subty(self.infcx, op_arg_ty, fn_arg) {
+                span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}",
+                             n, fn_arg, op_arg_ty, terr);
+            }
+        }
+    }
+
+    fn is_box_free(&self, operand: &Operand<'tcx>) -> bool {
+        match operand {
+            &Operand::Constant(Constant {
+                literal: Literal::Item { def_id, .. }, ..
+            }) => {
+                Some(def_id) == self.tcx().lang_items.box_free_fn()
+            }
+            _ => false,
+        }
+    }
+
+    fn check_box_free_inputs(&self,
+                             mir: &Mir<'tcx>,
+                             term: &Terminator<'tcx>,
+                             sig: &ty::FnSig<'tcx>,
+                             args: &[Operand<'tcx>])
+    {
+        debug!("check_box_free_inputs");
+
+        // box_free takes a Box as a pointer. Allow for that.
+
+        if sig.inputs.len() != 1 {
+            span_mirbug!(self, term, "box_free should take 1 argument");
+            return;
+        }
+
+        let pointee_ty = match sig.inputs[0].sty {
+            ty::TyRawPtr(mt) => mt.ty,
+            _ => {
+                span_mirbug!(self, term, "box_free should take a raw ptr");
+                return;
+            }
+        };
+
+        if args.len() != 1 {
+            span_mirbug!(self, term, "box_free called with wrong # of args");
+            return;
+        }
+
+        let arg_ty = match mir.operand_ty(self.tcx(), &args[0]).sty {
+            ty::TyRawPtr(mt) => mt.ty,
+            ty::TyBox(ty) => ty,
+            _ => {
+                span_mirbug!(self, term, "box_free called with bad arg ty");
+                return;
+            }
+        };
+
+        if let Err(terr) = infer::can_mk_subty(self.infcx, arg_ty, pointee_ty) {
+            span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}",
+                         pointee_ty, arg_ty, terr);
+        }
+    }
+
+    fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
+        self.last_span = mir.span;
+        debug!("run_on_mir: {:?}", mir.span);
+        for block in &mir.basic_blocks {
+            for stmt in &block.statements {
+                if stmt.span != DUMMY_SP {
+                    self.last_span = stmt.span;
+                }
+                self.check_stmt(mir, stmt);
+            }
+
+            if let Some(ref terminator) = block.terminator {
+                self.check_terminator(mir, terminator);
+            }
+        }
+    }
+}
+
+impl<'a, 'tcx> MirPass for TypeckMir<'a, 'tcx> {
+    fn run_on_mir<'tcx_>(&mut self,
+                         mir: &mut Mir<'tcx_>,
+                         _tcx: &ty::ctxt<'tcx_>) {
+        // FIXME: pass param_env to run_on_mir
+        let mir: &mut Mir<'tcx> = unsafe { ::std::mem::transmute(mir) };
+        let mut type_verifier = TypeVerifier::new(self.infcx, mir);
+        type_verifier.visit_mir(mir);
+
+        if type_verifier.errors_reported {
+            return;
+        }
+
+        self.typeck_mir(mir);
+    }
+}