about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAriel Ben-Yehuda <arielb1@mail.tau.ac.il>2015-05-05 19:36:47 +0300
committerAriel Ben-Yehuda <ariel.byd@gmail.com>2015-05-19 17:42:14 +0300
commit83acebc46213e30eac3e9e71b213440104914f4c (patch)
tree340faa875f6734c4fb7d3def83a5e68b204134a4
parenta172f4022dd8d63607860733f0ae8647d4651090 (diff)
downloadrust-83acebc46213e30eac3e9e71b213440104914f4c.tar.gz
rust-83acebc46213e30eac3e9e71b213440104914f4c.zip
Overhaul cast semantics and make them follow RFC401
This should hopefully fix all cast-related ICEs once and for all.

I managed to make diagnostics hate me and give me spurious "decoder error"
 - removing $build/tmp/extended-errors seems to fix it.
-rw-r--r--src/librustc/diagnostics.rs1
-rw-r--r--src/librustc/lib.rs1
-rw-r--r--src/librustc/metadata/common.rs1
-rw-r--r--src/librustc/middle/astencode.rs25
-rw-r--r--src/librustc/middle/cast.rs71
-rw-r--r--src/librustc/middle/check_const.rs35
-rw-r--r--src/librustc/middle/const_eval.rs2
-rw-r--r--src/librustc/middle/ty.rs24
-rw-r--r--src/librustc_trans/trans/consts.rs70
-rw-r--r--src/librustc_trans/trans/expr.rs252
-rw-r--r--src/librustc_typeck/check/cast.rs453
-rw-r--r--src/librustc_typeck/check/mod.rs13
-rw-r--r--src/librustc_typeck/diagnostics.rs8
-rw-r--r--src/librustc_typeck/lib.rs2
-rw-r--r--src/test/compile-fail/fat-ptr-cast.rs16
-rw-r--r--src/test/compile-fail/issue-22289.rs2
16 files changed, 611 insertions, 365 deletions
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index e9f4860f451..f5cb1bd25d6 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -801,7 +801,6 @@ struct Foo<T: 'static> {
 
 register_diagnostics! {
     E0011,
-    E0012,
     E0014,
     E0016,
     E0017,
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 2936b6b1c2b..993d0dcf115 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -94,6 +94,7 @@ pub mod back {
 pub mod middle {
     pub mod astconv_util;
     pub mod astencode;
+    pub mod cast;
     pub mod cfg;
     pub mod check_const;
     pub mod check_static_recursion;
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index f410626714f..478c0f2f564 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -148,6 +148,7 @@ enum_from_u32! {
         tag_table_capture_modes = 0x67,
         tag_table_object_cast_map = 0x68,
         tag_table_const_qualif = 0x69,
+        tag_table_cast_kinds = 0x6a,
     }
 }
 
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 328972c54e3..e325c03d52d 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -23,6 +23,7 @@ use metadata::tydecode;
 use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter};
 use metadata::tydecode::{RegionParameter, ClosureSource};
 use metadata::tyencode;
+use middle::cast;
 use middle::check_const::ConstQualif;
 use middle::mem_categorization::Typer;
 use middle::privacy::{AllPublic, LastMod};
@@ -688,6 +689,10 @@ pub fn encode_closure_kind(ebml_w: &mut Encoder, kind: ty::ClosureKind) {
     kind.encode(ebml_w).unwrap();
 }
 
+pub fn encode_cast_kind(ebml_w: &mut Encoder, kind: cast::CastKind) {
+    kind.encode(ebml_w).unwrap();
+}
+
 pub trait vtable_decoder_helpers<'tcx> {
     fn read_vec_per_param_space<T, F>(&mut self, f: F) -> VecPerParamSpace<T> where
         F: FnMut(&mut Self) -> T;
@@ -1248,6 +1253,13 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
         })
     }
 
+    if let Some(cast_kind) = tcx.cast_kinds.borrow().get(&id) {
+        rbml_w.tag(c::tag_table_cast_kinds, |rbml_w| {
+            rbml_w.id(id);
+            encode_cast_kind(rbml_w, *cast_kind)
+        })
+    }
+
     for &qualif in tcx.const_qualif_map.borrow().get(&id).iter() {
         rbml_w.tag(c::tag_table_const_qualif, |rbml_w| {
             rbml_w.id(id);
@@ -1289,6 +1301,8 @@ trait rbml_decoder_decoder_helpers<'tcx> {
                            -> subst::Substs<'tcx>;
     fn read_auto_adjustment<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
                                     -> ty::AutoAdjustment<'tcx>;
+    fn read_cast_kind<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
+                                 -> cast::CastKind;
     fn read_closure_kind<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
                                  -> ty::ClosureKind;
     fn read_closure_ty<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
@@ -1641,6 +1655,12 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
         }).unwrap()
     }
 
