diff options
| author | Oliver Schneider <oli-obk@users.noreply.github.com> | 2017-07-24 11:31:25 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-07-24 11:31:25 +0200 |
| commit | 03ca15e62d3a3386b824139a36597b4841e7d328 (patch) | |
| tree | 26d71a7362d957f19416c3961f1b4e59fe69ca19 | |
| parent | 6e10752fef8a72090a4625b73dee926da2c95330 (diff) | |
| parent | 4a34a1b50e9a554ef6cca390d1c5f666f0f86e2d (diff) | |
| download | rust-03ca15e62d3a3386b824139a36597b4841e7d328.tar.gz rust-03ca15e62d3a3386b824139a36597b4841e7d328.zip | |
Merge pull request #267 from RalfJung/cast
Fix ptr-int-casts
| -rw-r--r-- | src/librustc_mir/interpret/cast.rs | 69 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/eval_context.rs | 22 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/lvalue.rs | 8 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/memory.rs | 116 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/operator.rs | 6 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/step.rs | 2 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/terminator/mod.rs | 13 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/traits.rs | 13 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/value.rs | 61 | ||||
| -rw-r--r-- | tests/run-pass/ptr_int_casts.rs | 3 |
10 files changed, 184 insertions, 129 deletions
diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 0fa38366e1c..84de97488c5 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -4,6 +4,7 @@ use syntax::ast::{FloatTy, IntTy, UintTy}; use error::{EvalResult, EvalError}; use eval_context::EvalContext; use value::PrimVal; +use memory::{MemoryPointer, PointerArithmetic}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn cast_primval( @@ -12,38 +13,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { src_ty: Ty<'tcx>, dest_ty: Ty<'tcx> ) -> EvalResult<'tcx, PrimVal> { - let kind = self.ty_to_primval_kind(src_ty)?; - - use value::PrimValKind::*; - match kind { - F32 => self.cast_float(val.to_f32()? as f64, dest_ty), - F64 => self.cast_float(val.to_f64()?, dest_ty), - - I8 | I16 | I32 | I64 | I128 => { - if val.is_ptr() { - self.cast_ptr(val, dest_ty) - } else { - self.cast_signed_int(val.to_i128()?, dest_ty) + let src_kind = self.ty_to_primval_kind(src_ty)?; + + match val { + PrimVal::Undef => Ok(PrimVal::Undef), + PrimVal::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty), + val @ PrimVal::Bytes(_) => { + use value::PrimValKind::*; + match src_kind { + F32 => self.cast_from_float(val.to_f32()? as f64, dest_ty), + F64 => self.cast_from_float(val.to_f64()?, dest_ty), + + I8 | I16 | I32 | I64 | I128 => { + self.cast_from_signed_int(val.to_i128()?, dest_ty) + }, + + Bool | Char | U8 | U16 | U32 | U64 | U128 | FnPtr | Ptr => { + self.cast_from_int(val.to_u128()?, dest_ty, false) + }, } - }, - - Bool | Char | U8 | U16 | U32 | U64 | U128 => { - if val.is_ptr() { - self.cast_ptr(val, dest_ty) - } else { - self.cast_int(val.to_u128()?, dest_ty, false) - } - }, - - FnPtr | Ptr => self.cast_ptr(val, dest_ty), + } } } - fn cast_signed_int(&self, val: i128, ty: ty::Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - self.cast_int(val as u128, ty, val < 0) + fn cast_from_signed_int(&self, val: i128, ty: ty::Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + self.cast_from_int(val as u128, ty, val < 0) } - fn cast_int(&self, v: u128, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> { + fn cast_from_int(&self, v: u128, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; match ty.sty { // Casts to bool are not permitted by rustc, no need to handle them here. @@ -63,13 +60,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { TyInt(IntTy::Is) => { let int_ty = self.tcx.sess.target.int_type; let ty = self.tcx.mk_mach_int(int_ty); - self.cast_int(v, ty, negative) + self.cast_from_int(v, ty, negative) } TyUint(UintTy::Us) => { let uint_ty = self.tcx.sess.target.uint_type; let ty = self.tcx.mk_mach_uint(uint_ty); - self.cast_int(v, ty, negative) + self.cast_from_int(v, ty, negative) } TyFloat(FloatTy::F64) if negative => Ok(PrimVal::from_f64(v as i128 as f64)), @@ -80,21 +77,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), TyChar => Err(EvalError::InvalidChar(v)), - // No alignment check needed for raw pointers - TyRawPtr(_) => Ok(PrimVal::Bytes(v % (1 << self.memory.pointer_size()))), + // No alignment check needed for raw pointers. But we have to truncate to target ptr size. + TyRawPtr(_) => Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)), _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), } } - fn cast_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + fn cast_from_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; match ty.sty { // Casting negative floats to unsigned integers yields zero. - TyUint(_) if val < 0.0 => self.cast_int(0, ty, false), - TyInt(_) if val < 0.0 => self.cast_int(val as i128 as u128, ty, true), + TyUint(_) if val < 0.0 => self.cast_from_int(0, ty, false), + TyInt(_) if val < 0.0 => self.cast_from_int(val as i128 as u128, ty, true), - TyInt(_) | ty::TyUint(_) => self.cast_int(val as u128, ty, false), + TyInt(_) | ty::TyUint(_) => self.cast_from_int(val as u128, ty, false), TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(val)), TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(val as f32)), @@ -102,12 +99,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn cast_ptr(&self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + fn cast_from_ptr(&self, ptr: MemoryPointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; match ty.sty { // Casting to a reference or fn pointer is not permitted by rustc, no need to support it here. TyRawPtr(_) | TyInt(IntTy::Is) | TyUint(UintTy::Us) => - Ok(ptr), + Ok(PrimVal::Ptr(ptr)), TyInt(_) | TyUint(_) => Err(EvalError::ReadPointerAsBytes), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 2f529dcbf48..542cd5ae819 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -439,7 +439,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson) let dest_ptr = self.force_allocation(dest)?.to_ptr()?; - let discr_dest = dest_ptr.offset(discr_offset, self.memory.layout)?; + let discr_dest = dest_ptr.offset(discr_offset, &self)?; self.memory.write_uint(discr_dest, discr_val, discr_size)?; let dest = Lvalue::Ptr { @@ -585,7 +585,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr()?; - let dest = dest.offset(offset.bytes(), self.memory.layout)?; + let dest = dest.offset(offset.bytes(), &self)?; let dest_size = self.type_size(ty)? .expect("bad StructWrappedNullablePointer discrfield"); self.memory.write_int(dest, 0, dest_size)?; @@ -645,7 +645,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = Pointer::from(self.force_allocation(dest)?.to_ptr()?); for i in 0..length { - let elem_dest = dest.offset(i * elem_size, self.memory.layout)?; + let elem_dest = dest.offset(i * elem_size, &self)?; self.write_value_to_ptr(value, elem_dest, elem_ty)?; } } @@ -911,7 +911,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = offset.overflowing_mul(pointee_size).0; - ptr.wrapping_signed_offset(offset, self.memory.layout) + ptr.wrapping_signed_offset(offset, self) } pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { @@ -926,7 +926,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; return if let Some(offset) = offset.checked_mul(pointee_size) { - let ptr = ptr.signed_offset(offset, self.memory.layout)?; + let ptr = ptr.signed_offset(offset, self)?; // Do not do bounds-checking for integers; they can never alias a normal pointer anyway. if let PrimVal::Ptr(ptr) = ptr.into_inner_primval() { self.memory.check_bounds(ptr, false)?; @@ -1217,8 +1217,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field_1_ty = self.get_field_ty(ty, 1)?; let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); - self.memory.write_primval(ptr.offset(field_0, self.memory.layout)?.into(), a, field_0_size)?; - self.memory.write_primval(ptr.offset(field_1, self.memory.layout)?.into(), b, field_1_size)?; + let field_0_ptr = ptr.offset(field_0, &self)?.into(); + let field_1_ptr = ptr.offset(field_1, &self)?.into(); + self.memory.write_primval(field_0_ptr, a, field_0_size)?; + self.memory.write_primval(field_1_ptr, b, field_1_size)?; Ok(()) } @@ -1335,7 +1337,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(p.to_value()) } else { trace!("reading fat pointer extra of type {}", pointee_ty); - let extra = ptr.offset(self.memory.pointer_size(), self.memory.layout)?; + let extra = ptr.offset(self.memory.pointer_size(), self)?; match self.tcx.struct_tail(pointee_ty).sty { ty::TyDynamic(..) => Ok(p.to_value_with_vtable(self.memory.read_ptr(extra)?.to_ptr()?)), ty::TySlice(..) | @@ -1530,8 +1532,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let src_field_offset = self.get_field_offset(src_ty, i)?.bytes(); let dst_field_offset = self.get_field_offset(dest_ty, i)?.bytes(); - let src_f_ptr = src_ptr.offset(src_field_offset, self.memory.layout)?; - let dst_f_ptr = dest.offset(dst_field_offset, self.memory.layout)?; + let src_f_ptr = src_ptr.offset(src_field_offset, &self)?; + let dst_f_ptr = dest.offset(dst_field_offset, &self)?; if src_fty == dst_fty { self.copy(src_f_ptr, dst_f_ptr.into(), src_fty)?; } else { diff --git a/src/librustc_mir/interpret/lvalue.rs b/src/librustc_mir/interpret/lvalue.rs index 59f5dc8f4bb..56e8c7f2b18 100644 --- a/src/librustc_mir/interpret/lvalue.rs +++ b/src/librustc_mir/interpret/lvalue.rs @@ -329,7 +329,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => offset.bytes(), }; - let ptr = base_ptr.offset(offset, self.memory.layout)?; + let ptr = base_ptr.offset(offset, &self)?; let field_ty = self.monomorphize(field_ty, self.substs()); @@ -412,7 +412,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let usize = self.tcx.types.usize; let n = self.value_to_primval(n_ptr, usize)?.to_u64()?; assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len); - let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?; + let ptr = base_ptr.offset(n * elem_size, &self)?; (ptr, LvalueExtra::None, aligned) } @@ -431,7 +431,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { u64::from(offset) }; - let ptr = base_ptr.offset(index * elem_size, self.memory.layout)?; + let ptr = base_ptr.offset(index * elem_size, &self)?; (ptr, LvalueExtra::None, aligned) } @@ -443,7 +443,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (elem_ty, n) = base.elem_ty_and_len(base_ty); let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); assert!(u64::from(from) <= n - u64::from(to)); - let ptr = base_ptr.offset(u64::from(from) * elem_size, self.memory.layout)?; + let ptr = base_ptr.offset(u64::from(from) * elem_size, &self)?; let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from)); (ptr, extra, aligned) } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 481744b11c4..815d1e2e841 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -3,11 +3,11 @@ use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque}; use std::{fmt, iter, ptr, mem, io}; use rustc::ty; -use rustc::ty::layout::{self, TargetDataLayout}; +use rustc::ty::layout::{self, TargetDataLayout, HasDataLayout}; use syntax::ast::Mutability; use error::{EvalError, EvalResult}; -use value::{PrimVal, self, Pointer}; +use value::{PrimVal, Pointer}; use eval_context::EvalContext; //////////////////////////////////////////////////////////////////////////////// @@ -68,31 +68,31 @@ pub struct MemoryPointer { pub offset: u64, } -impl MemoryPointer { +impl<'tcx> MemoryPointer { pub fn new(alloc_id: AllocId, offset: u64) -> Self { MemoryPointer { alloc_id, offset } } - pub fn wrapping_signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> Self { - MemoryPointer::new(self.alloc_id, value::wrapping_signed_offset(self.offset, i, layout)) + pub(crate) fn wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> Self { + MemoryPointer::new(self.alloc_id, cx.data_layout().wrapping_signed_offset(self.offset, i)) } - pub fn overflowing_signed_offset<'tcx>(self, i: i128, layout: &TargetDataLayout) -> (Self, bool) { - let (res, over) = value::overflowing_signed_offset(self.offset, i, layout); + pub(crate) fn overflowing_signed_offset<C: HasDataLayout>(self, i: i128, cx: C) -> (Self, bool) { + let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset, i); (MemoryPointer::new(self.alloc_id, res), over) } - pub fn signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - Ok(MemoryPointer::new(self.alloc_id, value::signed_offset(self.offset, i, layout)?)) + pub(crate) fn signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> { + Ok(MemoryPointer::new(self.alloc_id, cx.data_layout().signed_offset(self.offset, i)?)) } - pub fn overflowing_offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> (Self, bool) { - let (res, over) = value::overflowing_offset(self.offset, i, layout); + pub(crate) fn overflowing_offset<C: HasDataLayout>(self, i: u64, cx: C) -> (Self, bool) { + let (res, over) = cx.data_layout().overflowing_offset(self.offset, i); (MemoryPointer::new(self.alloc_id, res), over) } - pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - Ok(MemoryPointer::new(self.alloc_id, value::offset(self.offset, i, layout)?)) + pub(crate) fn offset<C: HasDataLayout>(self, i: u64, cx: C) -> EvalResult<'tcx, Self> { + Ok(MemoryPointer::new(self.alloc_id, cx.data_layout().offset(self.offset, i)?)) } } @@ -540,7 +540,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&[]); } - self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) + self.check_bounds(ptr.offset(size, self)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); assert_eq!(size as usize as u64, size); @@ -1131,6 +1131,7 @@ fn bit_index(bits: u64) -> (usize, usize) { pub(crate) trait HasMemory<'a, 'tcx> { fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx>; + fn memory(&self) -> &Memory<'a, 'tcx>; // These are not supposed to be overriden. fn read_maybe_aligned<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T> @@ -1159,6 +1160,11 @@ impl<'a, 'tcx> HasMemory<'a, 'tcx> for Memory<'a, 'tcx> { fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx> { self } + + #[inline] + fn memory(&self) -> &Memory<'a, 'tcx> { + self + } } impl<'a, 'tcx> HasMemory<'a, 'tcx> for EvalContext<'a, 'tcx> { @@ -1166,4 +1172,86 @@ impl<'a, 'tcx> HasMemory<'a, 'tcx> for EvalContext<'a, 'tcx> { fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx> { &mut self.memory } + + #[inline] + fn memory(&self) -> &Memory<'a, 'tcx> { + &self.memory + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Pointer arithmetic +//////////////////////////////////////////////////////////////////////////////// + +pub trait PointerArithmetic : layout::HasDataLayout { + // These are not supposed to be overriden. + + //// Trunace the given value to the pointer size; also return whether there was an overflow + fn truncate_to_ptr(self, val: u128) -> (u64, bool) { + let max_ptr_plus_1 = 1u128 << self.data_layout().pointer_size.bits(); + ((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1) + } + + // Overflow checking only works properly on the range from -u64 to +u64. + fn overflowing_signed_offset(self, val: u64, i: i128) -> (u64, bool) { + // FIXME: is it possible to over/underflow here? + if i < 0 { + // trickery to ensure that i64::min_value() works fine + // this formula only works for true negative values, it panics for zero! + let n = u64::max_value() - (i as u64) + 1; + val.overflowing_sub(n) + } else { + self.overflowing_offset(val, i as u64) + } + } + + fn overflowing_offset(self, val: u64, i: u64) -> (u64, bool) { + let (res, over1) = val.overflowing_add(i); + let (res, over2) = self.truncate_to_ptr(res as u128); + (res, over1 || over2) + } + + fn signed_offset<'tcx>(self, val: u64, i: i64) -> EvalResult<'tcx, u64> { + let (res, over) = self.overflowing_signed_offset(val, i as i128); + if over { + Err(EvalError::OverflowingMath) + } else { + Ok(res) + } + } + + fn offset<'tcx>(self, val: u64, i: u64) -> EvalResult<'tcx, u64> { + let (res, over) = self.overflowing_offset(val, i); + if over { + Err(EvalError::OverflowingMath) + } else { + Ok(res) + } + } + + fn wrapping_signed_offset(self, val: u64, i: i64) -> u64 { + self.overflowing_signed_offset(val, i as i128).0 + } +} + +impl<T: layout::HasDataLayout> PointerArithmetic for T {} + +impl<'a, 'tcx> layout::HasDataLayout for &'a Memory<'a, 'tcx> { + #[inline] + fn data_layout(&self) -> &TargetDataLayout { + self.layout + } +} +impl<'a, 'tcx> layout::HasDataLayout for &'a EvalContext<'a, 'tcx> { + #[inline] + fn data_layout(&self) -> &TargetDataLayout { + self.memory().layout + } +} + +impl<'c, 'b, 'a, 'tcx> layout::HasDataLayout for &'c &'b mut EvalContext<'a, 'tcx> { + #[inline] + fn data_layout(&self) -> &TargetDataLayout { + self.memory().layout + } } diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 47bd66641b1..930fc5c4047 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -310,11 +310,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(match bin_op { Sub => // The only way this can overflow is by underflowing, so signdeness of the right operands does not matter - map_to_primval(left.overflowing_signed_offset(-right, self.memory.layout)), + map_to_primval(left.overflowing_signed_offset(-right, self)), Add if signed => - map_to_primval(left.overflowing_signed_offset(right, self.memory.layout)), + map_to_primval(left.overflowing_signed_offset(right, self)), Add if !signed => - map_to_primval(left.overflowing_offset(right as u64, self.memory.layout)), + map_to_primval(left.overflowing_offset(right as u64, self)), BitAnd if !signed => { let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align - 1); diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 4804059d799..6e54420e2eb 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -101,7 +101,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { if variant_index as u64 != nndiscr { let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; - let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(offset.bytes(), self.memory.layout)?; + let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(offset.bytes(), &self)?; trace!("struct wrapped nullable pointer type: {}", ty); // only the pointer part of a fat pointer is used for this space optimization let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index 73511c38341..a44713f221f 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -316,7 +316,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByRef(ptr, aligned) => { assert!(aligned, "Unaligned ByRef-values cannot occur as function arguments"); for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { - let arg = Value::ByRef(ptr.offset(offset, self.memory.layout)?, true); + let arg = Value::ByRef(ptr.offset(offset, &self)?, true); let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty); self.write_value(arg, dest, ty)?; @@ -398,7 +398,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::InstanceDef::Virtual(_, idx) => { let ptr_size = self.memory.pointer_size(); let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(&mut self.memory)?; - let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?; + let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), &self)?)?; let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?; let mut arg_operands = arg_operands.to_vec(); let ty = self.operand_ty(&arg_operands[0]); @@ -488,7 +488,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; - let nonnull = adt_ptr.offset(offset.bytes(), self.memory.layout)?; + let nonnull = adt_ptr.offset(offset.bytes(), self)?; trace!("struct wrapped nullable pointer type: {}", ty); // only the pointer part of a fat pointer is used for this space optimization let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); @@ -746,7 +746,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { - let new_ptr = ptr.offset(num - idx as u64 - 1, self.memory.layout)?; + let new_ptr = ptr.offset(num - idx as u64 - 1, &self)?; self.write_ptr(dest, new_ptr, dest_ty)?; } else { self.write_null(dest, dest_ty)?; @@ -758,7 +758,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { - let new_ptr = ptr.offset(idx as u64, self.memory.layout)?; + let new_ptr = ptr.offset(idx as u64, &self)?; self.write_ptr(dest, new_ptr, dest_ty)?; } else { self.write_null(dest, dest_ty)?; @@ -815,7 +815,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // +1 for the null terminator let value_copy = self.memory.allocate((value.len() + 1) as u64, 1, Kind::Env)?; self.memory.write_bytes(value_copy.into(), &value)?; - self.memory.write_bytes(value_copy.offset(value.len() as u64, self.memory.layout)?.into(), &[0])?; + let trailing_null = value_copy.offset(value.len() as u64, &self)?.into(); + self.memory.write_bytes(trailing_null, &[0])?; if let Some(var) = self.env_vars.insert(name.to_owned(), value_copy) { self.memory.deallocate(var, None, Kind::Env)?; } diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 615ea214595..fa5e5da8592 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -57,14 +57,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let drop = self.memory.create_fn_alloc(drop); self.memory.write_ptr(vtable, drop)?; - self.memory.write_usize(vtable.offset(ptr_size, self.memory.layout)?, size)?; - self.memory.write_usize(vtable.offset(ptr_size * 2, self.memory.layout)?, align)?; + let size_ptr = vtable.offset(ptr_size, &self)?; + self.memory.write_usize(size_ptr, size)?; + let align_ptr = vtable.offset(ptr_size * 2, &self)?; + self.memory.write_usize(align_ptr, align)?; for (i, method) in ::rustc::traits::get_vtable_methods(self.tcx, trait_ref).enumerate() { if let Some((def_id, substs)) = method { let instance = eval_context::resolve(self.tcx, def_id, substs); let fn_ptr = self.memory.create_fn_alloc(instance); - self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64), self.memory.layout)?, fn_ptr)?; + let method_ptr = vtable.offset(ptr_size * (3 + i as u64), &self)?; + self.memory.write_ptr(method_ptr, fn_ptr)?; } } @@ -85,8 +88,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn read_size_and_align_from_vtable(&self, vtable: MemoryPointer) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); - let size = self.memory.read_usize(vtable.offset(pointer_size, self.memory.layout)?)?; - let align = self.memory.read_usize(vtable.offset(pointer_size * 2, self.memory.layout)?)?; + let size = self.memory.read_usize(vtable.offset(pointer_size, self)?)?; + let align = self.memory.read_usize(vtable.offset(pointer_size * 2, self)?)?; Ok((size, align)) } diff --git a/src/librustc_mir/interpret/value.rs b/src/librustc_mir/interpret/value.rs index 606a3516485..a4115ddb5cc 100644 --- a/src/librustc_mir/interpret/value.rs +++ b/src/librustc_mir/interpret/value.rs @@ -1,10 +1,9 @@ #![allow(unknown_lints)] #![allow(float_cmp)] -use rustc::ty::layout::TargetDataLayout; - use error::{EvalError, EvalResult}; -use memory::{Memory, MemoryPointer, HasMemory}; +use memory::{Memory, MemoryPointer, HasMemory, PointerArithmetic}; +use rustc::ty::layout::HasDataLayout; pub(super) fn bytes_to_f32(bytes: u128) -> f32 { f32::from_bits(bytes as u32) @@ -61,33 +60,36 @@ impl<'tcx> Pointer { self.primval } - pub(crate) fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> { + let layout = cx.data_layout(); match self.primval { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); - Ok(Pointer::from(PrimVal::Bytes(signed_offset(b as u64, i, layout)? as u128))) + Ok(Pointer::from(PrimVal::Bytes(layout.signed_offset(b as u64, i)? as u128))) }, PrimVal::Ptr(ptr) => ptr.signed_offset(i, layout).map(Pointer::from), PrimVal::Undef => Err(EvalError::ReadUndefBytes), } } - pub(crate) fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn offset<C: HasDataLayout>(self, i: u64, cx: C) -> EvalResult<'tcx, Self> { + let layout = cx.data_layout(); match self.primval { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); - Ok(Pointer::from(PrimVal::Bytes(offset(b as u64, i, layout)? as u128))) + Ok(Pointer::from(PrimVal::Bytes(layout.offset(b as u64, i)? as u128))) }, PrimVal::Ptr(ptr) => ptr.offset(i, layout).map(Pointer::from), PrimVal::Undef => Err(EvalError::ReadUndefBytes), } } - pub(crate) fn wrapping_signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn wrapping_signed_offset<C: HasDataLayout>(self, i: i64, cx: C) -> EvalResult<'tcx, Self> { + let layout = cx.data_layout(); match self.primval { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); - Ok(Pointer::from(PrimVal::Bytes(wrapping_signed_offset(b as u64, i, layout) as u128))) + Ok(Pointer::from(PrimVal::Bytes(layout.wrapping_signed_offset(b as u64, i) as u128))) }, PrimVal::Ptr(ptr) => Ok(Pointer::from(ptr.wrapping_signed_offset(i, layout))), PrimVal::Undef => Err(EvalError::ReadUndefBytes), @@ -323,47 +325,6 @@ impl<'tcx> PrimVal { } } -// Overflow checking only works properly on the range from -u64 to +u64. -pub fn overflowing_signed_offset<'tcx>(val: u64, i: i128, layout: &TargetDataLayout) -> (u64, bool) { - // FIXME: is it possible to over/underflow here? - if i < 0 { - // trickery to ensure that i64::min_value() works fine - // this formula only works for true negative values, it panics for zero! - let n = u64::max_value() - (i as u64) + 1; - val.overflowing_sub(n) - } else { - overflowing_offset(val, i as u64, layout) - } -} - -pub fn overflowing_offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> (u64, bool) { - let (res, over) = val.overflowing_add(i); - ((res as u128 % (1u128 << layout.pointer_size.bits())) as u64, - over || res as u128 >= (1u128 << layout.pointer_size.bits())) -} - -pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { - let (res, over) = overflowing_signed_offset(val, i as i128, layout); - if over { - Err(EvalError::OverflowingMath) - } else { - Ok(res) - } -} - -pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { - let (res, over) = overflowing_offset(val, i, layout); - if over { - Err(EvalError::OverflowingMath) - } else { - Ok(res) - } -} - -pub fn wrapping_signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> u64 { - overflowing_signed_offset(val, i as i128, layout).0 -} - impl PrimValKind { pub fn is_int(self) -> bool { use self::PrimValKind::*; diff --git a/tests/run-pass/ptr_int_casts.rs b/tests/run-pass/ptr_int_casts.rs index 88fb16e069e..b1b06263056 100644 --- a/tests/run-pass/ptr_int_casts.rs +++ b/tests/run-pass/ptr_int_casts.rs @@ -29,4 +29,7 @@ fn main() { let x : fn() -> i32 = unsafe { mem::transmute(y as *mut u8) }; assert_eq!(x(), 42); } + + // involving types other than usize + assert_eq!((-1i32) as usize as *const i32 as usize, (-1i32) as usize); } |
