about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard Burtescu <edy.burt@gmail.com>2016-05-25 08:39:32 +0300
committerEduard Burtescu <edy.burt@gmail.com>2016-06-05 14:41:02 +0300
commit156b1fb9e15f63a8525e934a6857c37823b49c0e (patch)
treeffbc1db2d8b0734f6e1e819c615cacaed8892a69
parent7fbff36d01e21380894a323bbf537219a8359291 (diff)
downloadrust-156b1fb9e15f63a8525e934a6857c37823b49c0e.tar.gz
rust-156b1fb9e15f63a8525e934a6857c37823b49c0e.zip
Add a new Assert terminator to MIR for bounds & arithmetic checks.
-rw-r--r--src/librustc/mir/repr.rs47
-rw-r--r--src/librustc/mir/visit.rs30
-rw-r--r--src/librustc_borrowck/borrowck/mir/dataflow/mod.rs3
-rw-r--r--src/librustc_borrowck/borrowck/mir/gather_moves.rs16
-rw-r--r--src/librustc_const_math/err.rs4
-rw-r--r--src/librustc_mir/build/block.rs19
-rw-r--r--src/librustc_mir/build/expr/as_lvalue.rs15
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs53
-rw-r--r--src/librustc_mir/build/scope.rs69
-rw-r--r--src/librustc_mir/transform/no_landing_pads.rs1
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs66
-rw-r--r--src/librustc_mir/transform/simplify_cfg.rs10
-rw-r--r--src/librustc_mir/transform/type_check.rs18
-rw-r--r--src/librustc_trans/_match.rs2
-rw-r--r--src/librustc_trans/common.rs8
-rw-r--r--src/librustc_trans/controlflow.rs4
-rw-r--r--src/librustc_trans/expr.rs6
-rw-r--r--src/librustc_trans/glue.rs2
-rw-r--r--src/librustc_trans/mir/analyze.rs1
-rw-r--r--src/librustc_trans/mir/block.rs93
-rw-r--r--src/librustc_trans/mir/constant.rs26
-rw-r--r--src/librustc_trans/mir/rvalue.rs8
22 files changed, 305 insertions, 196 deletions
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index 2f16878a94c..9666741d032 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -10,7 +10,7 @@
 
 use graphviz::IntoCow;
 use middle::const_val::ConstVal;
-use rustc_const_math::{ConstUsize, ConstInt};
+use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
 use hir::def_id::DefId;
 use ty::subst::Substs;
 use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
@@ -354,6 +354,16 @@ pub enum TerminatorKind<'tcx> {
         /// Cleanups to be done if the call unwinds.
         cleanup: Option<BasicBlock>
     },
+
+    /// Jump to the target if the condition has the expected value,
+    /// otherwise panic with a message and a cleanup target.
+    Assert {
+        cond: Operand<'tcx>,
+        expected: bool,
+        msg: AssertMessage<'tcx>,
+        target: BasicBlock,
+        cleanup: Option<BasicBlock>
+    }
 }
 
 impl<'tcx> Terminator<'tcx> {
@@ -389,6 +399,8 @@ impl<'tcx> TerminatorKind<'tcx> {
             Drop { ref target, unwind: None, .. } => {
                 slice::ref_slice(target).into_cow()
             }
+            Assert { target, cleanup: Some(unwind), .. } => vec![target, unwind].into_cow(),
+            Assert { ref target, .. } => slice::ref_slice(target).into_cow(),
         }
     }
 
@@ -413,6 +425,8 @@ impl<'tcx> TerminatorKind<'tcx> {
             Drop { ref mut target, unwind: None, .. } => {
                 vec![target]
             }
+            Assert { ref mut target, cleanup: Some(ref mut unwind), .. } => vec![target, unwind],
+            Assert { ref mut target, .. } => vec![target]
         }
     }
 }
@@ -495,6 +509,26 @@ impl<'tcx> TerminatorKind<'tcx> {
                 }
                 write!(fmt, ")")
             }
+            Assert { ref cond, expected, ref msg, .. } => {
+                write!(fmt, "assert(")?;
+                if !expected {
+                    write!(fmt, "!")?;
+                }
+                write!(fmt, "{:?}, ", cond)?;
+
+                match *msg {
+                    AssertMessage::BoundsCheck { ref len, ref index } => {
+                        write!(fmt, "{:?}, {:?}, {:?}",
+                               "index out of bounds: the len is {} but the index is {}",
+                               len, index)?;
+                    }
+                    AssertMessage::Math(ref err) => {
+                        write!(fmt, "{:?}", err.description())?;
+                    }
+                }
+
+                write!(fmt, ")")
+            }
         }
     }
 
