implement overflowing ops
This commit is contained in:
parent
8db0bc0ce9
commit
58b4fac1ce
5 changed files with 118 additions and 23 deletions
21
src/error.rs
21
src/error.rs
|
@ -3,6 +3,9 @@ use std::fmt;
|
||||||
use rustc::mir::repr as mir;
|
use rustc::mir::repr as mir;
|
||||||
use rustc::ty::BareFnTy;
|
use rustc::ty::BareFnTy;
|
||||||
use memory::Pointer;
|
use memory::Pointer;
|
||||||
|
use rustc_const_math::ConstMathErr;
|
||||||
|
use syntax::codemap::Span;
|
||||||
|
use primval::PrimVal;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum EvalError<'tcx> {
|
pub enum EvalError<'tcx> {
|
||||||
|
@ -24,6 +27,10 @@ pub enum EvalError<'tcx> {
|
||||||
Unimplemented(String),
|
Unimplemented(String),
|
||||||
DerefFunctionPointer,
|
DerefFunctionPointer,
|
||||||
ExecuteMemory,
|
ExecuteMemory,
|
||||||
|
ArrayIndexOutOfBounds(Span, u64, u64),
|
||||||
|
Math(Span, ConstMathErr),
|
||||||
|
InvalidBitShiftRhs(PrimVal),
|
||||||
|
Overflow(PrimVal, PrimVal, mir::BinOp, PrimVal),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
|
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
|
||||||
|
@ -58,6 +65,14 @@ impl<'tcx> Error for EvalError<'tcx> {
|
||||||
"tried to dereference a function pointer",
|
"tried to dereference a function pointer",
|
||||||
EvalError::ExecuteMemory =>
|
EvalError::ExecuteMemory =>
|
||||||
"tried to treat a memory pointer as a function pointer",
|
"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) =>
|
EvalError::FunctionPointerTyMismatch(expected, got) =>
|
||||||
write!(f, "tried to call a function of type {:?} through a function pointer of type {:?}", 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()),
|
_ => write!(f, "{}", self.description()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -474,15 +474,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
self.frame_mut().block = target;
|
self.frame_mut().block = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert { ref cond, expected, ref msg, target, cleanup } => {
|
Assert { ref cond, expected, ref msg, target, .. } => {
|
||||||
let actual_ptr = self.eval_operand(cond)?;
|
let cond_ptr = self.eval_operand(cond)?;
|
||||||
let actual = self.memory.read_bool(actual_ptr)?;
|
if expected == self.memory.read_bool(cond_ptr)? {
|
||||||
if actual == expected {
|
|
||||||
self.frame_mut().block = target;
|
self.frame_mut().block = target;
|
||||||
} else {
|
} 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!(),
|
DropAndReplace { .. } => unimplemented!(),
|
||||||
Resume => unimplemented!(),
|
Resume => unimplemented!(),
|
||||||
|
@ -922,13 +930,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
let right_ty = self.operand_ty(right);
|
let right_ty = self.operand_ty(right);
|
||||||
let right_val = self.read_primval(right_ptr, right_ty)?;
|
let right_val = self.read_primval(right_ptr, right_ty)?;
|
||||||
|
|
||||||
let val = primval::binary_op(bin_op, left_val, right_val)?;
|
use rustc::ty::layout::Layout::*;
|
||||||
self.memory.write_primval(dest, val)?;
|
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
|
match primval::binary_op(bin_op, left_val, right_val) {
|
||||||
// Projection calculations so we can do the equivalent of `dest.1` here.
|
Ok(val) => {
|
||||||
let s = self.type_size(left_ty);
|
let offset = tup_layout.field_offset(0).bytes() as isize;
|
||||||
self.memory.write_bool(dest.offset(s as isize), false)?;
|
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) => {
|
UnaryOp(un_op, ref operand) => {
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
extern crate rustc_data_structures;
|
extern crate rustc_data_structures;
|
||||||
extern crate rustc_mir;
|
extern crate rustc_mir;
|
||||||
extern crate rustc_trans;
|
extern crate rustc_trans;
|
||||||
|
extern crate rustc_const_math;
|
||||||
extern crate syntax;
|
extern crate syntax;
|
||||||
#[macro_use] extern crate log;
|
#[macro_use] extern crate log;
|
||||||
extern crate log_settings;
|
extern crate log_settings;
|
||||||
|
|
|
@ -17,21 +17,32 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva
|
||||||
use rustc::mir::repr::BinOp::*;
|
use rustc::mir::repr::BinOp::*;
|
||||||
use self::PrimVal::*;
|
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 {
|
macro_rules! int_binops {
|
||||||
($v:ident, $l:ident, $r:ident) => ({
|
($v:ident, $l:ident, $r:ident) => ({
|
||||||
match bin_op {
|
match bin_op {
|
||||||
Add => $v($l + $r),
|
Add => overflow!($v, $v, $l, overflowing_add, $r),
|
||||||
Sub => $v($l - $r),
|
Sub => overflow!($v, $v, $l, overflowing_sub, $r),
|
||||||
Mul => $v($l * $r),
|
Mul => overflow!($v, $v, $l, overflowing_mul, $r),
|
||||||
Div => $v($l / $r),
|
Div => overflow!($v, $v, $l, overflowing_div, $r),
|
||||||
Rem => $v($l % $r),
|
Rem => overflow!($v, $v, $l, overflowing_rem, $r),
|
||||||
BitXor => $v($l ^ $r),
|
BitXor => $v($l ^ $r),
|
||||||
BitAnd => $v($l & $r),
|
BitAnd => $v($l & $r),
|
||||||
BitOr => $v($l | $r),
|
BitOr => $v($l | $r),
|
||||||
|
|
||||||
// TODO(solson): Can have differently-typed RHS.
|
// these have already been handled
|
||||||
Shl => $v($l << $r),
|
Shl => unreachable!(),
|
||||||
Shr => $v($l >> $r),
|
Shr => unreachable!(),
|
||||||
|
|
||||||
Eq => Bool($l == $r),
|
Eq => Bool($l == $r),
|
||||||
Ne => 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) {
|
let val = match (left, right) {
|
||||||
(I8(l), I8(r)) => int_binops!(I8, l, r),
|
(I8(l), I8(r)) => int_binops!(I8, l, r),
|
||||||
(I16(l), I16(r)) => int_binops!(I16, l, r),
|
(I16(l), I16(r)) => int_binops!(I16, l, r),
|
||||||
|
|
|
@ -79,11 +79,11 @@ fn compile_test() {
|
||||||
writeln!(stderr.lock(), "FAILED: {}", e).unwrap();
|
writeln!(stderr.lock(), "FAILED: {}", e).unwrap();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if failed {
|
||||||
|
panic!("some tests failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let stderr = std::io::stderr();
|
let stderr = std::io::stderr();
|
||||||
writeln!(stderr.lock(), "").unwrap();
|
writeln!(stderr.lock(), "").unwrap();
|
||||||
});
|
});
|
||||||
if failed {
|
|
||||||
panic!("some tests failed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue