about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-04-20 07:27:59 -0700
committerbors <bors@rust-lang.org>2016-04-20 07:27:59 -0700
commit6ece1447f02b2310e5f4c7ef1efd1312476cae80 (patch)
treee2fc7e887c386f7524d50282c9ef575821aff120
parent133f60f82012ad92c40693bf1ae28419b60146a7 (diff)
parentc7d564d8c96bf538cc64a3eeca1fcc3c99569625 (diff)
Auto merge of #32939 - eddyb:layout, r=nikomatsakis
Compute LLVM-agnostic type layouts in rustc.

Layout for monomorphic types, and some polymorphic ones (e.g. `&T` where `T: Sized`),
can now be computed by rustc without involving LLVM in the actual process.

This gives rustc the ability to evaluate `size_of` or `align_of`, as well as obtain field offsets.
MIR-based CTFE will eventually make use of these layouts, as will MIR trans, shortly.

Layout computation also comes with a `[breaking-change]`, or two:
* `"data-layout"` is now mandatory in custom target specifications, reverting the decision from #27076.
This string is needed because it describes endianness, pointer size and alignments for various types.
We have the first two and we could allow tweaking alignments in target specifications.
Or we could also extract the data layout from LLVM and feed it back into rustc.
However, that can vary with the LLVM version, which is fragile and undermines stability.
For built-in targets, I've added a check that the hardcoded data-layout matches LLVM defaults.
* `transmute` calls are checked in a stricter fashion, which fixes #32377

To expand on `transmute`, there are only 2 allowed patterns: between types with statically known sizes and between pointers with the same potentially-unsized "tail" (which determines the type of unsized metadata they use, if any).
If you're affected, my suggestions are:
* try to use casts (and raw pointer deref) instead of transmutes
* *really* try to avoid `transmute` where possible
* if you have a structure, try working on individual fields and unpack/repack the structure instead of transmuting it whole, e.g. `transmute::<RefCell<Box<T>>, RefCell<*mut T>>(x)` doesn't work, but `RefCell::new(Box::into_raw(x.into_inner()))` does (and `Box::into_raw` is just a `transmute`)
-rw-r--r--src/librustc/dep_graph/dep_node.rs2
-rw-r--r--src/librustc/diagnostics.rs26
-rw-r--r--src/librustc/middle/intrinsicck.rs289
-rw-r--r--src/librustc/ty/context.rs35
-rw-r--r--src/librustc/ty/layout.rs1336
-rw-r--r--src/librustc/ty/mod.rs32
-rw-r--r--src/librustc/ty/util.rs19
-rw-r--r--src/librustc_back/target/aarch64_apple_ios.rs1
-rw-r--r--src/librustc_back/target/aarch64_linux_android.rs1
-rw-r--r--src/librustc_back/target/aarch64_unknown_linux_gnu.rs1
-rw-r--r--src/librustc_back/target/arm_linux_androideabi.rs1
-rw-r--r--src/librustc_back/target/arm_unknown_linux_gnueabi.rs1
-rw-r--r--src/librustc_back/target/arm_unknown_linux_gnueabihf.rs1
-rw-r--r--src/librustc_back/target/armv7_apple_ios.rs1
-rw-r--r--src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs1
-rw-r--r--src/librustc_back/target/armv7s_apple_ios.rs1
-rw-r--r--src/librustc_back/target/asmjs_unknown_emscripten.rs1
-rw-r--r--src/librustc_back/target/i386_apple_ios.rs1
-rw-r--r--src/librustc_back/target/i686_apple_darwin.rs1
-rw-r--r--src/librustc_back/target/i686_linux_android.rs1
-rw-r--r--src/librustc_back/target/i686_pc_windows_gnu.rs1
-rw-r--r--src/librustc_back/target/i686_pc_windows_msvc.rs1
-rw-r--r--src/librustc_back/target/i686_unknown_dragonfly.rs1
-rw-r--r--src/librustc_back/target/i686_unknown_freebsd.rs1
-rw-r--r--src/librustc_back/target/i686_unknown_linux_gnu.rs1
-rw-r--r--src/librustc_back/target/i686_unknown_linux_musl.rs1
-rw-r--r--src/librustc_back/target/le32_unknown_nacl.rs1
-rw-r--r--src/librustc_back/target/mips_unknown_linux_gnu.rs1
-rw-r--r--src/librustc_back/target/mips_unknown_linux_musl.rs1
-rw-r--r--src/librustc_back/target/mipsel_unknown_linux_gnu.rs1
-rw-r--r--src/librustc_back/target/mipsel_unknown_linux_musl.rs1
-rw-r--r--src/librustc_back/target/mod.rs22
-rw-r--r--src/librustc_back/target/powerpc64_unknown_linux_gnu.rs1
-rw-r--r--src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs1
-rw-r--r--src/librustc_back/target/powerpc_unknown_linux_gnu.rs1
-rw-r--r--src/librustc_back/target/x86_64_apple_darwin.rs1
-rw-r--r--src/librustc_back/target/x86_64_apple_ios.rs1
-rw-r--r--src/librustc_back/target/x86_64_pc_windows_gnu.rs1
-rw-r--r--src/librustc_back/target/x86_64_pc_windows_msvc.rs1
-rw-r--r--src/librustc_back/target/x86_64_rumprun_netbsd.rs1
-rw-r--r--src/librustc_back/target/x86_64_sun_solaris.rs1
-rw-r--r--src/librustc_back/target/x86_64_unknown_bitrig.rs1
-rw-r--r--src/librustc_back/target/x86_64_unknown_dragonfly.rs1
-rw-r--r--src/librustc_back/target/x86_64_unknown_freebsd.rs1
-rw-r--r--src/librustc_back/target/x86_64_unknown_linux_gnu.rs1
-rw-r--r--src/librustc_back/target/x86_64_unknown_linux_musl.rs1
-rw-r--r--src/librustc_back/target/x86_64_unknown_netbsd.rs1
-rw-r--r--src/librustc_back/target/x86_64_unknown_openbsd.rs1
-rw-r--r--src/librustc_trans/abi.rs13
-rw-r--r--src/librustc_trans/base.rs7
-rw-r--r--src/librustc_trans/context.rs41
-rw-r--r--src/librustc_trans/diagnostics.rs26
-rw-r--r--src/librustc_trans/intrinsic.rs74
-rw-r--r--src/librustc_trans/type_of.rs32
-rw-r--r--src/test/compile-fail/issue-21174.rs2
-rw-r--r--src/test/compile-fail/issue-32377.rs27
-rw-r--r--src/test/compile-fail/transmute-from-fn-item-types-error.rs23
-rw-r--r--src/test/compile-fail/transmute-from-fn-item-types-lint.rs26
-rw-r--r--src/test/compile-fail/transmute-type-parameters.rs18
-rw-r--r--src/test/run-make/target-specs/my-awesome-platform.json1
60 files changed, 1698 insertions, 393 deletions
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index 446313f7037..536c739bf16 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -71,7 +71,6 @@ pub enum DepNode<D: Clone + Debug> {
     DeadCheck,
     StabilityCheck,
     LateLintCheck,
-    IntrinsicUseCheck,
     TransCrate,
     TransCrateItem(D),
     TransInlinedItem(D),
@@ -169,7 +168,6 @@ impl<D: Clone + Debug> DepNode<D> {
             DeadCheck => Some(DeadCheck),
             StabilityCheck => Some(StabilityCheck),
             LateLintCheck => Some(LateLintCheck),
-            IntrinsicUseCheck => Some(IntrinsicUseCheck),
             TransCrate => Some(TransCrate),
             TransWriteMetadata => Some(TransWriteMetadata),
             Hir(ref d) => op(d).map(Hir),
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 34b98a34677..e230836ef45 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -1410,6 +1410,32 @@ It is not possible to use stability attributes outside of the standard library.
 Also, for now, it is not possible to write deprecation messages either.
 "##,
 
+E0512: r##"
+Transmute with two differently sized types was attempted. Erroneous code
+example:
+
+```compile_fail
+fn takes_u8(_: u8) {}
+
+fn main() {
+    unsafe { takes_u8(::std::mem::transmute(0u16)); }
+    // error: transmute called with differently sized types
+}
+```
+
+Please use types with same size or use the expected type directly. Example:
+
+```
+fn takes_u8(_: u8) {}
+
+fn main() {
+    unsafe { takes_u8(::std::mem::transmute(0i8)); } // ok!
+    // or:
+    unsafe { takes_u8(0u8); } // ok!
+}
+```
+"##,
+
 E0517: r##"
 This error indicates that a `#[repr(..)]` attribute was placed on an
 unsupported item.
diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs
index 767c5adc81e..e84be7e4560 100644
--- a/src/librustc/middle/intrinsicck.rs
+++ b/src/librustc/middle/intrinsicck.rs
@@ -11,11 +11,10 @@
 use dep_graph::DepNode;
 use hir::def::Def;
 use hir::def_id::DefId;
-use ty::subst::{Subst, Substs, EnumeratedItems};
-use ty::{TransmuteRestriction, TyCtxt};
-use ty::{self, Ty, TypeFoldable};
-
-use std::fmt;
+use infer::{InferCtxt, new_infer_ctxt};
+use traits::ProjectionMode;
+use ty::{self, Ty, TyCtxt};
+use ty::layout::{LayoutError, Pointer, SizeSkeleton};
 
 use syntax::abi::Abi::RustIntrinsic;
 use syntax::ast;
@@ -24,219 +23,148 @@ use hir::intravisit::{self, Visitor, FnKind};
 use hir;
 
 pub fn check_crate(tcx: &TyCtxt) {
-    let mut visitor = IntrinsicCheckingVisitor {
-        tcx: tcx,
-        param_envs: Vec::new(),
-        dummy_sized_ty: tcx.types.isize,
-        dummy_unsized_ty: tcx.mk_slice(tcx.types.isize),
+    let mut visitor = ItemVisitor {
+        tcx: tcx
     };
     tcx.visit_all_items_in_krate(DepNode::IntrinsicCheck, &mut visitor);
 }
 
-struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
-    tcx: &'a TyCtxt<'tcx>,
+struct ItemVisitor<'a, 'tcx: 'a> {
+    tcx: &'a TyCtxt<'tcx>
+}
 
-    // As we traverse the AST, we keep a stack of the parameter
-    // environments for each function we encounter. When we find a
-    // call to `transmute`, we can check it in the context of the top
-    // of the stack (which ought not to be empty).
-    param_envs: Vec<ty::ParameterEnvironment<'a,'tcx>>,
+impl<'a, 'tcx> ItemVisitor<'a, 'tcx> {
+    fn visit_const(&mut self, item_id: ast::NodeId, expr: &hir::Expr) {
+        let param_env = ty::ParameterEnvironment::for_item(self.tcx, item_id);
+        let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
+                                   Some(param_env),
+                                   ProjectionMode::Any);
+        let mut visitor = ExprVisitor {
+            infcx: &infcx
+        };
+        visitor.visit_expr(expr);
+    }
+}
 
-    // Dummy sized/unsized types that use to substitute for type
-    // parameters in order to estimate how big a type will be for any
-    // possible instantiation of the type parameters in scope.  See
-    // `check_transmute` for more details.
-    dummy_sized_ty: Ty<'tcx>,
-    dummy_unsized_ty: Ty<'tcx>,
+struct ExprVisitor<'a, 'tcx: 'a> {
+    infcx: &'a InferCtxt<'a, 'tcx>
 }
 
-impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
+impl<'a, 'tcx> ExprVisitor<'a, 'tcx> {
     fn def_id_is_transmute(&self, def_id: DefId) -> bool {
-        let intrinsic = match self.tcx.lookup_item_type(def_id).ty.sty {
+        let intrinsic = match self.infcx.tcx.lookup_item_type(def_id).ty.sty {
             ty::TyFnDef(_, _, ref bfty) => bfty.abi == RustIntrinsic,
             _ => return false
         };
-        intrinsic && self.tcx.item_name(def_id).as_str() == "transmute"
+        intrinsic && self.infcx.tcx.item_name(def_id).as_str() == "transmute"
     }
 
     fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>, id: ast::NodeId) {
-        // Find the parameter environment for the most recent function that
-        // we entered.
+        let sk_from = SizeSkeleton::compute(from, self.infcx);
+        let sk_to = SizeSkeleton::compute(to, self.infcx);
 
-        let param_env = match self.param_envs.last() {
-            Some(p) => p,
-            None => {
-                span_bug!(
-                    span,
-                    "transmute encountered outside of any fn");
+        // Check for same size using the skeletons.
+        if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
+            if sk_from.same_size(sk_to) {
+                return;
             }
-        };
-
-        // Simple case: no type parameters involved.
-        if
-            !from.has_param_types() && !from.has_self_ty() &&
-            !to.has_param_types() && !to.has_self_ty()
-        {
-            let restriction = TransmuteRestriction {
-                span: span,
-                original_from: from,
-                original_to: to,
-                substituted_from: from,
-                substituted_to: to,
-                id: id,
-            };
-            self.push_transmute_restriction(restriction);
-            return;
-        }
 
-        // The rules around type parameters are a bit subtle. We are
-        // checking these rules before monomorphization, so there may
-        // be unsubstituted type parameters present in the
-        // types. Obviously we cannot create LLVM types for those.
-        // However, if a type parameter appears only indirectly (i.e.,
-        // through a pointer), it does not necessarily affect the
-        // size, so that should be allowed. The only catch is that we
-        // DO want to be careful around unsized type parameters, since
-        // fat pointers have a different size than a thin pointer, and
-        // hence `&T` and `&U` have different sizes if `T : Sized` but
-        // `U : Sized` does not hold.
-        //
-        // However, it's not as simple as checking whether `T :
-        // Sized`, because even if `T : Sized` does not hold, that
-        // just means that `T` *may* not be sized.  After all, even a
-        // type parameter `T: ?Sized` could be bound to a sized
-        // type. (Issue #20116)
-        //
-        // To handle this, we first check for "interior" type
-        // parameters, which are always illegal. If there are none of
-        // those, then we know that the only way that all type
-        // parameters `T` are referenced indirectly, e.g. via a
-        // pointer type like `&T`. In that case, we only care whether
-        // `T` is sized or not, because that influences whether `&T`
-        // is a thin or fat pointer.
-        //
-        // One could imagine establishing a sophisticated constraint
-        // system to ensure that the transmute is legal, but instead
-        // we do something brutally dumb. We just substitute dummy
-        // sized or unsized types for every type parameter in scope,
-        // exhaustively checking all possible combinations. Here are some examples:
-        //
-        // ```
-        // fn foo<T, U>() {
-        //     // T=int, U=int
-        // }
-        //
-        // fn bar<T: ?Sized, U>() {
-        //     // T=int, U=int
-        //     // T=[int], U=int
-        // }
-        //
-        // fn baz<T: ?Sized, U: ?Sized>() {
-        //     // T=int, U=int
-        //     // T=[int], U=int
-        //     // T=int, U=[int]
-        //     // T=[int], U=[int]
-        // }
-        // ```
-        //
-        // In all cases, we keep the original unsubstituted types
-        // around for error reporting.
-
-        let from_tc = from.type_contents(self.tcx);
-        let to_tc = to.type_contents(self.tcx);
-        if from_tc.interior_param() || to_tc.interior_param() {
-            span_err!(self.tcx.sess, span, E0139,
-                      "cannot transmute to or from a type that contains \
-                       unsubstituted type parameters");
-            return;
+            match (&from.sty, sk_to) {
+                (&ty::TyFnDef(..), SizeSkeleton::Known(size_to))
+                        if size_to == Pointer.size(&self.infcx.tcx.data_layout) => {
+                    // FIXME #19925 Remove this warning after a release cycle.
+                    let msg = format!("`{}` is now zero-sized and has to be cast \
+                                       to a pointer before transmuting to `{}`",
+                                      from, to);
+                    self.infcx.tcx.sess.add_lint(
+                        ::lint::builtin::TRANSMUTE_FROM_FN_ITEM_TYPES, id, span, msg);
+                    return;
+                }
+                _ => {}
+            }
         }
 
-        let mut substs = param_env.free_substs.clone();
-        self.with_each_combination(
-            span,
-            param_env,
-            param_env.free_substs.types.iter_enumerated(),
-            &mut substs,
-            &mut |substs| {
-                let restriction = TransmuteRestriction {
-                    span: span,
-                    original_from: from,
-                    original_to: to,
-                    substituted_from: from.subst(self.tcx, substs),
-                    substituted_to: to.subst(self.tcx, substs),
-                    id: id,
-                };
-                self.push_transmute_restriction(restriction);
-            });
-    }
-
-    fn with_each_combination(&self,
-                             span: Span,
-                             param_env: &ty::ParameterEnvironment<'a,'tcx>,
-                             mut types_in_scope: EnumeratedItems<Ty<'tcx>>,
-                             substs: &mut Substs<'tcx>,
-                             callback: &mut FnMut(&Substs<'tcx>))
-    {
-        // This parameter invokes `callback` many times with different
-        // substitutions that replace all the parameters in scope with
-        // either `int` or `[int]`, depending on whether the type
-        // parameter is known to be sized. See big comment above for
-        // an explanation of why this is a reasonable thing to do.
-
-        match types_in_scope.next() {
-            None => {
-                debug!("with_each_combination(substs={:?})",
-                       substs);
-
-                callback(substs);
+        // Try to display a sensible error with as much information as possible.
+        let skeleton_string = |ty: Ty<'tcx>, sk| {
+            match sk {
+                Ok(SizeSkeleton::Known(size)) => {
+                    format!("{} bits", size.bits())
+                }
+                Ok(SizeSkeleton::Pointer { tail, .. }) => {
+                    format!("pointer to {}", tail)
+                }
+                Err(LayoutError::Unknown(bad)) => {
+                    if bad == ty {
+                        format!("size can vary")
+                    } else {
+                        format!("size can vary because of {}", bad)
+                    }
+                }
+                Err(err) => err.to_string()
             }
+        };
 
-            Some((space, index, &param_ty)) => {
-                debug!("with_each_combination: space={:?}, index={}, param_ty={:?}",
-                       space, index, param_ty);
-
-                if !param_ty.is_sized(param_env, span) {
-                    debug!("with_each_combination: param_ty is not known to be sized");
+        span_err!(self.infcx.tcx.sess, span, E0512,
+                  "transmute called with differently sized types: \
+                   {} ({}) to {} ({})",
+                  from, skeleton_string(from, sk_from),
+                  to, skeleton_string(to, sk_to));
+    }
+}
 
-                    substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty;
-                    self.with_each_combination(span, param_env, types_in_scope.clone(),
-                                               substs, callback);
-                }
+impl<'a, 'tcx, 'v> Visitor<'v> for ItemVisitor<'a, 'tcx> {
+    // const, static and N in [T; N].
+    fn visit_expr(&mut self, expr: &hir::Expr) {
+        let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
+                                   None, ProjectionMode::Any);
+        let mut visitor = ExprVisitor {
+            infcx: &infcx
+        };
+        visitor.visit_expr(expr);
+    }
 
-                substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty;
-                self.with_each_combination(span, param_env, types_in_scope,
-                                           substs, callback);
-            }
+    fn visit_trait_item(&mut self, item: &hir::TraitItem) {
+        if let hir::ConstTraitItem(_, Some(ref expr)) = item.node {
+            self.visit_const(item.id, expr);
+        } else {
+            intravisit::walk_trait_item(self, item);
         }
     }
 
-    fn push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) {
-        debug!("Pushing transmute restriction: {:?}", restriction);
-        self.tcx.transmute_restrictions.borrow_mut().push(restriction);
+    fn visit_impl_item(&mut self, item: &hir::ImplItem) {
+        if let hir::ImplItemKind::Const(_, ref expr) = item.node {
+            self.visit_const(item.id, expr);
+        } else {
+            intravisit::walk_impl_item(self, item);
+        }
     }
-}
 
-impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
     fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
                 b: &'v hir::Block, s: Span, id: ast::NodeId) {
         match fk {
             FnKind::ItemFn(..) | FnKind::Method(..) => {
                 let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
-                self.param_envs.push(param_env);
-                intravisit::walk_fn(self, fk, fd, b, s);
-                self.param_envs.pop();
+                let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
+                                           Some(param_env),
+                                           ProjectionMode::Any);
+                let mut visitor = ExprVisitor {
+                    infcx: &infcx
+                };
+                visitor.visit_fn(fk, fd, b, s, id);
             }
             FnKind::Closure(..) => {
-                intravisit::walk_fn(self, fk, fd, b, s);
+                span_bug!(s, "intrinsicck: closure outside of function")
             }
         }
     }
+}
 
+impl<'a, 'tcx, 'v> Visitor<'v> for ExprVisitor<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &hir::Expr) {
         if let hir::ExprPath(..) = expr.node {
-            match self.tcx.resolve_expr(expr) {
+            match self.infcx.tcx.resolve_expr(expr) {
                 Def::Fn(did) if self.def_id_is_transmute(did) => {
-                    let typ = self.tcx.node_id_to_type(expr.id);
+                    let typ = self.infcx.tcx.node_id_to_type(expr.id);
                     match typ.sty {
                         ty::TyFnDef(_, _, ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
                             if let ty::FnConverging(to) = bare_fn_ty.sig.0.output {
@@ -256,14 +184,3 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
         intravisit::walk_expr(self, expr);
     }
 }
-
-impl<'tcx> fmt::Debug for TransmuteRestriction<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "TransmuteRestriction(id={}, original=({:?},{:?}), substituted=({:?},{:?}))",
-               self.id,
-               self.original_from,
-               self.original_to,
-               self.substituted_from,
-               self.substituted_to)
-    }
-}
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 61c49475ac4..10000607b54 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -31,6 +31,7 @@ use hir::FreevarMap;
 use ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, TraitTy};
 use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid};
 use ty::TypeVariants::*;
+use ty::layout::{Layout, TargetDataLayout};
 use ty::maps;
 use util::common::MemoizationMap;
 use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet};
@@ -55,6 +56,7 @@ pub struct CtxtArenas<'tcx> {
     bare_fn: TypedArena<BareFnTy<'tcx>>,
     region: TypedArena<Region>,
     stability: TypedArena<attr::Stability>,
+    layout: TypedArena<Layout>,
 
     // references
     trait_defs: TypedArena<ty::TraitDef<'tcx>>,
@@ -69,6 +71,7 @@ impl<'tcx> CtxtArenas<'tcx> {
             bare_fn: TypedArena::new(),
             region: TypedArena::new(),
             stability: TypedArena::new(),
+            layout: TypedArena::new(),
 
             trait_defs: TypedArena::new(),
             adt_defs: TypedArena::new()
@@ -229,6 +232,7 @@ pub struct TyCtxt<'tcx> {
     bare_fn_interner: RefCell<FnvHashMap<&'tcx BareFnTy<'tcx>, &'tcx BareFnTy<'tcx>>>,
     region_interner: RefCell<FnvHashMap<&'tcx Region, &'tcx Region>>,
     stability_interner: RefCell<FnvHashMap<&'tcx attr::Stability, &'tcx attr::Stability>>,
+    layout_interner: RefCell<FnvHashMap<&'tcx Layout, &'tcx Layout>>,
 
     pub dep_graph: DepGraph,
 
@@ -353,11 +357,6 @@ pub struct TyCtxt<'tcx> {
     pub node_lint_levels: RefCell<FnvHashMap<(NodeId, lint::LintId),
                                               lint::LevelSource>>,
 
-    /// The types that must be asserted to be the same size for `transmute`
-    /// to be valid. We gather up these restrictions in the intrinsicck pass
-    /// and check them in trans.
-    pub transmute_restrictions: RefCell<Vec<ty::TransmuteRestriction<'tcx>>>,
-
     /// Maps any item's def-id to its stability index.
     pub stability: RefCell<stability::Index<'tcx>>,
 
@@ -419,6 +418,12 @@ pub struct TyCtxt<'tcx> {
     /// The definite name of the current crate after taking into account
     /// attributes, commandline parameters, etc.
     pub crate_name: token::InternedString,
+
+    /// Data layout specification for the current target.
+    pub data_layout: TargetDataLayout,
+
+    /// Cache for layouts computed from types.
+    pub layout_cache: RefCell<FnvHashMap<Ty<'tcx>, &'tcx Layout>>,
 }
 
 impl<'tcx> TyCtxt<'tcx> {
@@ -500,6 +505,20 @@ impl<'tcx> TyCtxt<'tcx> {
         interned
     }
 
+    pub fn intern_layout(&self, layout: Layout) -> &'tcx Layout {
+        if let Some(layout) = self.layout_interner.borrow().get(&layout) {
+            return layout;
+        }
+
+        let interned = self.arenas.layout.alloc(layout);
+        if let Some(prev) = self.layout_interner
+                                .borrow_mut()
+                                .insert(interned, interned) {
+            bug!("Tried to overwrite interned Layout: {:?}", prev)
+        }
+        interned
+    }
+
     pub fn store_free_region_map(&self, id: NodeId, map: FreeRegionMap) {
         if self.free_region_maps.borrow_mut().insert(id, map).is_some() {
             bug!("Tried to overwrite interned FreeRegionMap for NodeId {:?}", id)
@@ -531,6 +550,7 @@ impl<'tcx> TyCtxt<'tcx> {
                                  f: F) -> R
                                  where F: FnOnce(&TyCtxt<'tcx>) -> R
     {
+        let data_layout = TargetDataLayout::parse(s);
         let interner = RefCell::new(FnvHashMap());
         let common_types = CommonTypes::new(&arenas.type_, &interner);
         let dep_graph = map.dep_graph.clone();
@@ -542,6 +562,7 @@ impl<'tcx> TyCtxt<'tcx> {
             bare_fn_interner: RefCell::new(FnvHashMap()),
             region_interner: RefCell::new(FnvHashMap()),
             stability_interner: RefCell::new(FnvHashMap()),
+            layout_interner: RefCell::new(FnvHashMap()),
             dep_graph: dep_graph.clone(),
             types: common_types,
             named_region_map: named_region_map,
@@ -579,7 +600,6 @@ impl<'tcx> TyCtxt<'tcx> {
             extern_const_statics: RefCell::new(DefIdMap()),
             extern_const_fns: RefCell::new(DefIdMap()),
             node_lint_levels: RefCell::new(FnvHashMap()),
-            transmute_restrictions: RefCell::new(Vec::new()),
             stability: RefCell::new(stability),
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
@@ -589,6 +609,8 @@ impl<'tcx> TyCtxt<'tcx> {
             cast_kinds: RefCell::new(NodeMap()),
             fragment_infos: RefCell::new(DefIdMap()),
             crate_name: token::intern_and_get_ident(crate_name),
+            data_layout: data_layout,
+            layout_cache: RefCell::new(FnvHashMap()),
        }, f)
     }
 }
@@ -762,6 +784,7 @@ impl<'tcx> TyCtxt<'tcx> {
         println!("BareFnTy interner: #{}", self.bare_fn_interner.borrow().len());
         println!("Region interner: #{}", self.region_interner.borrow().len());
         println!("Stability interner: #{}", self.stability_interner.borrow().len());
+        println!("Layout interner: #{}", self.layout_interner.borrow().len());
     }
 }
 
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
new file mode 100644
index 00000000000..3ea691b4dc7
--- /dev/null
+++ b/src/librustc/ty/layout.rs
@@ -0,0 +1,1336 @@
+// 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.
+
+pub use self::Integer::*;
+pub use self::Layout::*;
+pub use self::Primitive::*;
+
+use infer::{InferCtxt, drain_fulfillment_cx_or_panic};
+use session::Session;
+use traits;
+use ty::{self, Ty, TyCtxt, TypeFoldable};
+
+use syntax::ast::{FloatTy, IntTy, UintTy};
+use syntax::attr;
+use syntax::codemap::DUMMY_SP;
+
+use std::cmp;
+use std::fmt;
+use std::i64;
+
+/// Parsed [Data layout](http://llvm.org/docs/LangRef.html#data-layout)
+/// for a target, which contains everything needed to compute layouts.
+pub struct TargetDataLayout {
+    pub endian: Endian,
+    pub i1_align: Align,
+    pub i8_align: Align,
+    pub i16_align: Align,
+    pub i32_align: Align,
+    pub i64_align: Align,
+    pub f32_align: Align,
+    pub f64_align: Align,
+    pub pointer_size: Size,
+    pub pointer_align: Align,
+    pub aggregate_align: Align,
+
+    /// Alignments for vector types.
+    pub vector_align: Vec<(Size, Align)>
+}
+
+impl Default for TargetDataLayout {
+    fn default() -> TargetDataLayout {
+        TargetDataLayout {
+            endian: Endian::Big,
+            i1_align: Align::from_bits(8, 8).unwrap(),
+            i8_align: Align::from_bits(8, 8).unwrap(),
+            i16_align: Align::from_bits(16, 16).unwrap(),
+            i32_align: Align::from_bits(32, 32).unwrap(),
+            i64_align: Align::from_bits(32, 64).unwrap(),
+            f32_align: Align::from_bits(32, 32).unwrap(),
+            f64_align: Align::from_bits(64, 64).unwrap(),
+            pointer_size: Size::from_bits(64),
+            pointer_align: Align::from_bits(64, 64).unwrap(),
+            aggregate_align: Align::from_bits(0, 64).unwrap(),
+            vector_align: vec![
+                (Size::from_bits(64), Align::from_bits(64, 64).unwrap()),
+                (Size::from_bits(128), Align::from_bits(128, 128).unwrap())
+            ]
+        }
+    }
+}
+
+impl TargetDataLayout {
+    pub fn parse(sess: &Session) -> TargetDataLayout {
+        // Parse a bit count from a string.
+        let parse_bits = |s: &str, kind: &str, cause: &str| {
+            s.parse::<u64>().unwrap_or_else(|err| {
+                sess.err(&format!("invalid {} `{}` for `{}` in \"data-layout\": {}",
+                                  kind, s, cause, err));
+                0
+            })
+        };
+
+        // Parse a size string.
+        let size = |s: &str, cause: &str| {
+            Size::from_bits(parse_bits(s, "size", cause))
+        };
+
+        // Parse an alignment string.
+        let align = |s: &[&str], cause: &str| {
+            if s.is_empty() {
+                sess.err(&format!("missing alignment for `{}` in \"data-layout\"", cause));
+            }
+            let abi = parse_bits(s[0], "alignment", cause);
+            let pref = s.get(1).map_or(abi, |pref| parse_bits(pref, "alignment", cause));
+            Align::from_bits(abi, pref).unwrap_or_else(|err| {
+                sess.err(&format!("invalid alignment for `{}` in \"data-layout\": {}",
+                                  cause, err));
+                Align::from_bits(8, 8).unwrap()
+            })
+        };
+
+        let mut dl = TargetDataLayout::default();
+        for spec in sess.target.target.data_layout.split("-") {
+            match &spec.split(":").collect::<Vec<_>>()[..] {
+                ["e"] => dl.endian = Endian::Little,
+                ["E"] => dl.endian = Endian::Big,
+                ["a", a..] => dl.aggregate_align = align(a, "a"),
+                ["f32", a..] => dl.f32_align = align(a, "f32"),
+                ["f64", a..] => dl.f64_align = align(a, "f64"),
+                [p @ "p", s, a..] | [p @ "p0", s, a..] => {
+                    dl.pointer_size = size(s, p);
+                    dl.pointer_align = align(a, p);
+                }
+                [s, a..] if s.starts_with("i") => {
+                    let ty_align = match s[1..].parse::<u64>() {
+                        Ok(1) => &mut dl.i8_align,
+                        Ok(8) => &mut dl.i8_align,
+                        Ok(16) => &mut dl.i16_align,
+                        Ok(32) => &mut dl.i32_align,
+                        Ok(64) => &mut dl.i64_align,
+                        Ok(_) => continue,
+                        Err(_) => {
+                            size(&s[1..], "i"); // For the user error.
+                            continue;
+                        }
+                    };
+                    *ty_align = align(a, s);
+                }
+                [s, a..] if s.starts_with("v") => {
+                    let v_size = size(&s[1..], "v");
+                    let a = align(a, s);
+                    if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
+                        v.1 = a;
+                        continue;
+                    }
+                    // No existing entry, add a new one.
+                    dl.vector_align.push((v_size, a));
+                }
+                _ => {} // Ignore everything else.
+            }
+        }
+
+        // Perform consistency checks against the Target information.
+        let endian_str = match dl.endian {
+            Endian::Little => "little",
+            Endian::Big => "big"
+        };
+        if endian_str != sess.target.target.target_endian {
+            sess.err(&format!("inconsistent target specification: \"data-layout\" claims \
+                               architecture is {}-endian, while \"target-endian\" is `{}`",
+                              endian_str, sess.target.target.target_endian));
+        }
+
+        if dl.pointer_size.bits().to_string() != sess.target.target.target_pointer_width {
+            sess.err(&format!("inconsistent target specification: \"data-layout\" claims \
+                               pointers are {}-bit, while \"target-pointer-width\" is `{}`",
+                              dl.pointer_size.bits(), sess.target.target.target_pointer_width));
+        }
+
+        dl
+    }
+
+    /// Return exclusive upper bound on object size.
+    ///
+    /// The theoretical maximum object size is defined as the maximum positive `isize` value.
+    /// This ensures that the `offset` semantics remain well-defined by allowing it to correctly
+    /// index every address within an object along with one byte past the end, along with allowing
+    /// `isize` to store the difference between any two pointers into an object.
+    ///
+    /// The upper bound on 64-bit currently needs to be lower because LLVM uses a 64-bit integer
+    /// to represent object size in bits. It would need to be 1 << 61 to account for this, but is
+    /// currently conservatively bounded to 1 << 47 as that is enough to cover the current usable
+    /// address space on 64-bit ARMv8 and x86_64.
+    pub fn obj_size_bound(&self) -> u64 {
+        match self.pointer_size.bits() {
+            32 => 1 << 31,
+            64 => 1 << 47,
+            bits => bug!("obj_size_bound: unknown pointer bit size {}", bits)
+        }
+    }
+
+    pub fn ptr_sized_integer(&self) -> Integer {
+        match self.pointer_size.bits() {
+            32 => I32,
+            64 => I64,
+            bits => bug!("ptr_sized_integer: unknown pointer bit size {}", bits)
+        }
+    }
+}
+
+/// Endianness of the target, which must match cfg(target-endian).
+#[derive(Copy, Clone)]
+pub enum Endian {
+    Little,
+    Big
+}
+
+/// Size of a type in bytes.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub struct Size {
+    raw: u64
+}
+
+impl Size {
+    pub fn from_bits(bits: u64) -> Size {
+        Size::from_bytes((bits + 7) / 8)
+    }
+
+    pub fn from_bytes(bytes: u64) -> Size {
+        if bytes >= (1 << 61) {
+            bug!("Size::from_bytes: {} bytes in bits doesn't fit in u64", bytes)
+        }
+        Size {
+            raw: bytes
+        }
+    }
+
+    pub fn bytes(self) -> u64 {
+        self.raw
+    }
+
+    pub fn bits(self) -> u64 {
+        self.bytes() * 8
+    }
+
+    pub fn abi_align(self, align: Align) -> Size {
+        let mask = align.abi() - 1;
+        Size::from_bytes((self.bytes() + mask) & !mask)
+    }
+
+    pub fn checked_add(self, offset: Size, dl: &TargetDataLayout) -> Option<Size> {
+        // Each Size is less than dl.obj_size_bound(), so the sum is
+        // also less than 1 << 62 (and therefore can't overflow).
+        let bytes = self.bytes() + offset.bytes();
+
+        if bytes < dl.obj_size_bound() {
+            Some(Size::from_bytes(bytes))
+        } else {
+            None
+        }
+    }
+
+    pub fn checked_mul(self, count: u64, dl: &TargetDataLayout) -> Option<Size> {
+        // Each Size is less than dl.obj_size_bound(), so the sum is
+        // also less than 1 << 62 (and therefore can't overflow).
+        match self.bytes().checked_mul(count) {
+            Some(bytes) if bytes < dl.obj_size_bound() => {
+                Some(Size::from_bytes(bytes))
+            }
+            _ => None
+        }
+    }
+}
+
+/// Alignment of a type in bytes, both ABI-mandated and preferred.
+/// Since alignments are always powers of 2, we can pack both in one byte,
+/// giving each a nibble (4 bits) for a maximum alignment of 2^15 = 32768.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct Align {
+    raw: u8
+}
+
+impl Align {
+    pub fn from_bits(abi: u64, pref: u64) -> Result<Align, String> {
+        Align::from_bytes((abi + 7) / 8, (pref + 7) / 8)
+    }
+
+    pub fn from_bytes(abi: u64, pref: u64) -> Result<Align, String> {
+        let pack = |align: u64| {
+            // Treat an alignment of 0 bytes like 1-byte alignment.
+            if align == 0 {
+                return Ok(0);
+            }
+
+            let mut bytes = align;
+            let mut pow: u8 = 0;
+            while (bytes & 1) == 0 {
+                pow += 1;
+                bytes >>= 1;
+            }
+            if bytes != 1 {
+                Err(format!("`{}` is not a power of 2", align))
+            } else if pow > 0x0f {
+                Err(format!("`{}` is too large", align))
+            } else {
+                Ok(pow)
+            }
+        };
+
+        Ok(Align {
+            raw: pack(abi)? | (pack(pref)? << 4)
+        })
+    }
+
+    pub fn abi(self) -> u64 {
+        1 << (self.raw & 0xf)
+    }
+
+    pub fn pref(self) -> u64 {
+        1 << (self.raw >> 4)
+    }
+
+    pub fn min(self, other: Align) -> Align {
+        let abi = cmp::min(self.raw & 0x0f, other.raw & 0x0f);
+        let pref = cmp::min(self.raw & 0xf0, other.raw & 0xf0);
+        Align {
+            raw: abi | pref
+        }
+    }
+
+    pub fn max(self, other: Align) -> Align {
+        let abi = cmp::max(self.raw & 0x0f, other.raw & 0x0f);
+        let pref = cmp::max(self.raw & 0xf0, other.raw & 0xf0);
+        Align {
+            raw: abi | pref
+        }
+    }
+}
+
+/// Integers, also used for enum discriminants.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub enum Integer {
+    I1,
+    I8,
+    I16,
+    I32,
+    I64
+}
+
+impl Integer {
+    /// Find the smallest Integer type which can represent the signed value.
+    pub fn fit_signed(x: i64) -> Integer {
+        match x {
+            -0x0000_0001...0x0000_0000 => I1,
+            -0x0000_0080...0x0000_007f => I8,
+            -0x0000_8000...0x0000_7fff => I16,
+            -0x8000_0000...0x7fff_ffff => I32,
+            _ => I64
+        }
+    }
+
+    /// Find the smallest Integer type which can represent the unsigned value.
+    pub fn fit_unsigned(x: u64) -> Integer {
+        match x {
+            0...0x0000_0001 => I1,
+            0...0x0000_00ff => I8,
+            0...0x0000_ffff => I16,
+            0...0xffff_ffff => I32,
+            _ => I64
+        }
+    }
+
+    /// Get the Integer type from an attr::IntType.
+    pub fn from_attr(dl: &TargetDataLayout, ity: attr::IntType) -> Integer {
+        match ity {
+            attr::SignedInt(IntTy::I8) | attr::UnsignedInt(UintTy::U8) => I8,
+            attr::SignedInt(IntTy::I16) | attr::UnsignedInt(UintTy::U16) => I16,
+            attr::SignedInt(IntTy::I32) | attr::UnsignedInt(UintTy::U32) => I32,
+            attr::SignedInt(IntTy::I64) | attr::UnsignedInt(UintTy::U64) => I64,
+            attr::SignedInt(IntTy::Is) | attr::UnsignedInt(UintTy::Us) => {
+                dl.ptr_sized_integer()
+            }
+        }
+    }
+
+    /// Find the appropriate Integer type and signedness for the given
+    /// signed discriminant range and #[repr] attribute.
+    /// N.B.: u64 values above i64::MAX will be treated as signed, but
+    /// that shouldn't affect anything, other than maybe debuginfo.
+    pub fn repr_discr(tcx: &TyCtxt, hint: attr::ReprAttr, min: i64, max: i64)
+                      -> (Integer, bool) {
+        // Theoretically, negative values could be larger in unsigned representation
+        // than the unsigned representation of the signed minimum. However, if there
+        // are any negative values, the only valid unsigned representation is u64
+        // which can fit all i64 values, so the result remains unaffected.
+        let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u64, max as u64));
+        let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
+
+        let at_least = match hint {
+            attr::ReprInt(span, ity) => {
+                let discr = Integer::from_attr(&tcx.data_layout, ity);
+                let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
+                if discr < fit {
+                    span_bug!(span, "representation hint insufficient for discriminant range")
+                }
+                return (discr, ity.is_signed());
+            }
+            attr::ReprExtern => {
+                match &tcx.sess.target.target.arch[..] {
+                    // WARNING: the ARM EABI has two variants; the one corresponding
+                    // to `at_least == I32` appears to be used on Linux and NetBSD,
+                    // but some systems may use the variant corresponding to no
+                    // lower bound.  However, we don't run on those yet...?
+                    "arm" => I32,
+                    _ => I32,
+                }
+            }
+            attr::ReprAny => I8,
+            attr::ReprPacked => {
+                bug!("Integer::repr_discr: found #[repr(packed)] on an enum");
+            }
+            attr::ReprSimd => {
+                bug!("Integer::repr_discr: found #[repr(simd)] on an enum");
+            }
+        };
+
+        // If there are no negative values, we can use the unsigned fit.
+        if min >= 0 {
+            (cmp::max(unsigned_fit, at_least), false)
+        } else {
+            (cmp::max(signed_fit, at_least), true)
+        }
+    }
+}
+
+/// Fundamental unit of memory access and layout.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum Primitive {
+    Int(Integer),
+    F32,
+    F64,
+    Pointer
+}
+
+impl Primitive {
+    pub fn size(self, dl: &TargetDataLayout) -> Size {
+        match self {
+            Int(I1) | Int(I8) => Size::from_bits(8),
+            Int(I16) => Size::from_bits(16),
+            Int(I32) | F32 => Size::from_bits(32),
+            Int(I64) | F64 => Size::from_bits(64),
+            Pointer => dl.pointer_size
+        }
+    }
+
+    pub fn align(self, dl: &TargetDataLayout) -> Align {
+        match self {
+            Int(I1) => dl.i1_align,
+            Int(I8) => dl.i8_align,
+            Int(I16) => dl.i16_align,
+            Int(I32) => dl.i32_align,
+            Int(I64) => dl.i64_align,
+            F32 => dl.f32_align,
+            F64 => dl.f64_align,
+            Pointer => dl.pointer_align
+        }
+    }
+}
+
+/// Path through fields of nested structures.
+// FIXME(eddyb) use small vector optimization for the common case.
+pub type FieldPath = Vec<u32>;
+
+/// A structure, a product type in ADT terms.
+#[derive(PartialEq, Eq, Hash, Debug)]
+pub struct Struct {
+    pub align: Align,
+
+    /// If true, no alignment padding is used.
+    pub packed: bool,
+
+    /// If true, the size is exact, otherwise it's only a lower bound.
+    pub sized: bool,
+
+    /// Offsets for the first byte after each field.
+    /// That is, field_offset(i) = offset_after_field[i - 1] and the
+    /// whole structure's size is the last offset, excluding padding.
+    // FIXME(eddyb) use small vector optimization for the common case.
+    pub offset_after_field: Vec<Size>
+}
+
+impl Struct {
+    pub fn new(dl: &TargetDataLayout, packed: bool) -> Struct {
+        Struct {
+            align: if packed { dl.i8_align } else { dl.aggregate_align },
+            packed: packed,
+            sized: true,
+            offset_after_field: vec![]
+        }
+    }
+
+    /// Extend the Struct with more fields.
+    pub fn extend<'a, 'tcx, I>(&mut self, dl: &TargetDataLayout,
+                               fields: I,
+                               scapegoat: Ty<'tcx>)
+                               -> Result<(), LayoutError<'tcx>>
+    where I: Iterator<Item=Result<&'a Layout, LayoutError<'tcx>>> {
+        self.offset_after_field.reserve(fields.size_hint().0);
+
+        for field in fields {
+            if !self.sized {
+                bug!("Struct::compute: field #{} of `{}` comes after unsized field",
+                     self.offset_after_field.len(), scapegoat);
+            }
+
+            let field = field?;
+            if field.is_unsized() {
+                self.sized = false;
+            }
+
+            // Invariant: offset < dl.obj_size_bound() <= 1<<61
+            let mut offset = if !self.packed {
+                let align = field.align(dl);
+                self.align = self.align.max(align);
+                self.offset_after_field.last_mut().map_or(Size::from_bytes(0), |last| {
+                    *last = last.abi_align(align);
+                    *last
+                })
+            } else {
+                self.offset_after_field.last().map_or(Size::from_bytes(0), |&last| last)
+            };
+
+            offset = offset.checked_add(field.size(dl), dl)
+                           .map_or(Err(LayoutError::SizeOverflow(scapegoat)), Ok)?;
+
+            self.offset_after_field.push(offset);
+        }
+
+        Ok(())
+    }
+
+    /// Get the size without trailing alignment padding.
+    pub fn min_size(&self) -> Size {
+        self.offset_after_field.last().map_or(Size::from_bytes(0), |&last| last)
+    }
+
+    /// Get the size with trailing aligment padding.
+    pub fn stride(&self) -> Size {
+        self.min_size().abi_align(self.align)
+    }
+
+    /// Determine whether a structure would be zero-sized, given its fields.
+    pub fn would_be_zero_sized<'a, 'tcx, I>(dl: &TargetDataLayout, fields: I)
+                                            -> Result<bool, LayoutError<'tcx>>
+    where I: Iterator<Item=Result<&'a Layout, LayoutError<'tcx>>> {
+        for field in fields {
+            let field = field?;
+            if field.is_unsized() || field.size(dl).bytes() > 0 {
+                return Ok(false);
+            }
+        }
+        Ok(true)
+    }
+
+    /// Find the path leading to a non-zero leaf field, starting from
+    /// the given type and recursing through aggregates.
+    // FIXME(eddyb) track value ranges and traverse already optimized enums.
+    pub fn non_zero_field_in_type<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+                                            ty: Ty<'tcx>)
+                                            -> Result<Option<FieldPath>, LayoutError<'tcx>> {
+        let tcx = infcx.tcx;
+        match (ty.layout(infcx)?, &ty.sty) {
+            (&Scalar { non_zero: true, .. }, _) => Ok(Some(vec![])),
+            (&FatPointer { non_zero: true, .. }, _) => {
+                Ok(Some(vec![FAT_PTR_ADDR as u32]))
+            }
+
+            // Is this the NonZero lang item wrapping a pointer or integer type?
+            (&Univariant { non_zero: true, .. }, &ty::TyStruct(def, substs)) => {
+                let fields = &def.struct_variant().fields;
+                assert_eq!(fields.len(), 1);
+                let ty = normalize_associated_type(infcx, fields[0].ty(tcx, substs));
+                match *ty.layout(infcx)? {
+                    // FIXME(eddyb) also allow floating-point types here.
+                    Scalar { value: Int(_), non_zero: false } |
+                    Scalar { value: Pointer, non_zero: false } => {
+                        Ok(Some(vec![0]))
+                    }
+                    FatPointer { non_zero: false, .. } => {
+                        Ok(Some(vec![FAT_PTR_ADDR as u32, 0]))
+                    }
+                    _ => Ok(None)
+                }
+            }
+
+            // Perhaps one of the fields of this struct is non-zero
+            // let's recurse and find out
+            (_, &ty::TyStruct(def, substs)) => {
+                Struct::non_zero_field_path(infcx, def.struct_variant().fields
+                                                      .iter().map(|field| {
+                    normalize_associated_type(infcx, field.ty(tcx, substs))
+                }))
+            }
+
+            // Perhaps one of the upvars of this closure is non-zero
+            // Let's recurse and find out!
+            (_, &ty::TyClosure(_, box ty::ClosureSubsts { upvar_tys: ref tys, .. })) |
+            // Can we use one of the fields in this tuple?
+            (_, &ty::TyTuple(ref tys)) => {
+                Struct::non_zero_field_path(infcx, tys.iter().cloned())
+            }
+
+            // Is this a fixed-size array of something non-zero
+            // with at least one element?
+            (_, &ty::TyArray(ety, d)) if d > 0 => {
+                Struct::non_zero_field_path(infcx, Some(ety).into_iter())
+            }
+
+            // Anything else is not a non-zero type.
+            _ => Ok(None)
+        }
+    }
+
+    /// Find the path leading to a non-zero leaf field, starting from
+    /// the given set of fields and recursing through aggregates.
+    pub fn non_zero_field_path<'a, 'tcx, I>(infcx: &InferCtxt<'a, 'tcx>,
+                                            fields: I)
+                                            -> Result<Option<FieldPath>, LayoutError<'tcx>>
+    where I: Iterator<Item=Ty<'tcx>> {
+        for (i, ty) in fields.enumerate() {
+            if let Some(mut path) = Struct::non_zero_field_in_type(infcx, ty)? {
+                path.push(i as u32);
+                return Ok(Some(path));
+            }
+        }
+        Ok(None)
+    }
+}
+
+/// The first half of a fat pointer.
+/// - For a trait object, this is the address of the box.
+/// - For a slice, this is the base address.
+pub const FAT_PTR_ADDR: usize = 0;
+
+/// The second half of a fat pointer.
+/// - For a trait object, this is the address of the vtable.
+/// - For a slice, this is the length.
+pub const FAT_PTR_EXTRA: usize = 1;
+
+/// Type layout, from which size and alignment can be cheaply computed.
+/// For ADTs, it also includes field placement and enum optimizations.
+/// NOTE: Because Layout is interned, redundant information should be
+/// kept to a minimum, e.g. it includes no sub-component Ty or Layout.
+#[derive(Debug, PartialEq, Eq, Hash)]
+pub enum Layout {
+    /// TyBool, TyChar, TyInt, TyUint, TyFloat, TyRawPtr, TyRef or TyFnPtr.
+    Scalar {
+        value: Primitive,
+        // If true, the value cannot represent a bit pattern of all zeroes.
+        non_zero: bool
+    },
+
+    /// SIMD vectors, from TyStruct marked with #[repr(simd)].
+    Vector {
+        element: Primitive,
+        count: u64
+    },
+
+    /// TyArray, TySlice or TyStr.
+    Array {
+        /// If true, the size is exact, otherwise it's only a lower bound.
+        sized: bool,
+        align: Align,
+        size: Size
+    },
+
+    /// TyRawPtr or TyRef with a !Sized pointee.
+    FatPointer {
+        metadata: Primitive,
+        // If true, the pointer cannot be null.
+        non_zero: bool
+    },
+
+    // Remaining variants are all ADTs such as TyStruct, TyEnum or TyTuple.
+
+    /// C-like enums; basically an integer.
+    CEnum {
+        discr: Integer,
+        signed: bool,
+        // Inclusive discriminant range.
+        // If min > max, it represents min...u64::MAX followed by 0...max.
+        // FIXME(eddyb) always use the shortest range, e.g. by finding
+        // the largest space between two consecutive discriminants and
+        // taking everything else as the (shortest) discriminant range.
+        min: u64,
+        max: u64
+    },
+
+    /// Single-case enums, and structs/tuples.
+    Univariant {
+        variant: Struct,
+        // If true, the structure is NonZero.
+        // FIXME(eddyb) use a newtype Layout kind for this.
+        non_zero: bool
+    },
+
+    /// General-case enums: for each case there is a struct, and they
+    /// all start with a field for the discriminant.
+    General {
+        discr: Integer,
+        variants: Vec<Struct>,
+        size: Size,
+        align: Align
+    },
+
+    /// Two cases distinguished by a nullable pointer: the case with discriminant
+    /// `nndiscr` must have single field which is known to be nonnull due to its type.
+    /// The other case is known to be zero sized. Hence we represent the enum
+    /// as simply a nullable pointer: if not null it indicates the `nndiscr` variant,
+    /// otherwise it indicates the other case.
+    ///
+    /// For example, `std::option::Option` instantiated at a safe pointer type
+    /// is represented such that `None` is a null pointer and `Some` is the
+    /// identity function.
+    RawNullablePointer {
+        nndiscr: u64,
+        value: Primitive
+    },
+
+    /// Two cases distinguished by a nullable pointer: the case with discriminant
+    /// `nndiscr` is represented by the struct `nonnull`, where the `discrfield`th
+    /// field is known to be nonnull due to its type; if that field is null, then
+    /// it represents the other case, which is known to be zero sized.
+    StructWrappedNullablePointer {
+        nndiscr: u64,
+        nonnull: Struct,
+        // N.B. There is a 0 at the start, for LLVM GEP through a pointer.
+        discrfield: FieldPath
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum LayoutError<'tcx> {
+    Unknown(Ty<'tcx>),
+    SizeOverflow(Ty<'tcx>)
+}
+
+impl<'tcx> fmt::Display for LayoutError<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            LayoutError::Unknown(ty) => {
+                write!(f, "the type `{:?}` has an unknown layout", ty)
+            }
+            LayoutError::SizeOverflow(ty) => {
+                write!(f, "the type `{:?}` is too big for the current architecture", ty)
+            }
+        }
+    }
+}
+
+/// Helper function for normalizing associated types in an inference context.
+fn normalize_associated_type<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+                                       ty: Ty<'tcx>)
+                                       -> Ty<'tcx> {
+    if !ty.has_projection_types() {
+        return ty;
+    }
+
+    let mut selcx = traits::SelectionContext::new(infcx);
+    let cause = traits::ObligationCause::dummy();
+    let traits::Normalized { value: result, obligations } =
+        traits::normalize(&mut selcx, cause, &ty);
+
+    let mut fulfill_cx = traits::FulfillmentContext::new();
+
+    for obligation in obligations {
+        fulfill_cx.register_predicate_obligation(infcx, obligation);
+    }
+
+    drain_fulfillment_cx_or_panic(DUMMY_SP, infcx, &mut fulfill_cx, &result)
+}
+
+impl Layout {
+    pub fn compute_uncached<'a, 'tcx>(ty: Ty<'tcx>,
+                                      infcx: &InferCtxt<'a, 'tcx>)
+                                      -> Result<Layout, LayoutError<'tcx>> {
+        let tcx = infcx.tcx;
+        let dl = &tcx.data_layout;
+        assert!(!ty.has_infer_types());
+
+        let layout = match ty.sty {
+            // Basic scalars.
+            ty::TyBool => Scalar { value: Int(I1), non_zero: false },
+            ty::TyChar => Scalar { value: Int(I32), non_zero: false },
+            ty::TyInt(ity) => {
+                Scalar {
+                    value: Int(Integer::from_attr(dl, attr::SignedInt(ity))),
+                    non_zero: false
+                }
+            }
+            ty::TyUint(ity) => {
+                Scalar {
+                    value: Int(Integer::from_attr(dl, attr::UnsignedInt(ity))),
+                    non_zero: false
+                }
+            }
+            ty::TyFloat(FloatTy::F32) => Scalar { value: F32, non_zero: false },
+            ty::TyFloat(FloatTy::F64) => Scalar { value: F64, non_zero: false },
+            ty::TyFnPtr(_) => Scalar { value: Pointer, non_zero: true },
+
+            // Potentially-fat pointers.
+            ty::TyBox(pointee) |
+            ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) |
+            ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
+                let non_zero = !ty.is_unsafe_ptr();
+                if pointee.is_sized(&infcx.parameter_environment, DUMMY_SP) {
+                    Scalar { value: Pointer, non_zero: non_zero }
+                } else {
+                    let unsized_part = tcx.struct_tail(pointee);
+                    let meta = match unsized_part.sty {
+                        ty::TySlice(_) | ty::TyStr => {
+                            Int(dl.ptr_sized_integer())
+                        }
+                        ty::TyTrait(_) => Pointer,
+                        _ => return Err(LayoutError::Unknown(unsized_part))
+                    };
+                    FatPointer { metadata: meta, non_zero: non_zero }
+                }
+            }
+
+            // Arrays and slices.
+            ty::TyArray(element, count) => {
+                let element = element.layout(infcx)?;
+                Array {
+                    sized: true,
+                    align: element.align(dl),
+                    size: element.size(dl).checked_mul(count as u64, dl)
+                                 .map_or(Err(LayoutError::SizeOverflow(ty)), Ok)?
+                }
+            }
+            ty::TySlice(element) => {
+                Array {
+                    sized: false,
+                    align: element.layout(infcx)?.align(dl),
+                    size: Size::from_bytes(0)
+                }
+            }
+            ty::TyStr => {
+                Array {
+                    sized: false,
+                    align: dl.i8_align,
+                    size: Size::from_bytes(0)
+                }
+            }
+
+            // Odd unit types.
+            ty::TyFnDef(..) => {
+                Univariant {
+                    variant: Struct::new(dl, false),
+                    non_zero: false
+                }
+            }
+            ty::TyTrait(_) => {
+                let mut unit = Struct::new(dl, false);
+                unit.sized = false;
+                Univariant { variant: unit, non_zero: false }
+            }
+
+            // Tuples.
+            ty::TyClosure(_, box ty::ClosureSubsts { upvar_tys: ref tys, .. }) |
+            ty::TyTuple(ref tys) => {
+                let mut st = Struct::new(dl, false);
+                st.extend(dl, tys.iter().map(|ty| ty.layout(infcx)), ty)?;
+                Univariant { variant: st, non_zero: false }
+            }
+
+            // ADTs.
+            ty::TyStruct(def, substs) => {
+                if ty.is_simd() {
+                    // SIMD vector types.
+                    let element = ty.simd_type(tcx);
+                    match *element.layout(infcx)? {
+                        Scalar { value, .. } => {
+                            return Ok(Vector {
+                                element: value,
+                                count: ty.simd_size(tcx) as u64
+                            });
+                        }
+                        _ => {
+                            tcx.sess.fatal(&format!("monomorphising SIMD type `{}` with \
+                                                     a non-machine element type `{}`",
+                                                    ty, element));
+                        }
+                    }
+                }
+                let fields = def.struct_variant().fields.iter().map(|field| {
+                    normalize_associated_type(infcx, field.ty(tcx, substs))
+                        .layout(infcx)
+                });
+                let packed = tcx.lookup_packed(def.did);
+                let mut st = Struct::new(dl, packed);
+                st.extend(dl, fields, ty)?;
+
+                // FIXME(16758) don't add a drop flag to unsized structs, as it
+                // won't actually be in the location we say it is because it'll be after
+                // the unsized field. Several other pieces of code assume that the unsized
+                // field is definitely the last one.
+                if def.dtor_kind().has_drop_flag() &&
+                   ty.is_sized(&infcx.parameter_environment, DUMMY_SP) {
+                    st.extend(dl, Some(Ok(&Scalar {
+                        value: Int(I8),
+                        non_zero: false
+                    })).into_iter(), ty)?;
+                }
+                Univariant {
+                    variant: st,
+                    non_zero: Some(def.did) == tcx.lang_items.non_zero()
+                }
+            }
+            ty::TyEnum(def, substs) => {
+                let hint = *tcx.lookup_repr_hints(def.did).get(0)
+                    .unwrap_or(&attr::ReprAny);
+
+                let dtor = def.dtor_kind().has_drop_flag();
+                let drop_flag = if dtor {
+                    Some(Scalar { value: Int(I8), non_zero: false })
+                } else {
+                    None
+                };
+
+                if def.variants.is_empty() {
+                    // Uninhabitable; represent as unit
+                    // (Typechecking will reject discriminant-sizing attrs.)
+                    assert_eq!(hint, attr::ReprAny);
+
+                    let mut st = Struct::new(dl, false);
+                    st.extend(dl, drop_flag.iter().map(Ok), ty)?;
+                    return Ok(Univariant { variant: st, non_zero: false });
+                }
+
+                if !dtor && def.variants.iter().all(|v| v.fields.is_empty()) {
+                    // All bodies empty -> intlike
+                    let (mut min, mut max) = (i64::MAX, i64::MIN);
+                    for v in &def.variants {
+                        let x = v.disr_val.to_u64_unchecked() as i64;
+                        if x < min { min = x; }
+                        if x > max { max = x; }
+                    }
+
+                    let (discr, signed) = Integer::repr_discr(tcx, hint, min, max);
+                    return Ok(CEnum {
+                        discr: discr,
+                        signed: signed,
+                        min: min as u64,
+                        max: max as u64
+                    });
+                }
+
+                // Since there's at least one
+                // non-empty body, explicit discriminants should have
+                // been rejected by a checker before this point.
+                for (i, v) in def.variants.iter().enumerate() {
+                    if i as u64 != v.disr_val.to_u64_unchecked() {
+                        bug!("non-C-like enum {} with specified discriminants",
+                             tcx.item_path_str(def.did));
+                    }
+                }
+
+                if def.variants.len() == 1 {
+                    // Equivalent to a struct/tuple/newtype.
+                    // (Typechecking will reject discriminant-sizing attrs.)
+                    assert_eq!(hint, attr::ReprAny);
+                    let fields = def.variants[0].fields.iter().map(|field| {
+                        normalize_associated_type(infcx, field.ty(tcx, substs))
+                            .layout(infcx)
+                    });
+                    let mut st = Struct::new(dl, false);
+                    st.extend(dl, fields.chain(drop_flag.iter().map(Ok)), ty)?;
+                    return Ok(Univariant { variant: st, non_zero: false });
+                }
+
+                // Cache the substituted and normalized variant field types.
+                let variants = def.variants.iter().map(|v| {
+                    v.fields.iter().map(|field| {
+                        normalize_associated_type(infcx, field.ty(tcx, substs))
+                    }).collect::<Vec<_>>()
+                }).collect::<Vec<_>>();
+
+                if !dtor && variants.len() == 2 && hint == attr::ReprAny {
+                    // Nullable pointer optimization
+                    for discr in 0..2 {
+                        let other_fields = variants[1 - discr].iter().map(|ty| {
+                            ty.layout(infcx)
+                        });
+                        if !Struct::would_be_zero_sized(dl, other_fields)? {
+                            continue;
+                        }
+                        let path = Struct::non_zero_field_path(infcx,
+                            variants[discr].iter().cloned())?;
+                        let mut path = if let Some(p) = path { p } else { continue };
+
+                        // FIXME(eddyb) should take advantage of a newtype.
+                        if path == &[0] && variants[discr].len() == 1 {
+                            match *variants[discr][0].layout(infcx)? {
+                                Scalar { value, .. } => {
+                                    return Ok(RawNullablePointer {
+                                        nndiscr: discr as u64,
+                                        value: value
+                                    });
+                                }
+                                _ => {
+                                    bug!("Layout::compute: `{}`'s non-zero \
+                                          `{}` field not scalar?!",
+                                         ty, variants[discr][0])
+                                }
+                            }
+                        }
+
+                        path.push(0); // For GEP through a pointer.
+                        path.reverse();
+                        let mut st = Struct::new(dl, false);
+                        st.extend(dl, variants[discr].iter().map(|ty| {
+                            ty.layout(infcx)
+                        }), ty)?;
+                        return Ok(StructWrappedNullablePointer {
+                            nndiscr: discr as u64,
+                            nonnull: st,
+                            discrfield: path
+                        });
+                    }
+                }
+
+                // The general case.
+                let discr_max = (variants.len() - 1) as i64;
+                assert!(discr_max >= 0);
+                let (min_ity, _) = Integer::repr_discr(tcx, hint, 0, discr_max);
+
+                let mut align = dl.aggregate_align;
+                let mut size = Size::from_bytes(0);
+
+                // We're interested in the smallest alignment, so start large.
+                let mut start_align = Align::from_bytes(256, 256).unwrap();
+
+                // Create the set of structs that represent each variant
+                // Use the minimum integer type we figured out above
+                let discr = Some(Scalar { value: Int(min_ity), non_zero: false });
+                let mut variants = variants.into_iter().map(|fields| {
+                    let mut found_start = false;
+                    let fields = fields.into_iter().map(|field| {
+                        let field = field.layout(infcx)?;
+                        if !found_start {
+                            // Find the first field we can't move later
+                            // to make room for a larger discriminant.
+                            let field_align = field.align(dl);
+                            if field.size(dl).bytes() != 0 || field_align.abi() != 1 {
+                                start_align = start_align.min(field_align);
+                                found_start = true;
+                            }
+                        }
+                        Ok(field)
+                    });
+                    let mut st = Struct::new(dl, false);
+                    st.extend(dl, discr.iter().map(Ok).chain(fields)
+                                              .chain(drop_flag.iter().map(Ok)), ty)?;
+                    size = cmp::max(size, st.min_size());
+                    align = align.max(st.align);
+                    Ok(st)
+                }).collect::<Result<Vec<_>, _>>()?;
+
+                // Align the maximum variant size to the largest alignment.
+                size = size.abi_align(align);
+
+                if size.bytes() >= dl.obj_size_bound() {
+                    return Err(LayoutError::SizeOverflow(ty));
+                }
+
+                // Check to see if we should use a different type for the
+                // discriminant. We can safely use a type with the same size
+                // as the alignment of the first field of each variant.
+                // We increase the size of the discriminant to avoid LLVM copying
+                // padding when it doesn't need to. This normally causes unaligned
+                // load/stores and excessive memcpy/memset operations. By using a
+                // bigger integer size, LLVM can be sure about it's contents and
+                // won't be so conservative.
+
+                // Use the initial field alignment
+                let wanted = start_align.abi();
+                let mut ity = min_ity;
+                for &candidate in &[I16, I32, I64] {
+                    let ty = Int(candidate);
+                    if wanted == ty.align(dl).abi() && wanted == ty.size(dl).bytes() {
+                        ity = candidate;
+                        break;
+                    }
+                }
+
+                // FIXME(eddyb) conservative only to avoid diverging from trans::adt.
+                if align.abi() != start_align.abi() {
+                    ity = min_ity;
+                }
+
+                // If the alignment is not larger than the chosen discriminant size,
+                // don't use the alignment as the final size.
+                if ity <= min_ity {
+                    ity = min_ity;
+                } else {
+                    // Patch up the variants' first few fields.
+                    let old_ity_size = Int(min_ity).size(dl);
+                    let new_ity_size = Int(ity).size(dl);
+                    for variant in &mut variants {
+                        for offset in &mut variant.offset_after_field {
+                            if *offset > old_ity_size {
+                                break;
+                            }
+                            *offset = new_ity_size;
+                        }
+                    }
+                }
+
+                General {
+                    discr: ity,
+                    variants: variants,
+                    size: size,
+                    align: align
+                }
+            }
+
+            // Types with no meaningful known layout.
+            ty::TyProjection(_) | ty::TyParam(_) => {
+                return Err(LayoutError::Unknown(ty));
+            }
+            ty::TyInfer(_) | ty::TyError => {
+                bug!("Layout::compute: unexpected type `{}`", ty)
+            }
+        };
+
+        Ok(layout)
+    }
+
+    /// Returns true if the layout corresponds to an unsized type.
+    pub fn is_unsized(&self) -> bool {
+        match *self {
+            Scalar {..} | Vector {..} | FatPointer {..} |
+            CEnum {..} | General {..} |
+            RawNullablePointer {..} |
+            StructWrappedNullablePointer {..} => false,
+
+            Array { sized, .. } |
+            Univariant { variant: Struct { sized, .. }, .. } => !sized
+        }
+    }
+
+    pub fn size(&self, dl: &TargetDataLayout) -> Size {
+        match *self {
+            Scalar { value, .. } | RawNullablePointer { value, .. } => {
+                value.size(dl)
+            }
+
+            Vector { element, count } => {
+                let elem_size = element.size(dl);
+                let vec_size = match elem_size.checked_mul(count, dl) {
+                    Some(size) => size,
+                    None => bug!("Layout::size({:?}): {} * {} overflowed",
+                                 self, elem_size.bytes(), count)
+                };
+                vec_size.abi_align(self.align(dl))
+            }
+
+            FatPointer { metadata, .. } => {
+                // Effectively a (ptr, meta) tuple.
+                Pointer.size(dl).abi_align(metadata.align(dl))
+                       .checked_add(metadata.size(dl), dl).unwrap()
+                       .abi_align(self.align(dl))
+            }
+
+            CEnum { discr, .. } => Int(discr).size(dl),
+            Array { size, .. } | General { size, .. } => size,
+
+            Univariant { ref variant, .. } |
+            StructWrappedNullablePointer { nonnull: ref variant, .. } => {
+                variant.stride()
+            }
+        }
+    }
+
+    pub fn align(&self, dl: &TargetDataLayout) -> Align {
+        match *self {
+            Scalar { value, .. } | RawNullablePointer { value, .. } => {
+                value.align(dl)
+            }
+
+            Vector { element, count } => {
+                let elem_size = element.size(dl);
+                let vec_size = match elem_size.checked_mul(count, dl) {
+                    Some(size) => size,
+                    None => bug!("Layout::align({:?}): {} * {} overflowed",
+                                 self, elem_size.bytes(), count)
+                };
+                for &(size, align) in &dl.vector_align {
+                    if size == vec_size {
+                        return align;
+                    }
+                }
+                // Default to natural alignment, which is what LLVM does.
+                // That is, use the size, rounded up to a power of 2.
+                let align = vec_size.bytes().next_power_of_two();
+                Align::from_bytes(align, align).unwrap()
+            }
+
+            FatPointer { metadata, .. } => {
+                // Effectively a (ptr, meta) tuple.
+                Pointer.align(dl).max(metadata.align(dl))
+            }
+
+            CEnum { discr, .. } => Int(discr).align(dl),
+            Array { align, .. } | General { align, .. } => align,
+
+            Univariant { ref variant, .. } |
+            StructWrappedNullablePointer { nonnull: ref variant, .. } => {
+                variant.align
+            }
+        }
+    }
+}
+
+/// Type size "skeleton", i.e. the only information determining a type's size.
+/// While this is conservative, (aside from constant sizes, only pointers,
+/// newtypes thereof and null pointer optimized enums are allowed), it is
+/// enough to statically check common usecases of transmute.
+#[derive(Copy, Clone, Debug)]
+pub enum SizeSkeleton<'tcx> {
+    /// Any statically computable Layout.
+    Known(Size),
+
+    /// A potentially-fat pointer.
+    Pointer {
+        // If true, this pointer is never null.
+        non_zero: bool,
+        // The type which determines the unsized metadata, if any,
+        // of this pointer. Either a type parameter or a projection
+        // depending on one, with regions erased.
+        tail: Ty<'tcx>
+    }
+}
+
+impl<'tcx> SizeSkeleton<'tcx> {
+    pub fn compute<'a>(ty: Ty<'tcx>, infcx: &InferCtxt<'a, 'tcx>)
+                       -> Result<SizeSkeleton<'tcx>, LayoutError<'tcx>> {
+        let tcx = infcx.tcx;
+        assert!(!ty.has_infer_types());
+
+        // First try computing a static layout.
+        let err = match ty.layout(infcx) {
+            Ok(layout) => {
+                return Ok(SizeSkeleton::Known(layout.size(&tcx.data_layout)));
+            }
+            Err(err) => err
+        };
+
+        match ty.sty {
+            ty::TyBox(pointee) |
+            ty::TyRef(_, ty::TypeAndMut { ty: pointee, .. }) |
+            ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
+                let non_zero = !ty.is_unsafe_ptr();
+                let tail = tcx.struct_tail(pointee);
+                match tail.sty {
+                    ty::TyParam(_) | ty::TyProjection(_) => {
+                        assert!(tail.has_param_types() || tail.has_self_ty());
+                        Ok(SizeSkeleton::Pointer {
+                            non_zero: non_zero,
+                            tail: tcx.erase_regions(&tail)
+                        })
+                    }
+                    _ => {
+                        bug!("SizeSkeleton::compute({}): layout errored ({}), yet \
+                              tail `{}` is not a type parameter or a projection",
+                             ty, err, tail)
+                    }
+                }
+            }
+
+            ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
+                // Only newtypes and enums w/ nullable pointer optimization.
+                if def.variants.is_empty() || def.variants.len() > 2 {
+                    return Err(err);
+                }
+
+                // If there's a drop flag, it can't be just a pointer.
+                if def.dtor_kind().has_drop_flag() {
+                    return Err(err);
+                }
+
+                // Get a zero-sized variant or a pointer newtype.
+                let zero_or_ptr_variant = |i: usize| {
+                    let fields = def.variants[i].fields.iter().map(|field| {
+                        let ty = normalize_associated_type(infcx, &field.ty(tcx, substs));
+                        SizeSkeleton::compute(ty, infcx)
+                    });
+                    let mut ptr = None;
+                    for field in fields {
+                        let field = field?;
+                        match field {
+                            SizeSkeleton::Known(size) => {
+                                if size.bytes() > 0 {
+                                    return Err(err);
+                                }
+                            }
+                            SizeSkeleton::Pointer {..} => {
+                                if ptr.is_some() {
+                                    return Err(err);
+                                }
+                                ptr = Some(field);
+                            }
+                        }
+                    }
+                    Ok(ptr)
+                };
+
+                let v0 = zero_or_ptr_variant(0)?;
+                // Newtype.
+                if def.variants.len() == 1 {
+                    if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 {
+                        return Ok(SizeSkeleton::Pointer {
+                            non_zero: non_zero ||
+                                Some(def.did) == tcx.lang_items.non_zero(),
+                            tail: tail
+                        });
+                    } else {
+                        return Err(err);
+                    }
+                }
+
+                let v1 = zero_or_ptr_variant(1)?;
+                // Nullable pointer enum optimization.
+                match (v0, v1) {
+                    (Some(SizeSkeleton::Pointer { non_zero: true, tail }), None) |
+                    (None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => {
+                        Ok(SizeSkeleton::Pointer {
+                            non_zero: false,
+                            tail: tail
+                        })
+                    }
+                    _ => Err(err)
+                }
+            }
+
+            _ => Err(err)
+        }
+    }
+
+    pub fn same_size(self, other: SizeSkeleton) -> bool {
+        match (self, other) {
+            (SizeSkeleton::Known(a), SizeSkeleton::Known(b)) => a == b,
+            (SizeSkeleton::Pointer { tail: a, .. },
+             SizeSkeleton::Pointer { tail: b, .. }) => a == b,
+            _ => false
+        }
+    }
+}
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index dda5f699bae..238856197fe 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -84,6 +84,7 @@ pub mod error;
 pub mod fast_reject;
 pub mod fold;
 pub mod item_path;