@@ -532,10 +566,21 @@ impl<'tcx> TerminatorKind<'tcx> {
             Drop { unwind: Some(_), .. } => {
                 vec!["return".into_cow(), "unwind".into_cow()]
             }
+            Assert { cleanup: None, .. } => vec!["".into()],
+            Assert { .. } =>
+                vec!["success".into_cow(), "unwind".into_cow()]
         }
     }
 }
 
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
+pub enum AssertMessage<'tcx> {
+    BoundsCheck {
+        len: Operand<'tcx>,
+        index: Operand<'tcx>
+    },
+    Math(ConstMathErr)
+}
 
 ///////////////////////////////////////////////////////////////////////////
 // Statements
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 2f290c813fd..5c9582b945b 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -127,6 +127,11 @@ macro_rules! make_mir_visitor {
                 self.super_terminator_kind(block, kind);
             }
 
+            fn visit_assert_message(&mut self,
+                                    msg: & $($mutability)* AssertMessage<'tcx>) {
+                self.super_assert_message(msg);
+            }
+
             fn visit_rvalue(&mut self,
                             rvalue: & $($mutability)* Rvalue<'tcx>) {
                 self.super_rvalue(rvalue);
@@ -426,6 +431,31 @@ macro_rules! make_mir_visitor {
                         }
                         cleanup.map(|t| self.visit_branch(block, t));
                     }
+
+                    TerminatorKind::Assert { ref $($mutability)* cond,
+                                             expected: _,
+                                             ref $($mutability)* msg,
+                                             target,
+                                             cleanup } => {
+                        self.visit_operand(cond);
+                        self.visit_assert_message(msg);
+                        self.visit_branch(block, target);
+                        cleanup.map(|t| self.visit_branch(block, t));
+                    }
+                }
+            }
+
+            fn super_assert_message(&mut self,
+                                    msg: & $($mutability)* AssertMessage<'tcx>) {
+                match *msg {
+                    AssertMessage::BoundsCheck {
+                        ref $($mutability)* len,
+                        ref $($mutability)* index
+                    } => {
+                        self.visit_operand(len);
+                        self.visit_operand(index);
+                    }
+                    AssertMessage::Math(_) => {}
                 }
             }
 
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
index 293d2863733..81655b5e386 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
@@ -450,13 +450,14 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
             repr::TerminatorKind::Return |
             repr::TerminatorKind::Resume => {}
             repr::TerminatorKind::Goto { ref target } |
+            repr::TerminatorKind::Assert { ref target, cleanup: None, .. } |
             repr::TerminatorKind::Drop { ref target, location: _, unwind: None } |
-
             repr::TerminatorKind::DropAndReplace {
                 ref target, value: _, location: _, unwind: None
             } => {
                 self.propagate_bits_into_entry_set_for(in_out, changed, target);
             }
+            repr::TerminatorKind::Assert { ref target, cleanup: Some(ref unwind), .. } |
             repr::TerminatorKind::Drop { ref target, location: _, unwind: Some(ref unwind) } |
             repr::TerminatorKind::DropAndReplace {
                 ref target, value: _, location: _, unwind: Some(ref unwind)
diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
index de806eef36f..27d208240ac 100644
--- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs
+++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
@@ -663,6 +663,22 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
                 bb_ctxt.on_operand(SK::If, cond, source);
             }
 
+            TerminatorKind::Assert {
+                ref cond, expected: _,
+                ref msg, target: _, cleanup: _
+            } => {
+                // The `cond` is always of (copyable) type `bool`,
+                // so there will never be anything to move.
+                let _ = cond;
+                match *msg {
+                    AssertMessage:: BoundsCheck { ref len, ref index } => {
+                        // Same for the usize length and index in bounds-checking.
+                        let _ = (len, index);
+                    }
+                    AssertMessage::Math(_) => {}
+                }
+            }
+
             TerminatorKind::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } |
             TerminatorKind::Switch { adt_def: _, targets: _, ref discr } => {
                 // The `discr` is not consumed; that is instead
diff --git a/src/librustc_const_math/err.rs b/src/librustc_const_math/err.rs
index 126b3824efe..9970810d4e2 100644
--- a/src/librustc_const_math/err.rs
+++ b/src/librustc_const_math/err.rs
@@ -10,7 +10,7 @@
 
 use syntax::ast;
 
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, RustcEncodable, RustcDecodable)]
 pub enum ConstMathErr {
     NotInRange,
     CmpBetweenUnequalTypes,
@@ -25,7 +25,7 @@ pub enum ConstMathErr {
 }
 pub use self::ConstMathErr::*;
 
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, RustcEncodable, RustcDecodable)]
 pub enum Op {
     Add,
     Sub,
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index 0236a6c0c80..c1626b93f0c 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -12,7 +12,6 @@ use build::{BlockAnd, BlockAndExtension, Builder};
 use hair::*;
 use rustc::mir::repr::*;
 use rustc::hir;
-use syntax::codemap::Span;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     pub fn ast_block(&mut self,
@@ -82,22 +81,4 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             block.unit()
         })
     }
-
-    // Helper method for generating a conditional branch
-    // Returns (TrueBlock, FalseBlock)
-    pub fn build_cond_br(&mut self, block: BasicBlock, span: Span,
-                         cond: Operand<'tcx>) -> (BasicBlock, BasicBlock) {
-        let scope_id = self.innermost_scope_id();
-
-        let then_block = self.cfg.start_new_block();
-        let else_block = self.cfg.start_new_block();
-
-        self.cfg.terminate(block, scope_id, span,
-                           TerminatorKind::If {
-                               cond: cond,
-                               targets: (then_block, else_block)
-                           });
-
-        (then_block, else_block)
-    }
 }
diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs
index 15ea3f0e6e8..bb5aca2d8d7 100644
--- a/src/librustc_mir/build/expr/as_lvalue.rs
+++ b/src/librustc_mir/build/expr/as_lvalue.rs
@@ -66,15 +66,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                                            idx.clone(),
                                                            Operand::Consume(len.clone())));
 
-                let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block());
-                this.cfg.terminate(block,
-                                   scope_id,
-                                   expr_span,
-                                   TerminatorKind::If {
-                                       cond: Operand::Consume(lt),
-                                       targets: (success, failure),
-                                   });
-                this.panic_bounds_check(failure, idx.clone(), Operand::Consume(len), expr_span);
+                let msg = AssertMessage::BoundsCheck {
+                    len: Operand::Consume(len),
+                    index: idx.clone()
+                };
+                let success = this.assert(block, Operand::Consume(lt), true,
+                                          msg, expr_span);
                 success.and(slice.index(idx))
             }
             ExprKind::SelfRef => {
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index 683efdc141f..40b2fa61c13 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -12,6 +12,7 @@
 
 use std;
 
+use rustc_const_math::{ConstMathErr, Op};
 use rustc_data_structures::fnv::FnvHashMap;
 
 use build::{BlockAnd, BlockAndExtension, Builder};
@@ -88,10 +89,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     this.cfg.push_assign(block, scope_id, expr_span, &is_min,
                                          Rvalue::BinaryOp(BinOp::Eq, arg.clone(), minval));
 
-                    let (of_block, ok_block) = this.build_cond_br(block, expr_span,
-                                                                  Operand::Consume(is_min));
-                    this.panic(of_block, "attempted to negate with overflow", expr_span);
-                    block = ok_block;
+                    let err = ConstMathErr::Overflow(Op::Neg);
+                    block = this.assert(block, Operand::Consume(is_min), false,
+                                        AssertMessage::Math(err), expr_span);
                 }
                 block.and(Rvalue::UnaryOp(op, arg))
             }
@@ -261,27 +261,32 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             let val = result_value.clone().field(val_fld, ty);
             let of = result_value.field(of_fld, bool_ty);
 
-            let msg = if op == BinOp::Shl || op == BinOp::Shr {
-                "shift operation overflowed"
-            } else {
-                "arithmetic operation overflowed"
-            };
+            let err = ConstMathErr::Overflow(match op {
+                BinOp::Add => Op::Add,
+                BinOp::Sub => Op::Sub,
+                BinOp::Mul => Op::Mul,
+                BinOp::Shl => Op::Shl,
+                BinOp::Shr => Op::Shr,
+                _ => {
+                    bug!("MIR build_binary_op: {:?} is not checkable", op)
+                }
+            });
 
-            let (of_block, ok_block) = self.build_cond_br(block, span, Operand::Consume(of));
-            self.panic(of_block, msg, span);
+            block = self.assert(block, Operand::Consume(of), false,
+                                AssertMessage::Math(err), span);
 
-            ok_block.and(Rvalue::Use(Operand::Consume(val)))
+            block.and(Rvalue::Use(Operand::Consume(val)))
         } else {
             if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) {
                 // Checking division and remainder is more complex, since we 1. always check
                 // and 2. there are two possible failure cases, divide-by-zero and overflow.
 
-                let (zero_msg, overflow_msg) = if op == BinOp::Div {
-                    ("attempted to divide by zero",
-                     "attempted to divide with overflow")
+                let (zero_err, overflow_err) = if op == BinOp::Div {
+                    (ConstMathErr::DivisionByZero,
+                     ConstMathErr::Overflow(Op::Div))
                 } else {
-                    ("attempted remainder with a divisor of zero",
-                     "attempted remainder with overflow")
+                    (ConstMathErr::RemainderByZero,
+                     ConstMathErr::Overflow(Op::Rem))
                 };
 
                 // Check for / 0
@@ -290,11 +295,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 self.cfg.push_assign(block, scope_id, span, &is_zero,
                                      Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), zero));
 
-                let (zero_block, ok_block) = self.build_cond_br(block, span,
-                                                                Operand::Consume(is_zero));
-                self.panic(zero_block, zero_msg, span);
-
-                block = ok_block;
+                block = self.assert(block, Operand::Consume(is_zero), false,
+                                    AssertMessage::Math(zero_err), span);
 
                 // We only need to check for the overflow in one case:
                 // MIN / -1, and only for signed values.
@@ -318,11 +320,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     self.cfg.push_assign(block, scope_id, span, &of,
                                          Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min));
 
-                    let (of_block, ok_block) = self.build_cond_br(block, span,
-                                                                  Operand::Consume(of));
-                    self.panic(of_block, overflow_msg, span);
-
-                    block = ok_block;
+                    block = self.assert(block, Operand::Consume(of), false,
+                                        AssertMessage::Math(overflow_err), span);
                 }
             }
 
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index cd81fc764f4..209649dd2fd 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -517,7 +517,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     }
 
 
-
     pub fn build_drop_and_replace(&mut self,
                                   block: BasicBlock,
                                   span: Span,
@@ -538,48 +537,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         next_target.unit()
     }
 
-    // Panicking
-    // =========
-    // FIXME: should be moved into their own module
-    pub fn panic_bounds_check(&mut self,
-                              block: BasicBlock,
-                              index: Operand<'tcx>,
-                              len: Operand<'tcx>,
-                              span: Span) {
-        // fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> !
-        let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
-        let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem);
-        let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
-
-        let ref_ty = args[0];
-        let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
-            tyandmut.ty
-        } else {
-            span_bug!(span, "unexpected panic_bound_check type: {:?}", func.ty);
-        };
-
-        let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
-        let (file, line) = self.span_to_fileline_args(span);
-        let elems = vec![Operand::Constant(file), Operand::Constant(line)];
-        let scope_id = self.innermost_scope_id();
-        // FIXME: We should have this as a constant, rather than a stack variable (to not pollute
-        // icache with cold branch code), however to achieve that we either have to rely on rvalue
-        // promotion or have some way, in MIR, to create constants.
-        self.cfg.push_assign(block, scope_id, span, &tuple, // tuple = (file_arg, line_arg);
-                             Rvalue::Aggregate(AggregateKind::Tuple, elems));
-        // FIXME: is this region really correct here?
-        self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple;
-                             Rvalue::Ref(region, BorrowKind::Shared, tuple));
-        let cleanup = self.diverge_cleanup();
-        self.cfg.terminate(block, scope_id, span, TerminatorKind::Call {
-            func: Operand::Constant(func),
-            args: vec![Operand::Consume(tuple_ref), index, len],
-            destination: None,
-            cleanup: cleanup,
-        });
-    }
-
     /// Create diverge cleanup and branch to it from `block`.
+    // FIXME: Remove this (used only for unreachable cases in match).
     pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
         // fn(&(msg: &'static str filename: &'static str, line: u32)) -> !
         let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
@@ -622,6 +581,32 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         });
     }
 
+    /// Create an Assert terminator and return the success block.
+    /// If the boolean condition operand is not the expected value,
+    /// a runtime panic will be caused with the given message.
+    pub fn assert(&mut self, block: BasicBlock,
+                  cond: Operand<'tcx>,
+                  expected: bool,
+                  msg: AssertMessage<'tcx>,
+                  span: Span)
+                  -> BasicBlock {
+        let scope_id = self.innermost_scope_id();
+
+        let success_block = self.cfg.start_new_block();
+        let cleanup = self.diverge_cleanup();
+
+        self.cfg.terminate(block, scope_id, span,
+                           TerminatorKind::Assert {
+                               cond: cond,
+                               expected: expected,
+                               msg: msg,
+                               target: success_block,
+                               cleanup: cleanup
+                           });
+
+        success_block
+    }
+
     fn lang_function(&mut self, lang_item: lang_items::LangItem) -> Constant<'tcx> {
         let funcdid = match self.hir.tcx().lang_items.require(lang_item) {
             Ok(d) => d,
diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs
index 67710c43285..590106e0a22 100644
--- a/src/librustc_mir/transform/no_landing_pads.rs
+++ b/src/librustc_mir/transform/no_landing_pads.rs
@@ -30,6 +30,7 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
                 /* nothing to do */
             },
             TerminatorKind::Call { cleanup: ref mut unwind, .. } |
+            TerminatorKind::Assert { cleanup: ref mut unwind, .. } |
             TerminatorKind::DropAndReplace { ref mut unwind, .. } |
             TerminatorKind::Drop { ref mut unwind, .. } => {
                 unwind.take();
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 57fdfb281d2..b9eec6ecd9c 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -332,61 +332,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
         }
     }
 
-    /// Returns true if the block ends in a bounds check branch, i.e.:
-    /// len = Len(array);
-    /// cond = Lt(idx, len);
-    /// if cond {
-    ///     ...
-    /// } else {
-    ///     loc = (...);
-    ///     loc_ref = &loc;
-    ///     panic_bounds_check(loc_ref, idx, len);
-    /// }
-    fn is_bounds_check(&self, bb: BasicBlock,
-                       cond_op: &Operand<'tcx>,
-                       if_else: BasicBlock) -> bool {
-        use rustc::mir::repr::Lvalue::*;
-        use rustc::mir::repr::Operand::Consume;
-        use rustc::mir::repr::Rvalue::*;
-        use rustc::mir::repr::StatementKind::*;
-        use rustc::mir::repr::TerminatorKind::*;
-
-        let stmts = &self.mir[bb].statements;
-        let stmts_panic = &self.mir[if_else].statements;
-        if stmts.len() < 2 || stmts_panic.len() != 2 {
-            return false;
-        }
-
-        let all = (&stmts[stmts.len() - 2].kind,
-                   &stmts[stmts.len() - 1].kind,
-                   cond_op,
-                   &stmts_panic[0].kind,
-                   &stmts_panic[1].kind,
-                   &self.mir[if_else].terminator().kind);
-        match all {
-            (&Assign(Temp(len), Len(_)),
-             &Assign(Temp(cond), BinaryOp(BinOp::Lt, ref idx, Consume(Temp(len2)))),
-             /* if */ &Consume(Temp(cond2)), /* {...} else */
-             &Assign(Temp(loc), Aggregate(..)),
-             &Assign(Temp(loc_ref), Ref(_, _, Temp(loc2))),
-             &Call {
-                func: Operand::Constant(Constant {
-                    literal: Literal::Item { def_id, .. }, ..
-                }),
-                ref args,
-                destination: None,
-                ..
-            }) => {
-                len == len2 && cond == cond2 && loc == loc2 &&
-                args[0] == Consume(Temp(loc_ref)) &&
-                args[1] == *idx &&
-                args[2] == Consume(Temp(len)) &&
-                Some(def_id) == self.tcx.lang_items.panic_bounds_check_fn()
-            }
-            _ => false
-        }
-    }
-
     /// Qualify a whole const, static initializer or const fn.
     fn qualify_const(&mut self) -> Qualif {
         let mir = self.mir;
@@ -402,6 +347,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                 TerminatorKind::Goto { target } |
                 // Drops are considered noops.
                 TerminatorKind::Drop { target, .. } |
+                TerminatorKind::Assert { target, .. } |
                 TerminatorKind::Call { destination: Some((_, target)), .. } => {
                     Some(target)
                 }
@@ -411,15 +357,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                     return Qualif::empty();
                 }
 
-                // Need to allow bounds checking branches.
-                TerminatorKind::If { ref cond, targets: (if_true, if_else) } => {
-                    if self.is_bounds_check(bb, cond, if_else) {
-                        Some(if_true)
-                    } else {
-                        None
-                    }
-                }
-
+                TerminatorKind::If {..} |
                 TerminatorKind::Switch {..} |
                 TerminatorKind::SwitchInt {..} |
                 TerminatorKind::DropAndReplace { .. } |
diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs
index 526157a49c7..d008918026a 100644
--- a/src/librustc_mir/transform/simplify_cfg.rs
+++ b/src/librustc_mir/transform/simplify_cfg.rs
@@ -181,7 +181,17 @@ fn simplify_branches(mir: &mut Mir) {
                     }
                 }
 
+                TerminatorKind::Assert { target, cond: Operand::Constant(Constant {
+                    literal: Literal::Value {
+                        value: ConstVal::Bool(cond)
+                    }, ..
+                }), expected, .. } if cond == expected => {
+                    changed = true;
+                    TerminatorKind::Goto { target: target }
+                }
+
                 TerminatorKind::SwitchInt { ref targets, .. } if targets.len() == 1 => {
+                    changed = true;
                     TerminatorKind::Goto { target: targets[0] }
                 }
                 _ => continue
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index efac8ea8461..019ed670d1f 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -431,6 +431,21 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                     self.check_call_inputs(mir, term, &sig, args);
                 }
             }
+            TerminatorKind::Assert { ref cond, ref msg, .. } => {
+                let cond_ty = mir.operand_ty(tcx, cond);
+                if cond_ty != tcx.types.bool {
+                    span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
+                }
+
+                if let AssertMessage::BoundsCheck { ref len, ref index } = *msg {
+                    if mir.operand_ty(tcx, len) != tcx.types.usize {
+                        span_mirbug!(self, len, "bounds-check length non-usize {:?}", len)
+                    }
+                    if mir.operand_ty(tcx, index) != tcx.types.usize {
+                        span_mirbug!(self, index, "bounds-check index non-usize {:?}", index)
+                    }
+                }
+            }
         }
     }
 
@@ -561,7 +576,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 }
             }
             TerminatorKind::Drop { target, unwind, .. } |
-            TerminatorKind::DropAndReplace { target, unwind, .. } => {
+            TerminatorKind::DropAndReplace { target, unwind, .. } |
+            TerminatorKind::Assert { target, cleanup: unwind, .. } => {
                 self.assert_iscleanup(mir, block, target, is_cleanup);
                 if let Some(unwind) = unwind {
                     if is_cleanup {
diff --git a/src/librustc_trans/_match.rs b/src/librustc_trans/_match.rs
index 419e19532dd..f524bc8596a 100644
--- a/src/librustc_trans/_match.rs
+++ b/src/librustc_trans/_match.rs
@@ -880,7 +880,7 @@ fn compare_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                                rhs_t: Ty<'tcx>,
                                debug_loc: DebugLoc)
                                -> Result<'blk, 'tcx> {
-        let did = langcall(bcx,
+        let did = langcall(bcx.tcx(),
                            None,
                            &format!("comparison of `{}`", rhs_t),
                            StrEqFnLangItem);
diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs
index f35d87d0741..e9b7b590b19 100644
--- a/src/librustc_trans/common.rs
+++ b/src/librustc_trans/common.rs
@@ -1165,18 +1165,18 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     })
 }
 
-pub fn langcall(bcx: Block,
+pub fn langcall(tcx: TyCtxt,
                 span: Option<Span>,
                 msg: &str,
                 li: LangItem)
                 -> DefId {
-    match bcx.tcx().lang_items.require(li) {
+    match tcx.lang_items.require(li) {
         Ok(id) => id,
         Err(s) => {
             let msg = format!("{} {}", msg, s);
             match span {
-                Some(span) => bcx.tcx().sess.span_fatal(span, &msg[..]),
-                None => bcx.tcx().sess.fatal(&msg[..]),
+                Some(span) => tcx.sess.span_fatal(span, &msg[..]),
+                None => tcx.sess.fatal(&msg[..]),
             }
         }
     }
diff --git a/src/librustc_trans/controlflow.rs b/src/librustc_trans/controlflow.rs
index f793f0a6d55..0f686227c6f 100644
--- a/src/librustc_trans/controlflow.rs
+++ b/src/librustc_trans/controlflow.rs
@@ -400,7 +400,7 @@ pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let align = machine::llalign_of_min(ccx, val_ty(expr_file_line_const));
     let expr_file_line = consts::addr_of(ccx, expr_file_line_const, align, "panic_loc");
     let args = vec!(expr_file_line);
-    let did = langcall(bcx, Some(call_info.span), "", PanicFnLangItem);
+    let did = langcall(bcx.tcx(), Some(call_info.span), "", PanicFnLangItem);
     Callee::def(ccx, did, ccx.tcx().mk_substs(Substs::empty()))
         .call(bcx, call_info.debug_loc(), ArgVals(&args), None).bcx
 }
@@ -428,7 +428,7 @@ pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let align = machine::llalign_of_min(ccx, val_ty(file_line_const));
     let file_line = consts::addr_of(ccx, file_line_const, align, "panic_bounds_check_loc");
     let args = vec!(file_line, index, len);
-    let did = langcall(bcx, Some(call_info.span), "", PanicBoundsCheckFnLangItem);
+    let did = langcall(bcx.tcx(), Some(call_info.span), "", PanicBoundsCheckFnLangItem);
     Callee::def(ccx, did, ccx.tcx().mk_substs(Substs::empty()))
         .call(bcx, call_info.debug_loc(), ArgVals(&args), None).bcx
 }
diff --git a/src/librustc_trans/expr.rs b/src/librustc_trans/expr.rs
index d75516ff648..465ebace1b8 100644
--- a/src/librustc_trans/expr.rs
+++ b/src/librustc_trans/expr.rs
@@ -2230,11 +2230,11 @@ impl OverflowOpViaIntrinsic {
                         binop_debug_loc);
 
         let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1");
-        Call(bcx, expect, &[cond, C_integral(Type::i1(bcx.ccx()), 0, false)],
-             binop_debug_loc);
+        let expected = Call(bcx, expect, &[cond, C_bool(bcx.ccx(), false)],
+                            binop_debug_loc);
 
         let bcx =
-            base::with_cond(bcx, cond, |bcx|
+            base::with_cond(bcx, expected, |bcx|
                 controlflow::trans_fail(bcx, info,
                     InternedString::new("arithmetic operation overflowed")));
 
diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs
index 97879158401..211efeb4e4b 100644
--- a/src/librustc_trans/glue.rs
+++ b/src/librustc_trans/glue.rs
@@ -53,7 +53,7 @@ pub fn trans_exchange_free_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                            -> Block<'blk, 'tcx> {
     let _icx = push_ctxt("trans_exchange_free");
 
-    let def_id = langcall(bcx, None, "", ExchangeFreeFnLangItem);
+    let def_id = langcall(bcx.tcx(), None, "", ExchangeFreeFnLangItem);
     let args = [PointerCast(bcx, v, Type::i8p(bcx.ccx())), size, align];
     Callee::def(bcx.ccx(), def_id, bcx.tcx().mk_substs(Substs::empty()))
         .call(bcx, debug_loc, ArgVals(&args), None).bcx
diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs
index 03df1c451f0..bce639ac8f7 100644
--- a/src/librustc_trans/mir/analyze.rs
+++ b/src/librustc_trans/mir/analyze.rs
@@ -161,6 +161,7 @@ pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>,
                     /* nothing to do */
                 }
                 TerminatorKind::Call { cleanup: unwind, .. } |
+                TerminatorKind::Assert { cleanup: unwind, .. } |
                 TerminatorKind::DropAndReplace { unwind, .. } |
                 TerminatorKind::Drop { unwind, .. } => {
                     if let Some(unwind) = unwind {
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index eb962b66154..e4d137d36ac 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 use llvm::{self, ValueRef};
+use rustc::middle::lang_items;
 use rustc::ty;
 use rustc::mir::repr as mir;
 use abi::{Abi, FnType, ArgType};
@@ -16,7 +17,9 @@ use adt;
 use base;
 use build;
 use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual};
-use common::{self, type_is_fat_ptr, Block, BlockAndBuilder, LandingPad, C_undef};
+use common::{self, type_is_fat_ptr, Block, BlockAndBuilder, LandingPad};
+use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef};
+use consts;
 use debuginfo::DebugLoc;
 use Disr;
 use machine::{llalign_of_min, llbitsize_of_real};
@@ -24,7 +27,9 @@ use meth;
 use type_of;
 use glue;
 use type_::Type;
+
 use rustc_data_structures::fnv::FnvHashMap;
+use syntax::parse::token;
 
 use super::{MirContext, TempRef};
 use super::analyze::CleanupKind;
@@ -212,6 +217,92 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 }
             }
 
+            mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => {
+                let cond = self.trans_operand(&bcx, cond).immediate();
+                let const_cond = common::const_to_opt_uint(cond).map(|c| c == 1);
+
+                // Don't translate the panic block if success if known.
+                let lltarget = self.llblock(target);
+                if const_cond == Some(expected) {
+                    funclet_br(bcx, lltarget);
+                    return;
+                }
+
+                if const_cond == Some(!expected) {
+                    // Do nothing to end up with an unconditional panic.
+                } else {
+                    // Pass the condition through llvm.expect for branch hinting.
+                    let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1");
+                    let cond = bcx.call(expect, &[cond, C_bool(bcx.ccx(), expected)], None);
+
+                    // Create the failure block and the conditional branch to it.
+                    // After this point, bcx is the block for the call to panic.
+                    let panic_block = self.fcx.new_block("panic", None);
+                    if expected {
+                        bcx.cond_br(cond, lltarget, panic_block.llbb);
+                    } else {
+                        bcx.cond_br(cond, panic_block.llbb, lltarget);
+                    }
+                    bcx = panic_block.build();
+                }
+
+                // Get the location information.
+                let loc = bcx.sess().codemap().lookup_char_pos(terminator.span.lo);
+                let filename = token::intern_and_get_ident(&loc.file.name);
+                let filename = C_str_slice(bcx.ccx(), filename);
+                let line = C_u32(bcx.ccx(), loc.line as u32);
+
+                // Put together the arguments to the panic entry point.
+                let (lang_item, args) = match *msg {
+                    mir::AssertMessage::BoundsCheck { ref len, ref index } => {
+                        let len = self.trans_operand(&mut bcx, len);
+                        let index = self.trans_operand(&mut bcx, index);
+
+                        let file_line = C_struct(bcx.ccx(), &[filename, line], false);
+                        let align = llalign_of_min(bcx.ccx(), common::val_ty(file_line));
+                        let file_line = consts::addr_of(bcx.ccx(),
+                                                        file_line,
+                                                        align,
+                                                        "panic_bounds_check_loc");
+                        (lang_items::PanicBoundsCheckFnLangItem,
+                         vec![file_line, index.immediate(), len.immediate()])
+                    }
+                    mir::AssertMessage::Math(ref err) => {
+                        let msg_str = token::intern_and_get_ident(err.description());
+                        let msg_str = C_str_slice(bcx.ccx(), msg_str);
+                        let msg_file_line = C_struct(bcx.ccx(),
+                                                     &[msg_str, filename, line],
+                                                     false);
+                        let align = llalign_of_min(bcx.ccx(), common::val_ty(msg_file_line));
+                        let msg_file_line = consts::addr_of(bcx.ccx(),
+                                                            msg_file_line,
+                                                            align,
+                                                            "panic_loc");
+                        (lang_items::PanicFnLangItem, vec![msg_file_line])
+                    }
+                };
+
+                // Obtain the panic entry point.
+                let def_id = common::langcall(bcx.tcx(), Some(terminator.span), "", lang_item);
+                let callee = Callee::def(bcx.ccx(), def_id,
+                    bcx.ccx().empty_substs_for_def_id(def_id));
+                let llfn = callee.reify(bcx.ccx()).val;
+
+                // Translate the actual panic invoke/call.
+                if let Some(unwind) = cleanup {
+                    let uwbcx = self.bcx(unwind);
+                    let unwind = self.make_landing_pad(uwbcx);
+                    bcx.invoke(llfn,
+                               &args,
+                               self.unreachable_block().llbb,
+                               unwind.llbb(),
+                               cleanup_bundle.as_ref());
+                } else {
+                    bcx.call(llfn, &args, cleanup_bundle.as_ref());
+                    bcx.unreachable();
+                }
+            }
+
             mir::TerminatorKind::DropAndReplace { .. } => {
                 bug!("undesugared DropAndReplace in trans: {:?}", data);
             }
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index 0403c7b1f75..9498a244e80 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -287,14 +287,21 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                     }))
                 }
 
