about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOliver Schneider <oli-obk@users.noreply.github.com>2017-07-24 11:31:25 +0200
committerGitHub <noreply@github.com>2017-07-24 11:31:25 +0200
commit03ca15e62d3a3386b824139a36597b4841e7d328 (patch)
tree26d71a7362d957f19416c3961f1b4e59fe69ca19
parent6e10752fef8a72090a4625b73dee926da2c95330 (diff)
parent4a34a1b50e9a554ef6cca390d1c5f666f0f86e2d (diff)
downloadrust-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.rs69
-rw-r--r--src/librustc_mir/interpret/eval_context.rs22
-rw-r--r--src/librustc_mir/interpret/lvalue.rs8
-rw-r--r--src/librustc_mir/interpret/memory.rs116
-rw-r--r--src/librustc_mir/interpret/operator.rs6
-rw-r--r--src/librustc_mir/interpret/step.rs2
-rw-r--r--src/librustc_mir/interpret/terminator/mod.rs13
-rw-r--r--src/librustc_mir/interpret/traits.rs13
-rw-r--r--src/librustc_mir/interpret/value.rs61
-rw-r--r--tests/run-pass/ptr_int_casts.rs3
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);
 }