Merge pull request #15 from oli-obk/step_by_step
4 byte pointers + tests
This commit is contained in:
commit
c8151703b2
8 changed files with 131 additions and 86 deletions
|
|
@ -9,6 +9,10 @@ version = "0.1.0"
|
|||
[[bin]]
|
||||
doc = false
|
||||
name = "miri"
|
||||
test = false
|
||||
|
||||
[lib]
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
byteorder = "0.4.2"
|
||||
|
|
|
|||
23
src/error.rs
23
src/error.rs
|
|
@ -1,16 +1,24 @@
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use rustc::mir::repr as mir;
|
||||
use memory::Pointer;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum EvalError {
|
||||
DanglingPointerDeref,
|
||||
InvalidBool,
|
||||
InvalidDiscriminant,
|
||||
PointerOutOfBounds,
|
||||
PointerOutOfBounds {
|
||||
ptr: Pointer,
|
||||
size: usize,
|
||||
allocation_size: usize,
|
||||
},
|
||||
ReadPointerAsBytes,
|
||||
ReadBytesAsPointer,
|
||||
InvalidPointerMath,
|
||||
ReadUndefBytes,
|
||||
InvalidBoolOp(mir::BinOp),
|
||||
Unimplemented(String),
|
||||
}
|
||||
|
||||
pub type EvalResult<T> = Result<T, EvalError>;
|
||||
|
|
@ -24,7 +32,7 @@ impl Error for EvalError {
|
|||
"invalid boolean value read",
|
||||
EvalError::InvalidDiscriminant =>
|
||||
"invalid enum discriminant value read",
|
||||
EvalError::PointerOutOfBounds =>
|
||||
EvalError::PointerOutOfBounds { .. } =>
|
||||
"pointer offset outside bounds of allocation",
|
||||
EvalError::ReadPointerAsBytes =>
|
||||
"a raw memory access tried to access part of a pointer value as raw bytes",
|
||||
|
|
@ -34,6 +42,9 @@ impl Error for EvalError {
|
|||
"attempted to do math or a comparison on pointers into different allocations",
|
||||
EvalError::ReadUndefBytes =>
|
||||
"attempted to read undefined bytes",
|
||||
EvalError::InvalidBoolOp(_) =>
|
||||
"invalid boolean operation",
|
||||
EvalError::Unimplemented(ref msg) => msg,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,6 +53,12 @@ impl Error for EvalError {
|
|||
|
||||
impl fmt::Display for EvalError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.description())
|
||||
match *self {
|
||||
EvalError::PointerOutOfBounds { ptr, size, allocation_size } => {
|
||||
write!(f, "memory access of {}..{} outside bounds of allocation {} which has size {}",
|
||||
ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size)
|
||||
},
|
||||
_ => write!(f, "{}", self.description()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,11 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> {
|
|||
tcx: tcx,
|
||||
mir_map: mir_map,
|
||||
mir_cache: RefCell::new(DefIdMap()),
|
||||
memory: Memory::new(),
|
||||
memory: Memory::new(tcx.sess
|
||||
.target
|
||||
.uint_type
|
||||
.bit_width()
|
||||
.expect("Session::target::uint_type was usize")/8),
|
||||
substs_stack: Vec::new(),
|
||||
name_stack: Vec::new(),
|
||||
}
|
||||
|
|
@ -392,11 +396,11 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
TerminatorTarget::Call
|
||||
}
|
||||
|
||||
abi => panic!("can't handle function with {:?} ABI", abi),
|
||||
abi => return Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))),
|
||||
}
|
||||
}
|
||||
|
||||
_ => panic!("can't handle callee of type {:?}", func_ty),
|
||||
_ => return Err(EvalError::Unimplemented(format!("can't handle callee of type {:?}", func_ty))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -470,7 +474,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
}
|
||||
|
||||
StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
|
||||
let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield);
|
||||
let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield)?;
|
||||
let nonnull = adt_ptr.offset(offset.bytes() as isize);
|
||||
self.read_nonnull_discriminant_value(nonnull, nndiscr)?
|
||||
}
|
||||
|
|
@ -620,7 +624,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
self.memory.write_uint(dest, n * elem_size, dest_size)?;
|
||||
}
|
||||
|
||||
_ => panic!("unimplemented: size_of_val::<{:?}>", ty),
|
||||
_ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -631,7 +635,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
}
|
||||
"uninit" => self.memory.mark_definedness(dest, dest_size, false)?,
|
||||
|
||||
name => panic!("can't handle intrinsic: {}", name),
|
||||
name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))),
|
||||
}
|
||||
|
||||
// Since we pushed no stack frame, the main loop will act
|
||||
|
|
@ -693,7 +697,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
self.memory.write_int(dest, result, dest_size)?;
|
||||
}
|
||||
|
||||
_ => panic!("can't call C ABI function: {}", link_name),
|
||||
_ => return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name))),
|
||||
}
|
||||
|
||||
// Since we pushed no stack frame, the main loop will act
|
||||
|
|
@ -748,7 +752,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
let ptr = self.eval_operand(operand)?;
|
||||
let ty = self.operand_ty(operand);
|
||||
let val = self.read_primval(ptr, ty)?;
|
||||
self.memory.write_primval(dest, primval::unary_op(un_op, val))?;
|
||||
self.memory.write_primval(dest, primval::unary_op(un_op, val)?)?;
|
||||
}
|
||||
|
||||
Aggregate(ref kind, ref operands) => {
|
||||
|
|
@ -809,7 +813,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
try!(self.assign_fields(dest, offsets, operands));
|
||||
} else {
|
||||
assert_eq!(operands.len(), 0);
|
||||
let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield);
|
||||
let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield)?;
|
||||
let dest = dest.offset(offset.bytes() as isize);
|
||||
try!(self.memory.write_isize(dest, 0));
|
||||
}
|
||||
|
|
@ -834,8 +838,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
_ => panic!("can't handle destination layout {:?} when assigning {:?}",
|
||||
dest_layout, kind),
|
||||
_ => return Err(EvalError::Unimplemented(format!("can't handle destination layout {:?} when assigning {:?}", dest_layout, kind))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -904,7 +907,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
self.memory.write_usize(len_ptr, length as u64)?;
|
||||
}
|
||||
|
||||
_ => panic!("can't handle cast: {:?}", rvalue),
|
||||
_ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -914,7 +917,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
self.memory.copy(src, dest, size)?;
|
||||
}
|
||||
|
||||
_ => panic!("can't handle cast: {:?}", rvalue),
|
||||
_ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -925,7 +928,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> Size {
|
||||
fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<Size> {
|
||||
// Skip the constant 0 at the start meant for LLVM GEP.
|
||||
let mut path = discrfield.iter().skip(1).map(|&i| i as usize);
|
||||
|
||||
|
|
@ -946,49 +949,49 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
self.field_path_offset(inner_ty, path)
|
||||
}
|
||||
|
||||
fn field_path_offset<I: Iterator<Item = usize>>(&self, mut ty: Ty<'tcx>, path: I) -> Size {
|
||||
fn field_path_offset<I: Iterator<Item = usize>>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<Size> {
|
||||
let mut offset = Size::from_bytes(0);
|
||||
|
||||
// Skip the initial 0 intended for LLVM GEP.
|
||||
for field_index in path {
|
||||
let field_offset = self.get_field_offset(ty, field_index);
|
||||
ty = self.get_field_ty(ty, field_index);
|
||||
let field_offset = self.get_field_offset(ty, field_index)?;
|
||||
ty = self.get_field_ty(ty, field_index)?;
|
||||
offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap();
|
||||
}
|
||||
|
||||
offset
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> Ty<'tcx> {
|
||||
fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<Ty<'tcx>> {
|
||||
match ty.sty {
|
||||
ty::TyStruct(adt_def, substs) => {
|
||||
adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)
|
||||
Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs))
|
||||
}
|
||||
|
||||
ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
|
||||
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) |
|
||||
ty::TyBox(ty) => {
|
||||
assert_eq!(field_index, 0);
|
||||
ty
|
||||
Ok(ty)
|
||||
}
|
||||
_ => panic!("can't handle type: {:?}", ty),
|
||||
_ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}", ty))),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> Size {
|
||||
fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<Size> {
|
||||
let layout = self.type_layout(ty);
|
||||
|
||||
use rustc::ty::layout::Layout::*;
|
||||
match *layout {
|
||||
Univariant { .. } => {
|
||||
assert_eq!(field_index, 0);
|
||||
Size::from_bytes(0)
|
||||
Ok(Size::from_bytes(0))
|
||||
}
|
||||
FatPointer { .. } => {
|
||||
let bytes = layout::FAT_PTR_ADDR * self.memory.pointer_size;
|
||||
Size::from_bytes(bytes as u64)
|
||||
Ok(Size::from_bytes(bytes as u64))
|
||||
}
|
||||
_ => panic!("can't handle type: {:?}, with layout: {:?}", ty, layout),
|
||||
_ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, with layout: {:?}", ty, layout))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1197,23 +1200,25 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
|
||||
pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<PrimVal> {
|
||||
use syntax::ast::{IntTy, UintTy};
|
||||
let val = match ty.sty {
|
||||
ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?),
|
||||
ty::TyInt(IntTy::I8) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8),
|
||||
ty::TyInt(IntTy::I16) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16),
|
||||
ty::TyInt(IntTy::I32) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32),
|
||||
ty::TyInt(IntTy::I64) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64),
|
||||
ty::TyUint(UintTy::U8) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8),
|
||||
ty::TyUint(UintTy::U16) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16),
|
||||
ty::TyUint(UintTy::U32) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32),
|
||||
ty::TyUint(UintTy::U64) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64),
|
||||
let val = match (self.memory.pointer_size, &ty.sty) {
|
||||
(_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?),
|
||||
(_, &ty::TyInt(IntTy::I8)) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8),
|
||||
(2, &ty::TyInt(IntTy::Is)) |
|
||||
(_, &ty::TyInt(IntTy::I16)) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16),
|
||||
(4, &ty::TyInt(IntTy::Is)) |
|
||||
(_, &ty::TyInt(IntTy::I32)) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32),
|
||||
(8, &ty::TyInt(IntTy::Is)) |
|
||||
(_, &ty::TyInt(IntTy::I64)) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64),
|
||||
(_, &ty::TyUint(UintTy::U8)) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8),
|
||||
(2, &ty::TyUint(UintTy::Us)) |
|
||||
(_, &ty::TyUint(UintTy::U16)) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16),
|
||||
(4, &ty::TyUint(UintTy::Us)) |
|
||||
(_, &ty::TyUint(UintTy::U32)) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32),
|
||||
(8, &ty::TyUint(UintTy::Us)) |
|
||||
(_, &ty::TyUint(UintTy::U64)) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64),
|
||||
|
||||
// TODO(solson): Pick the PrimVal dynamically.
|
||||
ty::TyInt(IntTy::Is) => PrimVal::I64(self.memory.read_isize(ptr)?),
|
||||
ty::TyUint(UintTy::Us) => PrimVal::U64(self.memory.read_usize(ptr)?),
|
||||
|
||||
ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
|
||||
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
|
||||
(_, &ty::TyRef(_, ty::TypeAndMut { ty, .. })) |
|
||||
(_, &ty::TyRawPtr(ty::TypeAndMut { ty, .. })) => {
|
||||
if self.type_is_sized(ty) {
|
||||
match self.memory.read_ptr(ptr) {
|
||||
Ok(p) => PrimVal::AbstractPtr(p),
|
||||
|
|
@ -1223,7 +1228,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
|
|||
Err(e) => return Err(e),
|
||||
}
|
||||
} else {
|
||||
panic!("unimplemented: primitive read of fat pointer type: {:?}", ty);
|
||||
return Err(EvalError::Unimplemented(format!("unimplemented: primitive read of fat pointer type: {:?}", ty)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,14 +49,12 @@ pub struct Memory {
|
|||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn new() -> Self {
|
||||
// FIXME: pass tcx.data_layout (This would also allow it to use primitive type alignments to diagnose unaligned memory accesses.)
|
||||
pub fn new(pointer_size: usize) -> Self {
|
||||
Memory {
|
||||
alloc_map: HashMap::new(),
|
||||
next_id: AllocId(0),
|
||||
|
||||
// FIXME(solson): This should work for both 4 and 8, but it currently breaks some things
|
||||
// when set to 4.
|
||||
pointer_size: 8,
|
||||
pointer_size: pointer_size,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +78,7 @@ impl Memory {
|
|||
pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<()> {
|
||||
if ptr.offset != 0 {
|
||||
// TODO(solson): Report error about non-__rust_allocate'd pointer.
|
||||
panic!()
|
||||
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
|
||||
}
|
||||
|
||||
let alloc = self.get_mut(ptr.alloc_id)?;
|
||||
|
|
@ -90,7 +88,7 @@ impl Memory {
|
|||
alloc.bytes.extend(iter::repeat(0).take(amount));
|
||||
alloc.undef_mask.grow(amount, false);
|
||||
} else if size > new_size {
|
||||
unimplemented!()
|
||||
return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation")));
|
||||
// alloc.bytes.truncate(new_size);
|
||||
// alloc.undef_mask.len = new_size;
|
||||
// TODO: potentially remove relocations
|
||||
|
|
@ -103,7 +101,7 @@ impl Memory {
|
|||
pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<()> {
|
||||
if ptr.offset != 0 {
|
||||
// TODO(solson): Report error about non-__rust_allocate'd pointer.
|
||||
panic!()
|
||||
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
|
||||
}
|
||||
|
||||
if self.alloc_map.remove(&ptr.alloc_id).is_none() {
|
||||
|
|
@ -183,7 +181,11 @@ impl Memory {
|
|||
fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> {
|
||||
let alloc = self.get(ptr.alloc_id)?;
|
||||
if ptr.offset + size > alloc.bytes.len() {
|
||||
return Err(EvalError::PointerOutOfBounds);
|
||||
return Err(EvalError::PointerOutOfBounds {
|
||||
ptr: ptr,
|
||||
size: size,
|
||||
allocation_size: alloc.bytes.len(),
|
||||
});
|
||||
}
|
||||
Ok(&alloc.bytes[ptr.offset..ptr.offset + size])
|
||||
}
|
||||
|
|
@ -191,7 +193,11 @@ impl Memory {
|
|||
fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> {
|
||||
let alloc = self.get_mut(ptr.alloc_id)?;
|
||||
if ptr.offset + size > alloc.bytes.len() {
|
||||
return Err(EvalError::PointerOutOfBounds);
|
||||
return Err(EvalError::PointerOutOfBounds {
|
||||
ptr: ptr,
|
||||
size: size,
|
||||
allocation_size: alloc.bytes.len(),
|
||||
});
|
||||
}
|
||||
Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,8 +74,7 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResul
|
|||
BitOr => l | r,
|
||||
BitXor => l ^ r,
|
||||
BitAnd => l & r,
|
||||
Add | Sub | Mul | Div | Rem | Shl | Shr =>
|
||||
panic!("invalid binary operation on booleans: {:?}", bin_op),
|
||||
Add | Sub | Mul | Div | Rem | Shl | Shr => return Err(EvalError::InvalidBoolOp(bin_op)),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -99,33 +98,33 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResul
|
|||
Le => Bool(l <= r),
|
||||
Gt => Bool(l > r),
|
||||
Ge => Bool(l >= r),
|
||||
_ => unimplemented!(),
|
||||
_ => return Err(EvalError::Unimplemented(format!("unimplemented ptr op: {:?}", bin_op))),
|
||||
}
|
||||
}
|
||||
|
||||
_ => unimplemented!(),
|
||||
(l, r) => return Err(EvalError::Unimplemented(format!("unimplemented binary op: {:?}, {:?}, {:?}", l, r, bin_op))),
|
||||
};
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
pub fn unary_op(un_op: mir::UnOp, val: PrimVal) -> PrimVal {
|
||||
pub fn unary_op(un_op: mir::UnOp, val: PrimVal) -> EvalResult<PrimVal> {
|
||||
use rustc::mir::repr::UnOp::*;
|
||||
use self::PrimVal::*;
|
||||
match (un_op, val) {
|
||||
(Not, Bool(b)) => Bool(!b),
|
||||
(Not, I8(n)) => I8(!n),
|
||||
(Neg, I8(n)) => I8(-n),
|
||||
(Not, I16(n)) => I16(!n),
|
||||
(Neg, I16(n)) => I16(-n),
|
||||
(Not, I32(n)) => I32(!n),
|
||||
(Neg, I32(n)) => I32(-n),
|
||||
(Not, I64(n)) => I64(!n),
|
||||
(Neg, I64(n)) => I64(-n),
|
||||
(Not, U8(n)) => U8(!n),
|
||||
(Not, U16(n)) => U16(!n),
|
||||
(Not, U32(n)) => U32(!n),
|
||||
(Not, U64(n)) => U64(!n),
|
||||
_ => unimplemented!(),
|
||||
(Not, Bool(b)) => Ok(Bool(!b)),
|
||||
(Not, I8(n)) => Ok(I8(!n)),
|
||||
(Neg, I8(n)) => Ok(I8(-n)),
|
||||
(Not, I16(n)) => Ok(I16(!n)),
|
||||
(Neg, I16(n)) => Ok(I16(-n)),
|
||||
(Not, I32(n)) => Ok(I32(!n)),
|
||||
(Neg, I32(n)) => Ok(I32(-n)),
|
||||
(Not, I64(n)) => Ok(I64(!n)),
|
||||
(Neg, I64(n)) => Ok(I64(-n)),
|
||||
(Not, U8(n)) => Ok(U8(!n)),
|
||||
(Not, U16(n)) => Ok(U16(!n)),
|
||||
(Not, U32(n)) => Ok(U32(!n)),
|
||||
(Not, U64(n)) => Ok(U64(!n)),
|
||||
_ => Err(EvalError::Unimplemented(format!("unimplemented unary op: {:?}, {:?}", un_op, val))),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ fn overwriting_part_of_relocation_makes_the_rest_undefined() -> i32 {
|
|||
let mut p = &42;
|
||||
unsafe {
|
||||
let ptr: *mut _ = &mut p;
|
||||
*(ptr as *mut u32) = 123;
|
||||
*(ptr as *mut u8) = 123; // if we ever support 8 bit pointers, this is gonna cause
|
||||
// "attempted to interpret some raw bytes as a pointer address" instead of
|
||||
// "attempted to read undefined bytes"
|
||||
}
|
||||
*p //~ ERROR: attempted to read undefined bytes
|
||||
}
|
||||
|
|
@ -34,7 +36,7 @@ fn undefined_byte_read() -> u8 {
|
|||
#[miri_run]
|
||||
fn out_of_bounds_read() -> u8 {
|
||||
let v: Vec<u8> = vec![1, 2];
|
||||
unsafe { *v.get_unchecked(5) } //~ ERROR: pointer offset outside bounds of allocation
|
||||
unsafe { *v.get_unchecked(5) } //~ ERROR: memory access of 5..6 outside bounds of allocation 11 which has size 2
|
||||
}
|
||||
|
||||
#[miri_run]
|
||||
|
|
|
|||
|
|
@ -3,17 +3,24 @@ extern crate compiletest_rs as compiletest;
|
|||
use std::path::PathBuf;
|
||||
|
||||
fn run_mode(mode: &'static str) {
|
||||
let mut config = compiletest::default_config();
|
||||
config.rustc_path = "target/debug/miri".into();
|
||||
let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set");
|
||||
config.target_rustcflags = Some(format!("--sysroot {}", path));
|
||||
config.host_rustcflags = Some(format!("--sysroot {}", path));
|
||||
let cfg_mode = mode.parse().ok().expect("Invalid mode");
|
||||
// FIXME: read directories in sysroot/lib/rustlib and generate the test targets from that
|
||||
let targets = &["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"];
|
||||
|
||||
config.mode = cfg_mode;
|
||||
config.src_base = PathBuf::from(format!("tests/{}", mode));
|
||||
for &target in targets {
|
||||
let mut config = compiletest::default_config();
|
||||
config.rustc_path = "target/debug/miri".into();
|
||||
let path = std::env::var("RUST_SYSROOT").expect("env variable `RUST_SYSROOT` not set");
|
||||
config.run_lib_path = format!("{}/lib/rustlib/{}/lib", path, target);
|
||||
let path = format!("--sysroot {}", path);
|
||||
config.target_rustcflags = Some(path.clone());
|
||||
config.host_rustcflags = Some(path);
|
||||
let cfg_mode = mode.parse().ok().expect("Invalid mode");
|
||||
|
||||
compiletest::run_tests(&config);
|
||||
config.mode = cfg_mode;
|
||||
config.src_base = PathBuf::from(format!("tests/{}", mode));
|
||||
config.target = target.to_owned();
|
||||
compiletest::run_tests(&config);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -21,4 +21,9 @@ fn hello_bytes_fat() -> &'static [u8] {
|
|||
b"Hello, world!"
|
||||
}
|
||||
|
||||
#[miri_run]
|
||||
fn fat_pointer_on_32_bit() {
|
||||
Some(5).expect("foo");
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue