diff options
| author | Eduard Burtescu <edy.burt@gmail.com> | 2016-05-25 08:39:32 +0300 |
|---|---|---|
| committer | Eduard Burtescu <edy.burt@gmail.com> | 2016-06-05 14:41:02 +0300 |
| commit | 156b1fb9e15f63a8525e934a6857c37823b49c0e (patch) | |
| tree | ffbc1db2d8b0734f6e1e819c615cacaed8892a69 | |
| parent | 7fbff36d01e21380894a323bbf537219a8359291 (diff) | |
| download | rust-156b1fb9e15f63a8525e934a6857c37823b49c0e.tar.gz rust-156b1fb9e15f63a8525e934a6857c37823b49c0e.zip | |
Add a new Assert terminator to MIR for bounds & arithmetic checks.
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) } }; |