+pub mod layout;
 pub mod _match;
 pub mod maps;
 pub mod outlives;
@@ -479,37 +480,6 @@ pub struct CReaderCacheKey {
     pub pos: usize,
 }
 
-/// A restriction that certain types must be the same size. The use of
-/// `transmute` gives rise to these restrictions. These generally
-/// cannot be checked until trans; therefore, each call to `transmute`
-/// will push one or more such restriction into the
-/// `transmute_restrictions` vector during `intrinsicck`. They are
-/// then checked during `trans` by the fn `check_intrinsics`.
-#[derive(Copy, Clone)]
-pub struct TransmuteRestriction<'tcx> {
-    /// The span whence the restriction comes.
-    pub span: Span,
-
-    /// The type being transmuted from.
-    pub original_from: Ty<'tcx>,
-
-    /// The type being transmuted to.
-    pub original_to: Ty<'tcx>,
-
-    /// The type being transmuted from, with all type parameters
-    /// substituted for an arbitrary representative. Not to be shown
-    /// to the end user.
-    pub substituted_from: Ty<'tcx>,
-
-    /// The type being transmuted to, with all type parameters
-    /// substituted for an arbitrary representative. Not to be shown
-    /// to the end user.
-    pub substituted_to: Ty<'tcx>,
-
-    /// NodeId of the transmute intrinsic.
-    pub id: NodeId,
-}
-
 /// Describes the fragment-state associated with a NodeId.
 ///
 /// Currently only unfragmented paths have entries in the table,
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index 2251794def4..2e4f37f1cc1 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -18,6 +18,7 @@ use hir::pat_util;
 use traits::{self, ProjectionMode};
 use ty::{self, Ty, TyCtxt, TypeAndMut, TypeFlags, TypeFoldable};
 use ty::{Disr, ParameterEnvironment};