+    fn read_cast_kind<'b, 'c>(&mut self, _dcx: &DecodeContext<'b, 'c, 'tcx>)
+                              -> cast::CastKind
+    {
+        Decodable::decode(self).unwrap()
+    }
+
     fn read_closure_kind<'b, 'c>(&mut self, _dcx: &DecodeContext<'b, 'c, 'tcx>)
                                  -> ty::ClosureKind
     {
@@ -1801,6 +1821,11 @@ fn decode_side_tables(dcx: &DecodeContext,
                         dcx.tcx.closure_kinds.borrow_mut().insert(ast_util::local_def(id),
                                                                   closure_kind);
                     }
+                    c::tag_table_cast_kinds => {
+                        let cast_kind =
+                            val_dsr.read_cast_kind(dcx);
+                        dcx.tcx.cast_kinds.borrow_mut().insert(id, cast_kind);
+                    }
                     c::tag_table_const_qualif => {
                         let qualif: ConstQualif = Decodable::decode(val_dsr).unwrap();
                         dcx.tcx.const_qualif_map.borrow_mut().insert(id, qualif);
diff --git a/src/librustc/middle/cast.rs b/src/librustc/middle/cast.rs
new file mode 100644
index 00000000000..4b876cfd80f
--- /dev/null
+++ b/src/librustc/middle/cast.rs
@@ -0,0 +1,71 @@
+// Copyright 2015 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.
+
+// Helpers for handling cast expressions, used in both
+// typeck and trans.
+
+use middle::ty::{self, Ty};
+
+use syntax::ast;
+
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum IntTy {
+    U(ast::UintTy),
+    I,
+    CEnum,
+    Bool,
+    Char
+}
+
+// Valid types for the result of a non-coercion cast
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum CastTy<'tcx> {
+    Int(IntTy),
+    Float,
+    FPtr,
+    Ptr(&'tcx ty::mt<'tcx>),
+    RPtr(&'tcx ty::mt<'tcx>),
+}
+
+/// Cast Kind. See RFC 401 (or librustc_typeck/check/cast.rs)
+#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
+pub enum CastKind {
+    CoercionCast,
+    PtrPtrCast,
+    PtrAddrCast,
+    AddrPtrCast,
+    NumericCast,
+    EnumCast,
+    PrimIntCast,
+    U8CharCast,
+    ArrayPtrCast,
+    FPtrPtrCast,
+    FPtrAddrCast
+}
+
+impl<'tcx> CastTy<'tcx> {
+    pub fn recognize(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>)
+                     -> Option<CastTy<'tcx>> {
+        match t.sty {
+            ty::ty_bool => Some(CastTy::Int(IntTy::Bool)),
+            ty::ty_char => Some(CastTy::Int(IntTy::Char)),
+            ty::ty_int(_) => Some(CastTy::Int(IntTy::I)),
+            ty::ty_uint(u) => Some(CastTy::Int(IntTy::U(u))),
+            ty::ty_float(_) => Some(CastTy::Float),
+            ty::ty_enum(..) if ty::type_is_c_like_enum(
+                tcx, t) => Some(CastTy::Int(IntTy::CEnum)),
+            ty::ty_ptr(ref mt) => Some(CastTy::Ptr(mt)),
+            ty::ty_rptr(_, ref mt) => Some(CastTy::RPtr(mt)),
+            ty::ty_bare_fn(..) => Some(CastTy::FPtr),
+            _ => None,
+        }
+    }
+}
diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs
index 8bb83c54da8..6d2465a5cb9 100644
--- a/src/librustc/middle/check_const.rs
+++ b/src/librustc/middle/check_const.rs
@@ -24,6 +24,7 @@
 // - It's not possible to take the address of a static item with unsafe interior. This is enforced
 // by borrowck::gather_loans
 
+use middle::cast::{CastKind};
 use middle::const_eval;
 use middle::def;
 use middle::expr_use_visitor as euv;
@@ -32,11 +33,10 @@ use middle::mem_categorization as mc;
 use middle::traits;
 use middle::ty::{self, Ty};
 use util::nodemap::NodeMap;
-use util::ppaux;
+use util::ppaux::Repr;
 
 use syntax::ast;
 use syntax::codemap::Span;
-use syntax::print::pprust;
 use syntax::visit::{self, Visitor};
 
 use std::collections::hash_map::Entry;
@@ -197,7 +197,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
 
 impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
     fn visit_item(&mut self, i: &ast::Item) {
-        debug!("visit_item(item={})", pprust::item_to_string(i));
+        debug!("visit_item(item={})", i.repr(self.tcx));
         match i.node {
             ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
                 self.check_static_type(&**expr);
@@ -440,26 +440,17 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
             }
         }
         ast::ExprCast(ref from, _) => {
-            let toty = ty::expr_ty(v.tcx, e);
-            let fromty = ty::expr_ty(v.tcx, &**from);
-            let is_legal_cast =
-                ty::type_is_numeric(toty) ||
-                ty::type_is_unsafe_ptr(toty) ||
-                (ty::type_is_bare_fn(toty) && ty::type_is_bare_fn_item(fromty));
-            if !is_legal_cast {
-                v.add_qualif(ConstQualif::NOT_CONST);
-                if v.mode != Mode::Var {
-                    span_err!(v.tcx.sess, e.span, E0012,
-                              "can not cast to `{}` in {}s",
-                              ppaux::ty_to_string(v.tcx, toty), v.msg());
-                }
-            }
-            if ty::type_is_unsafe_ptr(fromty) && ty::type_is_numeric(toty) {
-                v.add_qualif(ConstQualif::NOT_CONST);
-                if v.mode != Mode::Var {
-                    span_err!(v.tcx.sess, e.span, E0018,
-                              "can not cast a pointer to an integer in {}s", v.msg());
+            debug!("Checking const cast(id={})", from.id);
+            match v.tcx.cast_kinds.borrow().get(&from.id) {
+                None => v.tcx.sess.span_bug(e.span, "no kind for cast"),
+                Some(&CastKind::PtrAddrCast) | Some(&CastKind::FPtrAddrCast) => {
+                    v.add_qualif(ConstQualif::NOT_CONST);
+                    if v.mode != Mode::Var {
+                        span_err!(v.tcx.sess, e.span, E0018,
+                                  "can not cast a pointer to an integer in {}s", v.msg());
+                    }
                 }
+                _ => {}
             }
         }
         ast::ExprPath(..) => {
diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs
index 72415f54336..03de553e648 100644
--- a/src/librustc/middle/const_eval.rs
+++ b/src/librustc/middle/const_eval.rs
@@ -1002,7 +1002,7 @@ fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: const_val, ty: Ty) -> CastResult
     macro_rules! convert_val {
         ($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => {
             match val {
-                const_bool(b) => Ok($const_type(b as $intermediate_ty as $target_ty)),
+                const_bool(b) => Ok($const_type(b as u64 as $intermediate_ty as $target_ty)),
                 const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
                 const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
                 const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index e988423ac57..15e1ac2f2c6 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -41,6 +41,7 @@ use session::Session;
 use lint;
 use metadata::csearch;
 use middle;
+use middle::cast;
 use middle::check_const;
 use middle::const_eval;
 use middle::def::{self, DefMap, ExportMap};
@@ -288,15 +289,6 @@ pub struct field_ty {
     pub origin: ast::DefId,  // The DefId of the struct in which the field is declared.
 }
 
-// Contains information needed to resolve types and (in the future) look up
-// the types of AST nodes.
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct creader_cache_key {
-    pub cnum: CrateNum,
-    pub pos: usize,
-    pub len: usize
-}
-
 #[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
 pub struct ItemVariances {
     pub types: VecPerParamSpace<Variance>,
@@ -562,6 +554,15 @@ pub enum vtable_origin<'tcx> {
 // expr to the associated trait ref.
 pub type ObjectCastMap<'tcx> = RefCell<NodeMap<ty::PolyTraitRef<'tcx>>>;
 
+// Contains information needed to resolve types and (in the future) look up
+// the types of AST nodes.
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct creader_cache_key {
+    pub cnum: CrateNum,
+    pub pos: usize,
+    pub len: 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`
@@ -827,6 +828,10 @@ pub struct ctxt<'tcx> {
 
     /// Caches CoerceUnsized kinds for impls on custom types.
     pub custom_coerce_unsized_kinds: RefCell<DefIdMap<CustomCoerceUnsized>>,
+
+    /// Maps a cast expression to its kind. This is keyed on the
+    /// *from* expression of the cast, not the cast itself.
+    pub cast_kinds: RefCell<NodeMap<cast::CastKind>>,
 }
 
 impl<'tcx> ctxt<'tcx> {
@@ -2817,6 +2822,7 @@ pub fn mk_ctxt<'tcx>(s: Session,
         type_impls_sized_cache: RefCell::new(HashMap::new()),
         const_qualif_map: RefCell::new(NodeMap()),
         custom_coerce_unsized_kinds: RefCell::new(DefIdMap()),
+        cast_kinds: RefCell::new(NodeMap()),
    }
 }
 
diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs
index 503bdf8dadb..29430ad10e6 100644
--- a/src/librustc_trans/trans/consts.rs
+++ b/src/librustc_trans/trans/consts.rs
@@ -29,6 +29,7 @@ use trans::declare;
 use trans::monomorphize;
 use trans::type_::Type;
 use trans::type_of;
+use middle::cast::{CastTy,IntTy};
 use middle::subst::Substs;
 use middle::ty::{self, Ty};
 use util::ppaux::{Repr, ty_to_string};
@@ -616,53 +617,62 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
               }
           }
           ast::ExprCast(ref base, _) => {
-            let llty = type_of::type_of(cx, ety);
-            let (v, basety) = const_expr(cx, &**base, param_substs);
-            if expr::cast_is_noop(basety, ety) {
+            let t_1 = ety;
+            let llty = type_of::type_of(cx, t_1);
+            let (v, t_e) = const_expr(cx, &**base, param_substs);
+            debug!("trans_const_cast({} as {})", t_e.repr(cx.tcx()), t_1.repr(cx.tcx()));
+            if expr::cast_is_noop(cx.tcx(), base, t_e, t_1) {
                 return v;
             }
-            match (expr::cast_type_kind(cx.tcx(), basety),
-                   expr::cast_type_kind(cx.tcx(), ety)) {
-
-              (expr::cast_integral, expr::cast_integral) => {
-                let s = ty::type_is_signed(basety) as Bool;
+            if type_is_fat_ptr(cx.tcx(), t_e) {
+                // Fat pointer casts.
+                let t_1_inner = ty::deref(t_1, true).expect("cast to non-pointer").ty;
+                let ptr_ty = type_of::in_memory_type_of(cx, t_1_inner).ptr_to();
+                let addr = ptrcast(const_get_elt(cx, v, &[abi::FAT_PTR_ADDR as u32]),
+                                   ptr_ty);
+                if type_is_fat_ptr(cx.tcx(), t_1) {
+                    let info = const_get_elt(cx, v, &[abi::FAT_PTR_EXTRA as u32]);
+                    return C_struct(cx, &[addr, info], false)
+                } else {
+                    return addr;
+                }
+            }
+            match (CastTy::recognize(cx.tcx(), t_e).expect("bad input type for cast"),
+                   CastTy::recognize(cx.tcx(), t_1).expect("bad output type for cast")) {
+              (CastTy::Int(IntTy::CEnum), CastTy::Int(_)) => {
+                let repr = adt::represent_type(cx, t_e);
+                let discr = adt::const_get_discrim(cx, &*repr, v);
+                let iv = C_integral(cx.int_type(), discr, false);
+                let s = adt::is_discr_signed(&*repr) as Bool;
+                llvm::LLVMConstIntCast(iv, llty.to_ref(), s)
+              }
+              (CastTy::Int(_), CastTy::Int(_)) => {
+                let s = ty::type_is_signed(t_e) as Bool;
                 llvm::LLVMConstIntCast(v, llty.to_ref(), s)
               }
-              (expr::cast_integral, expr::cast_float) => {
-                if ty::type_is_signed(basety) {
+              (CastTy::Int(_), CastTy::Float) => {
+                if ty::type_is_signed(t_e) {
                     llvm::LLVMConstSIToFP(v, llty.to_ref())
                 } else {
                     llvm::LLVMConstUIToFP(v, llty.to_ref())
                 }
               }
-              (expr::cast_float, expr::cast_float) => {
+              (CastTy::Float, CastTy::Float) => {
                 llvm::LLVMConstFPCast(v, llty.to_ref())
               }
-              (expr::cast_float, expr::cast_integral) => {
-                if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty.to_ref()) }
+              (CastTy::Float, CastTy::Int(_)) => {
+                if ty::type_is_signed(t_1) { llvm::LLVMConstFPToSI(v, llty.to_ref()) }
                 else { llvm::LLVMConstFPToUI(v, llty.to_ref()) }
               }
-              (expr::cast_enum, expr::cast_integral) => {
-                let repr = adt::represent_type(cx, basety);
-                let discr = adt::const_get_discrim(cx, &*repr, v);
-                let iv = C_integral(cx.int_type(), discr, false);
-                let ety_cast = expr::cast_type_kind(cx.tcx(), ety);
-                match ety_cast {
-                    expr::cast_integral => {
-                        let s = ty::type_is_signed(ety) as Bool;
-                        llvm::LLVMConstIntCast(iv, llty.to_ref(), s)
-                    }
-                    _ => cx.sess().bug("enum cast destination is not \
-                                        integral")
-                }
-              }
-              (expr::cast_pointer, expr::cast_pointer) => {
+              (CastTy::Ptr(_), CastTy::Ptr(_)) | (CastTy::FPtr, CastTy::Ptr(_))
+                    | (CastTy::RPtr(_), CastTy::Ptr(_)) => {
                 ptrcast(v, llty)
               }
-              (expr::cast_integral, expr::cast_pointer) => {
+              (CastTy::FPtr, CastTy::FPtr) => ptrcast(v, llty), // isn't this a coercion?
+              (CastTy::Int(_), CastTy::Ptr(_)) => {
                 llvm::LLVMConstIntToPtr(v, llty.to_ref())
               }
-              (expr::cast_pointer, expr::cast_integral) => {
+              (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FPtr, CastTy::Int(_)) => {
                 llvm::LLVMConstPtrToInt(v, llty.to_ref())
               }
               _ => {
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 270aacfe143..a65d4225a1f 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -48,7 +48,6 @@
 
 #![allow(non_camel_case_types)]
 
-pub use self::cast_kind::*;
 pub use self::Dest::*;
 use self::lazy_binop_ty::*;
 
@@ -73,6 +72,7 @@ use trans::meth;
 use trans::monomorphize;
 use trans::tvec;
 use trans::type_of;
+use middle::cast::{CastKind, CastTy};
 use middle::ty::{struct_fields, tup_fields};
 use middle::ty::{AdjustDerefRef, AdjustReifyFnPointer, AdjustUnsafeFnPointer};
 use middle::ty::{self, Ty};
@@ -1981,177 +1981,143 @@ fn trans_overloaded_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     bcx
 }
 
-fn int_cast(bcx: Block,
-            lldsttype: Type,
-            llsrctype: Type,
-            llsrc: ValueRef,
-            signed: bool)
-            -> ValueRef {
-    let _icx = push_ctxt("int_cast");
-    let srcsz = llsrctype.int_width();
-    let dstsz = lldsttype.int_width();
-    return if dstsz == srcsz {
-        BitCast(bcx, llsrc, lldsttype)
-    } else if srcsz > dstsz {
-        TruncOrBitCast(bcx, llsrc, lldsttype)
-    } else if signed {
-        SExtOrBitCast(bcx, llsrc, lldsttype)
-    } else {
-        ZExtOrBitCast(bcx, llsrc, lldsttype)
-    }
-}
-
-fn float_cast(bcx: Block,
-              lldsttype: Type,
-              llsrctype: Type,
-              llsrc: ValueRef)
-              -> ValueRef {
-    let _icx = push_ctxt("float_cast");
-    let srcsz = llsrctype.float_width();
-    let dstsz = lldsttype.float_width();
-    return if dstsz > srcsz {
-        FPExt(bcx, llsrc, lldsttype)
-    } else if srcsz > dstsz {
-        FPTrunc(bcx, llsrc, lldsttype)
-    } else { llsrc };
-}
-
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum cast_kind {
-    cast_pointer,
-    cast_fat_ptr,
-    cast_integral,
-    cast_float,
-    cast_enum,
-    cast_other,
-}
-
-pub fn cast_type_kind<'tcx>(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>) -> cast_kind {
-    match t.sty {
-        ty::ty_char        => cast_integral,
-        ty::ty_float(..)   => cast_float,
-        ty::ty_rptr(_, mt) | ty::ty_ptr(mt) => {
-            if type_is_sized(tcx, mt.ty) {
-                cast_pointer
-            } else {
-                cast_fat_ptr
-            }
-        }
-        ty::ty_bare_fn(..) => cast_pointer,
-        ty::ty_int(..)     => cast_integral,
-        ty::ty_uint(..)    => cast_integral,
-        ty::ty_bool        => cast_integral,
-        ty::ty_enum(..)    => cast_enum,
-        _                  => cast_other
+pub fn cast_is_noop<'tcx>(tcx: &ty::ctxt<'tcx>,
+                          expr: &ast::Expr,
+                          t_in: Ty<'tcx>,
+                          t_out: Ty<'tcx>)
+                          -> bool {
+    if let Some(&CastKind::CoercionCast) = tcx.cast_kinds.borrow().get(&expr.id) {
+        return true;
     }
-}
 
-pub fn cast_is_noop<'tcx>(t_in: Ty<'tcx>, t_out: Ty<'tcx>) -> bool {
     match (ty::deref(t_in, true), ty::deref(t_out, true)) {
         (Some(ty::mt{ ty: t_in, .. }), Some(ty::mt{ ty: t_out, .. })) => {
             t_in == t_out
         }
-        _ => false
+        _ => {
+            // This condition isn't redundant with the check for CoercionCast:
+            // different types can be substituted into the same type, and
+            // == equality can be overconservative if there are regions.
+            t_in == t_out
+        }
     }
 }
 
 fn trans_imm_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                               expr: &ast::Expr,
                               id: ast::NodeId)
-                              -> DatumBlock<'blk, 'tcx, Expr> {
+                              -> DatumBlock<'blk, 'tcx, Expr>
+{
+    use middle::cast::CastTy::*;
+    use middle::cast::IntTy::*;
+
+    fn int_cast(bcx: Block,
+                lldsttype: Type,
+                llsrctype: Type,
+                llsrc: ValueRef,
+                signed: bool)
+                -> ValueRef
+    {
+        let _icx = push_ctxt("int_cast");
+        let srcsz = llsrctype.int_width();
+        let dstsz = lldsttype.int_width();
+        return if dstsz == srcsz {
+            BitCast(bcx, llsrc, lldsttype)
+        } else if srcsz > dstsz {
+            TruncOrBitCast(bcx, llsrc, lldsttype)
+        } else if signed {
+            SExtOrBitCast(bcx, llsrc, lldsttype)
+        } else {
+            ZExtOrBitCast(bcx, llsrc, lldsttype)
+        }
+    }
+
+    fn float_cast(bcx: Block,
+                  lldsttype: Type,
+                  llsrctype: Type,
+                  llsrc: ValueRef)
+                  -> ValueRef
+    {
+        let _icx = push_ctxt("float_cast");
+        let srcsz = llsrctype.float_width();
+        let dstsz = lldsttype.float_width();
+        return if dstsz > srcsz {
+            FPExt(bcx, llsrc, lldsttype)
+        } else if srcsz > dstsz {
+            FPTrunc(bcx, llsrc, lldsttype)
+        } else { llsrc };
+    }
+
     let _icx = push_ctxt("trans_cast");
     let mut bcx = bcx;
     let ccx = bcx.ccx();
 
     let t_in = expr_ty_adjusted(bcx, expr);
     let t_out = node_id_type(bcx, id);
-    let k_in = cast_type_kind(bcx.tcx(), t_in);
-    let k_out = cast_type_kind(bcx.tcx(), t_out);
-    let s_in = k_in == cast_integral && ty::type_is_signed(t_in);
-    let ll_t_in = type_of::arg_type_of(ccx, t_in);
-    let ll_t_out = type_of::arg_type_of(ccx, t_out);
 
+    debug!("trans_cast({} as {})", t_in.repr(bcx.tcx()), t_out.repr(bcx.tcx()));
+    let mut ll_t_in = type_of::arg_type_of(ccx, t_in);
+    let ll_t_out = type_of::arg_type_of(ccx, t_out);
     // Convert the value to be cast into a ValueRef, either by-ref or
     // by-value as appropriate given its type:
     let mut datum = unpack_datum!(bcx, trans(bcx, expr));
 
     let datum_ty = monomorphize_type(bcx, datum.ty);
-    if cast_is_noop(datum_ty, t_out) {
+
+    if cast_is_noop(bcx.tcx(), expr, datum_ty, t_out) {
         datum.ty = t_out;
         return DatumBlock::new(bcx, datum);
     }
 
-    let newval = match (k_in, k_out) {
-        (cast_integral, cast_integral) => {
-            let llexpr = datum.to_llscalarish(bcx);
-            int_cast(bcx, ll_t_out, ll_t_in, llexpr, s_in)
-        }
-        (cast_float, cast_float) => {
-            let llexpr = datum.to_llscalarish(bcx);
-            float_cast(bcx, ll_t_out, ll_t_in, llexpr)
-        }
-        (cast_integral, cast_float) => {
-            let llexpr = datum.to_llscalarish(bcx);
-            if s_in {
-                SIToFP(bcx, llexpr, ll_t_out)
-            } else { UIToFP(bcx, llexpr, ll_t_out) }
-        }
-        (cast_float, cast_integral) => {
-            let llexpr = datum.to_llscalarish(bcx);
-            if ty::type_is_signed(t_out) {
-                FPToSI(bcx, llexpr, ll_t_out)
-            } else { FPToUI(bcx, llexpr, ll_t_out) }
-        }
-        (cast_integral, cast_pointer) => {
-            let llexpr = datum.to_llscalarish(bcx);
-            IntToPtr(bcx, llexpr, ll_t_out)
-        }
-        (cast_pointer, cast_integral) => {
-            let llexpr = datum.to_llscalarish(bcx);
-            PtrToInt(bcx, llexpr, ll_t_out)
-        }
-        (cast_fat_ptr, cast_integral) => {
-            let data_ptr = Load(bcx, get_dataptr(bcx, datum.val));
-            PtrToInt(bcx, data_ptr, ll_t_out)
-        }
-        (cast_pointer, cast_pointer) => {
-            let llexpr = datum.to_llscalarish(bcx);
-            PointerCast(bcx, llexpr, ll_t_out)
-        }
-        (cast_fat_ptr, cast_pointer) => {
-            let data_ptr = Load(bcx, get_dataptr(bcx, datum.val));
-            PointerCast(bcx, data_ptr, ll_t_out)
-        }
-        (cast_enum, cast_integral) |
-        (cast_enum, cast_float) => {
-            let mut bcx = bcx;
-            let repr = adt::represent_type(ccx, t_in);
-            let datum = unpack_datum!(
-                bcx, datum.to_lvalue_datum(bcx, "trans_imm_cast", expr.id));
-            let llexpr_ptr = datum.to_llref();
-            let lldiscrim_a =
-                adt::trans_get_discr(bcx, &*repr, llexpr_ptr, Some(Type::i64(ccx)));
-            match k_out {
-                cast_integral => int_cast(bcx, ll_t_out,
-                                          val_ty(lldiscrim_a),
-                                          lldiscrim_a, true),
-                cast_float => SIToFP(bcx, lldiscrim_a, ll_t_out),
-                _ => {
-                    ccx.sess().bug(&format!("translating unsupported cast: \
-                                            {} ({:?}) -> {} ({:?})",
-                                            t_in.repr(bcx.tcx()),
-                                            k_in,
-                                            t_out.repr(bcx.tcx()),
-                                            k_out))
-                }
-            }
+    if type_is_fat_ptr(bcx.tcx(), t_in) {
+        assert!(datum.kind.is_by_ref());
+        if type_is_fat_ptr(bcx.tcx(), t_out) {
+            return DatumBlock::new(bcx, Datum::new(
+                PointerCast(bcx, datum.val, ll_t_out.ptr_to()),
+                t_out,
+                Rvalue::new(ByRef)
+            )).to_expr_datumblock();
+        } else {
+            // Return the address
+            return immediate_rvalue_bcx(bcx,
+                                        Load(bcx, get_dataptr(bcx, datum.val)),
+                                        t_out).to_expr_datumblock();
         }
-        _ => ccx.sess().bug(&format!("translating unsupported cast: \
-                                    {} ({:?}) -> {} ({:?})",
-                                    t_in.repr(bcx.tcx()),
-                                    k_in,
-                                    t_out.repr(bcx.tcx()),
-                                    k_out))
+    }
+
+    let r_t_in = CastTy::recognize(bcx.tcx(), t_in).expect("bad input type for cast");
+    let r_t_out = CastTy::recognize(bcx.tcx(), t_out).expect("bad output type for cast");
+
+    let (llexpr, signed) = if let Int(CEnum) = r_t_in {
+        let repr = adt::represent_type(ccx, t_in);
+        let datum = unpack_datum!(
+            bcx, datum.to_lvalue_datum(bcx, "trans_imm_cast", expr.id));
+        let llexpr_ptr = datum.to_llref();
+        let discr = adt::trans_get_discr(bcx, &*repr, llexpr_ptr, Some(Type::i64(ccx)));
+        ll_t_in = val_ty(discr);
+        (discr, adt::is_discr_signed(&*repr))
+    } else {
+        (datum.to_llscalarish(bcx), ty::type_is_signed(t_in))
+    };
+
+    let newval = match (r_t_in, r_t_out) {
+        (Ptr(_), Ptr(_)) | (FPtr, Ptr(_)) | (RPtr(_), Ptr(_)) => PointerCast(bcx, llexpr, ll_t_out),
+        (Ptr(_), Int(_)) | (FPtr, Int(_)) => PtrToInt(bcx, llexpr, ll_t_out),
+        (Int(_), Ptr(_)) => IntToPtr(bcx, llexpr, ll_t_out),
+
+        (Int(_), Int(_)) => int_cast(bcx, ll_t_out, ll_t_in, llexpr, signed),
+        (Float, Float) => float_cast(bcx, ll_t_out, ll_t_in, llexpr),
+        (Int(_), Float) if signed => SIToFP(bcx, llexpr, ll_t_out),
+        (Int(_), Float) => UIToFP(bcx, llexpr, ll_t_out),
+        (Float, Int(I)) => FPToSI(bcx, llexpr, ll_t_out),
+        (Float, Int(_)) => FPToUI(bcx, llexpr, ll_t_out),
+
+        _ => ccx.sess().span_bug(expr.span,
+                                  &format!("translating unsupported cast: \
+                                            {} -> {}",
+                                           t_in.repr(bcx.tcx()),
+                                           t_out.repr(bcx.tcx()))
+                                 )
     };
     return immediate_rvalue_bcx(bcx, newval, t_out).to_expr_datumblock();
 }
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index bc6159c0cff..6ae6d21f987 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -9,6 +9,29 @@
 // except according to those terms.
 
 //! Code for type-checking cast expressions.
+//!
+//! A cast `e as U` is valid if one of the following holds:
+//! * `e` has type `T` and `T` coerces to `U`; *coercion-cast*
+//! * `e` has type `*T`, `U` is `*U_0`, and either `U_0: Sized` or
+//!    unsize_kind(`T`) = unsize_kind(`U_0`); *ptr-ptr-cast*
+//! * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast*
+//! * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast*
+//! * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast*
+//! * `e` is a C-like enum and `U` is an integer type; *enum-cast*
+//! * `e` has type `bool` or `char` and `U` is an integer; *prim-int-cast*
+//! * `e` has type `u8` and `U` is `char`; *u8-char-cast*
+//! * `e` has type `&[T; n]` and `U` is `*const T`; *array-ptr-cast*
+//! * `e` is a function pointer type and `U` has type `*T`,
+//!   while `T: Sized`; *fptr-ptr-cast*
+//! * `e` is a function pointer type and `U` is an integer; *fptr-addr-cast*
+//!
+//! where `&.T` and `*T` are references of either mutability,
+//! and where unsize_kind(`T`) is the kind of the unsize info
+//! in `T` - a vtable or a length (or `()` if `T: Sized`).
+//!
+//! Casting is not transitive, that is, even if `e as U1 as U2` is a valid
+//! expression, `e as U2` is not necessarily so (in fact it will only be valid if
+//! `U1` coerces to `U2`).
 
 use super::coercion;
 use super::demand;
@@ -16,11 +39,13 @@ use super::FnCtxt;
 use super::structurally_resolved_type;
 
 use lint;
-use middle::infer;
+use middle::cast::{CastKind, CastTy};
 use middle::ty;
 use middle::ty::Ty;
 use syntax::ast;
+use syntax::ast::UintTy::{TyU8};
 use syntax::codemap::Span;
+use util::ppaux::Repr;
 
 /// Reifies a cast check to be checked once we have full type information for
 /// a function context.
@@ -31,6 +56,46 @@ pub struct CastCheck<'tcx> {
     span: Span,
 }
 
+#[derive(Copy, Clone, PartialEq, Eq)]
+enum UnsizeKind<'tcx> {
+    Vtable,
+    Length,
+    OfTy(Ty<'tcx>)
+}
+
+/// Returns the kind of unsize information of t, or None
+/// if t is sized or it is unknown.
+fn unsize_kind<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                        t: Ty<'tcx>)
+                        -> Option<UnsizeKind<'tcx>> {
+    match t.sty {
+        ty::ty_vec(_, None) | ty::ty_str => Some(UnsizeKind::Length),
+        ty::ty_trait(_) => Some(UnsizeKind::Vtable),
+        ty::ty_struct(did, substs) => {
+            match ty::struct_fields(fcx.tcx(), did, substs).pop() {
+                None => None,
+                Some(f) => unsize_kind(fcx, f.mt.ty)
+            }
+        }
+        ty::ty_projection(..) | ty::ty_param(..) =>
+            Some(UnsizeKind::OfTy(t)),
+        _ => None
+    }
+}
+
+#[derive(Copy, Clone)]
+enum CastError {
+    CastToBool,
+    CastToChar,
+    DifferingKinds,
+    IllegalCast,
+    NeedViaPtr,
+    NeedViaInt,
+    NeedViaUsize,
+    NonScalar,
+    RefToMutPtr
+}
+
 impl<'tcx> CastCheck<'tcx> {
     pub fn new(expr: ast::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, span: Span)
                -> CastCheck<'tcx> {
@@ -41,155 +106,271 @@ impl<'tcx> CastCheck<'tcx> {
             span: span,
         }
     }
-}
 
-pub fn check_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, cast: &CastCheck<'tcx>) {
-    fn cast_through_integer_err<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                          span: Span,
-                                          t_1: Ty<'tcx>,
-                                          t_e: Ty<'tcx>) {
-        fcx.type_error_message(span, |actual| {
-            format!("illegal cast; cast through an \
-                    integer first: `{}` as `{}`",
-                    actual,
-                    fcx.infcx().ty_to_string(t_1))
-        }, t_e, None);
+    fn report_cast_error<'a>(&self, fcx: &FnCtxt<'a, 'tcx>,
+                             e: CastError) {
+        match e {
+            CastError::NeedViaPtr |
+            CastError::NeedViaInt |
+            CastError::NeedViaUsize => {
+                fcx.type_error_message(self.span, |actual| {
+                    format!("illegal cast; cast through {} first: `{}` as `{}`",
+                            match e {
+                                CastError::NeedViaPtr => "a raw pointer",
+                                CastError::NeedViaInt => "an integer",
+                                CastError::NeedViaUsize => "a usize",
+                                _ => unreachable!()
+                            },
+                            actual,
+                            fcx.infcx().ty_to_string(self.cast_ty))
+                }, self.expr_ty, None)
+            }
+            CastError::CastToBool => {
+                span_err!(fcx.tcx().sess, self.span, E0054,
+                          "cannot cast as `bool`, compare with zero instead");
+            }
+            CastError::CastToChar => {
+                fcx.type_error_message(self.span, |actual| {
+                    format!("only `u8` can be cast as `char`, not `{}`", actual)
+                }, self.expr_ty, None);
+            }
+            CastError::NonScalar => {
+                fcx.type_error_message(self.span, |actual| {
+                    format!("non-scalar cast: `{}` as `{}`",
+                            actual,
+                            fcx.infcx().ty_to_string(self.cast_ty))
+                }, self.expr_ty, None);
+            }
+            CastError::IllegalCast => {
+                fcx.type_error_message(self.span, |actual| {
+                    format!("illegal cast: `{}` as `{}`",
+                            actual,
+                            fcx.infcx().ty_to_string(self.cast_ty))
+                }, self.expr_ty, None);
+            }
+            CastError::DifferingKinds => {
+                fcx.type_error_message(self.span, |actual| {
+                    format!("illegal cast: `{}` as `{}`; vtable kinds may not match",
+                            actual,
+                            fcx.infcx().ty_to_string(self.cast_ty))
+                }, self.expr_ty, None);
+            }
+            CastError::RefToMutPtr => {
+                span_err!(fcx.tcx().sess, self.span, E0188,
+                          "cannot cast an immutable reference to a \
+                           mutable pointer");
+            }
+        }
+    }
+
+    fn trivial_cast_lint<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) {
+        let t_1 = self.cast_ty;
+        let t_e = self.expr_ty;
+        if ty::type_is_numeric(t_1) && ty::type_is_numeric(t_e) {
+            fcx.tcx().sess.add_lint(lint::builtin::TRIVIAL_NUMERIC_CASTS,
+                                    self.expr.id,
+                                    self.span,
+                                    format!("trivial numeric cast: `{}` as `{}`. Cast can be \
+                                             replaced by coercion, this might require type \
+                                             ascription or a temporary variable",
+                                            fcx.infcx().ty_to_string(t_e),
+                                            fcx.infcx().ty_to_string(t_1)));
+        } else {
+            fcx.tcx().sess.add_lint(lint::builtin::TRIVIAL_CASTS,
+                                    self.expr.id,
+                                    self.span,
+                                    format!("trivial cast: `{}` as `{}`. Cast can be \
+                                             replaced by coercion, this might require type \
+                                             ascription or a temporary variable",
+                                            fcx.infcx().ty_to_string(t_e),
+                                            fcx.infcx().ty_to_string(t_1)));
+        }
+
     }
 
-    let span = cast.span;
-    let e = &cast.expr;
-    let t_e = structurally_resolved_type(fcx, span, cast.expr_ty);
-    let t_1 = structurally_resolved_type(fcx, span, cast.cast_ty);
-    let tcx = fcx.tcx();
-
-    // Check for trivial casts.
-    if !ty::type_has_ty_infer(t_1) {
-        if let Ok(()) = coercion::mk_assignty(fcx, e, t_e, t_1) {
-            if ty::type_is_numeric(t_1) && ty::type_is_numeric(t_e) {
-                tcx.sess.add_lint(lint::builtin::TRIVIAL_NUMERIC_CASTS,
-                                  e.id,
-                                  span,
-                                  format!("trivial numeric cast: `{}` as `{}`. Cast can be \
-                                           replaced by coercion, this might require type \
-                                           ascription or a temporary variable",
-                                          fcx.infcx().ty_to_string(t_e),
-                                          fcx.infcx().ty_to_string(t_1)));
-            } else {
-                tcx.sess.add_lint(lint::builtin::TRIVIAL_CASTS,
-                                  e.id,
-                                  span,
-                                  format!("trivial cast: `{}` as `{}`. Cast can be \
-                                           replaced by coercion, this might require type \
-                                           ascription or a temporary variable",
-                                          fcx.infcx().ty_to_string(t_e),
-                                          fcx.infcx().ty_to_string(t_1)));
+    pub fn check<'a>(mut self, fcx: &FnCtxt<'a, 'tcx>) {
+        self.expr_ty = structurally_resolved_type(fcx, self.span, self.expr_ty);
+        self.cast_ty = structurally_resolved_type(fcx, self.span, self.cast_ty);
+
+        debug!("check_cast({}, {} as {})", self.expr.id, self.expr_ty.repr(fcx.tcx()),
+               self.cast_ty.repr(fcx.tcx()));
+
+        if ty::type_is_error(self.expr_ty) || ty::type_is_error(self.cast_ty) {
+            // No sense in giving duplicate error messages
+        } else if self.try_coercion_cast(fcx) {
+            self.trivial_cast_lint(fcx);
+            debug!(" -> CoercionCast");
+            fcx.tcx().cast_kinds.borrow_mut().insert(self.expr.id,
+                                                     CastKind::CoercionCast);
+        } else { match self.do_check(fcx) {
+            Ok(k) => {
+                debug!(" -> {:?}", k);
+                fcx.tcx().cast_kinds.borrow_mut().insert(self.expr.id, k);
             }
-            return;
+            Err(e) => self.report_cast_error(fcx, e)
+        };}
+    }
+
+    /// Check a cast, and report an error if one exists. In some cases,
+    /// this can return Ok and create type errors rather than returning
+    /// directly. coercion-cast is handled in check instead of here.
+    fn do_check<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> {
+        use middle::cast::IntTy::*;
+        use middle::cast::CastTy::*;
+
+        let (t_e, t_1) = match (CastTy::recognize(fcx.tcx(), self.expr_ty),
+                                CastTy::recognize(fcx.tcx(), self.cast_ty)) {
+            (Some(t_e), Some(t_1)) => (t_e, t_1),
+            _ => {
+                return Err(CastError::NonScalar)
+            }
+        };
+
+        match (t_e, t_1) {
+            // These types have invariants! can't cast into them.
+            (_, RPtr(_)) | (_, Int(CEnum)) | (_, FPtr) => Err(CastError::NonScalar),
+
+            // * -> Bool
+            (_, Int(Bool)) => Err(CastError::CastToBool),
+
+            // * -> Char
+            (Int(U(ast::TyU8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast
+            (_, Int(Char)) => Err(CastError::CastToChar),
+
+            // prim -> float,ptr
+            (Int(Bool), Float) | (Int(CEnum), Float) | (Int(Char), Float)
+                => Err(CastError::NeedViaInt),
+            (Int(Bool), Ptr(_)) | (Int(CEnum), Ptr(_)) | (Int(Char), Ptr(_))
+                => Err(CastError::NeedViaUsize),
+
+            // ptr -> *
+            (Ptr(m1), Ptr(m2)) => self.check_ptr_ptr_cast(fcx, m1, m2), // ptr-ptr-cast
+            (Ptr(m_e), Int(_)) => self.check_ptr_addr_cast(fcx, m_e), // ptr-addr-cast
+            (Ptr(_), Float) | (FPtr, Float) => Err(CastError::NeedViaUsize),
+            (FPtr, Int(_)) => Ok(CastKind::FPtrAddrCast),
+            (RPtr(_), Int(_)) | (RPtr(_), Float) => Err(CastError::NeedViaPtr),
+            // * -> ptr
+            (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // addr-ptr-cast
+            (FPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt),
+            (Float, Ptr(_)) => Err(CastError::NeedViaUsize),
+            (RPtr(rmt), Ptr(mt)) => self.check_ref_cast(fcx, rmt, mt), // array-ptr-cast
+
+            // prim -> prim
+            (Int(CEnum), Int(_)) => Ok(CastKind::EnumCast),
+            (Int(Char), Int(_)) | (Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast),
+
+            (Int(_), Int(_)) |
+            (Int(_), Float) |
+            (Float, Int(_)) |
+            (Float, Float) => Ok(CastKind::NumericCast),
+
         }
     }
 
-    let t_e_is_bare_fn_item = ty::type_is_bare_fn_item(t_e);
-    let t_e_is_scalar = ty::type_is_scalar(t_e);
-    let t_e_is_integral = ty::type_is_integral(t_e);
-    let t_e_is_float = ty::type_is_floating_point(t_e);
-    let t_e_is_c_enum = ty::type_is_c_like_enum(tcx, t_e);
-
-    let t_1_is_scalar = ty::type_is_scalar(t_1);
-    let t_1_is_integral = ty::type_is_integral(t_1);
-    let t_1_is_char = ty::type_is_char(t_1);
-    let t_1_is_bare_fn = ty::type_is_bare_fn(t_1);
-    let t_1_is_float = ty::type_is_floating_point(t_1);
-    let t_1_is_c_enum = ty::type_is_c_like_enum(tcx, t_1);
-    let t1_is_fat_ptr = fcx.type_is_fat_ptr(t_1, span);
-
-    // casts to scalars other than `char` and `bare fn` are trivial
-    let t_1_is_trivial = t_1_is_scalar && !t_1_is_char && !t_1_is_bare_fn;
-
-    if t_e_is_bare_fn_item && t_1_is_bare_fn {
-        demand::coerce(fcx, e.span, t_1, &e);
-    } else if t_1_is_char {
-        let t_e = fcx.infcx().shallow_resolve(t_e);
-        if t_e.sty != ty::ty_uint(ast::TyU8) {
-            fcx.type_error_message(span, |actual| {
-                format!("only `u8` can be cast as `char`, not `{}`", actual)
-            }, t_e, None);
+    fn check_ptr_ptr_cast<'a>(&self,
+                              fcx: &FnCtxt<'a, 'tcx>,
+                              m_e: &'tcx ty::mt<'tcx>,
+                              m_1: &'tcx ty::mt<'tcx>)
+                              -> Result<CastKind, CastError>
+    {
+        debug!("check_ptr_ptr_cast m_e={} m_1={}",
+               m_e.repr(fcx.tcx()), m_1.repr(fcx.tcx()));
+        // ptr-ptr cast. vtables must match.
+
+        // Cast to sized is OK
+        if fcx.type_is_known_to_be_sized(m_1.ty, self.span) {
+            return Ok(CastKind::PtrPtrCast);
         }
-    } else if t_1.sty == ty::ty_bool {
-        span_err!(tcx.sess, span, E0054,
-                  "cannot cast as `bool`, compare with zero instead");
-    } else if t_e_is_float && (t_1_is_scalar || t_1_is_c_enum) &&
-        !(t_1_is_integral || t_1_is_float) {
-        // Casts from float must go through an integer
-        cast_through_integer_err(fcx, span, t_1, t_e)
-    } else if t_1_is_float && (t_e_is_scalar || t_e_is_c_enum) &&
-        !(t_e_is_integral || t_e_is_float || t_e.sty == ty::ty_bool) {
-        // Casts to float must go through an integer or boolean
-        cast_through_integer_err(fcx, span, t_1, t_e)
-    } else if t_e_is_c_enum && t_1_is_trivial {
-        if ty::type_is_unsafe_ptr(t_1) {
-            // ... and likewise with C enum -> *T
-            cast_through_integer_err(fcx, span, t_1, t_e)
+
+        // sized -> unsized? report illegal cast (don't complain about vtable kinds)
+        if fcx.type_is_known_to_be_sized(m_e.ty, self.span) {
+            return Err(CastError::IllegalCast);
         }
-        // casts from C-like enums are allowed
-    } else if ty::type_is_region_ptr(t_e) && ty::type_is_unsafe_ptr(t_1) {
-        fn types_compatible<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span,
-                                      t1: Ty<'tcx>, t2: Ty<'tcx>) -> bool {
-            match t1.sty {
-                ty::ty_vec(_, Some(_)) => {}
-                _ => return false
-            }
-            if ty::type_needs_infer(t2) {
-                // This prevents this special case from going off when casting
-                // to a type that isn't fully specified; e.g. `as *_`. (Issue
-                // #14893.)
-                return false
-            }
 
-            let el = ty::sequence_element_type(fcx.tcx(), t1);
-            infer::mk_eqty(fcx.infcx(),
-                           false,
-                           infer::Misc(sp),
-                           el,
-                           t2).is_ok()
+        // vtable kinds must match
+        match (unsize_kind(fcx, m_1.ty), unsize_kind(fcx, m_e.ty)) {
+            (Some(a), Some(b)) if a == b => Ok(CastKind::PtrPtrCast),
+            _ => Err(CastError::DifferingKinds)
         }
+    }
 
-        // Due to the limitations of LLVM global constants,
-        // region pointers end up pointing at copies of
-        // vector elements instead of the original values.
-        // To allow unsafe pointers to work correctly, we
-        // need to special-case obtaining an unsafe pointer
-        // from a region pointer to a vector.
-
-        /* this cast is only allowed from &[T, ..n] to *T or
-        &T to *T. */
-        match (&t_e.sty, &t_1.sty) {
-            (&ty::ty_rptr(_, ty::mt { ty: mt1, mutbl: ast::MutImmutable }),
-             &ty::ty_ptr(ty::mt { ty: mt2, mutbl: ast::MutImmutable }))
-            if types_compatible(fcx, e.span, mt1, mt2) => {
-                /* this case is allowed */
-            }
-            _ => {
-                demand::coerce(fcx, e.span, t_1, &e);
+    fn check_fptr_ptr_cast<'a>(&self,
+                               fcx: &FnCtxt<'a, 'tcx>,
+                               m_1: &'tcx ty::mt<'tcx>)
+                               -> Result<CastKind, CastError>
+    {
+        // fptr-ptr cast. must be to sized ptr
+
+        if fcx.type_is_known_to_be_sized(m_1.ty, self.span) {
+            Ok(CastKind::FPtrPtrCast)
+        } else {
+            Err(CastError::IllegalCast)
+        }
+    }
+
+    fn check_ptr_addr_cast<'a>(&self,
+                               fcx: &FnCtxt<'a, 'tcx>,
+                               m_e: &'tcx ty::mt<'tcx>)
+                               -> Result<CastKind, CastError>
+    {
+        // ptr-addr cast. must be from sized ptr
+
+        if fcx.type_is_known_to_be_sized(m_e.ty, self.span) {
+            Ok(CastKind::PtrAddrCast)
+        } else {
+            Err(CastError::NeedViaPtr)
+        }
+    }
+
+    fn check_ref_cast<'a>(&self,
+                          fcx: &FnCtxt<'a, 'tcx>,
+                          m_e: &'tcx ty::mt<'tcx>,
+                          m_1: &'tcx ty::mt<'tcx>)
+                          -> Result<CastKind, CastError>
+    {
+        // array-ptr-cast.
+
+        if m_e.mutbl == ast::MutImmutable && m_1.mutbl == ast::MutImmutable {
+            if let ty::ty_vec(ety, Some(_)) = m_e.ty.sty {
+                // Due to the limitations of LLVM global constants,
+                // region pointers end up pointing at copies of
+                // vector elements instead of the original values.
+                // To allow unsafe pointers to work correctly, we
+                // need to special-case obtaining an unsafe pointer
+                // from a region pointer to a vector.
+                // TODO: explain comment.
+
+                 // this will report a type mismatch if needed
+                demand::eqtype(fcx, self.span, ety, m_1.ty);
+                return Ok(CastKind::ArrayPtrCast);
             }
         }
-    } else if t1_is_fat_ptr {
-        // FIXME This should be allowed where the lefthandside is also a fat
-        // pointer and is the same kind of fat pointer, i.e., array to array,
-        // trait object to trait object. That is a bit looser than the current
-        // rquirement that they are pointers to the same type.
-        if !(fcx.type_is_fat_ptr(t_e, span) &&
-             ty::deref(t_1, true).unwrap().ty == ty::deref(t_e, true).unwrap().ty) {
-            fcx.type_error_message(span, |actual| {
-                format!("cast to fat pointer: `{}` as `{}`",
-                        actual,
-                        fcx.infcx().ty_to_string(t_1))
-            }, t_e, None);
+
+        Err(CastError::IllegalCast)
+    }
+
+    fn check_addr_ptr_cast<'a>(&self,
+                               fcx: &FnCtxt<'a, 'tcx>,
+                               m_1: &'tcx ty::mt<'tcx>)
+                               -> Result<CastKind, CastError>
+    {
+        // ptr-addr cast. pointer must be thin.
+        if fcx.type_is_known_to_be_sized(m_1.ty, self.span) {
+           Ok(CastKind::AddrPtrCast)
+        } else {
+           Err(CastError::IllegalCast)
         }
-    } else if !(t_e_is_scalar && t_1_is_trivial) {
-        fcx.type_error_message(span, |actual| {
-            format!("non-scalar cast: `{}` as `{}`",
-                    actual,
-                    fcx.infcx().ty_to_string(t_1))
-        }, t_e, None);
     }
+
+    fn try_coercion_cast<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> bool {
+        if let Ok(()) = coercion::mk_assignty(fcx,
+                                              &self.expr,
+                                              self.expr_ty,
+                                              self.cast_ty) {
+            true
+        } else {
+            false
+        }
+    }
+
 }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index db39d3c05ed..d3cf9ae6dff 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1581,13 +1581,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                                  span)
     }
 
-    pub fn type_is_fat_ptr(&self, ty: Ty<'tcx>, span: Span) -> bool {
-        if let Some(mt) = ty::deref(ty, true) {
-            return !self.type_is_known_to_be_sized(mt.ty, span);
-        }
-        false
-    }
-
     pub fn register_builtin_bound(&self,
                                   ty: Ty<'tcx>,
                                   builtin_bound: ty::BuiltinBound,
@@ -1810,11 +1803,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn check_casts(&self) {
         let mut deferred_cast_checks = self.inh.deferred_cast_checks.borrow_mut();
-        for check in deferred_cast_checks.iter() {
-            cast::check_cast(self, check);
+        for cast in deferred_cast_checks.drain(..) {
+            cast.check(self);
         }
-
-        deferred_cast_checks.clear();
     }
 
     fn select_all_obligations_and_apply_defaults(&self) {
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index bb60de955f0..51c0f8fea1c 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -825,11 +825,11 @@ register_diagnostics! {
     E0185,
     E0186,
     E0187, // can't infer the kind of the closure
-    E0188, // types differ in mutability
-    E0189, // can only cast a boxed pointer to a boxed object
-    E0190, // can only cast a &-pointer to an &-object
+    E0188, // can not cast a immutable reference to a mutable pointer
+    E0189, // deprecated: can only cast a boxed pointer to a boxed object
+    E0190, // deprecated: can only cast a &-pointer to an &-object
     E0191, // value of the associated type must be specified
-    E0192, // negative imples are allowed just for `Send` and `Sync`
+    E0192, // negative impls are allowed just for `Send` and `Sync`
     E0193, // cannot bound type where clause bounds may only be attached to types
            // involving type parameters
     E0194,
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index baecaa2e221..25602cb0ee3 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -77,7 +77,7 @@ This API is completely unstable and subject to change.
 
 #![feature(box_patterns)]
 #![feature(box_syntax)]
-#![feature(collections)]
+#![feature(collections, collections_drain)]
 #![feature(core)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
diff --git a/src/test/compile-fail/fat-ptr-cast.rs b/src/test/compile-fail/fat-ptr-cast.rs
index 2099424b05c..672dec01e94 100644
--- a/src/test/compile-fail/fat-ptr-cast.rs
+++ b/src/test/compile-fail/fat-ptr-cast.rs
@@ -8,20 +8,24 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Make sure casts between thin pointer <-> fat pointer are illegal.
+// Make sure casts between thin-pointer <-> fat pointer obey RFC401
 
 pub trait Trait {}
 
 fn main() {
     let a: &[i32] = &[1, 2, 3];
     let b: Box<[i32]> = Box::new([1, 2, 3]);
+    let p = a as *const [i32];
+    let q = a.as_ptr();
 
-    a as usize; //~ ERROR non-scalar cast
+    a as usize; //~ ERROR illegal cast
     b as usize; //~ ERROR non-scalar cast
+    p as usize; //~ ERROR illegal cast
 
-    let a: usize = 42;
-    a as *const [i32]; //~ ERROR cast to fat pointer: `usize` as `*const [i32]`
+    // #22955
+    q as *const [i32]; //~ ERROR illegal cast
 
-    let a: *const u8 = &42;
-    a as *const [u8]; //~ ERROR cast to fat pointer: `*const u8` as `*const [u8]`
+    // #21397
+    let t: *mut (Trait + 'static) = 0 as *mut _; //~ ERROR illegal cast
+    let mut fail: *const str = 0 as *const str; //~ ERROR illegal cast
 }
diff --git a/src/test/compile-fail/issue-22289.rs b/src/test/compile-fail/issue-22289.rs
index 1fdc8735714..bcbc414d353 100644
--- a/src/test/compile-fail/issue-22289.rs
+++ b/src/test/compile-fail/issue-22289.rs
@@ -9,5 +9,5 @@
 // except according to those terms.
 
 fn main() {
-    0 as &std::any::Any; //~ ERROR cast to fat pointer: `i32` as `&core::any::Any`
+    0 as &std::any::Any; //~ ERROR non-scalar cast
 }