diff --git a/src/error.rs b/src/error.rs index 2d723e692352..5b39399070c2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -36,6 +36,10 @@ pub enum EvalError<'tcx> { }, ExecutionTimeLimitReached, StackFrameLimitReached, + AlignmentCheckFailed { + required: usize, + has: usize, + }, } pub type EvalResult<'tcx, T> = Result>; @@ -82,6 +86,8 @@ impl<'tcx> Error for EvalError<'tcx> { "reached the configured maximum execution time", EvalError::StackFrameLimitReached => "reached the configured maximum number of stack frames", + EvalError::AlignmentCheckFailed{..} => + "tried to execute a misaligned read or write", } } @@ -106,6 +112,9 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } => write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory", allocation_size, memory_size - memory_usage, memory_size), + EvalError::AlignmentCheckFailed { required, has } => + write!(f, "tried to access memory with alignment {}, but alignment {} is required", + has, required), _ => write!(f, "{}", self.description()), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 4200340e6a9c..c1e1fff999fd 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -152,7 +152,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match output_ty { ty::FnConverging(ty) => { let size = self.type_size_with_substs(ty, substs); - self.memory.allocate(size).map(Some) + let align = self.type_align_with_substs(ty, substs); + self.memory.allocate(size, align).map(Some) } ty::FnDiverging => Ok(None), } @@ -176,19 +177,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; macro_rules! i2p { ($i:ident, $n:expr) => {{ - let ptr = self.memory.allocate($n)?; + let ptr = self.memory.allocate($n, $n)?; self.memory.write_int(ptr, $i as i64, $n)?; Ok(ptr) }} } match *const_val { Float(ConstFloat::F32(f)) => { - let ptr = self.memory.allocate(4)?; + let ptr = self.memory.allocate(4, 4)?; self.memory.write_f32(ptr, f)?; Ok(ptr) }, Float(ConstFloat::F64(f)) => { - let ptr = self.memory.allocate(8)?; + let ptr = self.memory.allocate(8, 8)?; self.memory.write_f64(ptr, f)?; Ok(ptr) }, @@ -197,22 +198,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Integral(ConstInt::InferSigned(_)) => unreachable!(), Integral(ConstInt::I8(i)) => i2p!(i, 1), Integral(ConstInt::U8(i)) => i2p!(i, 1), + Integral(ConstInt::Isize(ConstIsize::Is16(i))) | Integral(ConstInt::I16(i)) => i2p!(i, 2), + Integral(ConstInt::Usize(ConstUsize::Us16(i))) | Integral(ConstInt::U16(i)) => i2p!(i, 2), + Integral(ConstInt::Isize(ConstIsize::Is32(i))) | Integral(ConstInt::I32(i)) => i2p!(i, 4), + Integral(ConstInt::Usize(ConstUsize::Us32(i))) | Integral(ConstInt::U32(i)) => i2p!(i, 4), + Integral(ConstInt::Isize(ConstIsize::Is64(i))) | Integral(ConstInt::I64(i)) => i2p!(i, 8), + Integral(ConstInt::Usize(ConstUsize::Us64(i))) | Integral(ConstInt::U64(i)) => i2p!(i, 8), - Integral(ConstInt::Isize(ConstIsize::Is16(i))) => i2p!(i, 2), - Integral(ConstInt::Isize(ConstIsize::Is32(i))) => i2p!(i, 4), - Integral(ConstInt::Isize(ConstIsize::Is64(i))) => i2p!(i, 8), - Integral(ConstInt::Usize(ConstUsize::Us16(i))) => i2p!(i, 2), - Integral(ConstInt::Usize(ConstUsize::Us32(i))) => i2p!(i, 4), - Integral(ConstInt::Usize(ConstUsize::Us64(i))) => i2p!(i, 8), Str(ref s) => { let psize = self.memory.pointer_size(); - let static_ptr = self.memory.allocate(s.len())?; - let ptr = self.memory.allocate(psize * 2)?; + let static_ptr = self.memory.allocate(s.len(), 1)?; + let ptr = self.memory.allocate(psize * 2, psize)?; self.memory.write_bytes(static_ptr, s.as_bytes())?; self.memory.write_ptr(ptr, static_ptr)?; self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?; @@ -220,19 +221,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } ByteStr(ref bs) => { let psize = self.memory.pointer_size(); - let static_ptr = self.memory.allocate(bs.len())?; - let ptr = self.memory.allocate(psize)?; + let static_ptr = self.memory.allocate(bs.len(), 1)?; + let ptr = self.memory.allocate(psize, psize)?; self.memory.write_bytes(static_ptr, bs)?; self.memory.write_ptr(ptr, static_ptr)?; Ok(ptr) } Bool(b) => { - let ptr = self.memory.allocate(1)?; + let ptr = self.memory.allocate(1, 1)?; self.memory.write_bool(ptr, b)?; Ok(ptr) } Char(c) => { - let ptr = self.memory.allocate(4)?; + let ptr = self.memory.allocate(4, 4)?; self.memory.write_uint(ptr, c as u64, 4)?; Ok(ptr) }, @@ -278,10 +279,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.type_size_with_substs(ty, self.substs()) } + fn type_align(&self, ty: Ty<'tcx>) -> usize { + self.type_align_with_substs(ty, self.substs()) + } + fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { self.type_layout_with_substs(ty, substs).size(&self.tcx.data_layout).bytes() as usize } + fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { + self.type_layout_with_substs(ty, substs).align(&self.tcx.data_layout).abi() as usize + } + fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout { self.type_layout_with_substs(ty, self.substs()) } @@ -315,7 +324,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let locals: EvalResult<'tcx, Vec> = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { let size = self.type_size_with_substs(ty, substs); - self.memory.allocate(size) + let align = self.type_align_with_substs(ty, substs); + self.memory.allocate(size, align) }).collect(); self.stack.push(Frame { @@ -519,15 +529,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Repeat(ref operand, _) => { - let (elem_size, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), n), + let (elem_size, elem_align, length) = match dest_ty.sty { + ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), self.type_align(elem_ty), n), _ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; let src = self.eval_operand(operand)?; for i in 0..length { let elem_dest = dest.offset((i * elem_size) as isize); - self.memory.copy(src, elem_dest, elem_size)?; + self.memory.copy(src, elem_dest, elem_size, elem_align)?; } } @@ -562,7 +572,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Box(ty) => { let size = self.type_size(ty); - let ptr = self.memory.allocate(size)?; + let align = self.type_align(ty); + let ptr = self.memory.allocate(size, align)?; self.memory.write_ptr(dest, ptr)?; } @@ -593,13 +604,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { warn!("misc cast from {:?} to {:?}", src_ty, dest_ty); let dest_size = self.type_size(dest_ty); let src_size = self.type_size(src_ty); + let dest_align = self.type_align(dest_ty); // Hack to support fat pointer -> thin pointer casts to keep tests for // other things passing for now. let is_fat_ptr_cast = pointee_type(src_ty).map_or(false, |ty| !self.type_is_sized(ty)); if dest_size == src_size || is_fat_ptr_cast { - self.memory.copy(src, dest, dest_size)?; + self.memory.copy(src, dest, dest_size, dest_align)?; } else { return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))); } @@ -710,7 +722,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { // function items are zero sized - Ok(self.memory.allocate(0)?) + Ok(self.memory.allocate(0, 0)?) } else { let cid = ConstantId { def_id: def_id, @@ -843,7 +855,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { let size = self.type_size(ty); - self.memory.copy(src, dest, size)?; + let align = self.type_align(ty); + self.memory.copy(src, dest, size, align)?; Ok(()) } @@ -967,9 +980,9 @@ pub fn eval_main<'a, 'tcx: 'a>( if mir.arg_decls.len() == 2 { // start function let ptr_size = ecx.memory().pointer_size(); - let nargs = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for nargs"); + let nargs = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for nargs"); ecx.memory_mut().write_usize(nargs, 0).unwrap(); - let args = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for arg pointer"); + let args = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for arg pointer"); ecx.memory_mut().write_usize(args, 0).unwrap(); ecx.frame_mut().locals[0] = nargs; ecx.frame_mut().locals[1] = args; diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 2fbc9a63ef68..568d8358f087 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -88,7 +88,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { let ptr = self.eval_operand(func)?; - assert_eq!(ptr.offset, 0); let fn_ptr = self.memory.read_ptr(ptr)?; let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?; if fn_ty != bare_fn_ty { @@ -290,10 +289,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); let elem_size = self.type_size(elem_ty); + let elem_align = self.type_align(elem_ty); let src = self.memory.read_ptr(args_ptrs[0])?; let dest = self.memory.read_ptr(args_ptrs[1])?; let count = self.memory.read_isize(args_ptrs[2])?; - self.memory.copy(src, dest, count as usize * elem_size)?; + self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; } "discriminant_value" => { @@ -308,8 +308,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?, "min_align_of" => { - // FIXME: use correct value - self.memory.write_int(dest, 1, pointer_size)?; + let elem_ty = *substs.types.get(subst::FnSpace, 0); + let elem_align = self.type_align(elem_ty); + self.memory.write_uint(dest, elem_align as u64, pointer_size)?; } "move_val_init" => { @@ -416,14 +417,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match &link_name[..] { "__rust_allocate" => { let size = self.memory.read_usize(args[0])?; - let ptr = self.memory.allocate(size as usize)?; + let align = self.memory.read_usize(args[1])?; + let ptr = self.memory.allocate(size as usize, align as usize)?; self.memory.write_ptr(dest, ptr)?; } "__rust_reallocate" => { let ptr = self.memory.read_ptr(args[0])?; let size = self.memory.read_usize(args[2])?; - let new_ptr = self.memory.reallocate(ptr, size as usize)?; + let align = self.memory.read_usize(args[3])?; + let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?; self.memory.write_ptr(dest, new_ptr)?; } diff --git a/src/memory.rs b/src/memory.rs index 90725fdbba57..d99d2c8132e3 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -29,6 +29,7 @@ pub struct Allocation { pub bytes: Vec, pub relocations: BTreeMap, pub undef_mask: UndefMask, + pub align: usize, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -98,15 +99,16 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { bytes: Vec::new(), relocations: BTreeMap::new(), undef_mask: UndefMask::new(0), + align: 1, }; mem.alloc_map.insert(ZST_ALLOC_ID, alloc); // check that additional zst allocs work - debug_assert!(mem.allocate(0).unwrap().points_to_zst()); + debug_assert!(mem.allocate(0, 1).unwrap().points_to_zst()); debug_assert!(mem.get(ZST_ALLOC_ID).is_ok()); mem } - pub fn allocations<'b>(&'b self) -> ::std::collections::hash_map::Iter<'b, AllocId, Allocation> { + pub fn allocations(&self) -> ::std::collections::hash_map::Iter { self.alloc_map.iter() } @@ -133,7 +135,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn allocate(&mut self, size: usize) -> EvalResult<'tcx, Pointer> { + pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointer> { + assert!(align != 0); if size == 0 { return Ok(Pointer::zst_ptr()); } @@ -149,6 +152,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { bytes: vec![0; size], relocations: BTreeMap::new(), undef_mask: UndefMask::new(size), + align: align, }; let id = self.next_id; self.next_id.0 += 1; @@ -161,16 +165,16 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. - pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<'tcx, Pointer> { + pub fn reallocate(&mut self, ptr: Pointer, new_size: usize, align: usize) -> EvalResult<'tcx, Pointer> { + // TODO(solson): Report error about non-__rust_allocate'd pointer. if ptr.offset != 0 { - // TODO(solson): Report error about non-__rust_allocate'd pointer. return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); } if ptr.points_to_zst() { - return self.allocate(new_size); + return self.allocate(new_size, align); } - let size = self.get_mut(ptr.alloc_id)?.bytes.len(); + let size = self.get(ptr.alloc_id)?.bytes.len(); if new_size > size { let amount = new_size - size; @@ -187,7 +191,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { alloc.undef_mask.truncate(new_size); } - Ok(ptr) + Ok(Pointer { + alloc_id: ptr.alloc_id, + offset: 0, + }) } // TODO(solson): See comment on `reallocate`. @@ -220,6 +227,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn endianess(&self) -> layout::Endian { self.layout.endian } + + pub fn check_align(&self, ptr: Pointer, align: usize) -> EvalResult<'tcx, ()> { + let alloc = self.get(ptr.alloc_id)?; + if alloc.align < align { + return Err(EvalError::AlignmentCheckFailed { + has: alloc.align, + required: align, + }); + } + if ptr.offset % align == 0 { + Ok(()) + } else { + Err(EvalError::AlignmentCheckFailed { + has: ptr.offset % align, + required: align, + }) + } + } } /// Allocation accessors @@ -337,7 +362,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size]) } - fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { + fn get_bytes(&self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &[u8]> { + self.check_align(ptr, align)?; if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); } @@ -345,7 +371,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.get_bytes_unchecked(ptr, size) } - fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &mut [u8]> { + fn get_bytes_mut(&mut self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &mut [u8]> { + self.check_align(ptr, align)?; self.clear_relocations(ptr, size)?; self.mark_definedness(ptr, size, true)?; self.get_bytes_unchecked_mut(ptr, size) @@ -354,11 +381,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { - pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> { + pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize, align: usize) -> EvalResult<'tcx, ()> { self.check_relocation_edges(src, size)?; let src_bytes = self.get_bytes_unchecked_mut(src, size)?.as_mut_ptr(); - let dest_bytes = self.get_bytes_mut(dest, size)?.as_mut_ptr(); + let dest_bytes = self.get_bytes_mut(dest, size, align)?.as_mut_ptr(); // SAFE: The above indexing would have panicked if there weren't at least `size` bytes // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and @@ -378,17 +405,17 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { - self.get_bytes(ptr, size) + self.get_bytes(ptr, size, 1) } pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx, ()> { - let bytes = self.get_bytes_mut(ptr, src.len())?; + let bytes = self.get_bytes_mut(ptr, src.len(), 1)?; bytes.clone_from_slice(src); Ok(()) } pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: usize) -> EvalResult<'tcx, ()> { - let bytes = self.get_bytes_mut(ptr, count)?; + let bytes = self.get_bytes_mut(ptr, count, 1)?; for b in bytes { *b = val; } Ok(()) } @@ -434,7 +461,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> { - let bytes = self.get_bytes(ptr, 1)?; + let bytes = self.get_bytes(ptr, 1, self.layout.i1_align.abi() as usize)?; match bytes[0] { 0 => Ok(false), 1 => Ok(true), @@ -443,27 +470,43 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<'tcx, ()> { - self.get_bytes_mut(ptr, 1).map(|bytes| bytes[0] = b as u8) + let align = self.layout.i1_align.abi() as usize; + self.get_bytes_mut(ptr, 1, align) + .map(|bytes| bytes[0] = b as u8) + } + + fn int_align(&self, size: usize) -> EvalResult<'tcx, usize> { + match size { + 1 => Ok(self.layout.i8_align.abi() as usize), + 2 => Ok(self.layout.i16_align.abi() as usize), + 4 => Ok(self.layout.i32_align.abi() as usize), + 8 => Ok(self.layout.i64_align.abi() as usize), + _ => panic!("bad integer size"), + } } pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, i64> { - self.get_bytes(ptr, size).map(|b| read_target_int(self.endianess(), b).unwrap()) + let align = self.int_align(size)?; + self.get_bytes(ptr, size, align).map(|b| read_target_int(self.endianess(), b).unwrap()) } pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<'tcx, ()> { + let align = self.int_align(size)?; let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, size)?; + let b = self.get_bytes_mut(ptr, size, align)?; write_target_int(endianess, b, n).unwrap(); Ok(()) } pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, u64> { - self.get_bytes(ptr, size).map(|b| read_target_uint(self.endianess(), b).unwrap()) + let align = self.int_align(size)?; + self.get_bytes(ptr, size, align).map(|b| read_target_uint(self.endianess(), b).unwrap()) } pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<'tcx, ()> { + let align = self.int_align(size)?; let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, size)?; + let b = self.get_bytes_mut(ptr, size, align)?; write_target_uint(endianess, b, n).unwrap(); Ok(()) } @@ -488,24 +531,28 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn write_f32(&mut self, ptr: Pointer, f: f32) -> EvalResult<'tcx, ()> { let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, 4)?; + let align = self.layout.f32_align.abi() as usize; + let b = self.get_bytes_mut(ptr, 4, align)?; write_target_f32(endianess, b, f).unwrap(); Ok(()) } pub fn write_f64(&mut self, ptr: Pointer, f: f64) -> EvalResult<'tcx, ()> { let endianess = self.endianess(); - let b = self.get_bytes_mut(ptr, 8)?; + let align = self.layout.f64_align.abi() as usize; + let b = self.get_bytes_mut(ptr, 8, align)?; write_target_f64(endianess, b, f).unwrap(); Ok(()) } pub fn read_f32(&self, ptr: Pointer) -> EvalResult<'tcx, f32> { - self.get_bytes(ptr, 4).map(|b| read_target_f32(self.endianess(), b).unwrap()) + self.get_bytes(ptr, 4, self.layout.f32_align.abi() as usize) + .map(|b| read_target_f32(self.endianess(), b).unwrap()) } pub fn read_f64(&self, ptr: Pointer) -> EvalResult<'tcx, f64> { - self.get_bytes(ptr, 8).map(|b| read_target_f64(self.endianess(), b).unwrap()) + self.get_bytes(ptr, 8, self.layout.f64_align.abi() as usize) + .map(|b| read_target_f64(self.endianess(), b).unwrap()) } } diff --git a/src/primval.rs b/src/primval.rs index 966196d8d1a5..1ec751681703 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -1,3 +1,6 @@ +#![allow(unknown_lints)] +#![allow(float_cmp)] + use rustc::mir::repr as mir; use error::{EvalError, EvalResult}; diff --git a/tests/compile-fail/alignment.rs b/tests/compile-fail/alignment.rs new file mode 100644 index 000000000000..4faaa359df62 --- /dev/null +++ b/tests/compile-fail/alignment.rs @@ -0,0 +1,11 @@ +fn main() { + // miri always gives allocations the worst possible alignment, so a `u8` array is guaranteed + // to be at the virtual location 1 (so one byte offset from the ultimate alignemnt location 0) + let mut x = [0u8; 20]; + let x_ptr: *mut u8 = &mut x[0]; + let y_ptr = x_ptr as *mut u64; + unsafe { + *y_ptr = 42; //~ ERROR tried to access memory with alignment 1, but alignment + } + panic!("unreachable in miri"); +}