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::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<T, EvalError<'tcx>>;
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue