diff --git a/src/error.rs b/src/error.rs index 35a978f2d2e..885f5af6321 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,6 +3,9 @@ use std::fmt; use rustc::mir::repr as mir; use rustc::ty::BareFnTy; use memory::Pointer; +use rustc_const_math::ConstMathErr; +use syntax::codemap::Span; +use primval::PrimVal; #[derive(Clone, Debug)] pub enum EvalError<'tcx> { @@ -24,6 +27,10 @@ pub enum EvalError<'tcx> { Unimplemented(String), DerefFunctionPointer, ExecuteMemory, + ArrayIndexOutOfBounds(Span, u64, u64), + Math(Span, ConstMathErr), + InvalidBitShiftRhs(PrimVal), + Overflow(PrimVal, PrimVal, mir::BinOp, PrimVal), } pub type EvalResult<'tcx, T> = Result>; @@ -58,6 +65,14 @@ impl<'tcx> Error for EvalError<'tcx> { "tried to dereference a function pointer", EvalError::ExecuteMemory => "tried to treat a memory pointer as a function pointer", + EvalError::ArrayIndexOutOfBounds(..) => + "array index out of bounds", + EvalError::Math(..) => + "mathematical operation failed", + EvalError::InvalidBitShiftRhs(..) => + "bit shift rhs negative or not an int", + EvalError::Overflow(..) => + "mathematical operation overflowed", } } @@ -73,6 +88,12 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { }, EvalError::FunctionPointerTyMismatch(expected, got) => write!(f, "tried to call a function of type {:?} through a function pointer of type {:?}", expected, got), + EvalError::ArrayIndexOutOfBounds(span, len, index) => + write!(f, "array index {} out of bounds {} at {:?}", index, len, span), + EvalError::Math(span, ref err) => + write!(f, "mathematical operation at {:?} failed with {:?}", span, err), + EvalError::Overflow(l, r, op, val) => + write!(f, "mathematical operation overflowed: {:?} {} {:?} => {:?}", l, op.to_hir_binop().as_str(), r, val), _ => write!(f, "{}", self.description()), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 213f813529f..6d367a3be2e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -474,15 +474,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.frame_mut().block = target; } - Assert { ref cond, expected, ref msg, target, cleanup } => { - let actual_ptr = self.eval_operand(cond)?; - let actual = self.memory.read_bool(actual_ptr)?; - if actual == expected { + Assert { ref cond, expected, ref msg, target, .. } => { + let cond_ptr = self.eval_operand(cond)?; + if expected == self.memory.read_bool(cond_ptr)? { self.frame_mut().block = target; } else { - panic!("unimplemented: jump to {:?} and print {:?}", cleanup, msg); + return match *msg { + mir::AssertMessage::BoundsCheck { ref len, ref index } => { + let len = self.eval_operand(len).expect("can't eval len"); + let len = self.memory.read_usize(len).expect("can't read len"); + let index = self.eval_operand(index).expect("can't eval index"); + let index = self.memory.read_usize(index).expect("can't read index"); + Err(EvalError::ArrayIndexOutOfBounds(terminator.source_info.span, len, index)) + }, + mir::AssertMessage::Math(ref err) => Err(EvalError::Math(terminator.source_info.span, err.clone())), + } } - } + }, DropAndReplace { .. } => unimplemented!(), Resume => unimplemented!(), @@ -922,13 +930,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let right_ty = self.operand_ty(right); let right_val = self.read_primval(right_ptr, right_ty)?; - let val = primval::binary_op(bin_op, left_val, right_val)?; - self.memory.write_primval(dest, val)?; + use rustc::ty::layout::Layout::*; + let tup_layout = match *dest_layout { + Univariant { ref variant, .. } => variant, + _ => panic!("checked bin op returns something other than a tuple"), + }; - // FIXME(solson): Find the result type size properly. Perhaps refactor out - // Projection calculations so we can do the equivalent of `dest.1` here. - let s = self.type_size(left_ty); - self.memory.write_bool(dest.offset(s as isize), false)?; + match primval::binary_op(bin_op, left_val, right_val) { + Ok(val) => { + let offset = tup_layout.field_offset(0).bytes() as isize; + self.memory.write_primval(dest.offset(offset), val)?; + let offset = tup_layout.field_offset(1).bytes() as isize; + self.memory.write_bool(dest.offset(offset), false)?; + }, + Err(EvalError::Overflow(l, r, op, val)) => { + debug!("mathematical operation overflowed: {:?} {} {:?} => {:?}", l, op.to_hir_binop().as_str(), r, val); + let offset = tup_layout.field_offset(0).bytes() as isize; + self.memory.write_primval(dest.offset(offset), val)?; + let offset = tup_layout.field_offset(1).bytes() as isize; + self.memory.write_bool(dest.offset(offset), true)?; + }, + Err(other) => return Err(other), + } } UnaryOp(un_op, ref operand) => { diff --git a/src/lib.rs b/src/lib.rs index 4bc5a07e3c2..c881e79e614 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ extern crate rustc_data_structures; extern crate rustc_mir; extern crate rustc_trans; +extern crate rustc_const_math; extern crate syntax; #[macro_use] extern crate log; extern crate log_settings; diff --git a/src/primval.rs b/src/primval.rs index 5e1cdac45a1..b1900874a9a 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -17,21 +17,32 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva use rustc::mir::repr::BinOp::*; use self::PrimVal::*; + macro_rules! overflow { + ($v:ident, $v2:ident, $l:ident, $op:ident, $r:ident) => ({ + let (val, of) = $l.$op($r); + if of { + return Err(EvalError::Overflow($v($l), $v2($r), bin_op, $v(val))); + } else { + $v(val) + } + }) + } + macro_rules! int_binops { ($v:ident, $l:ident, $r:ident) => ({ match bin_op { - Add => $v($l + $r), - Sub => $v($l - $r), - Mul => $v($l * $r), - Div => $v($l / $r), - Rem => $v($l % $r), + Add => overflow!($v, $v, $l, overflowing_add, $r), + Sub => overflow!($v, $v, $l, overflowing_sub, $r), + Mul => overflow!($v, $v, $l, overflowing_mul, $r), + Div => overflow!($v, $v, $l, overflowing_div, $r), + Rem => overflow!($v, $v, $l, overflowing_rem, $r), BitXor => $v($l ^ $r), BitAnd => $v($l & $r), BitOr => $v($l | $r), - // TODO(solson): Can have differently-typed RHS. - Shl => $v($l << $r), - Shr => $v($l >> $r), + // these have already been handled + Shl => unreachable!(), + Shr => unreachable!(), Eq => Bool($l == $r), Ne => Bool($l != $r), @@ -53,6 +64,45 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva } } + match bin_op { + // can have rhs with a different numeric type + Shl | Shr => { + let r = match right { + I8(i) if i >= 0 => i as u32, + I16(i) if i >= 0 => i as u32, + I32(i) if i >= 0 => i as u32, + I64(i) if i >= 0 && i as i32 as i64 == i => i as u32, + U8(i) => i as u32, + U16(i) => i as u32, + U32(i) => i, + U64(i) if i as u32 as u64 == i => i as u32, + _ => return Err(EvalError::InvalidBitShiftRhs(right)), + }; + macro_rules! shift { + ($v:ident, $l:ident, $r:ident) => ({ + match bin_op { + Shl => overflow!($v, U32, $l, overflowing_shl, $r), + Shr => overflow!($v, U32, $l, overflowing_shr, $r), + _ => unreachable!(), + } + }) + } + let val = match left { + I8(l) => shift!(I8, l, r), + I16(l) => shift!(I16, l, r), + I32(l) => shift!(I32, l, r), + I64(l) => shift!(I64, l, r), + U8(l) => shift!(U8, l, r), + U16(l) => shift!(U16, l, r), + U32(l) => shift!(U32, l, r), + U64(l) => shift!(U64, l, r), + _ => unreachable!(), + }; + return Ok(val); + }, + _ => {}, + } + let val = match (left, right) { (I8(l), I8(r)) => int_binops!(I8, l, r), (I16(l), I16(r)) => int_binops!(I16, l, r), diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 66b94ad88da..6869eb1eef0 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -79,11 +79,11 @@ fn compile_test() { writeln!(stderr.lock(), "FAILED: {}", e).unwrap(); }, } + if failed { + panic!("some tests failed"); + } } let stderr = std::io::stderr(); writeln!(stderr.lock(), "").unwrap(); }); - if failed { - panic!("some tests failed"); - } }