diff --git a/Cargo.lock b/Cargo.lock
index ae148d2cd131135aab980236b1c5688053a5aea9..fdaeb0fac088793922ff36675b7ecd5fd1fc82b9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -21,15 +21,31 @@ dependencies = [
  "serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "cfg-if"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "compiletest_rs"
-version = "0.2.10"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
+ "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "diff"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "dtoa"
 version = "0.4.2"
@@ -44,6 +60,21 @@ dependencies = [
  "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "filetime"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "getopts"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "itoa"
 version = "0.3.3"
@@ -94,6 +125,29 @@ dependencies = [
  "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "miow"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "net2"
+version = "0.2.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "num-traits"
 version = "0.1.40"
@@ -104,6 +158,11 @@ name = "quote"
 version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "redox_syscall"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "regex"
 version = "0.1.80"
@@ -132,7 +191,7 @@ version = "0.0.1"
 dependencies = [
  "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cargo_metadata 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "compiletest_rs 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "compiletest_rs 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -245,6 +304,15 @@ name = "winapi-build"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "ws2_32-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "z3-sys"
 version = "0.1.0"
@@ -258,9 +326,13 @@ dependencies = [
 "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
 "checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d"
 "checksum cargo_metadata 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "34fdab49a2904acb112c83b62f0118de3de3ce28e52a9188dec2858e43878f25"
-"checksum compiletest_rs 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2741d378feb7a434dba54228c89a70b4e427fee521de67cdda3750b8a0265f5a"
+"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
+"checksum compiletest_rs 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "562bafeec9aef1e3e08f1c5b0c542220bb80ff2894e5373a1f9d17c346412c66"
+"checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f"
+"checksum filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "aa75ec8f7927063335a9583e7fa87b0110bb888cf766dc01b54c0ff70d760c8e"
+"checksum getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "65922871abd2f101a2eb0eaebadc66668e54a87ad9c3dd82520b5f86ede5eff9"
 "checksum itoa 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ac17257442c2ed77dbc9fd555cf83c58b0c7f7d0e8f2ae08c0ac05c72842e1f6"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
 "checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417"
@@ -269,8 +341,11 @@ dependencies = [
 "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
 "checksum log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d382732ea0fbc09790c4899db3255bdea0fc78b54bf234bd18a63bb603915b6"
 "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
+"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
+"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09"
 "checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0"
 "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
+"checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509"
 "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
 "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
 "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
@@ -288,4 +363,5 @@ dependencies = [
 "checksum va_list 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35dd61c2a3fd97881058e5b401d981e8647d0630c64065b2a65c329049d6b5ef"
 "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
 "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
 "checksum z3-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "09ad369c3bebfb2b5585060ce765c1e0c4896b5a3bc58dd3870e9f73c0209b98"
diff --git a/Cargo.toml b/Cargo.toml
index 092ada206c370ad6be7b9e22548138a34e9698d9..07604e0926cce177d41cabea39f072220b9c0c29 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,4 +24,4 @@ cargo_metadata = "0.1"
 seer-z3 = "0.1.0"
 
 [dev-dependencies]
-compiletest_rs = "0.2.6"
+compiletest_rs = "0.3.2"
diff --git a/src/cast.rs b/src/cast.rs
index 9f436c290499400f66d1a0bb55879c97f23b63db..f01e8ac5261dde844015399ae1cac9f597c33bf0 100644
--- a/src/cast.rs
+++ b/src/cast.rs
@@ -1,6 +1,7 @@
 use rustc::ty::{self, Ty};
-use syntax::ast::{FloatTy, IntTy, UintTy};
+use syntax::ast::{IntTy, UintTy};
 
+use rustc_const_math::ConstFloat;
 use error::{EvalResult, EvalError};
 use eval_context::EvalContext;
 use memory::{Pointer, SByte};
@@ -13,14 +14,14 @@ 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)?;
+        let src_kind = self.ty_to_primval_kind(src_ty)?;
 
         use value::PrimValKind::*;
         match val {
             PrimVal::Abstract(mut sbytes) => {
                 let dest_kind = self.ty_to_primval_kind(dest_ty)?;
-                if (kind.is_int() || kind == Char) && (dest_kind.is_int() || kind == Char) {
-                    let src_size = kind.num_bytes();
+                if (src_kind.is_int() || src_kind == Char) && (dest_kind.is_int() || src_kind == Char) {
+                    let src_size = src_kind.num_bytes();
                     let dest_size = dest_kind.num_bytes();
                     for idx in dest_size .. src_size {
                         sbytes[idx] = SByte::Concrete(0);
@@ -28,7 +29,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                     // TODO(optimization): check to see if the cast has made
                     // the value concrete.
                     Ok(PrimVal::Abstract(sbytes))
-                } else if kind == Bool && dest_kind.is_int() {
+                } else if src_kind == Bool && dest_kind.is_int() {
                     let dest_kind = self.ty_to_primval_kind(dest_ty)?;
                     let primval = self.memory.constraints.add_if_then_else(
                         val,
@@ -40,91 +41,92 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                     unimplemented!()
                 }
             }
-            _ => {
-                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 => self.cast_signed_int(val.to_i128()?, dest_ty),
-
-                    Bool | Char | U8 | U16 | U32 | U64 | U128 => self.cast_int(val.to_u128()?, dest_ty, false),
+            PrimVal::Undef => Ok(PrimVal::Undef),
+            PrimVal::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty),
+            val @ PrimVal::Bytes(_) => {
+                use super::PrimValKind::*;
+                match src_kind {
+                    F32 => unimplemented!(),//self.cast_from_float(val.to_f32()?, dest_ty),
+                    F64 => unimplemented!(),//self.cast_from_float(val.to_f64()?, dest_ty),
+
+                    I8 | I16 | I32 | I64 | I128 => {
+                        self.cast_from_signed_int(val.to_i128()?, dest_ty)
+                    }
 
-                    FnPtr | Ptr => self.cast_ptr(val.to_ptr()?, dest_ty),
+                    Bool | Char | U8 | U16 | U32 | U64 | U128 | FnPtr | Ptr => {
+                        self.cast_from_int(val.to_u128()?, dest_ty, false)
+                    }
                 }
             }
         }
     }
 
-    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> {
-        use rustc::ty::TypeVariants::*;
-        match ty.sty {
-            TyBool if v == 0 => Ok(PrimVal::from_bool(false)),
-            TyBool if v == 1 => Ok(PrimVal::from_bool(true)),
-            TyBool => Err(EvalError::InvalidBool),
-
-            TyInt(IntTy::I8)  => Ok(PrimVal::Bytes(v as i128 as i8  as u128)),
-            TyInt(IntTy::I16) => Ok(PrimVal::Bytes(v as i128 as i16 as u128)),
-            TyInt(IntTy::I32) => Ok(PrimVal::Bytes(v as i128 as i32 as u128)),
-            TyInt(IntTy::I64) => Ok(PrimVal::Bytes(v as i128 as i64 as u128)),
-            TyInt(IntTy::I128) => Ok(PrimVal::Bytes(v as u128)),
-
-            TyUint(UintTy::U8)  => Ok(PrimVal::Bytes(v as u8  as u128)),
-            TyUint(UintTy::U16) => Ok(PrimVal::Bytes(v as u16 as u128)),
-            TyUint(UintTy::U32) => Ok(PrimVal::Bytes(v as u32 as u128)),
-            TyUint(UintTy::U64) => Ok(PrimVal::Bytes(v as u64 as u128)),
-            TyUint(UintTy::U128) => Ok(PrimVal::Bytes(v)),
-
-            TyInt(IntTy::Is) => {
-                let int_ty = self.tcx.sess.target.isize_ty;
-                let ty = self.tcx.mk_mach_int(int_ty);
-                self.cast_int(v, ty, negative)
+    fn int_to_int(&self, v: i128, ty: IntTy) -> u128 {
+        match ty {
+            IntTy::I8 => v as i8 as u128,
+            IntTy::I16 => v as i16 as u128,
+            IntTy::I32 => v as i32 as u128,
+            IntTy::I64 => v as i64 as u128,
+            IntTy::I128 => v as u128,
+            IntTy::Is => {
+                let ty = self.tcx.sess.target.isize_ty;
+                self.int_to_int(v, ty)
             }
-
-            TyUint(UintTy::Us) => {
-                let uint_ty = self.tcx.sess.target.usize_ty;
-                let ty = self.tcx.mk_mach_uint(uint_ty);
-                self.cast_int(v, ty, negative)
+        }
+    }
+    fn int_to_uint(&self, v: u128, ty: UintTy) -> u128 {
+        match ty {
+            UintTy::U8 => v as u8 as u128,
+            UintTy::U16 => v as u16 as u128,
+            UintTy::U32 => v as u32 as u128,
+            UintTy::U64 => v as u64 as u128,
+            UintTy::U128 => v,
+            UintTy::Us => {
+                let ty = self.tcx.sess.target.usize_ty;
+                self.int_to_uint(v, ty)
             }
-
-            TyFloat(FloatTy::F64) if negative => Ok(PrimVal::from_f64(v as i128 as f64)),
-            TyFloat(FloatTy::F64)             => Ok(PrimVal::from_f64(v as f64)),
-            TyFloat(FloatTy::F32) if negative => Ok(PrimVal::from_f32(v as i128 as f32)),
-            TyFloat(FloatTy::F32)             => Ok(PrimVal::from_f32(v as f32)),
-
-            TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)),
-            TyChar => Err(EvalError::InvalidChar(v)),
-
-            TyRawPtr(_) => Ok(PrimVal::Bytes(v % (1 << self.memory.pointer_size()))),
-
-            _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))),
         }
     }
 
-    fn cast_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
+    fn cast_from_int(
+        &self,
+        v: u128,
+        ty: ty::Ty<'tcx>,
+        negative: bool,
+    ) -> EvalResult<'tcx, PrimVal> {
+        trace!("cast_from_int: {}, {}, {}", v, ty, negative);
         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),
+            // Casts to bool are not permitted by rustc, no need to handle them here.
+            TyInt(ty) => Ok(PrimVal::Bytes(self.int_to_int(v as i128, ty))),
+            TyUint(ty) => Ok(PrimVal::Bytes(self.int_to_uint(v, ty))),
+
+            TyFloat(fty) if negative => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)),
+            TyFloat(fty) => Ok(PrimVal::Bytes(ConstFloat::from_u128(v, fty).bits)),
+
+            TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)),
+            TyChar => return Err(EvalError::InvalidChar(v)),
 
-            TyInt(_) | ty::TyUint(_) => self.cast_int(val as u128, ty, false),
+            // 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)),
 
-            TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(val)),
-            TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(val as f32)),
-            _ => Err(EvalError::Unimplemented(format!("float to {:?} cast", ty))),
+            _ => return Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))),
         }
     }
 
-    fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
+    fn cast_from_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
         use rustc::ty::TypeVariants::*;
         match ty.sty {
-            TyRef(..) | TyRawPtr(_) | TyFnPtr(_) | TyInt(_) | TyUint(_) =>
-                Ok(PrimVal::Ptr(ptr)),
-            _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))),
+            // 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(PrimVal::Ptr(ptr)),
+            TyInt(_) | TyUint(_) => return Err(EvalError::ReadPointerAsBytes),
+            _ => return Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))),
         }
     }
 }
diff --git a/src/error.rs b/src/error.rs
index b146e57e58f3b216de868314790d4fbf89252359..053a5c8beb62c1cfd9fcf492e443c9421c64be51 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -60,6 +60,7 @@ pub enum EvalError<'tcx> {
     Unreachable,
     Panic,
     ReadFromReturnPointer,
+    TypeckError,
 }
 
 pub type EvalResult<'tcx, T = ()> = Result<T, EvalError<'tcx>>;
@@ -148,6 +149,8 @@ impl<'tcx> Error for EvalError<'tcx> {
                 "the evaluated program panicked",
             EvalError::ReadFromReturnPointer =>
                 "tried to read from the return pointer",
+            EvalError::TypeckError =>
+                "encountered constants with type errors, stopping evaluation",
         }
     }
 
@@ -245,6 +248,7 @@ pub enum StaticEvalError {
     Unreachable,
     Panic,
     ReadFromReturnPointer,
+    TypeckError,
 }
 
 impl <'tcx> From<EvalError<'tcx>> for StaticEvalError {
@@ -332,6 +336,8 @@ impl <'tcx> From<EvalError<'tcx>> for StaticEvalError {
                 StaticEvalError::Panic,
             EvalError::ReadFromReturnPointer =>
                 StaticEvalError::ReadFromReturnPointer,
+            EvalError::TypeckError =>
+                StaticEvalError::TypeckError,
         }
     }
 }
