about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard Burtescu <edy.burt@gmail.com>2016-04-19 17:03:30 +0300
committerAlex Crichton <alex@alexcrichton.com>2016-05-04 11:28:15 -0700
commit0ab80b651626666a6215fbeac23231f2fc04f97b (patch)
tree4eb14c5abf955b8fdcc16eabb5dc715515fb50f4
parentaacc89072a3d8c7f290641d69b3c3879995a119a (diff)
downloadrust-0ab80b651626666a6215fbeac23231f2fc04f97b.tar.gz
rust-0ab80b651626666a6215fbeac23231f2fc04f97b.zip
Check transmutes between types without statically known sizes.
-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.rs6
-rw-r--r--src/librustc/ty/layout.rs135
-rw-r--r--src/librustc/ty/mod.rs31
-rw-r--r--src/librustc_trans/base.rs7
-rw-r--r--src/librustc_trans/diagnostics.rs26
-rw-r--r--src/librustc_trans/intrinsic.rs74
-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
14 files changed, 341 insertions, 351 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 4abb1c8b98a..0ef130127a4 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 1f40d9cecde..8b07a97b1a1 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -357,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>>,
 
@@ -605,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(),
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index 494335933b6..3ea691b4dc7 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -1199,3 +1199,138 @@ impl Layout {
         }
     }
 }
+
+/// 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 0cf1be186d8..76e18565d65 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -472,37 +472,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_trans/base.rs b/src/librustc_trans/base.rs
index 17230eff6e6..104a74a63c9 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -77,7 +77,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;
@@ -2745,13 +2744,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/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 5924ae1ad84..0f9b04c04f6 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/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() {}