diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7b65d5f645..3a31aa4f36 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -35,7 +35,7 @@ pub struct EvalContext<'a, 'tcx: 'a> { mir_cache: RefCell>>>, /// The virtual memory system. - memory: Memory<'tcx>, + memory: Memory<'a, 'tcx>, /// Precomputed statics, constants and promoteds. statics: HashMap, Pointer>, @@ -138,11 +138,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { tcx: tcx, mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), - memory: Memory::new(tcx.sess - .target - .uint_type - .bit_width() - .expect("Session::target::uint_type was usize")/8), + memory: Memory::new(&tcx.data_layout), statics: HashMap::new(), stack: Vec::new(), } @@ -162,7 +158,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &self.memory } - pub fn memory_mut(&mut self) -> &mut Memory<'tcx> { + pub fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx> { &mut self.memory } @@ -182,7 +178,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(ptr) } Str(ref s) => { - let psize = self.memory.pointer_size; + let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(s.len()); let ptr = self.memory.allocate(psize * 2); self.memory.write_bytes(static_ptr, s.as_bytes())?; @@ -191,7 +187,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(ptr) } ByteStr(ref bs) => { - let psize = self.memory.pointer_size; + let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(bs.len()); let ptr = self.memory.allocate(psize); self.memory.write_bytes(static_ptr, bs)?; @@ -515,7 +511,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match lv.extra { LvalueExtra::None => {}, LvalueExtra::Length(len) => { - let len_ptr = dest.offset(self.memory.pointer_size as isize); + let len_ptr = dest.offset(self.memory.pointer_size() as isize); self.memory.write_usize(len_ptr, len)?; } LvalueExtra::DowncastVariant(..) => @@ -541,7 +537,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { - let len_ptr = dest.offset(self.memory.pointer_size as isize); + let len_ptr = dest.offset(self.memory.pointer_size() as isize); self.memory.write_usize(len_ptr, length as u64)?; } @@ -655,7 +651,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Size::from_bytes(0)) } FatPointer { .. } => { - let bytes = layout::FAT_PTR_ADDR * self.memory.pointer_size; + let bytes = layout::FAT_PTR_ADDR * self.memory.pointer_size(); Ok(Size::from_bytes(bytes as u64)) } _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, with layout: {:?}", ty, layout))), @@ -766,7 +762,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.read_ptr(base.ptr)?; let extra = match pointee_ty.sty { ty::TySlice(_) | ty::TyStr => { - let len_ptr = base.ptr.offset(self.memory.pointer_size as isize); + let len_ptr = base.ptr.offset(self.memory.pointer_size() as isize); let len = self.memory.read_usize(len_ptr)?; LvalueExtra::Length(len) } @@ -815,7 +811,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use syntax::ast::{IntTy, UintTy}; - let val = match (self.memory.pointer_size, &ty.sty) { + let val = match (self.memory.pointer_size(), &ty.sty) { (_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?), (_, &ty::TyChar) => { let c = self.memory.read_uint(ptr, 4)? as u32; @@ -923,7 +919,7 @@ pub fn eval_main<'a, 'tcx: 'a>( if mir.arg_decls.len() == 2 { // start function - let ptr_size = ecx.memory().pointer_size; + let ptr_size = ecx.memory().pointer_size(); let nargs = ecx.memory_mut().allocate(ptr_size); ecx.memory_mut().write_usize(nargs, 0).unwrap(); let args = ecx.memory_mut().allocate(ptr_size); diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 3d7bef825b..536d9b678a 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -277,7 +277,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .collect(); let args_ptrs = args_res?; - let pointer_size = self.memory.pointer_size; + let pointer_size = self.memory.pointer_size(); match name { "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, @@ -368,7 +368,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); let elem_size = self.type_size(elem_ty) as u64; - let ptr_size = self.memory.pointer_size as isize; + let ptr_size = self.memory.pointer_size() as isize; let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?; self.memory.write_uint(dest, n * elem_size, pointer_size)?; } @@ -557,7 +557,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.deallocate(contents_ptr)?; } Err(EvalError::ReadBytesAsPointer) => { - let size = self.memory.pointer_size; + let size = self.memory.pointer_size(); let possible_drop_fill = self.memory.read_bytes(ptr, size)?; if possible_drop_fill.iter().all(|&b| b == mem::POST_DROP_U8) { return Ok(()); diff --git a/src/memory.rs b/src/memory.rs index a16fbc57a6..89c7ad7b75 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -6,6 +6,7 @@ use std::{fmt, iter, mem, ptr}; use rustc::hir::def_id::DefId; use rustc::ty::BareFnTy; use rustc::ty::subst::Substs; +use rustc::ty::layout::TargetDataLayout; use error::{EvalError, EvalResult}; use primval::PrimVal; @@ -53,7 +54,7 @@ pub struct FunctionDefinition<'tcx> { // Top-level interpreter memory //////////////////////////////////////////////////////////////////////////////// -pub struct Memory<'tcx> { +pub struct Memory<'a, 'tcx> { /// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations) alloc_map: HashMap, /// Function "allocations". They exist solely so pointers have something to point to, and @@ -62,18 +63,17 @@ pub struct Memory<'tcx> { /// Inverse map of `functions` so we don't allocate a new pointer every time we need one function_alloc_cache: HashMap, AllocId>, next_id: AllocId, - pub pointer_size: usize, + pub layout: &'a TargetDataLayout, } -impl<'tcx> Memory<'tcx> { - // 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 { +impl<'a, 'tcx> Memory<'a, 'tcx> { + pub fn new(layout: &'a TargetDataLayout) -> Self { Memory { alloc_map: HashMap::new(), functions: HashMap::new(), function_alloc_cache: HashMap::new(), next_id: AllocId(0), - pointer_size: pointer_size, + layout: layout, } } @@ -156,10 +156,13 @@ impl<'tcx> Memory<'tcx> { Ok(()) } - //////////////////////////////////////////////////////////////////////////////// - // Allocation accessors - //////////////////////////////////////////////////////////////////////////////// + pub fn pointer_size(&self) -> usize { + self.layout.pointer_size.bytes() as usize + } +} +/// Allocation accessors +impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> { match self.alloc_map.get(&id) { Some(alloc) => Ok(alloc), @@ -235,21 +238,20 @@ impl<'tcx> Memory<'tcx> { if !relocations.is_empty() { print!("{:1$}", "", prefix.len()); // Print spaces. let mut pos = 0; - let relocation_width = (self.pointer_size - 1) * 3; + let relocation_width = (self.pointer_size() - 1) * 3; for (i, target_id) in relocations { print!("{:1$}", "", (i - pos) * 3); print!("└{0:─^1$}┘ ", format!("({})", target_id), relocation_width); - pos = i + self.pointer_size; + pos = i + self.pointer_size(); } println!(""); } } } +} - //////////////////////////////////////////////////////////////////////////////// - // Byte accessors - //////////////////////////////////////////////////////////////////////////////// - +/// Byte accessors +impl<'a, 'tcx> Memory<'a, 'tcx> { fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { let alloc = self.get(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { @@ -287,11 +289,10 @@ impl<'tcx> Memory<'tcx> { self.mark_definedness(ptr, size, true)?; self.get_bytes_unchecked_mut(ptr, size) } +} - //////////////////////////////////////////////////////////////////////////////// - // Reading and writing - //////////////////////////////////////////////////////////////////////////////// - +/// Reading and writing +impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { self.check_relocation_edges(src, size)?; @@ -336,7 +337,7 @@ impl<'tcx> Memory<'tcx> { } pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, Pointer> { - let size = self.pointer_size; + let size = self.pointer_size(); self.check_defined(ptr, size)?; let offset = self.get_bytes_unchecked(ptr, size)? .read_uint::(size).unwrap() as usize; @@ -349,7 +350,7 @@ impl<'tcx> Memory<'tcx> { pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx, ()> { { - let size = self.pointer_size; + let size = self.pointer_size(); let mut bytes = self.get_bytes_mut(dest, size)?; bytes.write_uint::(ptr.offset as u64, size).unwrap(); } @@ -358,7 +359,7 @@ impl<'tcx> Memory<'tcx> { } pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> { - let pointer_size = self.pointer_size; + let pointer_size = self.pointer_size(); match val { PrimVal::Bool(b) => self.write_bool(ptr, b), PrimVal::I8(n) => self.write_int(ptr, n as i64, 1), @@ -406,31 +407,30 @@ impl<'tcx> Memory<'tcx> { } pub fn read_isize(&self, ptr: Pointer) -> EvalResult<'tcx, i64> { - self.read_int(ptr, self.pointer_size) + self.read_int(ptr, self.pointer_size()) } pub fn write_isize(&mut self, ptr: Pointer, n: i64) -> EvalResult<'tcx, ()> { - let size = self.pointer_size; + let size = self.pointer_size(); self.write_int(ptr, n, size) } pub fn read_usize(&self, ptr: Pointer) -> EvalResult<'tcx, u64> { - self.read_uint(ptr, self.pointer_size) + self.read_uint(ptr, self.pointer_size()) } pub fn write_usize(&mut self, ptr: Pointer, n: u64) -> EvalResult<'tcx, ()> { - let size = self.pointer_size; + let size = self.pointer_size(); self.write_uint(ptr, n, size) } +} - //////////////////////////////////////////////////////////////////////////////// - // Relocations - //////////////////////////////////////////////////////////////////////////////// - +/// Relocations +impl<'a, 'tcx> Memory<'a, 'tcx> { fn relocations(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, btree_map::Range> { - let start = ptr.offset.saturating_sub(self.pointer_size - 1); + let start = ptr.offset.saturating_sub(self.pointer_size() - 1); let end = ptr.offset + size; Ok(self.get(ptr.alloc_id)?.relocations.range(Included(&start), Excluded(&end))) } @@ -444,7 +444,7 @@ impl<'tcx> Memory<'tcx> { let start = ptr.offset; let end = start + size; let first = *keys.first().unwrap(); - let last = *keys.last().unwrap() + self.pointer_size; + let last = *keys.last().unwrap() + self.pointer_size(); let alloc = self.get_mut(ptr.alloc_id)?; @@ -478,11 +478,10 @@ impl<'tcx> Memory<'tcx> { self.get_mut(dest.alloc_id)?.relocations.extend(relocations); Ok(()) } +} - //////////////////////////////////////////////////////////////////////////////// - // Undefined bytes - //////////////////////////////////////////////////////////////////////////////// - +/// Undefined bytes +impl<'a, 'tcx> Memory<'a, 'tcx> { // FIXME(solson): This is a very naive, slow version. fn copy_undef_mask(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { // The bits have to be saved locally before writing to dest in case src and dest overlap. diff --git a/tests/compile-fail/invalid_enum_discriminant.rs b/tests/compile-fail/invalid_enum_discriminant.rs new file mode 100644 index 0000000000..bde78200b3 --- /dev/null +++ b/tests/compile-fail/invalid_enum_discriminant.rs @@ -0,0 +1,14 @@ +#[repr(C)] +pub enum Foo { + A, B, C, D +} + +fn main() { + let f = unsafe { std::mem::transmute::(42) }; + match f { + Foo::A => {}, //~ ERROR invalid enum discriminant value read + Foo::B => {}, + Foo::C => {}, + Foo::D => {}, + } +} diff --git a/tests/compile-fail/match_char.rs b/tests/compile-fail/match_char.rs new file mode 100644 index 0000000000..a91c7fef6a --- /dev/null +++ b/tests/compile-fail/match_char.rs @@ -0,0 +1,8 @@ +fn main() { + assert!(std::char::from_u32(-1_i32 as u32).is_none()); + match unsafe { std::mem::transmute::(-1) } { + 'a' => {}, //~ERROR tried to interpret an invalid 32-bit value as a char: 4294967295 + 'b' => {}, + _ => {}, + } +} diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 5430501f05..a401257c6a 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -77,7 +77,7 @@ fn compile_test() { match cmd.output() { Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(), Ok(output) => { - writeln!(stderr.lock(), "FAILED with exit code {}", output.status.code().unwrap_or(0)).unwrap(); + writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap(); panic!("some tests failed");