diff --git a/src/eval_context.rs b/src/eval_context.rs
index c91fd45792388d7574cf0ea22169eb8ed598359b..43cd9fe56640220796ae7af798b4b167d58abbe4 100644
--- a/src/eval_context.rs
+++ b/src/eval_context.rs
@@ -5,13 +5,12 @@ use rustc::hir::def_id::DefId;
 use rustc::middle::const_val::ConstVal;
 use rustc::mir;
 use rustc::traits::{Reveal};
-use rustc::ty::layout::{self, Layout, Size};
+use rustc::ty::layout::{self, Size, HasDataLayout, LayoutOf, TyLayout};
 use rustc::ty::subst::{Subst, Substs, Kind};
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Binder};
 use rustc_data_structures::indexed_vec::Idx;
 use syntax::codemap::{self, DUMMY_SP};
 use syntax::ast;
-use syntax::abi::Abi;
 
 use error::{EvalError, EvalResult};
 use lvalue::{Global, GlobalId, Lvalue, LvalueExtra};
@@ -150,6 +149,81 @@ impl Default for ResourceLimits {
     }
 }
 
+#[derive(Copy, Clone, Debug)]
+pub struct TyAndPacked<'tcx> {
+    pub ty: Ty<'tcx>,
+    pub packed: bool,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct ValTy<'tcx> {
+    pub value: Value,
+    pub ty: Ty<'tcx>,
+}
+
+impl<'tcx> ::std::ops::Deref for ValTy<'tcx> {
+    type Target = Value;
+    fn deref(&self) -> &Value {
+        &self.value
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct PtrAndAlign {
+    pub ptr: Pointer,
+    /// Remember whether this lvalue is *supposed* to be aligned.
+    pub aligned: bool,
+}
+
+impl<'a, 'tcx> HasDataLayout for &'a EvalContext<'a, 'tcx> {
+    #[inline]
+    fn data_layout(&self) -> &layout::TargetDataLayout {
+        &self.tcx.data_layout
+    }
+}
+
+impl<'c, 'b, 'a, 'tcx> HasDataLayout
+    for &'c &'b mut EvalContext<'a, 'tcx> {
+    #[inline]
+    fn data_layout(&self) -> &layout::TargetDataLayout {
+        &self.tcx.data_layout
+    }
+}
+
+impl<'a, 'tcx> layout::HasTyCtxt<'tcx> for &'a EvalContext<'a, 'tcx> {
+    #[inline]
+    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> {
+        self.tcx
+    }
+}
+
+impl<'c, 'b, 'a, 'tcx> layout::HasTyCtxt<'tcx>
+    for &'c &'b mut EvalContext<'a, 'tcx> {
+    #[inline]
+    fn tcx<'d>(&'d self) -> TyCtxt<'d, 'tcx, 'tcx> {
+        self.tcx
+    }
+}
+
+impl<'a, 'tcx> LayoutOf<Ty<'tcx>> for &'a EvalContext<'a, 'tcx> {
+    type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>;
+
+    fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout {
+        (self.tcx, ::rustc::ty::ParamEnv::empty(Reveal::All)).layout_of(ty)
+            .map_err(|layout| EvalError::Layout(layout).into())
+    }
+}
+
+impl<'c, 'b, 'a, 'tcx> LayoutOf<Ty<'tcx>>
+    for &'c &'b mut EvalContext<'a, 'tcx> {
+    type TyLayout = EvalResult<'tcx, TyLayout<'tcx>>;
+
+    #[inline]
+    fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout {
+        (&**self).layout_of(ty)
+    }
+}
+
 impl<'a, 'tcx> EvalContext<'a, 'tcx> {
     pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self {
         EvalContext {
@@ -246,6 +320,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         Ok(Value::ByVal(primval))
     }
 
+    pub(super) fn resolve(&self, def_id: DefId, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, ty::Instance<'tcx>> {
+        let substs = self.tcx.trans_apply_param_substs(self.substs(), &substs);
+        ::rustc::ty::Instance::resolve(
+            self.tcx,
+            ::rustc::ty::ParamEnv::empty(Reveal::All), // XXX ?
+            def_id,
+            substs,
+        ).ok_or(EvalError::TypeckError.into()) // turn error prop into a panic to expose associated type in const issue
+    }
+
     pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
         // generics are weird, don't run this function on a generic
         assert!(!ty.needs_subst());
@@ -292,23 +376,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         if layout.is_unsized() {
             Ok(None)
         } else {
-            Ok(Some(layout.size(&self.tcx.data_layout).bytes()))
+            Ok(Some(layout.size.bytes()))
         }
     }
 
     fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, u64> {
-        self.type_layout_with_substs(ty, substs).map(|layout| layout.align(&self.tcx.data_layout).abi())
+        self.type_layout_with_substs(ty, substs).map(|layout| {
+            layout.align.abi()
+        })
     }
 
-    pub(super) fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, &'tcx Layout> {
+    pub(super) fn type_layout(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, TyLayout<'tcx>> {
         self.type_layout_with_substs(ty, self.substs())
     }
 
-    fn type_layout_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, &'tcx Layout> {
+    fn type_layout_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, TyLayout<'tcx>> {
         // TODO(solson): Is this inefficient? Needs investigation.
         let ty = self.monomorphize(ty, substs);
 
-        ty.layout(self.tcx, ty::ParamEnv::empty(Reveal::All)).map_err(EvalError::Layout)
+        self.layout_of(ty)
     }
 
     pub fn push_stack_frame(
@@ -392,64 +478,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         Ok(())
     }
 
-    pub fn assign_discr_and_fields<
-        V: IntoValTyPair<'tcx>,
-        J: IntoIterator<Item = V>,
-    >(
-        &mut self,
-        dest: Lvalue<'tcx>,
-        dest_ty: Ty<'tcx>,
-        discr_offset: u64,
-        operands: J,
-        discr_val: u128,
-        variant_idx: usize,
-        discr_size: u64,
-    ) -> EvalResult<'tcx>
-        where J::IntoIter: ExactSizeIterator,
-    {
-        // FIXME(solson)
-        let dest_ptr = self.force_allocation(dest)?.to_ptr()?;
-
-        let discr_dest = dest_ptr.offset(discr_offset, self.memory.layout)?;
-        self.memory.write_uint(discr_dest, discr_val, discr_size)?;
-
-        let dest = Lvalue::Ptr {
-            ptr: PrimVal::Ptr(dest_ptr),
-            extra: LvalueExtra::DowncastVariant(variant_idx),
-        };
-
-        self.assign_fields(dest, dest_ty, operands)
-    }
-
-    pub fn assign_fields<
-        V: IntoValTyPair<'tcx>,
-        J: IntoIterator<Item = V>,
-    >(
-        &mut self,
-        dest: Lvalue<'tcx>,
-        dest_ty: Ty<'tcx>,
-        operands: J,
-    ) -> EvalResult<'tcx>
-        where J::IntoIter: ExactSizeIterator,
-    {
-        if self.type_size(dest_ty)? == Some(0) {
-            // zst assigning is a nop
-            return Ok(());
-        }
-        if self.ty_to_primval_kind(dest_ty).is_ok() {
-            let mut iter = operands.into_iter();
-            assert_eq!(iter.len(), 1);
-            let (value, value_ty) = iter.next().unwrap().into_val_ty_pair(self)?;
-            return self.write_value(value, dest, value_ty);
-        }
-        for (field_index, operand) in operands.into_iter().enumerate() {
-            let (value, value_ty) = operand.into_val_ty_pair(self)?;
-            let field_dest = self.lvalue_field(dest, field_index, dest_ty, value_ty)?;
-            self.write_value(value, field_dest, value_ty)?;
-        }
-        Ok(())
-    }
-
     /// Evaluate an assignment statement.
     ///
     /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue
@@ -461,7 +489,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
     ) -> EvalResult<'tcx> {
         let dest = self.eval_lvalue(lvalue)?;
         let dest_ty = self.lvalue_ty(lvalue);
-        let dest_layout = self.type_layout(dest_ty)?;
 
         use rustc::mir::Rvalue::*;
         match *rvalue {
@@ -491,127 +518,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
 
             Aggregate(ref kind, ref operands) => {
                 self.inc_step_counter_and_check_limit(operands.len() as u64)?;
-                use rustc::ty::layout::Layout::*;
-                match *dest_layout {
-                    Univariant { ref variant, .. } => {
-                        if variant.packed {
-                            let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
-                            self.memory.mark_packed(ptr, variant.stride().bytes());
-                        }
-                        self.assign_fields(dest, dest_ty, operands)?;
-                    }
-
-                    Array { .. } => {
-                        self.assign_fields(dest, dest_ty, operands)?;
-                    }
-
-                    General { discr, ref variants, .. } => {
-                        if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind {
-                            let discr_val = adt_def.discriminants(self.tcx)
-                                .nth(variant)
-                                .expect("broken mir: Adt variant id invalid")
-                                .to_u128_unchecked();
-                            let discr_size = discr.size().bytes();
-                            if variants[variant].packed {
-                                let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
-                                self.memory.mark_packed(ptr, variants[variant].stride().bytes());
-                            }
-
-                            self.assign_discr_and_fields(
-                                dest,
-                                dest_ty,
-                                variants[variant].offsets[0].bytes(),
-                                operands,
-                                discr_val,
-                                variant,
-                                discr_size,
-                            )?;
-                        } else {
-                            bug!("tried to assign {:?} to Layout::General", kind);
-                        }
-                    }
-
-                    RawNullablePointer { nndiscr, .. } => {
-                        if let mir::AggregateKind::Adt(_, variant, _, _) = **kind {
-                            if nndiscr == variant as u64 {
-                                assert_eq!(operands.len(), 1);
-                                let operand = &operands[0];
-                                let value = self.eval_operand(operand)?;
-                                let value_ty = self.operand_ty(operand);
-                                self.write_value(value, dest, value_ty)?;
-                            } else {
-                                if let Some(operand) = operands.get(0) {
-                                    assert_eq!(operands.len(), 1);
-                                    let operand_ty = self.operand_ty(operand);
-                                    assert_eq!(self.type_size(operand_ty)?, Some(0));
-                                }
-                                self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
-                            }
-                        } else {
-                            bug!("tried to assign {:?} to Layout::RawNullablePointer", kind);
-                        }
-                    }
-
-                    StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield_source, .. } => {
-                        if let mir::AggregateKind::Adt(_, variant, _, _) = **kind {
-                            if nonnull.packed {
-                                let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
-                                self.memory.mark_packed(ptr, nonnull.stride().bytes());
-                            }
-                            if nndiscr == variant as u64 {
-                                self.assign_fields(dest, dest_ty, operands)?;
-                            } else {
-                                for operand in operands {
-                                    let operand_ty = self.operand_ty(operand);
-                                    assert_eq!(self.type_size(operand_ty)?, Some(0));
-                                }
-                                let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield_source)?;
-
-                                // FIXME(solson)
-                                let dest = self.force_allocation(dest)?.to_ptr()?;
-
-                                let dest = dest.offset(offset.bytes(), self.memory.layout)?;
-                                let dest_size = self.type_size(ty)?
-                                    .expect("bad StructWrappedNullablePointer discrfield");
-                                self.memory.write_int(dest, 0, dest_size)?;
-                            }
+                let mut layout = self.type_layout(dest_ty)?;
+                let (dest, active_field_index) = match **kind {
+                    mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => {
+                        self.write_discriminant_value(dest_ty, dest, variant_index)?;
+                        layout = layout.for_variant(&self, variant_index);
+                        if adt_def.is_enum() {
+                            (self.lvalue_downcast(dest, variant_index)?, active_field_index)
                         } else {
-                            bug!("tried to assign {:?} to Layout::RawNullablePointer", kind);
+                            (dest, active_field_index)
                         }
                     }
+                    _ => (dest, None)
+                };
 
-                    CEnum { .. } => {
-                        assert_eq!(operands.len(), 0);
-                        if let mir::AggregateKind::Adt(adt_def, variant, _, _) = **kind {
-                            let n = adt_def.discriminants(self.tcx)
-                                .nth(variant)
-                                .expect("broken mir: Adt variant index invalid")
-                                .to_u128_unchecked();
-                            self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?;
-                        } else {
-                            bug!("tried to assign {:?} to Layout::CEnum", kind);
-                        }
-                    }
-
-                    Vector { count, .. } => {
-                        debug_assert_eq!(count, operands.len() as u64);
-                        self.assign_fields(dest, dest_ty, operands)?;
-                    }
-
-                    UntaggedUnion { .. } => {
-                        assert_eq!(operands.len(), 1);
-                        let operand = &operands[0];
-                        let value = self.eval_operand(operand)?;
-                        let value_ty = self.operand_ty(operand);
-                        self.write_value(value, dest, value_ty)?;
-                    }
-
-                    _ => {
-                        return Err(EvalError::Unimplemented(format!(
-                            "can't handle destination layout {:?} when assigning {:?}",
-                            dest_layout,
-                            kind
-                        )));
+                for (i, operand) in operands.iter().enumerate() {
+                    let value = self.eval_operand(operand)?;
+                    let value_ty = self.operand_ty(operand);
+                    // Ignore zero-sized fields.
+                    if !self.type_layout(value_ty)?.is_zst() {
+                        let field_index = active_field_index.unwrap_or(i);
+                        let (field_dest, _) = self.lvalue_field(dest, mir::Field::new(field_index), layout)?;
+                        self.write_value(value, field_dest, value_ty)?;
                     }
                 }
             }
@@ -724,7 +652,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
 
                     ReifyFnPointer => match self.operand_ty(operand).sty {
                         ty::TyFnDef(def_id, substs) => {
-                            let instance = resolve(self.tcx, def_id, substs);
+                            let instance = self.resolve(def_id, substs)?;
                             let fn_ptr = self.memory.create_fn_alloc(instance);
                             self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?;
                         },
@@ -753,16 +681,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
             Discriminant(ref lvalue) => {
                 let lval = self.eval_lvalue(lvalue)?;
                 let ty = self.lvalue_ty(lvalue);
-                let ptr = self.force_allocation(lval)?.to_ptr()?;
-                let discr_val = self.read_discriminant_value(ptr, ty)?;
+                let discr_val = self.read_discriminant_value(lval, ty)?;
                 if let ty::TyAdt(adt_def, _) = ty.sty {
-                    if adt_def.discriminants(self.tcx).all(|v| discr_val != v.to_u128_unchecked()) {
+                    trace!("Read discriminant {}, valid discriminants {:?}", discr_val, adt_def.discriminants(self.tcx).collect::<Vec<_>>());
+                    if adt_def.discriminants(self.tcx).all(|v| {
+                        discr_val != v.to_u128_unchecked()
+                    })
+                    {
                         return Err(EvalError::InvalidDiscriminant);
                     }
+                    self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?;
                 } else {
                     bug!("rustc only generates Rvalue::Discriminant for enums");
                 }
-                self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?;
             },
         }
 
@@ -782,109 +713,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         }
     }
 
-    pub(super) fn nonnull_offset_and_ty(
+    /// Returns the field type and whether the field is packed
+    pub fn get_field_ty(
         &self,
         ty: Ty<'tcx>,
-        nndiscr: u64,
-        discrfield: &[u32],
-    ) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
-        // Skip the constant 0 at the start meant for LLVM GEP and the outer non-null variant
-        let path = discrfield.iter().skip(2).map(|&i| i as usize);
-
-        // Handle the field index for the outer non-null variant.
-        let (inner_offset, inner_ty) = match ty.sty {
-            ty::TyAdt(adt_def, substs) => {
-                let variant = &adt_def.variants[nndiscr as usize];
-                let index = discrfield[1];
-                let field = &variant.fields[index as usize];
-                (self.get_field_offset(ty, index as usize)?, field.ty(self.tcx, substs))
-            }
-            _ => bug!("non-enum for StructWrappedNullablePointer: {}", ty),
-        };
-
-        self.field_path_offset_and_ty(inner_offset, inner_ty, path)
-    }
-
-    fn field_path_offset_and_ty<I: Iterator<Item = usize>>(
-        &self,
-        mut offset: Size,
-        mut ty: Ty<'tcx>,
-        path: I,
-    ) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
-        // Skip the initial 0 intended for LLVM GEP.
-        for field_index in path {
-            let field_offset = self.get_field_offset(ty, field_index)?;
-            trace!("field_path_offset_and_ty: {}, {}, {:?}, {:?}", field_index, ty, field_offset, offset);
-            ty = self.get_field_ty(ty, field_index)?;
-            offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap();
-        }
-
-        Ok((offset, ty))
-    }
-    fn get_fat_field(&self, pointee_ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> {
-        match (field_index, &self.tcx.struct_tail(pointee_ty).sty) {
-            (1, &ty::TyStr) |
-            (1, &ty::TySlice(_)) => Ok(self.tcx.types.usize),
-            (1, &ty::TyDynamic(..)) |
-            (0, _) => Ok(self.tcx.mk_imm_ptr(self.tcx.types.u8)),
-            _ => bug!("invalid fat pointee type: {}", pointee_ty),
-        }
-    }
-
-    pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> {
-        match ty.sty {
-            ty::TyAdt(adt_def, _) if adt_def.is_box() => self.get_fat_field(ty.boxed_ty(), field_index),
-            ty::TyAdt(adt_def, substs) => {
-                Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs))
-            }
-
-            ty::TyTuple(fields, _) => Ok(fields[field_index]),
-
-            ty::TyRef(_, ref tam) |
-            ty::TyRawPtr(ref tam) => self.get_fat_field(tam.ty, field_index),
-
-            ty::TyClosure(def_id, ref closure_substs) =>
-                Ok(closure_substs.upvar_tys(def_id, self.tcx).nth(field_index).unwrap()),
-
-            _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))),
-        }
+        field_index: usize,
+    ) -> EvalResult<'tcx, TyAndPacked<'tcx>> {
+        let layout = self.type_layout(ty)?.field(self, field_index)?;
+        Ok(TyAndPacked {
+            ty: layout.ty,
+            packed: layout.is_packed()
+        })
     }
 
     fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Size> {
-        let layout = self.type_layout(ty)?;
-
-        use rustc::ty::layout::Layout::*;
-        match *layout {
-            Univariant { ref variant, .. } => {
-                Ok(variant.offsets[field_index])
-            }
-            FatPointer { .. } => {
-                let bytes = field_index as u64 * self.memory.pointer_size();
-                Ok(Size::from_bytes(bytes))
-            }
-            StructWrappedNullablePointer { ref nonnull, .. } => {
-                Ok(nonnull.offsets[field_index])
-            }
-            _ => {
-                let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout);
-                Err(EvalError::Unimplemented(msg))
-            }
-        }
+        Ok(self.type_layout(ty)?.fields.offset(field_index))
     }
 
-    pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> {
-        let layout = self.type_layout(ty)?;
-
-        use rustc::ty::layout::Layout::*;
-        match *layout {
-            Univariant { ref variant, .. } => Ok(variant.offsets.len()),
-            FatPointer { .. } => Ok(2),
-            StructWrappedNullablePointer { ref nonnull, .. } => Ok(nonnull.offsets.len()),
-            _ => {
-                let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout);
-                Err(EvalError::Unimplemented(msg))
-            }
-        }
+    pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, u64> {
+        Ok(self.type_layout(ty)?.fields.count() as u64)
     }
 
     pub(super) fn wrapping_pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> {
@@ -950,6 +797,99 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         }
     }
 
+    pub fn read_discriminant_value(
+        &mut self,
+        lvalue: Lvalue<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> EvalResult<'tcx, u128> {
+        let layout = self.type_layout(ty)?;
+        match layout.variants {
+            layout::Variants::Single { index } => {
+                return Ok(index as u128);
+            }
+            layout::Variants::Tagged { .. } |
+            layout::Variants::NicheFilling { .. } => {},
+        }
+
+        let (discr_lvalue, discr) = self.lvalue_field(lvalue, mir::Field::new(0), layout)?;
+        let read_discr_lvalue = self.read_lvalue(discr_lvalue)?;
+        let raw_discr_primval = self.value_to_primval(
+            read_discr_lvalue,
+            discr.ty)?;
+        let discr_val = match layout.variants {
+            layout::Variants::Single { .. } => bug!(),
+            layout::Variants::Tagged { .. } => raw_discr_primval.to_bytes()?,
+            layout::Variants::NicheFilling {
+                dataful_variant,
+                ref niche_variants,
+                niche_start,
+                ..
+            } => {
+                match raw_discr_primval {
+                    PrimVal::Bytes(raw_discr) => {
+                        let variants_start = niche_variants.start as u128;
+                        let variants_end = niche_variants.end as u128;
+                        let discr = raw_discr.wrapping_sub(niche_start)
+                            .wrapping_add(variants_start);
+
+                        if variants_start <= discr && discr <= variants_end {
+                            discr
+                        } else {
+                            dataful_variant as u128
+                        }
+                    }
+                    _ => dataful_variant as u128,
+                }
+            }
+        };
+
+        Ok(discr_val)
+    }
+
+    pub(crate) fn write_discriminant_value(
+        &mut self,
+        dest_ty: Ty<'tcx>,
+        dest: Lvalue<'tcx>,
+        variant_index: usize,
+    ) -> EvalResult<'tcx> {
+        let layout = self.type_layout(dest_ty)?;
+
+        match layout.variants {
+            layout::Variants::Single { index } => {
+                if index != variant_index {
+                    // If the layout of an enum is `Single`, all
+                    // other variants are necessarily uninhabited.
+                    assert_eq!(layout.for_variant(&self, variant_index).abi,
+                               layout::Abi::Uninhabited);
+                }
+            }
+            layout::Variants::Tagged { .. } => {
+                let discr_val = dest_ty.ty_adt_def().unwrap()
+                    .discriminant_for_variant(self.tcx, variant_index)
+                    .to_u128_unchecked();
+
+                let (discr_dest, discr) = self.lvalue_field(dest, mir::Field::new(0), layout)?;
+                self.write_primval(discr_dest, PrimVal::Bytes(discr_val), discr.ty)?;
+            }
+            layout::Variants::NicheFilling {
+                dataful_variant,
+                ref niche_variants,
+                niche_start,
+                ..
+            } => {
+                if variant_index != dataful_variant {
+                    let (niche_dest, niche) =
+                        self.lvalue_field(dest, mir::Field::new(0), layout)?;
+                    let niche_value = ((variant_index - niche_variants.start) as u128)
+                        .wrapping_add(niche_start);
+                    self.write_primval(niche_dest, PrimVal::Bytes(niche_value), niche.ty)?;
+                }
+            }
+        }
+
+        Ok(())
+    }
+
     pub(super) fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> {
         self.monomorphize(operand.ty(self.mir(), self.tcx), self.substs())
     }
@@ -1143,20 +1083,33 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         a: PrimVal,
         b: PrimVal,
         ptr: Pointer,
-        mut ty: Ty<'tcx>
+        ty: Ty<'tcx>
     ) -> EvalResult<'tcx> {
-        while self.get_field_count(ty)? == 1 {
-            ty = self.get_field_ty(ty, 0)?;
-        }
-        assert_eq!(self.get_field_count(ty)?, 2);
-        let field_0 = self.get_field_offset(ty, 0)?.bytes();
-        let field_1 = self.get_field_offset(ty, 1)?.bytes();
-        let field_0_ty = self.get_field_ty(ty, 0)?;
-        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(PrimVal::Ptr(ptr.offset(field_0, self.memory.layout)?), a, field_0_size)?;
-        self.memory.write_primval(PrimVal::Ptr(ptr.offset(field_1, self.memory.layout)?), b, field_1_size)?;
+        let mut layout = self.type_layout(ty)?;
+        let mut _packed = layout.is_packed();
+
+        // er, this is kinda fishy
+        while layout.fields.count() != 2
+            || layout.field(&self, 0)?.size.bytes() == 0
+            || layout.field(&self, 1)?.size.bytes() == 0 {
+                layout = layout.field(&self, 0)?;
+                _packed |= layout.is_packed();
+            }
+
+        assert_eq!(layout.fields.count(), 2);
+        let field_0 = layout.field(&self, 0)?;
+        let field_1 = layout.field(&self, 1)?;
+        assert_eq!(
+            field_0.is_packed(),
+            field_1.is_packed(),
+            "the two fields must agree on being packed"
+        );
+        _packed |= field_0.is_packed();
+        let field_0_ptr = ptr.offset(layout.fields.offset(0).bytes(), (&self).data_layout())?.into();
+        let field_1_ptr = ptr.offset(layout.fields.offset(1).bytes(), (&self).data_layout())?.into();
+
+        self.memory.write_primval(PrimVal::Ptr(field_0_ptr), a, field_0.size.bytes())?;
+        self.memory.write_primval(PrimVal::Ptr(field_1_ptr), b, field_1.size.bytes())?;
         Ok(())
     }
 
@@ -1203,41 +1156,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
 
             ty::TyAdt(ref def, _) if def.is_box() => PrimValKind::Ptr,
 
-            ty::TyAdt(ref def, substs) => {
-                use rustc::ty::layout::Layout::*;
-                match *self.type_layout(ty)? {
-                    CEnum { discr, signed, .. } => {
-                        let size = discr.size().bytes();
-                        if signed {
-                            PrimValKind::from_int_size(size)
-                        } else {
-                            PrimValKind::from_uint_size(size)
-                        }
-                    }
-
-                    RawNullablePointer { value, .. } => {
+            ty::TyAdt(..) => {
+                match self.type_layout(ty)?.abi {
+                    layout::Abi::Scalar(ref scalar) => {
                         use rustc::ty::layout::Primitive::*;
-                        match value {
-                            // TODO(solson): Does signedness matter here? What should the sign be?
-                            Int(int) => PrimValKind::from_uint_size(int.size().bytes()),
+                        match scalar.value {
+                            Int(i, false) => PrimValKind::from_uint_size(i.size().bytes()),
+                            Int(i, true) => PrimValKind::from_int_size(i.size().bytes()),
                             F32 => PrimValKind::F32,
                             F64 => PrimValKind::F64,
                             Pointer => PrimValKind::Ptr,
                         }
                     }
 
-                    // represent single field structs as their single field
-                    Univariant { .. } => {
-                        // enums with just one variant are no different, but `.struct_variant()` doesn't work for enums
-                        let variant = &def.variants[0];
-                        // FIXME: also allow structs with only a single non zst field
-                        if variant.fields.len() == 1 {
-                            return self.ty_to_primval_kind(variant.fields[0].ty(self.tcx, substs));
-                        } else {
-                            return Err(EvalError::TypeNotPrimitive(ty));
-                        }
-                    }
-
                     _ => return Err(EvalError::TypeNotPrimitive(ty)),
                 }
             }
@@ -1370,13 +1301,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                 if def.is_box() {
                     return self.read_ptr(ptr, ty.boxed_ty()).map(Some);
                 }
-                use rustc::ty::layout::Layout::*;
-                if let CEnum { discr, signed, .. } = *self.type_layout(ty)? {
-                    let size = discr.size().bytes();
-                    if signed {
-                        PrimVal::from_i128(self.memory.read_int(ptr, size)?)
+
+                if let layout::Abi::Scalar(ref scalar) = self.type_layout(ty)?.abi {
+                    let mut signed = false;
+                    if let layout::Int(_, s) = scalar.value {
+                        signed = s;
+                    }
+                    let size = scalar.value.size(&self).bytes();
+                    if self.memory.points_to_concrete(ptr, size)? {
+                        self.memory.read_primval(ptr, size, signed)?
                     } else {
-                        PrimVal::from_u128(self.memory.read_uint(ptr, size)?)
+                        return Ok(None);
                     }
                 } else {
                     return Ok(None);
@@ -1461,8 +1396,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                     return self.unsize_into_ptr(src, src_ty, dest, dest_ty, src_ty.boxed_ty(), dest_ty.boxed_ty());
                 }
                 if self.ty_to_primval_kind(src_ty).is_ok() {
-                    let sty = self.get_field_ty(src_ty, 0)?;
-                    let dty = self.get_field_ty(dest_ty, 0)?;
+                    let sty = self.get_field_ty(src_ty, 0)?.ty;
+                    let dty = self.get_field_ty(dest_ty, 0)?.ty;
                     return self.unsize_into(src, sty, dest, dty);
                 }
                 // unsizing of generic struct with pointer fields
@@ -1595,7 +1530,7 @@ impl IntegerExt for layout::Integer {
     fn size(self) -> Size {
         use rustc::ty::layout::Integer::*;
         match self {
-            I1 | I8 => Size::from_bits(8),
+            I8 => Size::from_bits(8),
             I16 => Size::from_bits(16),
             I32 => Size::from_bits(32),
             I64 => Size::from_bits(64),
@@ -1640,7 +1575,7 @@ pub fn resolve_closure<'a, 'tcx> (
     substs: ty::ClosureSubsts<'tcx>,
     requested_kind: ty::ClosureKind,
 ) -> ty::Instance<'tcx> {
-    let actual_kind = tcx.closure_kind(def_id);
+    let actual_kind = substs.closure_kind(def_id, tcx);
     match needs_fn_once_adapter_shim(actual_kind, requested_kind) {
         Ok(true) => fn_once_adapter_instance(tcx, def_id, substs),
         _ => ty::Instance::new(def_id, substs.substs)
@@ -1709,155 +1644,6 @@ fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind,
     }
 }
 
-/// The point where linking happens. Resolve a (def_id, substs)
-/// pair to an instance.
-pub fn resolve<'a, 'tcx>(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    def_id: DefId,
-    substs: &'tcx Substs<'tcx>
-) -> ty::Instance<'tcx> {
-    debug!("resolve(def_id={:?}, substs={:?})",
-           def_id, substs);
-    let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) {
-        debug!(" => associated item, attempting to find impl");
-        let item = tcx.associated_item(def_id);
-        resolve_associated_item(tcx, &item, trait_def_id, substs)
-    } else {
-        let item_type = def_ty(tcx, def_id, substs);
-        let def = match item_type.sty {
-            ty::TyFnDef(..) if {
-                    let f = item_type.fn_sig(tcx);
-                    f.abi() == Abi::RustIntrinsic ||
-                    f.abi() == Abi::PlatformIntrinsic
-                } =>
-            {
-                debug!(" => intrinsic");
-                ty::InstanceDef::Intrinsic(def_id)
-            }
-            _ => {
-                if Some(def_id) == tcx.lang_items().drop_in_place_fn() {
-                    let ty = substs.type_at(0);
-                    if needs_drop_glue(tcx, ty) {
-                        debug!(" => nontrivial drop glue");
-                        ty::InstanceDef::DropGlue(def_id, Some(ty))
-                    } else {
-                        debug!(" => trivial drop glue");
-                        ty::InstanceDef::DropGlue(def_id, None)
-                    }
-                } else {
-                    debug!(" => free item");
-                    ty::InstanceDef::Item(def_id)
-                }
-            }
-        };
-        ty::Instance { def, substs }
-    };
-    debug!("resolve(def_id={:?}, substs={:?}) = {}",
-           def_id, substs, result);
-    result
-}
-
-pub fn needs_drop_glue<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, t: Ty<'tcx>) -> bool {
-    assert!(t.is_normalized_for_trans());
-
-    let t = tcx.erase_regions(&t);
-
-    // FIXME (#22815): note that type_needs_drop conservatively
-    // approximates in some cases and may say a type expression
-    // requires drop glue when it actually does not.
-    //
-    // (In this case it is not clear whether any harm is done, i.e.
-    // erroneously returning `true` in some cases where we could have
-    // returned `false` does not appear unsound. The impact on
-    // code quality is unknown at this time.)
-
-    let env = ty::ParamEnv::empty(Reveal::All);
-    if !t.needs_drop(tcx, env) {
-        return false;
-    }
-    match t.sty {
-        ty::TyAdt(def, _) if def.is_box() => {
-            let typ = t.boxed_ty();
-            if !typ.needs_drop(tcx, env) && type_is_sized(tcx, typ) {
-                let layout = t.layout(tcx, env).unwrap();
-                if layout.size(&tcx.data_layout).bytes() == 0 {
-                    // `Box<ZeroSizeType>` does not allocate.
-                    false
-                } else {
-                    true
-                }
-            } else {
-                true
-            }
-        }
-        _ => true
-    }
-}
-
-fn resolve_associated_item<'a, 'tcx>(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    trait_item: &ty::AssociatedItem,
-    trait_id: DefId,
-    rcvr_substs: &'tcx Substs<'tcx>
-) -> ty::Instance<'tcx> {
-    let def_id = trait_item.def_id;
-    debug!("resolve_associated_item(trait_item={:?}, \
-                                    trait_id={:?}, \
-                                    rcvr_substs={:?})",
-           def_id, trait_id, rcvr_substs);
-
-    let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs);
-    let vtbl = tcx.trans_fulfill_obligation((ty::ParamEnv::empty(Reveal::All),
-                                             ty::Binder(trait_ref)));
-
-    // Now that we know which impl is being used, we can dispatch to
-    // the actual function:
-    match vtbl {
-        ::rustc::traits::VtableImpl(impl_data) => {
-            let (def_id, substs) = ::rustc::traits::find_associated_item(
-                tcx, trait_item, rcvr_substs, &impl_data);
-            let substs = tcx.erase_regions(&substs);
-            ty::Instance::new(def_id, substs)
-        }
-        ::rustc::traits::VtableClosure(closure_data) => {
-            let trait_closure_kind = tcx.lang_items().fn_trait_kind(trait_id).unwrap();
-            resolve_closure(tcx, closure_data.closure_def_id, closure_data.substs,
-                            trait_closure_kind)
-        }
-        ::rustc::traits::VtableFnPointer(ref data) => {
-            ty::Instance {
-                def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty),
-                substs: rcvr_substs
-            }
-        }
-        ::rustc::traits::VtableObject(ref data) => {
-            let index = tcx.get_vtable_index_of_object_method(data, def_id);
-            ty::Instance {
-                def: ty::InstanceDef::Virtual(def_id, index),
-                substs: rcvr_substs
-            }
-        }
-        ::rustc::traits::VtableBuiltin(..) if Some(trait_id) == tcx.lang_items().clone_trait() => {
-            ty::Instance {
-                def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()),
-                substs: rcvr_substs,
-            }
-        }
-        _ => {
-            bug!("static call to invalid vtable: {:?}", vtbl)
-        }
-    }
-}
-
-pub fn def_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                        def_id: DefId,
-                        substs: &'tcx Substs<'tcx>)
-                        -> Ty<'tcx>
-{
-    let ty = tcx.type_of(def_id);
-    apply_param_substs(tcx, substs, &ty)
-}
-
 /// Monomorphizes a type from the AST by first applying the in-scope
 /// substitutions and then normalizing any associated types.
 pub fn apply_param_substs<'a, 'tcx, T>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -1901,12 +1687,6 @@ impl<'a, 'tcx> ::rustc::ty::fold::TypeFolder<'tcx, 'tcx> for AssociatedTypeNorma
     }
 }
 
-fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool {
-    // generics are weird, don't run this function on a generic
-    assert!(!ty.needs_subst());
-    ty.is_sized(tcx, ty::ParamEnv::empty(Reveal::All), DUMMY_SP)
-}
-
 pub fn resolve_drop_in_place<'a, 'tcx>(
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     ty: Ty<'tcx>,
@@ -1914,5 +1694,5 @@ pub fn resolve_drop_in_place<'a, 'tcx>(
 {
     let def_id = tcx.require_lang_item(::rustc::middle::lang_items::DropInPlaceFnLangItem);
     let substs = tcx.intern_substs(&[Kind::from(ty)]);
-    resolve(tcx, def_id, substs)
+    ::rustc::ty::Instance::resolve(tcx, ::rustc::ty::ParamEnv::empty(Reveal::All), def_id, substs).unwrap()
 }
diff --git a/src/lib.rs b/src/lib.rs
index 98b18fe4a785cf6c94e8909a3b09d0e50005399f..67c0d3e0bf905ef5180d4b38ece6ca148d067c7c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,6 @@
 #![feature(
     i128_type,
+    inclusive_range,
     rustc_private,
 )]
 
diff --git a/src/lvalue.rs b/src/lvalue.rs
index 9b9957c15affd663f5ee69468ba120e6b7d0e1a1..6e13155a3a8f2db787cd4c0a6dc329ec1d1c3937 100644
--- a/src/lvalue.rs
+++ b/src/lvalue.rs
@@ -1,5 +1,5 @@
 use rustc::mir;
-use rustc::ty::layout::{Size, Align};
+use rustc::ty::layout::{HasDataLayout, TyLayout};
 use rustc::ty::{self, Ty};
 use rustc_data_structures::indexed_vec::Idx;
 
@@ -196,6 +196,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         }
     }
 
+    pub fn read_lvalue(&self, lvalue: Lvalue) -> EvalResult<'tcx, Value> {
+        match lvalue {
+            Lvalue::Ptr { ptr, extra } => {
+                assert_eq!(extra, LvalueExtra::None);
+                Ok(Value::ByRef(ptr.to_ptr()?))
+            }
+            Lvalue::Local { frame, local } => self.stack[frame].get_local(local),
+            Lvalue::Global(cid) => {
+                Ok(self.globals.get(&cid).expect("global not cached").value)
+            }
+        }
+    }
+
     pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
         use rustc::mir::Lvalue::*;
         let lvalue = match *mir_lvalue {
@@ -207,7 +220,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                 Lvalue::Global(GlobalId { instance, promoted: None })
             }
 
-            Projection(ref proj) => return self.eval_lvalue_projection(proj),
+            Projection(ref proj) => {
+                let ty = self.lvalue_ty(&proj.base);
+                let lvalue = self.eval_lvalue(&proj.base)?;
+                return self.eval_lvalue_projection(lvalue, ty, &proj.elem);
+            }
         };
 
         if log_enabled!(::log::LogLevel::Trace) {
@@ -220,216 +237,224 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
     pub fn lvalue_field(
         &mut self,
         base: Lvalue<'tcx>,
-        field_index: usize,
-        base_ty: Ty<'tcx>,
-        field_ty: Ty<'tcx>,
-    ) -> EvalResult<'tcx, Lvalue<'tcx>> {
-        let base_layout = self.type_layout(base_ty)?;
-
-        use rustc::ty::layout::Layout::*;
-        let (offset, packed) = match *base_layout {
-            Univariant { ref variant, .. } => {
-                (variant.offsets[field_index], variant.packed)
-            },
-
-            General { ref variants, .. } => {
-                let (_, base_extra) = base.to_ptr_and_extra();
-                if let LvalueExtra::DowncastVariant(variant_idx) = base_extra {
-                    // +1 for the discriminant, which is field 0
-                    (variants[variant_idx].offsets[field_index + 1], variants[variant_idx].packed)
-                } else {
-                    bug!("field access on enum had no variant index");
-                }
-            }
-
-            RawNullablePointer { .. } => {
-                assert_eq!(field_index, 0);
-                return Ok(base);
-            }
-
-            StructWrappedNullablePointer { ref nonnull, .. } => {
-                (nonnull.offsets[field_index], nonnull.packed)
-            }
-
-            UntaggedUnion { .. } => return Ok(base),
-
-            Vector { element, count } => {
-                let field = field_index as u64;
-                assert!(field < count);
-                let elem_size = element.size(&self.tcx.data_layout).bytes();
-                (Size::from_bytes(field * elem_size), false)
+        field: mir::Field,
+        base_layout: TyLayout<'tcx>,
+    ) -> EvalResult<'tcx, (Lvalue<'tcx>, TyLayout<'tcx>)> {
+        let base_layout: TyLayout<'tcx> = match base {
+            Lvalue::Ptr { extra: LvalueExtra::DowncastVariant(n), .. } => {
+                base_layout.for_variant(&self, n)
             }
+            _ => base_layout,
+        };
+        let field_index = field.index();
+        let field = base_layout.field(&self, field_index)?;
+        let offset = base_layout.fields.offset(field_index);
 
-            // We treat arrays + fixed sized indexing like field accesses
-            Array { .. } => {
-                let field = field_index as u64;
-                let elem_size = match base_ty.sty {
-                    ty::TyArray(elem_ty, n) => {
-                        assert!(field < n.val.to_const_int().unwrap().to_u64().unwrap()  as u64);
-                        self.type_size(elem_ty)?.expect("array elements are sized") as u64
-                    },
-                    _ => bug!("lvalue_field: got Array layout but non-array type {:?}", base_ty),
-                };
-                (Size::from_bytes(field * elem_size), false)
+        // Do not allocate in trivial cases
+        let (base_ptr, base_extra) = match base {
+            Lvalue::Ptr { ptr, extra } => (ptr, extra),
+            Lvalue::Local { frame, local } => {
+                match self.stack[frame].get_local(local)? {
+                    // in case the field covers the entire type, just return the value
+                    Value::ByVal(_) if offset.bytes() == 0 &&
+                                       field.size == base_layout.size => {
+                        return Ok((base, field));
+                    }
+                    Value::ByRef { .. } |
+                    Value::ByValPair(..) |
+                    Value::ByVal(_) => self.force_allocation(base)?.to_ptr_and_extra(),
+                }
             }
-
-            FatPointer { .. } => {
-                let bytes = field_index as u64 * self.memory.pointer_size();
-                let offset = Size::from_bytes(bytes);
-                (offset, false)
+            Lvalue::Global(_cid) => {
+                self.force_allocation(base)?.to_ptr_and_extra()
             }
-
-            _ => bug!("field access on non-product type: {:?}", base_layout),
         };
 
-        let (base_ptr, base_extra) = self.force_allocation(base)?.to_ptr_and_extra();
-
         let offset = match base_extra {
             LvalueExtra::Vtable(tab) => {
-                let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(base_ptr, PrimVal::Ptr(tab)))?;
-                offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes()
+                let (_, align) = self.size_and_align_of_dst(
+                    base_layout.ty,
+                    base_ptr.to_ptr()?.to_value_with_vtable(tab),
+                )?;
+                offset.abi_align(align).bytes()
             }
             _ => offset.bytes(),
         };
 
-        let ptr = match base_ptr.to_ptr()?.offset {
-            PointerOffset::Concrete(_) => base_ptr.offset(offset, self.memory.layout)?,
-            PointerOffset::Abstract(sbytes) => {
-                let new_offset = self.memory.constraints.add_binop_constraint(
-                    mir::BinOp::Add,
-                    PrimVal::Bytes(offset as u128),
-                    PrimVal::Abstract(sbytes),
-                    PrimValKind::U64);
-                if let PrimVal::Abstract(sb) = new_offset {
-                    PrimVal::Ptr(Pointer::new_abstract(base_ptr.to_ptr()?.alloc_id, sb))
-                } else {
-                    unreachable!()
-                }
-            }
-        };
-
-        let field_ty = self.monomorphize(field_ty, self.substs());
+        let ptr = base_ptr.to_ptr()?.offset(offset, (&self).data_layout())?;
+        // if we were unaligned, stay unaligned
+        // no matter what we were, if we are packed, we must not be aligned anymore
+        //ptr.aligned &= !base_layout.is_packed();
 
-        if packed {
-            let size = self.type_size(field_ty)?.expect("packed struct must be sized");
-            self.memory.mark_packed(ptr.to_ptr()?, size);
-        }
-
-        let extra = if self.type_is_sized(field_ty) {
+        let extra = if !field.is_unsized() {
             LvalueExtra::None
         } else {
             match base_extra {
                 LvalueExtra::None => bug!("expected fat pointer"),
-                LvalueExtra::DowncastVariant(..) =>
-                    bug!("Rust doesn't support unsized fields in enum variants"),
+                LvalueExtra::DowncastVariant(..) => {
+                    bug!("Rust doesn't support unsized fields in enum variants")
+                }
                 LvalueExtra::Vtable(_) |
-                LvalueExtra::Length(_) => {},
+                LvalueExtra::Length(_) => {}
             }
             base_extra
         };
 
+        Ok((Lvalue::Ptr { ptr: PrimVal::Ptr(ptr), extra }, field))
+    }
+
+    pub(super) fn val_to_lvalue(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
+        Ok(match self.tcx.struct_tail(ty).sty {
+            ty::TyDynamic(..) => {
+                let (ptr, vtable) = val.into_ptr_vtable_pair(&self.memory)?;
+                Lvalue::Ptr {
+                    ptr: ptr,
+                    extra: LvalueExtra::Vtable(vtable),
+                }
+            }
+            ty::TyStr | ty::TySlice(_) => {
+                let (ptr, len) = val.into_slice(&self.memory)?;
+                Lvalue::Ptr {
+                    ptr: ptr,
+                    extra: LvalueExtra::Length(len),
+                }
+            }
+            _ => Lvalue::from_primval_ptr(val.read_ptr(&self.memory)?),
+        })
+    }
+
+    pub(super) fn lvalue_index(
+        &mut self,
+        base: Lvalue<'tcx>,
+        outer_ty: Ty<'tcx>,
+        idx: PrimVal,
+    ) -> EvalResult<'tcx, Lvalue<'tcx>> {
+        // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length.
+        let base = self.force_allocation(base)?;
+        let (base_ptr, _) = base.to_ptr_and_extra();
+
+        let (elem_ty, len) = base.elem_ty_and_len(outer_ty);
+        let elem_size = self.type_size(elem_ty)?.expect(
+            "slice element must be sized",
+        );
+        if idx.is_concrete() {
+            let n = idx.to_u64()?;
+            assert!(
+                n < len,
+                "Tried to access element {} of array/slice with length {}",
+                n,
+                len
+            );
+            let ptr_primval = match (base_ptr.to_ptr(), elem_size) {
+                (Ok(p), _) => PrimVal::Ptr(p.offset(n * elem_size, (&self).data_layout())?),
+                (Err(_), 0) => base_ptr,
+                (Err(e), _) => return Err(e),
+            };
+            Ok(Lvalue::Ptr {
+                ptr: ptr_primval,
+                extra: LvalueExtra::None,
+            })
+        } else {
+            let ptr_primval = match (base_ptr.to_ptr(), elem_size) {
+                (Ok(p), _) => {
+                    let byte_index = self.memory.constraints.add_binop_constraint(
+                        mir::BinOp::Mul,
+                        idx,
+                        PrimVal::Bytes(elem_size as u128),
+                        PrimValKind::U64);
+
+                    let base_offset_primval = match p.offset {
+                        PointerOffset::Concrete(n) => PrimVal::Bytes(n as u128),
+                        PointerOffset::Abstract(sbytes) => PrimVal::Abstract(sbytes),
+                    };
+
+                    match self.memory.constraints.add_binop_constraint(
+                        mir::BinOp::Add,
+                        base_offset_primval,
+                        byte_index,
+                        PrimValKind::U64) {
+                        PrimVal::Abstract(sbytes) => {
+                            PrimVal::Ptr(Pointer::new_abstract(p.alloc_id, sbytes))
+                        }
+                        _ => unreachable!(),
+                    }
+
+                }
+                (Err(_), 0) => base_ptr,
+                (Err(e), _) => return Err(e),
+            };
+
+            Ok(Lvalue::Ptr {
+                ptr: ptr_primval,
+                extra: LvalueExtra::None,
+            })
+        }
+    }
+
+    pub(super) fn lvalue_downcast(
+        &mut self,
+        base: Lvalue<'tcx>,
+        variant: usize,
+    ) -> EvalResult<'tcx, Lvalue<'tcx>> {
+        // FIXME(solson)
+        let base = self.force_allocation(base)?;
+        let (ptr, _) = base.to_ptr_and_extra();
+        let extra = LvalueExtra::DowncastVariant(variant);
         Ok(Lvalue::Ptr { ptr, extra })
     }
 
-    fn eval_lvalue_projection(
+    pub(super) fn eval_lvalue_projection(
         &mut self,
-        proj: &mir::LvalueProjection<'tcx>,
+        base: Lvalue<'tcx>,
+        base_ty: Ty<'tcx>,
+        proj_elem: &mir::ProjectionElem<'tcx, mir::Local, Ty<'tcx>>,
     ) -> EvalResult<'tcx, Lvalue<'tcx>> {
         use rustc::mir::ProjectionElem::*;
-        let (ptr, extra): (PrimVal, _) = match proj.elem {
-            Field(field, field_ty) => {
-                let base = self.eval_lvalue(&proj.base)?;
-                let base_ty = self.lvalue_ty(&proj.base);
-                return self.lvalue_field(base, field.index(), base_ty, field_ty);
+        let (ptr, extra) = match *proj_elem {
+            Field(field, _) => {
+                let layout = self.type_layout(base_ty)?;
+                return Ok(self.lvalue_field(base, field, layout)?.0);
             }
 
             Downcast(_, variant) => {
-                let base = self.eval_lvalue(&proj.base)?;
-                let base_ty = self.lvalue_ty(&proj.base);
-                let base_layout = self.type_layout(base_ty)?;
-                // FIXME(solson)
-                let base = self.force_allocation(base)?;
-                let (base_ptr, base_extra) = base.to_ptr_and_extra();
-
-                use rustc::ty::layout::Layout::*;
-                let extra = match *base_layout {
-                    General { .. } => LvalueExtra::DowncastVariant(variant),
-                    RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => base_extra,
-                    _ => bug!("variant downcast on non-aggregate: {:?}", base_layout),
-                };
-                (base_ptr, extra)
+                return self.lvalue_downcast(base, variant);
             }
 
             Deref => {
-                let base_ty = self.lvalue_ty(&proj.base);
-                let val = self.eval_and_read_lvalue(&proj.base)?;
+                let val = self.read_lvalue(base)?;
 
                 let pointee_type = match base_ty.sty {
                     ty::TyRawPtr(ref tam) |
                     ty::TyRef(_, ref tam) => tam.ty,
-                    ty::TyAdt(ref def, _) if def.is_box() => base_ty.boxed_ty(),
+                    ty::TyAdt(def, _) if def.is_box() => base_ty.boxed_ty(),
                     _ => bug!("can only deref pointer types"),
                 };
 
                 trace!("deref to {} on {:?}", pointee_type, val);
 
-                match self.tcx.struct_tail(pointee_type).sty {
-                    ty::TyDynamic(..) => {
-                        let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?;
-                        (ptr, LvalueExtra::Vtable(vtable))
-                    },
-                    ty::TyStr | ty::TySlice(_) => {
-                        let (ptr, len) = val.expect_slice(&self.memory)?;
-                        (ptr, LvalueExtra::Length(len))
-                    },
-                    _ => (val.read_ptr(&self.memory)?, LvalueExtra::None),
-                }
+                return self.val_to_lvalue(val, pointee_type);
             }
 
             Index(local) => {
-                let base = self.eval_lvalue(&proj.base)?;
-                let base_ty = self.lvalue_ty(&proj.base);
-                // FIXME(solson)
-                let base = self.force_allocation(base)?;
-                let (base_ptr, _) = base.to_ptr_and_extra();
-
-                let (elem_ty, len) = base.elem_ty_and_len(base_ty);
-                let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
-
                 let value = self.frame().get_local(local)?;
                 let ty = self.tcx.types.usize;
-                let primval = self.value_to_primval(value, ty)?;
-                if let PrimVal::Abstract(_sbytes) = primval {
-                    // byte_offset = sbytes * elem_size
-                    let byte_offset = self.memory.constraints.add_binop_constraint(
-                        mir::BinOp::Mul,
-                        primval,
-                        PrimVal::Bytes(elem_size as u128),
-                        PrimValKind::U64);
-
-                    if let PrimVal::Abstract(sbytes) = byte_offset {
-                        (PrimVal::Ptr(Pointer::new_abstract(base_ptr.to_ptr()?.alloc_id, sbytes)),
-                         LvalueExtra::None)
-                    } else {
-                        unreachable!()
-                    }
-                } else {
-                    let n = primval.to_u64()?;
-                    assert!(n < len);
-                    let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?;
-                    (ptr, LvalueExtra::None)
-                }
+                let idx = self.value_to_primval(value, ty)?;
+                return self.lvalue_index(base, base_ty, idx);
             }
 
-            ConstantIndex { offset, min_length, from_end } => {
-                let base = self.eval_lvalue(&proj.base)?;
-                let base_ty = self.lvalue_ty(&proj.base);
+            ConstantIndex {
+                offset,
+                min_length,
+                from_end,
+            } => {
                 // FIXME(solson)
                 let base = self.force_allocation(base)?;
                 let (base_ptr, _) = base.to_ptr_and_extra();
 
                 let (elem_ty, n) = base.elem_ty_and_len(base_ty);
-                let elem_size = self.type_size(elem_ty)?.expect("sequence element must be sized");
+                let elem_size = self.type_size(elem_ty)?.expect(
+                    "sequence element must be sized",
+                );
                 assert!(n >= min_length as u64);
 
                 let index = if from_end {
@@ -438,27 +463,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                     u64::from(offset)
                 };
 
-                let ptr = base_ptr.offset(index * elem_size, self.memory.layout)?;
+                let ptr = base_ptr.to_ptr()?.offset(index * elem_size, (&self).data_layout())?;
                 (ptr, LvalueExtra::None)
             }
 
             Subslice { from, to } => {
-                let base = self.eval_lvalue(&proj.base)?;
-                let base_ty = self.lvalue_ty(&proj.base);
                 // FIXME(solson)
                 let base = self.force_allocation(base)?;
                 let (base_ptr, _) = base.to_ptr_and_extra();
 
                 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");
+                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 extra = LvalueExtra::Length(PrimVal::from_u128((n - u64::from(to) - u64::from(from))as u128));
+                let ptr = base_ptr.to_ptr()?.offset(u64::from(from) * elem_size, (&self).data_layout())?;
+                // sublicing arrays produces arrays
+                let extra = if self.type_is_sized(base_ty) {
+                    LvalueExtra::None
+                } else {
+                    LvalueExtra::Length(PrimVal::Bytes((n - u64::from(to) - u64::from(from)) as u128))
+                };
                 (ptr, extra)
             }
         };
 
-        Ok(Lvalue::Ptr { ptr, extra })
+        Ok(Lvalue::Ptr { ptr: PrimVal::Ptr(ptr), extra })
     }
 
     pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
diff --git a/src/memory.rs b/src/memory.rs
index b06f5f1ca7ce2b82f62266d4a836c463bf41b089..7c6ba67e061cf47887142468d8cfe8c3aece3a41 100644
--- a/src/memory.rs
+++ b/src/memory.rs
@@ -7,7 +7,7 @@ use rustc::ty::layout::{self, TargetDataLayout};
 
 use constraints::ConstraintContext;
 use error::{EvalError, EvalResult};
-use value::{self, PrimVal, PrimValKind};
+use value::{self, PrimVal, PrimValKind, Value};
 
 ////////////////////////////////////////////////////////////////////////////////
 // Allocations and pointers
@@ -140,6 +140,10 @@ impl Pointer {
             _ => unimplemented!(),
         }
     }
+
+    pub fn to_value_with_vtable(self, vtable: Pointer) -> Value {
+        Value::ByValPair(PrimVal::Ptr(self), PrimVal::Ptr(vtable))
+    }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -213,6 +217,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
         }
     }
 
+    //// Trunace the given value to the pointer size; also return whether there was an overflow
+    pub fn truncate_to_ptr(&self, val: u128) -> (u64, bool) {
+        let max_ptr_plus_1 = 1u128 << self.layout.pointer_size.bits();
+        ((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1)
+    }
+
     pub fn allocations(&self) -> ::std::collections::hash_map::Iter<AllocId, Allocation> {
         self.alloc_map.iter()
     }
@@ -858,6 +868,42 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
         Ok(())
     }
 
+    pub fn read_primval(&self, ptr: Pointer, size: u64, signed: bool) -> EvalResult<'tcx, PrimVal> {
+        self.check_relocation_edges(ptr, size)?; // Make sure we don't read part of a pointer as a pointer
+        let endianess = self.endianness();
+        let bytes = self.get_bytes_unchecked(ptr, size, self.int_align(size)?)?;
+        // Undef check happens *after* we established that the alignment is correct.
+        // We must not return Ok() for unaligned pointers!
+        if self.check_defined(ptr, size).is_err() {
+            return Ok(PrimVal::Undef.into());
+        }
+        // Now we do the actual reading
+        let bytes = if signed {
+            read_target_int(endianess, bytes).unwrap() as u128
+        } else {
+            read_target_uint(endianess, bytes).unwrap()
+        };
+        // See if we got a pointer
+        if size != self.pointer_size() {
+            if self.relocations(ptr, size)?.count() != 0 {
+                return Err(EvalError::ReadPointerAsBytes);
+            }
+        } else {
+            let alloc = self.get(ptr.alloc_id)?;
+            match ptr.offset {
+                PointerOffset::Concrete(off) => {
+                    match alloc.relocations.get(&off) {
+                        Some(&alloc_id) => return Ok(PrimVal::Ptr(Pointer::new(alloc_id, bytes as u64))),
+                        None => {},
+                    }
+                }
+                PointerOffset::Abstract(_) => unimplemented!(),
+            }
+        }
+        // We don't. Just return the bytes.
+        Ok(PrimVal::Bytes(bytes))
+    }
+
     pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, PrimVal> {
         let size = self.pointer_size();
         if self.check_defined(ptr, size).is_err() {
diff --git a/src/step.rs b/src/step.rs
index 8895817647f735578ddb8552ef3207535c443b78..3eab447ed9ec17d0393e043ea7c5a0e676a60a56 100644
--- a/src/step.rs
+++ b/src/step.rs
@@ -7,7 +7,6 @@ use rustc::hir;
 use rustc::mir::visit::{Visitor, LvalueContext};
 use rustc::mir;
 use rustc::traits::Reveal;
-use rustc::ty::layout::Layout;
 use rustc::ty::{subst, self};
 use rustc::middle::const_val::ConstVal;
 
@@ -15,7 +14,6 @@ use error::{EvalResult, EvalError};
 use eval_context::{EvalContext, StackPopCleanup};
 use executor::FinishStep;
 use lvalue::{Global, GlobalId, Lvalue};
-use value::{Value, PrimVal};
 use syntax::codemap::Span;
 
 impl<'a, 'tcx> EvalContext<'a, 'tcx> {
@@ -86,32 +84,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         match stmt.kind {
             Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?,
 
-            SetDiscriminant { ref lvalue, variant_index } => {
+            SetDiscriminant {
+                ref lvalue,
+                variant_index,
+            } => {
                 let dest = self.eval_lvalue(lvalue)?;
                 let dest_ty = self.lvalue_ty(lvalue);
-                let dest_layout = self.type_layout(dest_ty)?;
-
-                match *dest_layout {
-                    Layout::General { discr, .. } => {
-                        // FIXME: I (oli-obk) think we need to check the
-                        // `dest_ty` for the variant's discriminant and write
-                        // instead of the variant index
-                        // We don't have any tests actually going through these lines
-                        let discr_ty = discr.to_ty(&self.tcx, false);
-                        let discr_lval = self.lvalue_field(dest, 0, dest_ty, discr_ty)?;
-
-                        self.write_value(Value::ByVal(PrimVal::Bytes(variant_index as u128)), discr_lval, discr_ty)?;
-                    }
-
-                    Layout::RawNullablePointer { nndiscr, .. } => {
-                        use value::PrimVal;
-                        if variant_index as u64 != nndiscr {
-                            self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
-                        }
-                    }
-
-                    _ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout),
-                }
+                self.write_discriminant_value(dest_ty, dest, variant_index)?;
             }
 
             // Miri can safely ignore these. Only translation needs it.
diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs
index 0fbc88becd0a973b1f4bead2c2884cbb814a633d..8fc92b9a0c45646f8a99e130d2bb096dbcf7b425 100644
--- a/src/terminator/intrinsic.rs
+++ b/src/terminator/intrinsic.rs
@@ -1,6 +1,6 @@
 use rustc::mir;
 use rustc::traits::Reveal;
-use rustc::ty::layout::{Layout, Size, Align};
+use rustc::ty::layout::{Size, Align};
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty};
 
@@ -17,7 +17,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         args: &[mir::Operand<'tcx>],
         dest: Lvalue<'tcx>,
         dest_ty: Ty<'tcx>,
-        dest_layout: &'tcx Layout,
+        dest_layout: ty::layout::TyLayout<'tcx>,
         target: mir::BasicBlock,
     ) -> EvalResult<'tcx> {
         let arg_vals: EvalResult<Vec<Value>> = args.iter()
@@ -179,7 +179,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
             "discriminant_value" => {
                 let ty = instance.substs.type_at(0);
                 let adt_ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?;
-                let discr_val = self.read_discriminant_value(adt_ptr, ty)?;
+                let discr_val = self.read_discriminant_value(Lvalue::from_ptr(adt_ptr), ty)?;
                 self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?;
             }
 
@@ -290,7 +290,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
             "pref_align_of" => {
                 let ty = instance.substs.type_at(0);
                 let layout = self.type_layout(ty)?;
-                let align = layout.align(&self.tcx.data_layout).pref();
+                let align = layout.align.pref();
                 let align_val = PrimVal::from_u128(align as u128);
                 self.write_primval(dest, align_val, dest_ty)?;
             }
@@ -404,14 +404,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
             "size_of_val" => {
                 let ty = instance.substs.type_at(0);
                 let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?;
-                self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?;
+                self.write_primval(dest, PrimVal::from_u128(size.bytes() as u128), dest_ty)?;
             }
 
             "min_align_of_val" |
             "align_of_val" => {
                 let ty = instance.substs.type_at(0);
                 let (_, align) = self.size_and_align_of_dst(ty, arg_vals[0])?;
-                self.write_primval(dest, PrimVal::from_u128(align as u128), dest_ty)?;
+                self.write_primval(dest, PrimVal::from_u128(align.abi() as u128), dest_ty)?;
             }
 
             "type_name" => {
@@ -436,7 +436,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
             }
 
             "uninit" => {
-                let size = dest_layout.size(&self.tcx.data_layout).bytes();
+                let size = dest_layout.size.bytes();
                 let uninit = |this: &mut Self, val: Value| {
                     match val {
                         Value::ByRef(ptr) => {
@@ -481,41 +481,51 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         Ok(())
     }
 
+    /// Return the size and aligment of the value at the given type.
+    /// Note that the value does not matter if the type is sized. For unsized types,
+    /// the value has to be a fat pointer, and we only care about the "extra" data in it.
     pub fn size_and_align_of_dst(
-        &self,
+        &mut self,
         ty: ty::Ty<'tcx>,
         value: Value,
-    ) -> EvalResult<'tcx, (u64, u64)> {
-        if let Some(size) = self.type_size(ty)? {
-            Ok((size as u64, self.type_align(ty)? as u64))
+    ) -> EvalResult<'tcx, (Size, Align)> {
+        let layout = self.type_layout(ty)?;
+        if !layout.is_unsized() {
+            Ok(layout.size_and_align())
         } else {
             match ty.sty {
-                ty::TyAdt(def, substs) => {
+                ty::TyAdt(..) | ty::TyTuple(..) => {
                     // First get the size of all statically known fields.
                     // Don't use type_of::sizing_type_of because that expects t to be sized,
                     // and it also rounds up to alignment, which we want to avoid,
                     // as the unsized field's alignment could be smaller.
                     assert!(!ty.is_simd());
-                    let layout = self.type_layout(ty)?;
                     debug!("DST {} layout: {:?}", ty, layout);
 
-                    let (sized_size, sized_align) = match *layout {
-                        ty::layout::Layout::Univariant { ref variant, .. } => {
-                            (variant.offsets.last().map_or(0, |o| o.bytes()), variant.align)
-                        }
-                        _ => {
-                            bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}",
-                                 ty, layout);
-                        }
-                    };
-                    debug!("DST {} statically sized prefix size: {} align: {:?}",
-                           ty, sized_size, sized_align);
+                    let sized_size = layout.fields.offset(layout.fields.count() - 1);
+                    let sized_align = layout.align;
+                    debug!(
+                        "DST {} statically sized prefix size: {:?} align: {:?}",
+                        ty,
+                        sized_size,
+                        sized_align
+                    );
 
                     // Recurse to get the size of the dynamically sized field (must be
                     // the last field).
-                    let last_field = def.struct_variant().fields.last().unwrap();
-                    let field_ty = self.field_ty(substs, last_field);
-                    let (unsized_size, unsized_align) = self.size_and_align_of_dst(field_ty, value)?;
+                    let (unsized_size, unsized_align) = match ty.sty {
+                        ty::TyAdt(def, substs) => {
+                            let last_field = def.struct_variant().fields.last().unwrap();
+                            let field_ty = self.field_ty(substs, last_field);
+                            self.size_and_align_of_dst(field_ty, value)?
+                        }
+                        ty::TyTuple(ref types, _) => {
+                            let field_ty = types.last().unwrap();
+                            let field_ty = self.tcx.fully_normalize_monormophic_ty(field_ty);
+                            self.size_and_align_of_dst(field_ty, value)?
+                        }
+                        _ => bug!("We already checked that we know this type"),
+                    };
 
                     // FIXME (#26403, #27023): We should be adding padding
                     // to `sized_size` (to accommodate the `unsized_align`
@@ -529,7 +539,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
 
                     // Choose max of two known alignments (combined value must
                     // be aligned according to more restrictive of the two).
-                    let align = sized_align.max(Align::from_bytes(unsized_align, unsized_align).unwrap());
+                    let align = sized_align.max(unsized_align);
 
                     // Issue #27023: must add any necessary padding to `size`
                     // (to make it a multiple of `align`) before returning it.
@@ -542,30 +552,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                     //
                     //   `(size + (align-1)) & -align`
 
-                    let size = Size::from_bytes(size).abi_align(align).bytes();
-                    Ok((size, align.abi()))
+                    Ok((size.abi_align(align), align))
                 }
                 ty::TyDynamic(..) => {
-                    let (_, vtable) = value.expect_ptr_vtable_pair(&self.memory)?;
+                    let (_, vtable) = value.into_ptr_vtable_pair(&mut self.memory)?;
                     // the second entry in the vtable is the dynamic size of the object.
                     self.read_size_and_align_from_vtable(vtable)
                 }
 
                 ty::TySlice(_) | ty::TyStr => {
-                    let elem_ty = ty.sequence_element_type(self.tcx);
-                    let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized") as u64;
-                    let (_, len) = value.expect_slice(&self.memory)?;
-                    let align = self.type_align(elem_ty)?;
-                    match len {
-                        PrimVal::Bytes(n) => Ok(((n as u64) * elem_size, align as u64)),
-                        _ => unimplemented!(),
-                    }
+                    let (elem_size, align) = layout.field(&self, 0)?.size_and_align();
+                    let (_, len) = value.into_slice(&mut self.memory)?;
+                    Ok((elem_size * len.to_u64()?, align))
                 }
 
                 _ => bug!("size_of_val::<{:?}>", ty),
             }
         }
     }
+
     /// Returns the normalized type of a struct field
     fn field_ty(
         &self,
diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs
index a4c558dcceae204d8a20ca5a249aa1bcf031347e..e382098afa8819b4daf23c9f1d1b7ca3929c7de3 100644
--- a/src/terminator/mod.rs
+++ b/src/terminator/mod.rs
@@ -1,17 +1,17 @@
 use rustc::hir::def_id::DefId;
 use rustc::mir;
 use rustc::ty::{self, TypeVariants, Ty};
-use rustc::ty::layout::Layout;
+use rustc::ty::layout::HasDataLayout;
 use syntax::codemap::Span;
 use syntax::attr;
 use syntax::abi::Abi;
 
 use constraints::Constraint;
 use error::{EvalError, EvalResult};
-use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited};
+use eval_context::{EvalContext, StackPopCleanup, ValTy, is_inhabited};
 use executor::{FinishStep, FinishStepVariant};
 use lvalue::Lvalue;
-use memory::{Pointer, SByte};
+use memory::{SByte};
 use value::{PrimVal, PrimValKind};
 use value::Value;
 use rustc_data_structures::indexed_vec::Idx;
@@ -128,7 +128,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                         }
                         (instance, sig)
                     },
-                    ty::TyFnDef(def_id, substs) => (::eval_context::resolve(self.tcx, def_id, substs), func_ty.fn_sig(self.tcx)),
+                    ty::TyFnDef(def_id, substs) => (self.resolve(def_id, substs)?, func_ty.fn_sig(self.tcx)),
                     _ => {
                         let msg = format!("can't handle callee of type {:?}", func_ty);
                         return Err(EvalError::Unimplemented(msg));
@@ -279,9 +279,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                 if check_ty_compat(sig.output(), real_sig.output()) && real_sig.inputs_and_output.len() == 3 => {
                 // First argument of real_sig must be a ZST
                 let fst_ty = real_sig.inputs_and_output[0];
-                let layout = self.type_layout(fst_ty)?;
-                let size = layout.size(&self.tcx.data_layout).bytes();
-                if size == 0 {
+                if self.type_layout(fst_ty)?.is_zst() {
                     // Second argument must be a tuple matching the argument list of sig
                     let snd_ty = real_sig.inputs_and_output[1];
                     match snd_ty.sty {
@@ -294,6 +292,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                 }
             }
             _ => {}
+
         };
 
         // Nope, this doesn't work.
@@ -382,7 +381,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                 for arg in arg_operands {
                     let arg_val = self.eval_operand(arg)?;
                     let arg_ty = self.operand_ty(arg);
-                    args.push((arg_val, arg_ty));
+                    args.push(ValTy { value: arg_val, ty: arg_ty });
                 }
 
                 if self.eval_fn_call_inner(
@@ -401,7 +400,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                 trace!("arg_operands: {:?}", arg_operands);
                 match sig.abi {
                     Abi::Rust => {
-                        for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) {
+                        for (arg_local, ValTy { value: arg_val, ty: arg_ty }) in arg_locals.zip(args) {
                             let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
                             self.write_value(arg_val, dest, arg_ty)?;
                         }
@@ -412,41 +411,54 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                         {   // write first argument
                             let first_local = arg_locals.next().unwrap();
                             let dest = self.eval_lvalue(&mir::Lvalue::Local(first_local))?;
-                            let (arg_val, arg_ty) = args.remove(0);
-                            self.write_value(arg_val, dest, arg_ty)?;
+                            self.write_value(args[0].value, dest, args[0].ty)?;
                         }
 
                         // unpack and write all other args
-                        let (arg_val, arg_ty) = args.remove(0);
-                        let layout = self.type_layout(arg_ty)?;
-                        if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) {
-                            trace!("fields: {:?}", fields);
-                            if self.frame().mir.args_iter().count() == fields.len() + 1 {
-                                let offsets = variant.offsets.iter().map(|s| s.bytes());
-                                match arg_val {
+                        let layout = self.type_layout(args[1].ty)?;
+                        if let ty::TyTuple(..) = args[1].ty.sty {
+                            if self.frame().mir.args_iter().count() == layout.fields.count() + 1 {
+                                match args[1].value {
                                     Value::ByRef(ptr) => {
-                                        for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) {
-                                            let arg = Value::ByRef(ptr.offset(offset, self.memory.layout)?);
-                                            let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
-                                            trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty);
-                                            self.write_value(arg, dest, ty)?;
+                                        for (i, arg_local) in arg_locals.enumerate() {
+                                            let field = layout.field(&self, i)?;
+                                            let offset = layout.fields.offset(i).bytes();
+                                            let arg = Value::ByRef(ptr.offset(offset, (&self).data_layout())?);
+                                            let dest =
+                                                self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
+                                            trace!(
+                                                "writing arg {:?} to {:?} (type: {})",
+                                                arg,
+                                                dest,
+                                                field.ty
+                                            );
+                                            self.write_value(arg, dest, field.ty)?;
                                         }
-                                    },
-                                    Value::ByVal(PrimVal::Undef) => {},
+                                    }
+                                    Value::ByVal(PrimVal::Undef) => {}
                                     other => {
-                                        assert_eq!(fields.len(), 1);
-                                        let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?;
-                                        self.write_value(other, dest, fields[0])?;
+                                        assert_eq!(layout.fields.count(), 1);
+                                        let dest = self.eval_lvalue(&mir::Lvalue::Local(
+                                            arg_locals.next().unwrap(),
+                                        ))?;
+                                        let field_ty = layout.field(&self, 0)?.ty;
+                                        self.write_value(other, dest, field_ty)?;
                                     }
                                 }
                             } else {
                                 trace!("manual impl of rust-call ABI");
                                 // called a manual impl of a rust-call function
-                                let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?;
-                                self.write_value(arg_val, dest, arg_ty)?;
+                                let dest = self.eval_lvalue(
+                                    &mir::Lvalue::Local(arg_locals.next().unwrap()),
+                                )?;
+                                self.write_value(args[1].value, dest, args[1].ty)?;
                             }
                         } else {
-                            bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout);
+                            bug!(
+                                "rust-call ABI tuple argument was {:#?}, {:#?}",
+                                args[1].ty,
+                                layout
+                            );
                         }
                     }
                     _ => unimplemented!(),
@@ -455,12 +467,12 @@ 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])?.expect_ptr_vtable_pair(&self.memory)?;
+                let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(&self.memory)?;
                 let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?;
                 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]);
-                let ty = self.get_field_ty(ty, 0)?;
+                let ty = self.get_field_ty(ty, 0)?.ty;
                 match arg_operands[0] {
                     mir::Operand::Consume(ref mut lval) => *lval = lval.clone().field(mir::Field::new(0), ty),
                     _ => bug!("virtual call first arg cannot be a constant"),
@@ -498,8 +510,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                         self.goto_block(block);
                         return Ok(true);
                     }
-                    "<std::io::Stdin as std::io::Read>::read" |
-                    "<std::io::Stdin as std::io::Read>::read_exact" => {
+                    "<std::io::Stdin as std::io::Read>::read" => {
                         let (lval, block) = destination.expect("Stdin::read() does not diverge");
                         let args_res: EvalResult<Vec<Value>> = arg_operands.iter()
                             .map(|arg| self.eval_operand(arg))
@@ -532,6 +543,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                         self.goto_block(block);
                         return Ok(true);
                     }
+                    "<std::io::Stdin as std::io::Read>::read_exact" => {
+                        let (lval, block) = destination.expect("Stdin::read() does not diverge");
+                        let args_res: EvalResult<Vec<Value>> = arg_operands.iter()
+                            .map(|arg| self.eval_operand(arg))
+                            .collect();
+                        let args = args_res?;
+
+                        match args[1] {
+                            Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len)) => {
+                                self.memory.write_fresh_abstract_bytes(ptr, len as u64)?;
+                            }
+                            _ => {
+                                unimplemented!()
+                            }
+                        }
+
+                        let dest_ty = sig.output();
+
+                        // FIXME make this more robust
+                        self.write_discriminant_value(
+                            dest_ty,
+                            lval,
+                            0)?;
+
+                        self.goto_block(block);
+                        return Ok(true);
+                    }
+
                     "std::io::Stdin::lock" => {
                         return Err(
                             EvalError::Unimplemented(
@@ -570,56 +609,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         Ok(false)
     }
 
-    pub fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
-        use rustc::ty::layout::Layout::*;
-        let adt_layout = self.type_layout(adt_ty)?;
-        trace!("read_discriminant_value {:#?}", adt_layout);
-
-        let discr_val = match *adt_layout {
-            General { discr, .. } | CEnum { discr, signed: false, .. } => {
-                let discr_size = discr.size().bytes();
-                self.memory.read_uint(adt_ptr, discr_size)?
-            }
-
-            CEnum { discr, signed: true, .. } => {
-                let discr_size = discr.size().bytes();
-                self.memory.read_int(adt_ptr, discr_size)? as u128
-            }
-
-            RawNullablePointer { nndiscr, value } => {
-                let discr_size = value.size(&self.tcx.data_layout).bytes();
-                trace!("rawnullablepointer with size {}", discr_size);
-                self.read_nonnull_discriminant_value(adt_ptr, nndiscr as u128, discr_size)?
-            }
-
-            StructWrappedNullablePointer { nndiscr, ref discrfield_source, .. } => {
-                let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield_source)?;
-                let nonnull = adt_ptr.offset(offset.bytes(), self.memory.layout)?;
-                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");
-                self.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size)?
-            }
-
-            // The discriminant_value intrinsic returns 0 for non-sum types.
-            Array { .. } | FatPointer { .. } | Scalar { .. } | Univariant { .. } |
-            Vector { .. } | UntaggedUnion { .. } => 0,
-        };
-
-        Ok(discr_val)
-    }
-
-    fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u128, discr_size: u64) -> EvalResult<'tcx, u128> {
-        trace!("read_nonnull_discriminant_value: {:?}, {}, {}", ptr, nndiscr, discr_size);
-        let not_null = match self.memory.read_uint(ptr, discr_size) {
-            Ok(0) => false,
-            Ok(_) | Err(EvalError::ReadPointerAsBytes) => true,
-            Err(e) => return Err(e),
-        };
-        assert!(nndiscr == 0 || nndiscr == 1);
-        Ok(if not_null { nndiscr } else { 1 - nndiscr })
-    }
-
     /// Returns Ok() when the function was handled, fail otherwise
     fn call_missing_fn(
         &mut self,
diff --git a/src/traits.rs b/src/traits.rs
index c5302baa42e4f8fe701a2a4e7a10ac25ae83c74b..ebbae109c86d90cafe8ee180eb13a6c7b8492ed4 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -7,6 +7,7 @@ use value::{Value, PrimVal};
 use rustc::hir::def_id::DefId;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty};
+use rustc::ty::layout::{Size, Align, HasDataLayout};
 use syntax::codemap::DUMMY_SP;
 use syntax::ast;
 
@@ -62,7 +63,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
 
         for (i, method) in self.tcx.vtable_methods(trait_ref).iter().enumerate() {
             if let Some((def_id, substs)) = *method {
-                let instance = ::eval_context::resolve(self.tcx, def_id, substs);
+                let instance = self.resolve(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)?;
             }
@@ -86,11 +87,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         self.memory.get_fn(drop_fn).map(Some)
     }
 
-    pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> {
+    pub fn read_size_and_align_from_vtable(
+        &self,
+        vtable: Pointer,
+    ) -> EvalResult<'tcx, (Size, Align)> {
         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)?)?;
-        Ok((size, align))
+        let size = self.memory.read_usize(vtable.offset(pointer_size, self.data_layout())?)?;
+        let align = self.memory.read_usize(vtable.offset(pointer_size * 2, self.data_layout())?)?;
+        Ok((Size::from_bytes(size), Align::from_bytes(align, align).unwrap()))
     }
 
     pub(crate) fn resolve_associated_const(
diff --git a/src/value.rs b/src/value.rs
index ba49541f7c0cd2c3a5a4e970d1ac0f24b0540575..c83e6630267f445411c33647edf8ae0826e4472d 100644
--- a/src/value.rs
+++ b/src/value.rs
@@ -83,7 +83,7 @@ impl<'a, 'tcx: 'a> Value {
         }
     }
 