+use ty::layout::{Layout, LayoutError};
 use ty::TypeVariants::*;
 
 use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
@@ -597,6 +598,24 @@ impl<'tcx> ty::TyS<'tcx> {
         result
     }
 
+    #[inline]
+    pub fn layout<'a>(&'tcx self, infcx: &infer::InferCtxt<'a, 'tcx>)
+                      -> Result<&'tcx Layout, LayoutError<'tcx>> {
+        let can_cache = !self.has_param_types() && !self.has_self_ty();
+        if can_cache {
+            if let Some(&cached) = infcx.tcx.layout_cache.borrow().get(&self) {
+                return Ok(cached);
+            }
+        }
+
+        let layout = Layout::compute_uncached(self, infcx)?;
+        let layout = infcx.tcx.intern_layout(layout);
+        if can_cache {
+            infcx.tcx.layout_cache.borrow_mut().insert(self, layout);
+        }
+        Ok(layout)
+    }
+
 
     /// Check whether a type is representable. This means it cannot contain unboxed
     /// structural recursion. This check is needed for structs and enums.
diff --git a/src/librustc_back/target/aarch64_apple_ios.rs b/src/librustc_back/target/aarch64_apple_ios.rs
index e1242560e62..2f0a043f9a7 100644
--- a/src/librustc_back/target/aarch64_apple_ios.rs
+++ b/src/librustc_back/target/aarch64_apple_ios.rs
@@ -16,6 +16,7 @@ pub fn target() -> Target {
         llvm_target: "arm64-apple-ios".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(),
         arch: "aarch64".to_string(),
         target_os: "ios".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/aarch64_linux_android.rs b/src/librustc_back/target/aarch64_linux_android.rs
index c6901a4cc42..c4212e70212 100644
--- a/src/librustc_back/target/aarch64_linux_android.rs
+++ b/src/librustc_back/target/aarch64_linux_android.rs
@@ -15,6 +15,7 @@ pub fn target() -> Target {
         llvm_target: "aarch64-linux-android".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:e-i64:64-i128:128-n32:64-S128".to_string(),
         arch: "aarch64".to_string(),
         target_os: "android".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs
index 51abab6609a..3bf4e92fb6a 100644
--- a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs
+++ b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs
@@ -17,6 +17,7 @@ pub fn target() -> Target {
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
         target_env: "gnu".to_string(),
+        data_layout: "e-m:e-i64:64-i128:128-n32:64-S128".to_string(),
         arch: "aarch64".to_string(),
         target_os: "linux".to_string(),
         target_vendor: "unknown".to_string(),
diff --git a/src/librustc_back/target/arm_linux_androideabi.rs b/src/librustc_back/target/arm_linux_androideabi.rs
index 732f1a353a8..0a61b147634 100644
--- a/src/librustc_back/target/arm_linux_androideabi.rs
+++ b/src/librustc_back/target/arm_linux_androideabi.rs
@@ -18,6 +18,7 @@ pub fn target() -> Target {
         llvm_target: "arm-linux-androideabi".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
         arch: "arm".to_string(),
         target_os: "android".to_string(),
         target_env: "gnu".to_string(),
diff --git a/src/librustc_back/target/arm_unknown_linux_gnueabi.rs b/src/librustc_back/target/arm_unknown_linux_gnueabi.rs
index 7c35b43fd4b..0cb0949d462 100644
--- a/src/librustc_back/target/arm_unknown_linux_gnueabi.rs
+++ b/src/librustc_back/target/arm_unknown_linux_gnueabi.rs
@@ -16,6 +16,7 @@ pub fn target() -> Target {
         llvm_target: "arm-unknown-linux-gnueabi".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
         arch: "arm".to_string(),
         target_os: "linux".to_string(),
         target_env: "gnueabi".to_string(),
diff --git a/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs b/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs
index a99ec45996c..05b9401a063 100644
--- a/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs
+++ b/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs
@@ -16,6 +16,7 @@ pub fn target() -> Target {
         llvm_target: "arm-unknown-linux-gnueabihf".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
         arch: "arm".to_string(),
         target_os: "linux".to_string(),
         target_env: "gnueabihf".to_string(),
diff --git a/src/librustc_back/target/armv7_apple_ios.rs b/src/librustc_back/target/armv7_apple_ios.rs
index d3064800291..d131f8b2ef0 100644
--- a/src/librustc_back/target/armv7_apple_ios.rs
+++ b/src/librustc_back/target/armv7_apple_ios.rs
@@ -16,6 +16,7 @@ pub fn target() -> Target {
         llvm_target: "armv7-apple-ios".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:o-p:32:32-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".to_string(),
         arch: "arm".to_string(),
         target_os: "ios".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs b/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs
index d7dcd714a10..9c9bb72f76c 100644
--- a/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs
+++ b/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs
@@ -16,6 +16,7 @@ pub fn target() -> Target {
         llvm_target: "armv7-unknown-linux-gnueabihf".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
         arch: "arm".to_string(),
         target_os: "linux".to_string(),
         target_env: "gnueabihf".to_string(),
diff --git a/src/librustc_back/target/armv7s_apple_ios.rs b/src/librustc_back/target/armv7s_apple_ios.rs
index 66ec6efca0e..d317589bf36 100644
--- a/src/librustc_back/target/armv7s_apple_ios.rs
+++ b/src/librustc_back/target/armv7s_apple_ios.rs
@@ -16,6 +16,7 @@ pub fn target() -> Target {
         llvm_target: "armv7s-apple-ios".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:o-p:32:32-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".to_string(),
         arch: "arm".to_string(),
         target_os: "ios".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/asmjs_unknown_emscripten.rs b/src/librustc_back/target/asmjs_unknown_emscripten.rs
index 4330e2e7b5f..546f9df605b 100644
--- a/src/librustc_back/target/asmjs_unknown_emscripten.rs
+++ b/src/librustc_back/target/asmjs_unknown_emscripten.rs
@@ -31,6 +31,7 @@ pub fn target() -> Target {
         target_os: "emscripten".to_string(),
         target_env: "".to_string(),
         target_vendor: "unknown".to_string(),
+        data_layout: "e-p:32:32-i64:64-v128:32:128-n32-S128".to_string(),
         arch: "asmjs".to_string(),
         options: opts,
     }
diff --git a/src/librustc_back/target/i386_apple_ios.rs b/src/librustc_back/target/i386_apple_ios.rs
index 52b5901192c..d149d4bbdc2 100644
--- a/src/librustc_back/target/i386_apple_ios.rs
+++ b/src/librustc_back/target/i386_apple_ios.rs
@@ -16,6 +16,7 @@ pub fn target() -> Target {
         llvm_target: "i386-apple-ios".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128".to_string(),
         arch: "x86".to_string(),
         target_os: "ios".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/i686_apple_darwin.rs b/src/librustc_back/target/i686_apple_darwin.rs
index 98f4654ecab..b6e2f4d8e8a 100644
--- a/src/librustc_back/target/i686_apple_darwin.rs
+++ b/src/librustc_back/target/i686_apple_darwin.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "i686-apple-darwin".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:o-p:32:32-f64:32:64-f80:128-n8:16:32-S128".to_string(),
         arch: "x86".to_string(),
         target_os: "macos".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/i686_linux_android.rs b/src/librustc_back/target/i686_linux_android.rs
index f548fdad3cb..b338a971ff7 100644
--- a/src/librustc_back/target/i686_linux_android.rs
+++ b/src/librustc_back/target/i686_linux_android.rs
@@ -18,6 +18,7 @@ pub fn target() -> Target {
         llvm_target: "i686-linux-android".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
         arch: "x86".to_string(),
         target_os: "android".to_string(),
         target_env: "gnu".to_string(),
diff --git a/src/librustc_back/target/i686_pc_windows_gnu.rs b/src/librustc_back/target/i686_pc_windows_gnu.rs
index fa12bbd8932..48203cc74d6 100644
--- a/src/librustc_back/target/i686_pc_windows_gnu.rs
+++ b/src/librustc_back/target/i686_pc_windows_gnu.rs
@@ -22,6 +22,7 @@ pub fn target() -> Target {
         llvm_target: "i686-pc-windows-gnu".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(),
         arch: "x86".to_string(),
         target_os: "windows".to_string(),
         target_env: "gnu".to_string(),
diff --git a/src/librustc_back/target/i686_pc_windows_msvc.rs b/src/librustc_back/target/i686_pc_windows_msvc.rs
index 7fe65906189..501219ad607 100644
--- a/src/librustc_back/target/i686_pc_windows_msvc.rs
+++ b/src/librustc_back/target/i686_pc_windows_msvc.rs
@@ -27,6 +27,7 @@ pub fn target() -> Target {
         llvm_target: "i686-pc-windows-msvc".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32".to_string(),
         arch: "x86".to_string(),
         target_os: "windows".to_string(),
         target_env: "msvc".to_string(),
diff --git a/src/librustc_back/target/i686_unknown_dragonfly.rs b/src/librustc_back/target/i686_unknown_dragonfly.rs
index 32a15b9f2d4..cdbbd5eafdd 100644
--- a/src/librustc_back/target/i686_unknown_dragonfly.rs
+++ b/src/librustc_back/target/i686_unknown_dragonfly.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "i686-unknown-dragonfly".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
         arch: "x86".to_string(),
         target_os: "dragonfly".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/i686_unknown_freebsd.rs b/src/librustc_back/target/i686_unknown_freebsd.rs
index 812ba11cd79..fadedc24149 100644
--- a/src/librustc_back/target/i686_unknown_freebsd.rs
+++ b/src/librustc_back/target/i686_unknown_freebsd.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "i686-unknown-freebsd".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
         arch: "x86".to_string(),
         target_os: "freebsd".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/i686_unknown_linux_gnu.rs b/src/librustc_back/target/i686_unknown_linux_gnu.rs
index ac2af0c64fd..a1f3ab76907 100644
--- a/src/librustc_back/target/i686_unknown_linux_gnu.rs
+++ b/src/librustc_back/target/i686_unknown_linux_gnu.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "i686-unknown-linux-gnu".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
         arch: "x86".to_string(),
         target_os: "linux".to_string(),
         target_env: "gnu".to_string(),
diff --git a/src/librustc_back/target/i686_unknown_linux_musl.rs b/src/librustc_back/target/i686_unknown_linux_musl.rs
index 77bc7bb5175..cce023b8430 100644
--- a/src/librustc_back/target/i686_unknown_linux_musl.rs
+++ b/src/librustc_back/target/i686_unknown_linux_musl.rs
@@ -37,6 +37,7 @@ pub fn target() -> Target {
         llvm_target: "i686-unknown-linux-musl".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
         arch: "x86".to_string(),
         target_os: "linux".to_string(),
         target_env: "musl".to_string(),
diff --git a/src/librustc_back/target/le32_unknown_nacl.rs b/src/librustc_back/target/le32_unknown_nacl.rs
index d37102e558d..472b73302a3 100644
--- a/src/librustc_back/target/le32_unknown_nacl.rs
+++ b/src/librustc_back/target/le32_unknown_nacl.rs
@@ -34,6 +34,7 @@ pub fn target() -> Target {
         target_os: "nacl".to_string(),
         target_env: "newlib".to_string(),
         target_vendor: "unknown".to_string(),
+        data_layout: "e-i64:64:64-p:32:32:32-v128:32:32".to_string(),
         arch: "le32".to_string(),
         options: opts,
     }
diff --git a/src/librustc_back/target/mips_unknown_linux_gnu.rs b/src/librustc_back/target/mips_unknown_linux_gnu.rs
index 01f2de4a269..863f5ceab0a 100644
--- a/src/librustc_back/target/mips_unknown_linux_gnu.rs
+++ b/src/librustc_back/target/mips_unknown_linux_gnu.rs
@@ -15,6 +15,7 @@ pub fn target() -> Target {
         llvm_target: "mips-unknown-linux-gnu".to_string(),
         target_endian: "big".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(),
         arch: "mips".to_string(),
         target_os: "linux".to_string(),
         target_env: "gnu".to_string(),
diff --git a/src/librustc_back/target/mips_unknown_linux_musl.rs b/src/librustc_back/target/mips_unknown_linux_musl.rs
index 050fb91aa19..ac0fde5449f 100644
--- a/src/librustc_back/target/mips_unknown_linux_musl.rs
+++ b/src/librustc_back/target/mips_unknown_linux_musl.rs
@@ -15,6 +15,7 @@ pub fn target() -> Target {
         llvm_target: "mips-unknown-linux-musl".to_string(),
         target_endian: "big".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(),
         arch: "mips".to_string(),
         target_os: "linux".to_string(),
         target_env: "musl".to_string(),
diff --git a/src/librustc_back/target/mipsel_unknown_linux_gnu.rs b/src/librustc_back/target/mipsel_unknown_linux_gnu.rs
index e9eef72e8c3..ff33effa3e7 100644
--- a/src/librustc_back/target/mipsel_unknown_linux_gnu.rs
+++ b/src/librustc_back/target/mipsel_unknown_linux_gnu.rs
@@ -15,6 +15,7 @@ pub fn target() -> Target {
         llvm_target: "mipsel-unknown-linux-gnu".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(),
         arch: "mips".to_string(),
         target_os: "linux".to_string(),
         target_env: "gnu".to_string(),
diff --git a/src/librustc_back/target/mipsel_unknown_linux_musl.rs b/src/librustc_back/target/mipsel_unknown_linux_musl.rs
index 383a0d891ca..d9fb1405036 100644
--- a/src/librustc_back/target/mipsel_unknown_linux_musl.rs
+++ b/src/librustc_back/target/mipsel_unknown_linux_musl.rs
@@ -15,6 +15,7 @@ pub fn target() -> Target {
         llvm_target: "mipsel-unknown-linux-musl".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(),
         arch: "mips".to_string(),
         target_os: "linux".to_string(),
         target_env: "musl".to_string(),
diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs
index 72c99d57963..3f75201aad2 100644
--- a/src/librustc_back/target/mod.rs
+++ b/src/librustc_back/target/mod.rs
@@ -39,10 +39,10 @@
 //! this module defines the format the JSON file should take, though each
 //! underscore in the field names should be replaced with a hyphen (`-`) in the
 //! JSON file. Some fields are required in every target specification, such as
-//! `llvm-target`, `target-endian`, `target-pointer-width`, `arch`, and
-//! `os`. In general, options passed to rustc with `-C` override the target's
-//! settings, though `target-feature` and `link-args` will *add* to the list
-//! specified by the target, rather than replace.
+//! `llvm-target`, `target-endian`, `target-pointer-width`, `data-layout`,
+//! `arch`, and `os`. In general, options passed to rustc with `-C` override
+//! the target's settings, though `target-feature` and `link-args` will *add*
+//! to the list specified by the target, rather than replace.
 
 use serialize::json::Json;
 use std::default::Default;
@@ -75,7 +75,8 @@ macro_rules! supported_targets {
             if false { }
             $(
                 else if target == stringify!($module) {
-                    let t = $module::target();
+                    let mut t = $module::target();
+                    t.options.is_builtin = true;
                     debug!("Got builtin target: {:?}", t);
                     return Some(t);
                 }
@@ -161,6 +162,8 @@ pub struct Target {
     /// Architecture to use for ABI considerations. Valid options: "x86",
     /// "x86_64", "arm", "aarch64", "mips", "powerpc", and "powerpc64".
     pub arch: String,
+    /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM.
+    pub data_layout: String,
     /// Optional settings with defaults.
     pub options: TargetOptions,
 }
@@ -171,8 +174,9 @@ pub struct Target {
 /// these try to take "minimal defaults" that don't assume anything about the runtime they run in.
 #[derive(Clone, Debug)]
 pub struct TargetOptions {
-    /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM.
-    pub data_layout: Option<String>,
+    /// Whether the target is built-in or loaded from a custom target specification.
+    pub is_builtin: bool,
+
     /// Linker to invoke. Defaults to "cc".
     pub linker: String,
     /// Archive utility to use when managing archives. Defaults to "ar".
@@ -293,7 +297,7 @@ impl Default for TargetOptions {
     /// incomplete, and if used for compilation, will certainly not work.
     fn default() -> TargetOptions {
         TargetOptions {
-            data_layout: None,
+            is_builtin: false,
             linker: option_env!("CFG_DEFAULT_LINKER").unwrap_or("cc").to_string(),
             ar: option_env!("CFG_DEFAULT_AR").unwrap_or("ar").to_string(),
             pre_link_args: Vec::new(),
@@ -378,6 +382,7 @@ impl Target {
             llvm_target: get_req_field("llvm-target"),
             target_endian: get_req_field("target-endian"),
             target_pointer_width: get_req_field("target-pointer-width"),
+            data_layout: get_req_field("data-layout"),
             arch: get_req_field("arch"),
             target_os: get_req_field("os"),
             target_env: get_opt_field("env", ""),
@@ -426,7 +431,6 @@ impl Target {
         key!(staticlib_prefix);
         key!(staticlib_suffix);
         key!(features);
-        key!(data_layout, optional);
         key!(dynamic_linking, bool);
         key!(executables, bool);
         key!(disable_redzone, bool);
diff --git a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs
index 3ba0c671d2e..fe7daaec1cd 100644
--- a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs
+++ b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "powerpc64-unknown-linux-gnu".to_string(),
         target_endian: "big".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "E-m:e-i64:64-n32:64".to_string(),
         arch: "powerpc64".to_string(),
         target_os: "linux".to_string(),
         target_env: "gnu".to_string(),
diff --git a/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs
index f0fac14dae0..4aab2b1802c 100644
--- a/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs
+++ b/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "powerpc64le-unknown-linux-gnu".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:e-i64:64-n32:64".to_string(),
         arch: "powerpc64".to_string(),
         target_os: "linux".to_string(),
         target_env: "gnu".to_string(),
diff --git a/src/librustc_back/target/powerpc_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc_unknown_linux_gnu.rs
index 6664abf5458..1df36442c06 100644
--- a/src/librustc_back/target/powerpc_unknown_linux_gnu.rs
+++ b/src/librustc_back/target/powerpc_unknown_linux_gnu.rs
@@ -18,6 +18,7 @@ pub fn target() -> Target {
         llvm_target: "powerpc-unknown-linux-gnu".to_string(),
         target_endian: "big".to_string(),
         target_pointer_width: "32".to_string(),
+        data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(),
         arch: "powerpc".to_string(),
         target_os: "linux".to_string(),
         target_env: "gnu".to_string(),
diff --git a/src/librustc_back/target/x86_64_apple_darwin.rs b/src/librustc_back/target/x86_64_apple_darwin.rs
index 3e19e148290..c8b5dd0eccc 100644
--- a/src/librustc_back/target/x86_64_apple_darwin.rs
+++ b/src/librustc_back/target/x86_64_apple_darwin.rs
@@ -20,6 +20,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-apple-darwin".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "macos".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/x86_64_apple_ios.rs b/src/librustc_back/target/x86_64_apple_ios.rs
index 63234c0baee..d038e88f2b4 100644
--- a/src/librustc_back/target/x86_64_apple_ios.rs
+++ b/src/librustc_back/target/x86_64_apple_ios.rs
@@ -16,6 +16,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-apple-ios".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "ios".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/x86_64_pc_windows_gnu.rs b/src/librustc_back/target/x86_64_pc_windows_gnu.rs
index 3e843853915..f0a09ae71ef 100644
--- a/src/librustc_back/target/x86_64_pc_windows_gnu.rs
+++ b/src/librustc_back/target/x86_64_pc_windows_gnu.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-pc-windows-gnu".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:w-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "windows".to_string(),
         target_env: "gnu".to_string(),
diff --git a/src/librustc_back/target/x86_64_pc_windows_msvc.rs b/src/librustc_back/target/x86_64_pc_windows_msvc.rs
index 14ce2735051..b3fbd6ef051 100644
--- a/src/librustc_back/target/x86_64_pc_windows_msvc.rs
+++ b/src/librustc_back/target/x86_64_pc_windows_msvc.rs
@@ -18,6 +18,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-pc-windows-msvc".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:w-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "windows".to_string(),
         target_env: "msvc".to_string(),
diff --git a/src/librustc_back/target/x86_64_rumprun_netbsd.rs b/src/librustc_back/target/x86_64_rumprun_netbsd.rs
index c97b434b9e0..652159d10fd 100644
--- a/src/librustc_back/target/x86_64_rumprun_netbsd.rs
+++ b/src/librustc_back/target/x86_64_rumprun_netbsd.rs
@@ -27,6 +27,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-rumprun-netbsd".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "netbsd".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/x86_64_sun_solaris.rs b/src/librustc_back/target/x86_64_sun_solaris.rs
index 541c1306b33..5aa08ea9c8c 100644
--- a/src/librustc_back/target/x86_64_sun_solaris.rs
+++ b/src/librustc_back/target/x86_64_sun_solaris.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-pc-solaris".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "solaris".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/x86_64_unknown_bitrig.rs b/src/librustc_back/target/x86_64_unknown_bitrig.rs
index 04456b1b271..e8b95ed80d9 100644
--- a/src/librustc_back/target/x86_64_unknown_bitrig.rs
+++ b/src/librustc_back/target/x86_64_unknown_bitrig.rs
@@ -18,6 +18,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-unknown-bitrig".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "bitrig".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/x86_64_unknown_dragonfly.rs b/src/librustc_back/target/x86_64_unknown_dragonfly.rs
index 62654176aa4..3fa46c31a5e 100644
--- a/src/librustc_back/target/x86_64_unknown_dragonfly.rs
+++ b/src/librustc_back/target/x86_64_unknown_dragonfly.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-unknown-dragonfly".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "dragonfly".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/x86_64_unknown_freebsd.rs b/src/librustc_back/target/x86_64_unknown_freebsd.rs
index 888b7f58bff..d345a321794 100644
--- a/src/librustc_back/target/x86_64_unknown_freebsd.rs
+++ b/src/librustc_back/target/x86_64_unknown_freebsd.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-unknown-freebsd".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "freebsd".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/x86_64_unknown_linux_gnu.rs b/src/librustc_back/target/x86_64_unknown_linux_gnu.rs
index e3ccd9c4c7e..69e333a1350 100644
--- a/src/librustc_back/target/x86_64_unknown_linux_gnu.rs
+++ b/src/librustc_back/target/x86_64_unknown_linux_gnu.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-unknown-linux-gnu".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "linux".to_string(),
         target_env: "gnu".to_string(),
diff --git a/src/librustc_back/target/x86_64_unknown_linux_musl.rs b/src/librustc_back/target/x86_64_unknown_linux_musl.rs
index dafbb924a9c..622a1fe8baf 100644
--- a/src/librustc_back/target/x86_64_unknown_linux_musl.rs
+++ b/src/librustc_back/target/x86_64_unknown_linux_musl.rs
@@ -73,6 +73,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-unknown-linux-musl".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "linux".to_string(),
         target_env: "musl".to_string(),
diff --git a/src/librustc_back/target/x86_64_unknown_netbsd.rs b/src/librustc_back/target/x86_64_unknown_netbsd.rs
index 4101fabe734..74bf7189119 100644
--- a/src/librustc_back/target/x86_64_unknown_netbsd.rs
+++ b/src/librustc_back/target/x86_64_unknown_netbsd.rs
@@ -18,6 +18,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-unknown-netbsd".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "netbsd".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_back/target/x86_64_unknown_openbsd.rs b/src/librustc_back/target/x86_64_unknown_openbsd.rs
index 8c995113c21..521de5373d0 100644
--- a/src/librustc_back/target/x86_64_unknown_openbsd.rs
+++ b/src/librustc_back/target/x86_64_unknown_openbsd.rs
@@ -19,6 +19,7 @@ pub fn target() -> Target {
         llvm_target: "x86_64-unknown-openbsd".to_string(),
         target_endian: "little".to_string(),
         target_pointer_width: "64".to_string(),
+        data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
         arch: "x86_64".to_string(),
         target_os: "openbsd".to_string(),
         target_env: "".to_string(),
diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs
index cb29f27b83f..9bbe0cb5f69 100644
--- a/src/librustc_trans/abi.rs
+++ b/src/librustc_trans/abi.rs
@@ -32,18 +32,7 @@ use rustc::ty::{self, Ty};
 use libc::c_uint;
 
 pub use syntax::abi::Abi;
-
-/// The first half of a fat pointer.
-/// - For a closure, this is the code address.
-/// - For an object or trait instance, this is the address of the box.
-/// - For a slice, this is the base address.
-pub const FAT_PTR_ADDR: usize = 0;
-
-/// The second half of a fat pointer.
-/// - For a closure, this is the address of the environment.
-/// - For an object or trait instance, this is the address of the vtable.
-/// - For a slice, this is the length.
-pub const FAT_PTR_EXTRA: usize = 1;
+pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
 
 #[derive(Clone, Copy, PartialEq, Debug)]
 enum ArgKind {
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index f084d30e0c0..8052fc21cc7 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -78,7 +78,6 @@ use declare;
 use expr;
 use glue;
 use inline;
-use intrinsic;
 use machine;
 use machine::{llalign_of_min, llsize_of, llsize_of_real};
 use meth;
@@ -2750,13 +2749,9 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>,
 
     {
         let ccx = shared_ccx.get_ccx(0);
-
-        // First, verify intrinsics.
-        intrinsic::check_intrinsics(&ccx);
-
         collect_translation_items(&ccx);
 
-        // Next, translate all items. See `TransModVisitor` for
+        // Translate all items. See `TransModVisitor` for
         // details on why we walk in this particular way.
         {
             let _icx = push_ctxt("text");
diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs
index c1802a5f0a9..1217b2b5a1b 100644
--- a/src/librustc_trans/context.rs
+++ b/src/librustc_trans/context.rs
@@ -36,11 +36,12 @@ use session::Session;
 use util::sha2::Sha256;
 use util::nodemap::{NodeMap, NodeSet, DefIdMap, FnvHashMap, FnvHashSet};
 
-use std::ffi::CString;
+use std::ffi::{CStr, CString};
 use std::cell::{Cell, RefCell};
 use std::marker::PhantomData;
 use std::ptr;
 use std::rc::Rc;
+use std::str;
 use syntax::ast;
 use syntax::parse::token::InternedString;
 
@@ -255,15 +256,28 @@ unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextR
     let mod_name = CString::new(mod_name).unwrap();
     let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx);
 
-    if let Some(ref custom_data_layout) = sess.target.target.options.data_layout {
-        let data_layout = CString::new(&custom_data_layout[..]).unwrap();
-        llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr());
-    } else {
+    // Ensure the data-layout values hardcoded remain the defaults.
+    if sess.target.target.options.is_builtin {
         let tm = ::back::write::create_target_machine(sess);
         llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm);
         llvm::LLVMRustDisposeTargetMachine(tm);
+
+        let data_layout = llvm::LLVMGetDataLayout(llmod);
+        let data_layout = str::from_utf8(CStr::from_ptr(data_layout).to_bytes())
+            .ok().expect("got a non-UTF8 data-layout from LLVM");
+
+        if sess.target.target.data_layout != data_layout {
+            bug!("data-layout for builtin `{}` target, `{}`, \
+                  differs from LLVM default, `{}`",
+                 sess.target.target.llvm_target,
+                 sess.target.target.data_layout,
+                 data_layout);
+        }
     }
 
+    let data_layout = CString::new(&sess.target.target.data_layout[..]).unwrap();
+    llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr());
+
     let llvm_target = sess.target.target.llvm_target.as_bytes();
     let llvm_target = CString::new(llvm_target).unwrap();
     llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr());
@@ -770,23 +784,8 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         &self.local.trait_cache
     }
 
-    /// Return exclusive upper bound on object size.
-    ///
-    /// The theoretical maximum object size is defined as the maximum positive `int` value. This
-    /// ensures that the `offset` semantics remain well-defined by allowing it to correctly index
-    /// every address within an object along with one byte past the end, along with allowing `int`
-    /// to store the difference between any two pointers into an object.
-    ///
-    /// The upper bound on 64-bit currently needs to be lower because LLVM uses a 64-bit integer to
-    /// represent object size in bits. It would need to be 1 << 61 to account for this, but is
-    /// currently conservatively bounded to 1 << 47 as that is enough to cover the current usable
-    /// address space on 64-bit ARMv8 and x86_64.
     pub fn obj_size_bound(&self) -> u64 {
-        match &self.sess().target.target.target_pointer_width[..] {
-            "32" => 1 << 31,
-            "64" => 1 << 47,
-            _ => bug!() // error handled by config::build_target_config
-        }
+        self.tcx().data_layout.obj_size_bound()
     }
 
     pub fn report_overbig_object(&self, obj: Ty<'tcx>) -> ! {
diff --git a/src/librustc_trans/diagnostics.rs b/src/librustc_trans/diagnostics.rs
index 5ae60d18240..5e4902cf3ca 100644
--- a/src/librustc_trans/diagnostics.rs
+++ b/src/librustc_trans/diagnostics.rs
@@ -83,32 +83,6 @@ unsafe { simd_add(i32x1(0), i32x1(1)); } // ok!
 ```
 "##,
 
-E0512: r##"
-Transmute with two differently sized types was attempted. Erroneous code
-example:
-
-```compile_fail
-fn takes_u8(_: u8) {}
-
-fn main() {
-    unsafe { takes_u8(::std::mem::transmute(0u16)); }
-    // error: transmute called with differently sized types
-}
-```
-
-Please use types with same size or use the expected type directly. Example:
-
-```
-fn takes_u8(_: u8) {}
-
-fn main() {
-    unsafe { takes_u8(::std::mem::transmute(0i8)); } // ok!
-    // or:
-    unsafe { takes_u8(0u8); } // ok!
-}
-```
-"##,
-
 E0515: r##"
 A constant index expression was out of bounds. Erroneous code example:
 
diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs
index d7932046243..1220fbafa29 100644
--- a/src/librustc_trans/intrinsic.rs
+++ b/src/librustc_trans/intrinsic.rs
@@ -36,16 +36,14 @@ use glue;
 use type_of;
 use machine;
 use type_::Type;
-use rustc::ty::{self, Ty, TypeFoldable};
+use rustc::ty::{self, Ty};
 use Disr;
 use rustc::ty::subst::Substs;
-use rustc::dep_graph::DepNode;
 use rustc::hir;
 use syntax::ast;
 use syntax::ptr::P;
 use syntax::parse::token;
 
-use rustc::lint;
 use rustc::session::Session;
 use syntax::codemap::{Span, DUMMY_SP};
 
@@ -97,76 +95,6 @@ fn get_simple_intrinsic(ccx: &CrateContext, name: &str) -> Option<ValueRef> {
     Some(ccx.get_intrinsic(&llvm_name))
 }
 
-pub fn span_transmute_size_error(a: &Session, b: Span, msg: &str) {
-    span_err!(a, b, E0512, "{}", msg);
-}
-
-/// Performs late verification that intrinsics are used correctly. At present,
-/// the only intrinsic that needs such verification is `transmute`.
-pub fn check_intrinsics(ccx: &CrateContext) {
-    let _task = ccx.tcx().dep_graph.in_task(DepNode::IntrinsicUseCheck);
-    let mut last_failing_id = None;
-    for transmute_restriction in ccx.tcx().transmute_restrictions.borrow().iter() {
-        // Sometimes, a single call to transmute will push multiple
-        // type pairs to test in order to exhaustively test the
-        // possibility around a type parameter. If one of those fails,
-        // there is no sense reporting errors on the others.
-        if last_failing_id == Some(transmute_restriction.id) {
-            continue;
-        }
-
-        debug!("transmute_restriction: {:?}", transmute_restriction);
-
-        assert!(!transmute_restriction.substituted_from.has_param_types());
-        assert!(!transmute_restriction.substituted_to.has_param_types());
-
-        let llfromtype = type_of::sizing_type_of(ccx,
-                                                 transmute_restriction.substituted_from);
-        let lltotype = type_of::sizing_type_of(ccx,
-                                               transmute_restriction.substituted_to);
-        let from_type_size = machine::llbitsize_of_real(ccx, llfromtype);
-        let to_type_size = machine::llbitsize_of_real(ccx, lltotype);
-
-        if let ty::TyFnDef(..) = transmute_restriction.substituted_from.sty {
-            if to_type_size == machine::llbitsize_of_real(ccx, ccx.int_type()) {
-                // FIXME #19925 Remove this warning after a release cycle.
-                lint::raw_emit_lint(&ccx.tcx().sess,
-                                    &ccx.tcx().sess.lint_store.borrow(),
-                                    lint::builtin::TRANSMUTE_FROM_FN_ITEM_TYPES,
-                                    (lint::Warn, lint::LintSource::Default),
-                                    Some(transmute_restriction.span),
-                                    &format!("`{}` is now zero-sized and has to be cast \
-                                              to a pointer before transmuting to `{}`",
-                                             transmute_restriction.substituted_from,
-                                             transmute_restriction.substituted_to));
-                continue;
-            }
-        }
-        if from_type_size != to_type_size {
-            last_failing_id = Some(transmute_restriction.id);
-
-            if transmute_restriction.original_from != transmute_restriction.substituted_from {
-                span_transmute_size_error(ccx.sess(), transmute_restriction.span,
-                    &format!("transmute called with differently sized types: \
-                              {} (could be {} bits) to {} (could be {} bits)",
-                             transmute_restriction.original_from,
-                             from_type_size,
-                             transmute_restriction.original_to,
-                             to_type_size));
-            } else {
-                span_transmute_size_error(ccx.sess(), transmute_restriction.span,
-                    &format!("transmute called with differently sized types: \
-                              {} ({} bits) to {} ({} bits)",
-                             transmute_restriction.original_from,
-                             from_type_size,
-                             transmute_restriction.original_to,
-                             to_type_size));
-            }
-        }
-    }
-    ccx.sess().abort_if_errors();
-}
-
 /// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs,
 /// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics,
 /// add them to librustc_trans/trans/context.rs
diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs
index 1f0a18ad4fa..863ae3f942e 100644
--- a/src/librustc_trans/type_of.rs
+++ b/src/librustc_trans/type_of.rs
@@ -17,6 +17,7 @@ use abi::FnType;
 use adt;
 use common::*;
 use machine;
+use rustc::traits::ProjectionMode;
 use rustc::ty::{self, Ty, TypeFoldable};
 
 use type_::Type;
@@ -121,6 +122,37 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
     debug!("--> mapped t={:?} to llsizingty={:?}", t, llsizingty);
 
     cx.llsizingtypes().borrow_mut().insert(t, llsizingty);
+
+    // FIXME(eddyb) Temporary sanity check for ty::layout.
+    let infcx = infer::normalizing_infer_ctxt(cx.tcx(), &cx.tcx().tables, ProjectionMode::Any);
+    match t.layout(&infcx) {
+        Ok(layout) => {
+            if !type_is_sized(cx.tcx(), t) {
+                if !layout.is_unsized() {
+                    bug!("layout should be unsized for type `{}` / {:#?}",
+                         t, layout);
+                }
+
+                // Unsized types get turned into a fat pointer for LLVM.
+                return llsizingty;
+            }
+            let r = layout.size(&cx.tcx().data_layout).bytes();
+            let l = machine::llsize_of_alloc(cx, llsizingty);
+            if r != l {
+                bug!("size differs (rustc: {}, llvm: {}) for type `{}` / {:#?}",
+                     r, l, t, layout);
+            }
+            let r = layout.align(&cx.tcx().data_layout).abi();
+            let l = machine::llalign_of_min(cx, llsizingty) as u64;
+            if r != l {
+                bug!("align differs (rustc: {}, llvm: {}) for type `{}` / {:#?}",
+                     r, l, t, layout);
+            }
+        }
+        Err(e) => {
+            bug!("failed to get layout for `{}`: {}", t, e);
+        }
+    }
     llsizingty
 }
 
diff --git a/src/test/compile-fail/issue-21174.rs b/src/test/compile-fail/issue-21174.rs
index 30fd2eb4d2f..c92a404b71a 100644
--- a/src/test/compile-fail/issue-21174.rs
+++ b/src/test/compile-fail/issue-21174.rs
@@ -15,7 +15,7 @@ trait Trait<'a> {
 
 fn foo<'a, T: Trait<'a>>(value: T::A) {
     let new: T::B = unsafe { std::mem::transmute(value) };
-//~^ ERROR: cannot transmute to or from a type that contains unsubstituted type parameters [E0139]
+//~^ ERROR: transmute called with differently sized types
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/issue-32377.rs b/src/test/compile-fail/issue-32377.rs
new file mode 100644
index 00000000000..6e8126348da
--- /dev/null
+++ b/src/test/compile-fail/issue-32377.rs
@@ -0,0 +1,27 @@
+// 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.
+
+use std::mem;
+use std::marker::PhantomData;
+
+trait Foo {
+    type Error;
+}
+
+struct Bar<U: Foo> {
+    stream: PhantomData<U::Error>,
+}
+
+fn foo<U: Foo>(x: [usize; 2]) -> Bar<U> {
+    unsafe { mem::transmute(x) }
+    //~^ ERROR transmute called with differently sized types
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/transmute-from-fn-item-types-error.rs b/src/test/compile-fail/transmute-from-fn-item-types-error.rs
new file mode 100644
index 00000000000..50bcd53ecb8
--- /dev/null
+++ b/src/test/compile-fail/transmute-from-fn-item-types-error.rs
@@ -0,0 +1,23 @@
+// 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.
+
+use std::mem;
+
+unsafe fn bar() {
+    // Error, still, if the resulting type is not pointer-sized.
+    mem::transmute::<_, u8>(main);
+    //~^ ERROR transmute called with differently sized types
+}
+
+fn main() {
+    unsafe {
+        bar();
+    }
+}
diff --git a/src/test/compile-fail/transmute-from-fn-item-types-lint.rs b/src/test/compile-fail/transmute-from-fn-item-types-lint.rs
index 3eae76f9492..42c3cb7f181 100644
--- a/src/test/compile-fail/transmute-from-fn-item-types-lint.rs
+++ b/src/test/compile-fail/transmute-from-fn-item-types-lint.rs
@@ -8,39 +8,37 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![deny(transmute_from_fn_item_types)]
+
 use std::mem;
 
 unsafe fn foo() -> (isize, *const (), Option<fn()>) {
     let i = mem::transmute(bar);
-    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
-    //~^^ WARN was previously accepted
+    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ ERROR was previously accepted
 
     let p = mem::transmute(foo);
-    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
-    //~^^ WARN was previously accepted
+    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ ERROR was previously accepted
 
     let of = mem::transmute(main);
-    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
-    //~^^ WARN was previously accepted
+    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ ERROR was previously accepted
 
     (i, p, of)
 }
 
 unsafe fn bar() {
     mem::transmute::<_, *mut ()>(foo);
-    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
-    //~^^ WARN was previously accepted
+    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ ERROR was previously accepted
 
     mem::transmute::<_, fn()>(bar);
-    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
-    //~^^ WARN was previously accepted
+    //~^ ERROR is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ ERROR was previously accepted
 
     // No error if a coercion would otherwise occur.
     mem::transmute::<fn(), usize>(main);
-
-    // Error, still, if the resulting type is not pointer-sized.
-    mem::transmute::<_, u8>(main);
-    //~^ ERROR transmute called with differently sized types
 }
 
 fn main() {
diff --git a/src/test/compile-fail/transmute-type-parameters.rs b/src/test/compile-fail/transmute-type-parameters.rs
index b06966bd867..b6e7e32663e 100644
--- a/src/test/compile-fail/transmute-type-parameters.rs
+++ b/src/test/compile-fail/transmute-type-parameters.rs
@@ -13,15 +13,18 @@
 use std::mem::transmute;
 
 unsafe fn f<T>(x: T) {
-    let _: isize = transmute(x);  //~ ERROR cannot transmute
+    let _: isize = transmute(x);
+//~^ ERROR differently sized types: T (size can vary) to isize
 }
 
 unsafe fn g<T>(x: (T, isize)) {
-    let _: isize = transmute(x);  //~ ERROR cannot transmute
+    let _: isize = transmute(x);
+//~^ ERROR differently sized types: (T, isize) (size can vary because of T) to isize
 }
 
 unsafe fn h<T>(x: [T; 10]) {
-    let _: isize = transmute(x);  //~ ERROR cannot transmute
+    let _: isize = transmute(x);
+//~^ ERROR differently sized types: [T; 10] (size can vary because of T) to isize
 }
 
 struct Bad<T> {
@@ -29,7 +32,8 @@ struct Bad<T> {
 }
 
 unsafe fn i<T>(x: Bad<T>) {
-    let _: isize = transmute(x);  //~ ERROR cannot transmute
+    let _: isize = transmute(x);
+//~^ ERROR differently sized types: Bad<T> (size can vary because of T) to isize
 }
 
 enum Worse<T> {
@@ -38,11 +42,13 @@ enum Worse<T> {
 }
 
 unsafe fn j<T>(x: Worse<T>) {
-    let _: isize = transmute(x);  //~ ERROR cannot transmute
+    let _: isize = transmute(x);
+//~^ ERROR differently sized types: Worse<T> (size can vary because of T) to isize
 }
 
 unsafe fn k<T>(x: Option<T>) {
-    let _: isize = transmute(x);  //~ ERROR cannot transmute
+    let _: isize = transmute(x);
+//~^ ERROR differently sized types: std::option::Option<T> (size can vary because of T) to isize
 }
 
 fn main() {}
diff --git a/src/test/run-make/target-specs/my-awesome-platform.json b/src/test/run-make/target-specs/my-awesome-platform.json
index e3080d29ec3..b7083c2776a 100644
--- a/src/test/run-make/target-specs/my-awesome-platform.json
+++ b/src/test/run-make/target-specs/my-awesome-platform.json
@@ -1,4 +1,5 @@
 {
+    "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128",
     "llvm-target": "i686-unknown-linux-gnu",
     "target-endian": "little",
     "target-pointer-width": "32",