-                // This is only supported to make bounds checking work.
-                mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => {
+                mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, .. } => {
                     let cond = self.const_operand(cond, span)?;
-                    if common::const_to_uint(cond.llval) != 0 {
-                        true_bb
-                    } else {
-                        false_bb
+                    let cond_bool = common::const_to_uint(cond.llval) != 0;
+                    if cond_bool != expected {
+                        let err = match *msg {
+                            mir::AssertMessage::BoundsCheck {..} => {
+                                ErrKind::IndexOutOfBounds
+                            }
+                            mir::AssertMessage::Math(ref err) => {
+                                ErrKind::Math(err.clone())
+                            }
+                        };
+                        consts::const_err(self.ccx, span, Err(err), TrueConst::Yes)?;
                     }
+                    target
                 }
 
                 mir::TerminatorKind::Call { ref func, ref args, ref destination, .. } => {
@@ -308,13 +315,6 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                                        func, fn_ty)
                     };
 
-                    // Indexing OOB doesn't call a const fn, handle it.
-                    if Some(instance.def) == tcx.lang_items.panic_bounds_check_fn() {
-                        consts::const_err(self.ccx, span,
-                                          Err(ErrKind::IndexOutOfBounds),
-                                          TrueConst::Yes)?;
-                    }
-
                     let args = args.iter().map(|arg| {
                         self.const_operand(arg, span)
                     }).collect::<Result<Vec<_>, _>>()?;
diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs
index cbd39fffce8..b0567ab26e7 100644
--- a/src/librustc_trans/mir/rvalue.rs
+++ b/src/librustc_trans/mir/rvalue.rs
@@ -17,13 +17,13 @@ use asm;
 use base;
 use callee::Callee;
 use common::{self, val_ty,
-             C_null,
-             C_uint, C_undef, C_u8, BlockAndBuilder, Result};
+             C_null, C_uint, C_undef, BlockAndBuilder, Result};
 use datum::{Datum, Lvalue};
 use debuginfo::DebugLoc;
 use adt;
 use machine;
 use type_of;
+use type_::Type;
 use tvec;
 use value::Value;
 use Disr;
@@ -611,9 +611,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 (val, bcx.zext(of, Type::bool(bcx.ccx())))
             }
             _ => {
-                // Fall back to regular translation with a constant-false overflow flag
-                (self.trans_scalar_binop(bcx, op, lhs, rhs, input_ty),
-                 C_u8(bcx.ccx(), 0))
+                bug!("Operator `{:?}` is not a checkable operator", op)
             }
         };