-    pub(super) fn expect_ptr_vtable_pair(
+    pub(super) fn into_ptr_vtable_pair(
         &self,
         mem: &Memory<'a, 'tcx>
     ) -> EvalResult<'tcx, (PrimVal, Pointer)> {
@@ -101,16 +101,16 @@ impl<'a, 'tcx: 'a> Value {
         }
     }
 
-    pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (PrimVal, PrimVal)> {
+    pub(super) fn into_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (PrimVal, PrimVal)> {
         use self::Value::*;
         match *self {
             ByRef(ref_ptr) => {
                 let ptr = mem.read_ptr(ref_ptr)?;
                 let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?)?;
-                Ok((ptr, PrimVal::from_u128(len as u128)))
+                Ok((ptr, PrimVal::Bytes(len as u128)))
             },
-            ByValPair(ptr, len) => {
-                Ok((ptr, len))
+            ByValPair(ptr, val) => {
+                Ok((ptr, val))
             },
             _ => unimplemented!(),
         }
@@ -153,7 +153,7 @@ impl<'tcx> PrimVal {
         match self {
             PrimVal::Bytes(b) => Ok(b),
             PrimVal::Abstract(_) => unimplemented!(),
-            PrimVal::Ptr(_) => Err(EvalError::ReadPointerAsBytes),
+            PrimVal::Ptr(_) => Err(EvalError::ReadBytesAsPointer),
             PrimVal::Undef => Err(EvalError::ReadUndefBytes),
         }
     }
diff --git a/tests/compile-fail/reference_to_packed.rs b/tests/compile-fail/reference_to_packed.rs
deleted file mode 100644
index 119225f3e369d1cd679f9b9099bed7e4806588bc..0000000000000000000000000000000000000000
--- a/tests/compile-fail/reference_to_packed.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#![allow(dead_code, unused_variables)]
-
-#[repr(packed)]
-struct Foo {
-    x: i32,
-    y: i32,
-}
-
-fn main() {
-    let foo = Foo {
-        x: 42,
-        y: 99,
-    };
-    let p = &foo.x;
-    let i = *p; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required
-}
\ No newline at end of file
diff --git a/tests/compile-fail/reference_to_packed_unsafe.rs b/tests/compile-fail/reference_to_packed_unsafe.rs
deleted file mode 100644
index 5761f23b7dd41a6dc8b4648b58a13df1e9fb0b69..0000000000000000000000000000000000000000
--- a/tests/compile-fail/reference_to_packed_unsafe.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#![allow(dead_code, unused_variables)]
-
-#[repr(packed)]
-struct Foo {
-    x: i32,
-    y: i32,
-}
-
-fn main() {
-    let foo = Foo {
-        x: 42,
-        y: 99,
-    };
-    let p: *const i32 = &foo.x;
-    let x = unsafe { *p + foo.x }; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required
-}
diff --git a/tests/compile-fail/repeat.rs b/tests/compile-fail/repeat.rs
deleted file mode 100644
index abe89e233e7cd824849d93b527c3a176949aa8c4..0000000000000000000000000000000000000000
--- a/tests/compile-fail/repeat.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-fn main() {
-    let data: [u8; std::usize::MAX] = [42; std::usize::MAX];
-    //~^ ERROR: rustc layout computation failed: SizeOverflow([u8;
-    assert_eq!(data.len(), 1024);
-}
diff --git a/tests/compiletest.rs b/tests/compiletest.rs
index 4a2d5e5a206ac7e1ae9193df96c0a48eb1723367..47f944257e4d8411916c110015381c6adfb20112 100644
--- a/tests/compiletest.rs
+++ b/tests/compiletest.rs
@@ -3,6 +3,16 @@ extern crate compiletest_rs as compiletest;
 use std::path::{PathBuf, Path};
 use std::io::Write;
 
+fn dylib_env_var() -> &'static str {
+    if cfg!(windows) {
+        "PATH"
+    } else if cfg!(target_os = "macos") {
+        "DYLD_LIBRARY_PATH"
+    } else {
+        "LD_LIBRARY_PATH"
+    }
+}
+
 fn compile_fail(sysroot: &Path) {
     let flags = format!("--emit-error --sysroot {} -Dwarnings", sysroot.to_str().expect("non utf8 path"));
     for_all_targets(&sysroot, |target| {
@@ -106,7 +116,7 @@ fn compile_test() {
             let libs = Path::new(&sysroot).join("lib");
             let sysroot = libs.join("rustlib").join(&host).join("lib");
             let paths = std::env::join_paths(&[libs, sysroot]).unwrap();
-            cmd.env(compiletest::procsrv::dylib_env_var(), paths);
+            cmd.env(dylib_env_var(), paths);
 
             match cmd.output() {
                 Ok(ref output) if output.status.success() => {
diff --git a/tests/run-pass/packed_struct.rs b/tests/run-pass/packed_struct.rs
deleted file mode 100644
index 5b3f09c0dd096bfd264939d5e6d799a53f663699..0000000000000000000000000000000000000000
--- a/tests/run-pass/packed_struct.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-#[repr(packed)]
-struct S {
-    a: i32,
-    b: i64,
-}
-
-fn main() {
-    let x = S {
-        a: 42,
-        b: 99,
-    };
-    let a = x.a;
-    let b = x.b;
-    assert_eq!(a, 42);
-    assert_eq!(b, 99);
-    // can't do `assert_eq!(x.a, 42)`, because `assert_eq!` takes a reference
-    assert_eq!({x.a}, 42);
-    assert_eq!({x.b}, 99);
-}
diff --git a/tests/run-pass/u128.rs b/tests/run-pass/u128.rs
deleted file mode 100644
index bd68157e4bc22745b8919bbf83303556fa89e07b..0000000000000000000000000000000000000000
--- a/tests/run-pass/u128.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// ignore-stage0
-// ignore-stage1
-
-// ignore-emscripten
-
-#![feature(i128_type)]
-
-fn b<T>(t: T) -> T { t }
-
-fn main() {
-    let x: u128 = 0xFFFF_FFFF_FFFF_FFFF__FFFF_FFFF_FFFF_FFFF;
-    assert_eq!(0, !x);
-    assert_eq!(0, !x);
-    let y: u128 = 0xFFFF_FFFF_FFFF_FFFF__FFFF_FFFF_FFFF_FFFE;
-    assert_eq!(!1, y);
-    assert_eq!(x, y | 1);
-    assert_eq!(0xFAFF_0000_FF8F_0000__FFFF_0000_FFFF_FFFE,
-               y &
-               0xFAFF_0000_FF8F_0000__FFFF_0000_FFFF_FFFF);
-    let z: u128 = 0xABCD_EF;
-    assert_eq!(z * z, 0x734C_C2F2_A521);
-    assert_eq!(z * z * z * z, 0x33EE_0E2A_54E2_59DA_A0E7_8E41);
-    assert_eq!(z + z + z + z, 0x2AF3_7BC);
-    let k: u128 = 0x1234_5678_9ABC_DEFF_EDCB_A987_6543_210;
-    assert_eq!(k + k, 0x2468_ACF1_3579_BDFF_DB97_530E_CA86_420);
-    assert_eq!(0, k - k);
-    assert_eq!(0x1234_5678_9ABC_DEFF_EDCB_A987_5A86_421, k - z);
-    assert_eq!(0x1000_0000_0000_0000_0000_0000_0000_000,
-               k - 0x234_5678_9ABC_DEFF_EDCB_A987_6543_210);
-    assert_eq!(0x6EF5_DE4C_D3BC_2AAA_3BB4_CC5D_D6EE_8, k / 42);
-    assert_eq!(0, k % 42);
-    assert_eq!(15, z % 42);
-    assert_eq!(0x169D_A8020_CEC18, k % 0x3ACB_FE49_FF24_AC);
-    assert_eq!(0x91A2_B3C4_D5E6_F7, k >> 65);
-    assert_eq!(0xFDB9_7530_ECA8_6420_0000_0000_0000_0000, k << 65);
-    assert!(k > z);
-    assert!(y > k);
-    assert!(y < x);
-    assert_eq!(x as u64, !0);
-    assert_eq!(z as u64, 0xABCD_EF);
-    assert_eq!(k as u64, 0xFEDC_BA98_7654_3210);
-    assert_eq!(k as i128, 0x1234_5678_9ABC_DEFF_EDCB_A987_6543_210);
-    assert_eq!((z as f64) as u128, z);
-    assert_eq!((z as f32) as u128, z);
-    assert_eq!((z as f64 * 16.0) as u128, z * 16);
-    assert_eq!((z as f32 * 16.0) as u128, z * 16);
-    let l :u128 = 432 << 100;
-    assert_eq!((l as f32) as u128, l);
-    assert_eq!((l as f64) as u128, l);
-    // formatting
-    let j: u128 = 1 << 67;
-    /*
-    assert_eq!("147573952589676412928", format!("{}", j));
-    assert_eq!("80000000000000000", format!("{:x}", j));
-    assert_eq!("20000000000000000000000", format!("{:o}", j));
-    assert_eq!("10000000000000000000000000000000000000000000000000000000000000000000",
-               format!("{:b}", j));
-    assert_eq!("340282366920938463463374607431768211455",
-        format!("{}", u128::max_value()));
-    assert_eq!("147573952589676412928", format!("{:?}", j));
-    */
-    // common traits
-    assert_eq!(x, b(x.clone()));
-    // overflow checks
-    assert_eq!((z).checked_mul(z), Some(0x734C_C2F2_A521));
-    assert_eq!((k).checked_mul(k), None);
-    let l: u128 = b(u128::max_value() - 10);
-    let o: u128 = b(17);
-    assert_eq!(l.checked_add(b(11)), None);
-    assert_eq!(l.checked_sub(l), Some(0));
-    assert_eq!(o.checked_sub(b(18)), None);
-    assert_eq!(b(1u128).checked_shl(b(127)), Some(1 << 127));
-    assert_eq!(o.checked_shl(b(128)), None);
-}