about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-05-19 20:56:51 +0000
committerbors <bors@rust-lang.org>2015-05-19 20:56:51 +0000
commitc322dbbf8a518d00c6db689ca80b0047f2328890 (patch)
treeac2c9c3f341d636046f5f653259ff9a20bbf4938
parentf34ff7af7362053e8aee7a35365d6320ed6e88b8 (diff)
parente7e1fd20deb95f74c4a33c76921907e039dff894 (diff)
downloadrust-c322dbbf8a518d00c6db689ca80b0047f2328890.tar.gz
rust-c322dbbf8a518d00c6db689ca80b0047f2328890.zip
Auto merge of #24333 - arielb1:implement-rfc401, r=nrc
-rw-r--r--src/liballoc/arc.rs8
-rw-r--r--src/liballoc/rc.rs4
-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.rs77
-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_lint/builtin.rs2
-rw-r--r--src/librustc_trans/trans/adt.rs37
-rw-r--r--src/librustc_trans/trans/base.rs3
-rw-r--r--src/librustc_trans/trans/common.rs6
-rw-r--r--src/librustc_trans/trans/consts.rs72
-rw-r--r--src/librustc_trans/trans/controlflow.rs31
-rw-r--r--src/librustc_trans/trans/expr.rs254
-rw-r--r--src/librustc_typeck/check/cast.rs458
-rw-r--r--src/librustc_typeck/check/mod.rs46
-rw-r--r--src/librustc_typeck/diagnostics.rs8
-rw-r--r--src/librustc_typeck/lib.rs2
-rw-r--r--src/test/compile-fail/associated-types-incomplete-object.rs8
-rw-r--r--src/test/compile-fail/cast-ptr-to-int-const.rs15
-rw-r--r--src/test/compile-fail/cast-rfc0401.rs75
-rw-r--r--src/test/compile-fail/cast-to-bare-fn.rs2
-rw-r--r--src/test/compile-fail/const-cast-different-types.rs14
-rw-r--r--src/test/compile-fail/destructure-trait-ref.rs18
-rw-r--r--src/test/compile-fail/fat-ptr-cast.rs19
-rw-r--r--src/test/compile-fail/issue-14845.rs14
-rw-r--r--src/test/compile-fail/issue-17444.rs2
-rw-r--r--src/test/compile-fail/issue-17458.rs2
-rw-r--r--src/test/compile-fail/issue-21554.rs2
-rw-r--r--src/test/compile-fail/issue-22289.rs2
-rw-r--r--src/test/compile-fail/issue-5153.rs2
-rw-r--r--src/test/compile-fail/kindck-impl-type-params.rs1
-rw-r--r--src/test/compile-fail/typeck-cast-pointer-to-float.rs2
-rw-r--r--src/test/compile-fail/vector-cast-weirdness.rs2
-rw-r--r--src/test/run-pass-valgrind/cast-enum-with-dtor.rs44
-rw-r--r--src/test/run-pass/cast-rfc0401-vtable-kinds.rs55
-rw-r--r--src/test/run-pass/cast-rfc0401.rs170
-rw-r--r--src/test/run-pass/fat-ptr-cast.rs6
-rw-r--r--src/test/run-pass/supported-cast.rs3
42 files changed, 1071 insertions, 484 deletions
diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs
index ff942444a61..97e85b114b0 100644
--- a/src/liballoc/arc.rs
+++ b/src/liballoc/arc.rs
@@ -394,7 +394,9 @@ impl<T: ?Sized> Drop for Arc<T> {
         // it's run more than once)
         let ptr = *self._ptr;
         // if ptr.is_null() { return }
-        if ptr as usize == 0 || ptr as usize == mem::POST_DROP_USIZE { return }
+        if ptr as *mut u8 as usize == 0 || ptr as *mut u8 as usize == mem::POST_DROP_USIZE {
+            return
+        }
 
         // Because `fetch_sub` is already atomic, we do not need to synchronize
         // with other threads unless we are going to delete the object. This
@@ -524,7 +526,9 @@ impl<T: ?Sized> Drop for Weak<T> {
         let ptr = *self._ptr;
 
         // see comments above for why this check is here
-        if ptr as usize == 0 || ptr as usize == mem::POST_DROP_USIZE { return }
+        if ptr as *mut u8 as usize == 0 || ptr as *mut u8 as usize == mem::POST_DROP_USIZE {
+            return
+        }
 
         // If we find out that we were the last weak pointer, then its time to
         // deallocate the data entirely. See the discussion in Arc::drop() about
diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs
index 88c5c38172a..b8d8e6ad0a1 100644
--- a/src/liballoc/rc.rs
+++ b/src/liballoc/rc.rs
@@ -543,7 +543,7 @@ impl<T: ?Sized> Drop for Rc<T> {
         unsafe {
             let ptr = *self._ptr;
             if !(*(&ptr as *const _ as *const *const ())).is_null() &&
-               ptr as usize != mem::POST_DROP_USIZE {
+               ptr as *const () as usize != mem::POST_DROP_USIZE {
                 self.dec_strong();
                 if self.strong() == 0 {
                     // destroy the contained object
@@ -1051,7 +1051,7 @@ impl<T: ?Sized> Drop for Weak<T> {
         unsafe {
             let ptr = *self._ptr;
             if !(*(&ptr as *const _ as *const *const ())).is_null() &&
-               ptr as usize != mem::POST_DROP_USIZE {
+               ptr as *const () as usize != mem::POST_DROP_USIZE {
                 self.dec_weak();
                 // the weak count starts at 1, and will only go to zero if all
                 // the strong pointers have disappeared.
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..c534a3ca038
--- /dev/null
+++ b/src/librustc/middle/cast.rs
@@ -0,0 +1,77 @@
+// 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;
+
+/// Types that are represented as ints.
+#[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> {
+    /// Various types that are represented as ints and handled mostly
+    /// in the same way, merged for easier matching.
+    Int(IntTy),
+    /// Floating-Point types
+    Float,
+    /// Function Pointers
+    FnPtr,
+    /// Raw pointers
+    Ptr(&'tcx ty::mt<'tcx>),
+    /// References
+    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,
+    FnPtrPtrCast,
+    FnPtrAddrCast
+}
+
+impl<'tcx> CastTy<'tcx> {
+    pub fn from_ty(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::FnPtr),
+            _ => None,
+        }
+    }
+}
diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs
index 8bb83c54da8..794cc4ff38d 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::FnPtrAddrCast) => {
+                    v.add_qualif(ConstQualif::NOT_CONST);
+                    if v.mode != Mode::Var {
+                        span_err!(v.tcx.sess, e.span, E0018,
+                                  "can't 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_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 8d555240e70..3716ee395bf 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -206,7 +206,7 @@ impl LintPass for TypeLimits {
                                 let (min, max) = int_ty_range(int_type);
                                 let negative = self.negated_expr_id == e.id;
 
-                                if (negative && v > (min.abs() as u64)) ||
+                                if (negative && v > min.wrapping_neg() as u64) ||
                                    (!negative && v > (max.abs() as u64)) {
                                     cx.span_lint(OVERFLOWING_LITERALS, e.span,
                                                  &*format!("literal out of range for {:?}", t));
diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs
index 17c9fa24818..6d4c72c132a 100644
--- a/src/librustc_trans/trans/adt.rs
+++ b/src/librustc_trans/trans/adt.rs
@@ -795,43 +795,40 @@ pub fn trans_switch<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     }
 }
 
-
+pub fn is_discr_signed<'tcx>(r: &Repr<'tcx>) -> bool {
+    match *r {
+        CEnum(ity, _, _) => ity.is_signed(),
+        General(ity, _, _) => ity.is_signed(),
+        Univariant(..) => false,
+        RawNullablePointer { .. } => false,
+        StructWrappedNullablePointer { .. } => false,
+    }
+}
 
 /// Obtain the actual discriminant of a value.
 pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
                                    scrutinee: ValueRef, cast_to: Option<Type>)
     -> ValueRef {
-    let signed;
-    let val;
     debug!("trans_get_discr r: {:?}", r);
-    match *r {
-        CEnum(ity, min, max) => {
-            val = load_discr(bcx, ity, scrutinee, min, max);
-            signed = ity.is_signed();
-        }
+    let val = match *r {
+        CEnum(ity, min, max) => load_discr(bcx, ity, scrutinee, min, max),
         General(ity, ref cases, _) => {
             let ptr = GEPi(bcx, scrutinee, &[0, 0]);
-            val = load_discr(bcx, ity, ptr, 0, (cases.len() - 1) as Disr);
-            signed = ity.is_signed();
-        }
-        Univariant(..) => {
-            val = C_u8(bcx.ccx(), 0);
-            signed = false;
+            load_discr(bcx, ity, ptr, 0, (cases.len() - 1) as Disr)
         }
+        Univariant(..) => C_u8(bcx.ccx(), 0),
         RawNullablePointer { nndiscr, nnty, .. } =>  {
             let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
             let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
-            val = ICmp(bcx, cmp, Load(bcx, scrutinee), C_null(llptrty), DebugLoc::None);
-            signed = false;
+            ICmp(bcx, cmp, Load(bcx, scrutinee), C_null(llptrty), DebugLoc::None)
         }
         StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
-            val = struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee);
-            signed = false;
+            struct_wrapped_nullable_bitdiscr(bcx, nndiscr, discrfield, scrutinee)
         }
-    }
+    };
     match cast_to {
         None => val,
-        Some(llty) => if signed { SExt(bcx, val, llty) } else { ZExt(bcx, val, llty) }
+        Some(llty) => if is_discr_signed(r) { SExt(bcx, val, llty) } else { ZExt(bcx, val, llty) }
     }
 }
 
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 4879975dde6..06841501124 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -869,8 +869,7 @@ pub fn with_cond<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
 {
     let _icx = push_ctxt("with_cond");
 
-    if bcx.unreachable.get() ||
-            (common::is_const(val) && common::const_to_uint(val) == 0) {
+    if bcx.unreachable.get() || common::const_to_opt_uint(val) == Some(0) {
         return bcx;
     }
 
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index 03dda57e568..758702f54c0 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -919,12 +919,6 @@ pub fn const_get_elt(cx: &CrateContext, v: ValueRef, us: &[c_uint])
     }
 }
 
-pub fn is_const(v: ValueRef) -> bool {
-    unsafe {
-        llvm::LLVMIsConstant(v) == True
-    }
-}
-
 pub fn const_to_int(v: ValueRef) -> i64 {
     unsafe {
         llvm::LLVMConstIntGetSExtValue(v)
diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs
index 503bdf8dadb..5ca14b63bce 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,64 @@ 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_cast = ety;
+            let llty = type_of::type_of(cx, t_cast);
+            let (v, t_expr) = const_expr(cx, &**base, param_substs);
+            debug!("trans_const_cast({} as {})", t_expr.repr(cx.tcx()), t_cast.repr(cx.tcx()));
+            if expr::cast_is_noop(cx.tcx(), base, t_expr, t_cast) {
                 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_expr) {
+                // Fat pointer casts.
+                let t_cast_inner = ty::deref(t_cast, true).expect("cast to non-pointer").ty;
+                let ptr_ty = type_of::in_memory_type_of(cx, t_cast_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_cast) {
+                    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::from_ty(cx.tcx(), t_expr).expect("bad input type for cast"),
+                   CastTy::from_ty(cx.tcx(), t_cast).expect("bad output type for cast")) {
+              (CastTy::Int(IntTy::CEnum), CastTy::Int(_)) => {
+                let repr = adt::represent_type(cx, t_expr);
+                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_expr) 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_expr) {
                     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()) }
-                else { llvm::LLVMConstFPToUI(v, llty.to_ref()) }
+              (CastTy::Float, CastTy::Int(IntTy::I)) => {
+                llvm::LLVMConstFPToSI(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")
-                }
+              (CastTy::Float, CastTy::Int(_)) => {
+                llvm::LLVMConstFPToUI(v, llty.to_ref())
               }
-              (expr::cast_pointer, expr::cast_pointer) => {
+              (CastTy::Ptr(_), CastTy::Ptr(_)) | (CastTy::FnPtr, CastTy::Ptr(_))
+                    | (CastTy::RPtr(_), CastTy::Ptr(_)) => {
                 ptrcast(v, llty)
               }
-              (expr::cast_integral, expr::cast_pointer) => {
+              (CastTy::FnPtr, CastTy::FnPtr) => 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::FnPtr, CastTy::Int(_)) => {
                 llvm::LLVMConstPtrToInt(v, llty.to_ref())
               }
               _ => {
diff --git a/src/librustc_trans/trans/controlflow.rs b/src/librustc_trans/trans/controlflow.rs
index 8cecc39ec39..ab8cfa0ce3b 100644
--- a/src/librustc_trans/trans/controlflow.rs
+++ b/src/librustc_trans/trans/controlflow.rs
@@ -166,31 +166,24 @@ pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool());
 
     // Drop branches that are known to be impossible
-    if is_const(cond_val) && !is_undef(cond_val) {
-        if const_to_uint(cond_val) == 1 {
-            match els {
-                Some(elexpr) => {
-                    let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx };
-                    trans.visit_expr(&*elexpr);
-                }
-                None => {}
-            }
+    if let Some(cv) = const_to_opt_uint(cond_val) {
+        if cv == 1 {
             // if true { .. } [else { .. }]
             bcx = trans_block(bcx, &*thn, dest);
             trans::debuginfo::clear_source_location(bcx.fcx);
+
+            if let Some(elexpr) = els {
+                let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx };
+                trans.visit_expr(&*elexpr);
+            }
         } else {
-            let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ;
+            // if false { .. } [else { .. }]
+            let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx };
             trans.visit_block(&*thn);
 
-            match els {
-                // if false { .. } else { .. }
-                Some(elexpr) => {
-                    bcx = expr::trans_into(bcx, &*elexpr, dest);
-                    trans::debuginfo::clear_source_location(bcx.fcx);
-                }
-
-                // if false { .. }
-                None => { }
+            if let Some(elexpr) = els {
+                bcx = expr::trans_into(bcx, &*elexpr, dest);
+                trans::debuginfo::clear_source_location(bcx.fcx);
             }
         }
 
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 270aacfe143..33eb3814087 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,147 @@ 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)
+    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,
+                                        PointerCast(bcx,
+                                                    Load(bcx, get_dataptr(bcx, datum.val)),
+                                                    ll_t_out),
+                                        t_out).to_expr_datumblock();
         }
-        (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))
-                }
-            }
+    }
+
+    let r_t_in = CastTy::from_ty(bcx.tcx(), t_in).expect("bad input type for cast");
+    let r_t_out = CastTy::from_ty(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(_)) | (FnPtr, Ptr(_)) | (RPtr(_), Ptr(_)) => {
+            PointerCast(bcx, llexpr, ll_t_out)
         }
-        _ => ccx.sess().bug(&format!("translating unsupported cast: \
-                                    {} ({:?}) -> {} ({:?})",
-                                    t_in.repr(bcx.tcx()),
-                                    k_in,
-                                    t_out.repr(bcx.tcx()),
-                                    k_out))
+        (Ptr(_), Int(_)) | (FnPtr, 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..5258a77204d 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,52 @@ pub struct CastCheck<'tcx> {
     span: Span,
 }
 
+/// The kind of the unsize info (length or vtable) - we only allow casts between
+/// fat pointers if their unsize-infos have the same kind.
+#[derive(Copy, Clone, PartialEq, Eq)]
+enum UnsizeKind<'tcx> {
+    Vtable,
+    Length,
+    /// The unsize info of this projection
+    OfProjection(&'tcx ty::ProjectionTy<'tcx>),
+    /// The unsize info of this parameter
+    OfParam(&'tcx ty::ParamTy)
+}
+
+/// 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)
+            }
+        }
+        // We should really try to normalize here.
+        ty::ty_projection(ref pi) => Some(UnsizeKind::OfProjection(pi)),
+        ty::ty_param(ref p) => Some(UnsizeKind::OfParam(p)),
+        _ => 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 +112,270 @@ 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_cast = self.cast_ty;
+        let t_expr = self.expr_ty;
+        if ty::type_is_numeric(t_cast) && ty::type_is_numeric(t_expr) {
+            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_expr),
+                                            fcx.infcx().ty_to_string(t_cast)));
+        } 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_expr),
+                                            fcx.infcx().ty_to_string(t_cast)));
+        }
+
     }
 
-    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 in the fcx 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_from, t_cast) = match (CastTy::from_ty(fcx.tcx(), self.expr_ty),
+                                      CastTy::from_ty(fcx.tcx(), self.cast_ty)) {
+            (Some(t_from), Some(t_cast)) => (t_from, t_cast),
+            _ => {
+                return Err(CastError::NonScalar)
+            }
+        };
+
+        match (t_from, t_cast) {
+            // These types have invariants! can't cast into them.
+            (_, RPtr(_)) | (_, Int(CEnum)) | (_, FnPtr) => 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(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast
+            (Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr), // ptr-addr-cast
+            (Ptr(_), Float) | (FnPtr, Float) => Err(CastError::NeedViaUsize),
+            (FnPtr, Int(_)) => Ok(CastKind::FnPtrAddrCast),
+            (RPtr(_), Int(_)) | (RPtr(_), Float) => Err(CastError::NeedViaPtr),
+            // * -> ptr
+            (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // addr-ptr-cast
+            (FnPtr, 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_expr: &'tcx ty::mt<'tcx>,
+                              m_cast: &'tcx ty::mt<'tcx>)
+                              -> Result<CastKind, CastError>
+    {
+        debug!("check_ptr_ptr_cast m_expr={} m_cast={}",
+               m_expr.repr(fcx.tcx()), m_cast.repr(fcx.tcx()));
+        // ptr-ptr cast. vtables must match.
+
+        // Cast to sized is OK
+        if fcx.type_is_known_to_be_sized(m_cast.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_expr.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_cast.ty), unsize_kind(fcx, m_expr.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_cast: &'tcx ty::mt<'tcx>)
+                               -> Result<CastKind, CastError>
+    {
+        // fptr-ptr cast. must be to sized ptr
+
+        if fcx.type_is_known_to_be_sized(m_cast.ty, self.span) {
+            Ok(CastKind::FnPtrPtrCast)
+        } else {
+            Err(CastError::IllegalCast)
+        }
+    }
+
+    fn check_ptr_addr_cast<'a>(&self,
+                               fcx: &FnCtxt<'a, 'tcx>,
+                               m_expr: &'tcx ty::mt<'tcx>)
+                               -> Result<CastKind, CastError>
+    {
+        // ptr-addr cast. must be from sized ptr
+
+        if fcx.type_is_known_to_be_sized(m_expr.ty, self.span) {
+            Ok(CastKind::PtrAddrCast)
+        } else {
+            Err(CastError::NeedViaPtr)
+        }
+    }
+
+    fn check_ref_cast<'a>(&self,
+                          fcx: &FnCtxt<'a, 'tcx>,
+                          m_expr: &'tcx ty::mt<'tcx>,
+                          m_cast: &'tcx ty::mt<'tcx>)
+                          -> Result<CastKind, CastError>
+    {
+        // array-ptr-cast.
+
+        if m_expr.mutbl == ast::MutImmutable && m_cast.mutbl == ast::MutImmutable {
+            if let ty::ty_vec(ety, Some(_)) = m_expr.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.
+
+                // this will report a type mismatch if needed
+                demand::eqtype(fcx, self.span, ety, m_cast.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_cast: &'tcx ty::mt<'tcx>)
+                               -> Result<CastKind, CastError>
+    {
+        // ptr-addr cast. pointer must be thin.
+        if fcx.type_is_known_to_be_sized(m_cast.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..0e64063d6a4 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -528,6 +528,9 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
             upvar::closure_analyze_fn(&fcx, fn_id, decl, body);
             fcx.select_all_obligations_or_error();
             fcx.check_casts();
+
+            fcx.select_all_obligations_or_error(); // Casts can introduce new obligations.
+
             regionck::regionck_fn(&fcx, fn_id, fn_span, decl, body);
             writeback::resolve_type_vars_in_fn(&fcx, decl, body);
         }
@@ -1112,20 +1115,20 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                          span: Span,
                                          t_span: Span,
                                          e_span: Span,
-                                         t_1: Ty<'tcx>,
-                                         t_e: Ty<'tcx>,
+                                         t_cast: Ty<'tcx>,
+                                         t_expr: Ty<'tcx>,
                                          id: ast::NodeId) {
-    let tstr = fcx.infcx().ty_to_string(t_1);
+    let tstr = fcx.infcx().ty_to_string(t_cast);
     fcx.type_error_message(span, |actual| {
         format!("cast to unsized type: `{}` as `{}`", actual, tstr)
-    }, t_e, None);
-    match t_e.sty {
+    }, t_expr, None);
+    match t_expr.sty {
         ty::ty_rptr(_, ty::mt { mutbl: mt, .. }) => {
             let mtstr = match mt {
                 ast::MutMutable => "mut ",
                 ast::MutImmutable => ""
             };
-            if ty::type_is_trait(t_1) {
+            if ty::type_is_trait(t_cast) {
                 match fcx.tcx().sess.codemap().span_to_snippet(t_span) {
                     Ok(s) => {
                         fcx.tcx().sess.span_suggestion(t_span,
@@ -1581,13 +1584,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 +1806,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) {
@@ -3410,24 +3404,24 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
 
         // Find the type of `e`. Supply hints based on the type we are casting to,
         // if appropriate.
-        let t_1 = fcx.to_ty(t);
-        let t_1 = structurally_resolved_type(fcx, expr.span, t_1);
-        check_expr_with_expectation(fcx, e, ExpectCastableToType(t_1));
-        let t_e = fcx.expr_ty(e);
+        let t_cast = fcx.to_ty(t);
+        let t_cast = structurally_resolved_type(fcx, expr.span, t_cast);
+        check_expr_with_expectation(fcx, e, ExpectCastableToType(t_cast));
+        let t_expr = fcx.expr_ty(e);
 
         // Eagerly check for some obvious errors.
-        if ty::type_is_error(t_e) {
+        if ty::type_is_error(t_expr) {
             fcx.write_error(id);
-        } else if !fcx.type_is_known_to_be_sized(t_1, expr.span) {
-            report_cast_to_unsized_type(fcx, expr.span, t.span, e.span, t_1, t_e, id);
+        } else if !fcx.type_is_known_to_be_sized(t_cast, expr.span) {
+            report_cast_to_unsized_type(fcx, expr.span, t.span, e.span, t_cast, t_expr, id);
         } else {
             // Write a type for the whole expression, assuming everything is going
             // to work out Ok.
-            fcx.write_ty(id, t_1);
+            fcx.write_ty(id, t_cast);
 
             // Defer other checks until we're done type checking.
             let mut deferred_cast_checks = fcx.inh.deferred_cast_checks.borrow_mut();
-            let cast_check = cast::CastCheck::new((**e).clone(), t_e, t_1, expr.span);
+            let cast_check = cast::CastCheck::new((**e).clone(), t_expr, t_cast, expr.span);
             deferred_cast_checks.push(cast_check);
         }
       }
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 67e637198b4..c9176f4d7cf 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -977,11 +977,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/associated-types-incomplete-object.rs b/src/test/compile-fail/associated-types-incomplete-object.rs
index 1c708da30a7..c1feb8ac459 100644
--- a/src/test/compile-fail/associated-types-incomplete-object.rs
+++ b/src/test/compile-fail/associated-types-incomplete-object.rs
@@ -28,15 +28,15 @@ impl Foo for isize {
 }
 
 pub fn main() {
-    let a = &42 as &Foo<A=usize, B=char>;
+    let a = &42isize as &Foo<A=usize, B=char>;
 
-    let b = &42 as &Foo<A=usize>;
+    let b = &42isize as &Foo<A=usize>;
     //~^ ERROR the value of the associated type `B` (from the trait `Foo`) must be specified
 
-    let c = &42 as &Foo<B=char>;
+    let c = &42isize as &Foo<B=char>;
     //~^ ERROR the value of the associated type `A` (from the trait `Foo`) must be specified
 
-    let d = &42 as &Foo;
+    let d = &42isize as &Foo;
     //~^ ERROR the value of the associated type `A` (from the trait `Foo`) must be specified
     //~| ERROR the value of the associated type `B` (from the trait `Foo`) must be specified
 }
diff --git a/src/test/compile-fail/cast-ptr-to-int-const.rs b/src/test/compile-fail/cast-ptr-to-int-const.rs
new file mode 100644
index 00000000000..7c32e31d23b
--- /dev/null
+++ b/src/test/compile-fail/cast-ptr-to-int-const.rs
@@ -0,0 +1,15 @@
+// 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.
+
+fn main() {
+    const X: u32 = main as u32; //~ ERROR E0018
+    const Y: u32 = 0;
+    const Z: u32 = &Y as *const u32 as u32; //~ ERROR E0018
+}
diff --git a/src/test/compile-fail/cast-rfc0401.rs b/src/test/compile-fail/cast-rfc0401.rs
new file mode 100644
index 00000000000..f3537e54135
--- /dev/null
+++ b/src/test/compile-fail/cast-rfc0401.rs
@@ -0,0 +1,75 @@
+// 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.
+
+fn illegal_cast<U:?Sized,V:?Sized>(u: *const U) -> *const V
+{
+    u as *const V //~ ERROR vtable kinds
+}
+
+fn illegal_cast_2<U:?Sized>(u: *const U) -> *const str
+{
+    u as *const str //~ ERROR vtable kinds
+}
+
+trait Foo { fn foo(&self) {} }
+impl<T> Foo for T {}
+
+enum E {
+    A, B
+}
+
+fn main()
+{
+    let f: f32 = 1.2;
+    let v = 0 as *const u8;
+    let fat_v : *const [u8] = unsafe { &*(0 as *const [u8; 1])};
+    let foo: &Foo = &f;
+
+    let _ = v as &u8; //~ ERROR non-scalar
+    let _ = v as E; //~ ERROR non-scalar
+    let _ = v as fn(); //~ ERROR non-scalar
+    let _ = v as (u32,); //~ ERROR non-scalar
+    let _ = Some(&v) as *const u8; //~ ERROR non-scalar
+
+    let _ = v as f32; //~ ERROR through a usize first
+    let _ = main as f64; //~ ERROR through a usize first
+    let _ = &v as usize; //~ ERROR through a raw pointer first
+    let _ = f as *const u8; //~ ERROR through a usize first
+    let _ = 3 as bool; //~ ERROR compare with zero
+    let _ = E::A as bool; //~ ERROR compare with zero
+    let _ = 0x61u32 as char; //~ ERROR only `u8` can be cast
+
+    let _ = false as f32; //~ ERROR through an integer first
+    let _ = E::A as f32; //~ ERROR through an integer first
+    let _ = 'a' as f32; //~ ERROR through an integer first
+
+    let _ = false as *const u8; //~ ERROR through a usize first
+    let _ = E::A as *const u8; //~ ERROR through a usize first
+    let _ = 'a' as *const u8; //~ ERROR through a usize first
+
+    let _ = 42usize as *const [u8]; //~ ERROR illegal cast
+    let _ = v as *const [u8]; //~ ERROR illegal cast
+    let _ = fat_v as *const Foo;
+    //~^ ERROR `core::marker::Sized` is not implemented for the type `[u8]`
+    let _ = foo as *const str; //~ ERROR illegal cast
+    let _ = foo as *mut str; //~ ERROR illegal cast
+    let _ = main as *mut str; //~ ERROR illegal cast
+    let _ = &f as *mut f32; //~ ERROR illegal cast
+    let _ = &f as *const f64; //~ ERROR illegal cast
+    let _ = fat_v as usize; //~ ERROR through a raw pointer first
+
+    let a : *const str = "hello";
+    let _ = a as *const Foo;
+    //~^ ERROR `core::marker::Sized` is not implemented for the type `str`
+
+    // check no error cascade
+    let _ = main.f as *const u32; //~ ERROR attempted access of field
+
+}
diff --git a/src/test/compile-fail/cast-to-bare-fn.rs b/src/test/compile-fail/cast-to-bare-fn.rs
index a7f0917ed86..7cc5c727bc7 100644
--- a/src/test/compile-fail/cast-to-bare-fn.rs
+++ b/src/test/compile-fail/cast-to-bare-fn.rs
@@ -13,7 +13,7 @@ fn foo(_x: isize) { }
 fn main() {
     let v: u64 = 5;
     let x = foo as extern "C" fn() -> isize;
-    //~^ ERROR mismatched types
+    //~^ ERROR non-scalar cast
     let y = v as extern "Rust" fn(isize) -> (isize, isize);
     //~^ ERROR non-scalar cast
     y(x());
diff --git a/src/test/compile-fail/const-cast-different-types.rs b/src/test/compile-fail/const-cast-different-types.rs
index 6e3732908ac..e6851f02cb6 100644
--- a/src/test/compile-fail/const-cast-different-types.rs
+++ b/src/test/compile-fail/const-cast-different-types.rs
@@ -9,18 +9,8 @@
 // except according to those terms.
 
 static a: &'static str = "foo";
-static b: *const u8 = a as *const u8;
-//~^ ERROR mismatched types
-//~| expected *const u8
-//~| found &'static str
-//~| expected u8
-//~| found str
-static c: *const u8 = &a as *const u8;
-//~^ ERROR mismatched types
-//~| expected *const u8
-//~| found &&'static str
-//~| expected u8
-//~| found &-ptr
+static b: *const u8 = a as *const u8; //~ ERROR illegal cast
+static c: *const u8 = &a as *const u8; //~ ERROR illegal cast
 
 fn main() {
 }
diff --git a/src/test/compile-fail/destructure-trait-ref.rs b/src/test/compile-fail/destructure-trait-ref.rs
index 3f455e148a0..08db643df00 100644
--- a/src/test/compile-fail/destructure-trait-ref.rs
+++ b/src/test/compile-fail/destructure-trait-ref.rs
@@ -28,29 +28,29 @@ fn main() {
     // if n > m, it's a type mismatch error.
 
     // n < m
-    let &x = &(&1 as &T);
-    let &x = &&(&1 as &T);
-    let &&x = &&(&1 as &T);
+    let &x = &(&1isize as &T);
+    let &x = &&(&1isize as &T);
+    let &&x = &&(&1isize as &T);
 
     // n == m
-    let &x = &1 as &T;      //~ ERROR type `&T` cannot be dereferenced
-    let &&x = &(&1 as &T);  //~ ERROR type `&T` cannot be dereferenced
-    let box x = box 1 as Box<T>; //~ ERROR the trait `core::marker::Sized` is not implemented
+    let &x = &1isize as &T;      //~ ERROR type `&T` cannot be dereferenced
+    let &&x = &(&1isize as &T);  //~ ERROR type `&T` cannot be dereferenced
+    let box x = box 1isize as Box<T>; //~ ERROR the trait `core::marker::Sized` is not implemented
 
     // n > m
-    let &&x = &1 as &T;
+    let &&x = &1isize as &T;
     //~^ ERROR mismatched types
     //~| expected `T`
     //~| found `&_`
     //~| expected trait T
     //~| found &-ptr
-    let &&&x = &(&1 as &T);
+    let &&&x = &(&1isize as &T);
     //~^ ERROR mismatched types
     //~| expected `T`
     //~| found `&_`
     //~| expected trait T
     //~| found &-ptr
-    let box box x = box 1 as Box<T>;
+    let box box x = box 1isize as Box<T>;
     //~^ ERROR mismatched types
     //~| expected `T`
     //~| found `Box<_>`
diff --git a/src/test/compile-fail/fat-ptr-cast.rs b/src/test/compile-fail/fat-ptr-cast.rs
index 2099424b05c..25cab09b7cb 100644
--- a/src/test/compile-fail/fat-ptr-cast.rs
+++ b/src/test/compile-fail/fat-ptr-cast.rs
@@ -8,20 +8,23 @@
 // 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.
-
-pub trait Trait {}
+trait Trait {}
 
+// Make sure casts between thin-pointer <-> fat pointer obey RFC401
 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; cast through a raw pointer
 
-    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-14845.rs b/src/test/compile-fail/issue-14845.rs
index 3f994102a17..219f08ad35a 100644
--- a/src/test/compile-fail/issue-14845.rs
+++ b/src/test/compile-fail/issue-14845.rs
@@ -15,18 +15,8 @@ struct X {
 
 fn main() {
     let x = X { a: [0] };
-    let _f = &x.a as *mut u8;
-    //~^ ERROR mismatched types
-    //~| expected `*mut u8`
-    //~| found `&[u8; 1]`
-    //~| expected u8
-    //~| found array of 1 elements
+    let _f = &x.a as *mut u8; //~ ERROR illegal cast
 
     let local: [u8; 1] = [0];
-    let _v = &local as *mut u8;
-    //~^ ERROR mismatched types
-    //~| expected `*mut u8`
-    //~| found `&[u8; 1]`
-    //~| expected u8,
-    //~| found array of 1 elements
+    let _v = &local as *mut u8; //~ ERROR illegal cast
 }
diff --git a/src/test/compile-fail/issue-17444.rs b/src/test/compile-fail/issue-17444.rs
index 011a9c17776..a079161d42e 100644
--- a/src/test/compile-fail/issue-17444.rs
+++ b/src/test/compile-fail/issue-17444.rs
@@ -14,5 +14,5 @@ enum Test {
 
 fn main() {
     let _x = Test::Foo as *const isize;
-    //~^ ERROR illegal cast; cast through an integer first: `Test` as `*const isize`
+    //~^ ERROR illegal cast; cast through a usize first: `Test` as `*const isize`
 }
diff --git a/src/test/compile-fail/issue-17458.rs b/src/test/compile-fail/issue-17458.rs
index d6f70ae1e57..a3a9e17cb3c 100644
--- a/src/test/compile-fail/issue-17458.rs
+++ b/src/test/compile-fail/issue-17458.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 static X: usize = 0 as *const usize as usize;
-//~^ ERROR: can not cast a pointer to an integer in statics
+//~^ ERROR: can't cast a pointer to an integer in statics
 
 fn main() {
     assert_eq!(X, 0);
diff --git a/src/test/compile-fail/issue-21554.rs b/src/test/compile-fail/issue-21554.rs
index a2cac55033c..16ce84715b1 100644
--- a/src/test/compile-fail/issue-21554.rs
+++ b/src/test/compile-fail/issue-21554.rs
@@ -11,5 +11,5 @@
 struct Inches(i32);
 
 fn main() {
-    Inches as f32; //~ ERROR illegal cast; cast through an integer first
+    Inches as f32; //~ ERROR illegal cast; cast through a usize first
 }
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
 }
diff --git a/src/test/compile-fail/issue-5153.rs b/src/test/compile-fail/issue-5153.rs
index da32408e199..b1d96f9b527 100644
--- a/src/test/compile-fail/issue-5153.rs
+++ b/src/test/compile-fail/issue-5153.rs
@@ -17,6 +17,6 @@ impl Foo for isize {
 }
 
 fn main() {
-    (&5 as &Foo).foo();
+    (&5isize as &Foo).foo();
     //~^ ERROR: no method named `foo` found for type `&Foo` in the current scope
 }
diff --git a/src/test/compile-fail/kindck-impl-type-params.rs b/src/test/compile-fail/kindck-impl-type-params.rs
index d4ee93e9ca5..988a7837b59 100644
--- a/src/test/compile-fail/kindck-impl-type-params.rs
+++ b/src/test/compile-fail/kindck-impl-type-params.rs
@@ -28,7 +28,6 @@ fn f<T>(val: T) {
     let a = &t as &Gettable<T>;
     //~^ ERROR the trait `core::marker::Send` is not implemented
     //~^^ ERROR the trait `core::marker::Copy` is not implemented
-    //~^^^ ERROR the parameter type `T` may not live long enough
 }
 
 fn g<T>(val: T) {
diff --git a/src/test/compile-fail/typeck-cast-pointer-to-float.rs b/src/test/compile-fail/typeck-cast-pointer-to-float.rs
index 285a5dbee05..e10a76c65bc 100644
--- a/src/test/compile-fail/typeck-cast-pointer-to-float.rs
+++ b/src/test/compile-fail/typeck-cast-pointer-to-float.rs
@@ -11,5 +11,5 @@
 fn main() {
     let x : i16 = 22;
     ((&x) as *const i16) as f32;
-    //~^ ERROR illegal cast; cast through an integer first: `*const i16` as `f32`
+    //~^ ERROR illegal cast; cast through a usize first: `*const i16` as `f32`
 }
diff --git a/src/test/compile-fail/vector-cast-weirdness.rs b/src/test/compile-fail/vector-cast-weirdness.rs
index cac52306d6a..10227f1820d 100644
--- a/src/test/compile-fail/vector-cast-weirdness.rs
+++ b/src/test/compile-fail/vector-cast-weirdness.rs
@@ -28,7 +28,7 @@ fn main() {
     let mut x1 = X { y: [0, 0] };
 
     // This is still an error since we don't allow casts from &mut [T; n] to *mut T.
-    let p1: *mut u8 = &mut x1.y as *mut _;  //~ ERROR mismatched types
+    let p1: *mut u8 = &mut x1.y as *mut _;  //~ ERROR illegal cast
     let t1: *mut [u8; 2] = &mut x1.y as *mut _;
     let h1: *mut [u8; 2] = &mut x1.y as *mut [u8; 2];
 }
diff --git a/src/test/run-pass-valgrind/cast-enum-with-dtor.rs b/src/test/run-pass-valgrind/cast-enum-with-dtor.rs
new file mode 100644
index 00000000000..0bc1e33ce46
--- /dev/null
+++ b/src/test/run-pass-valgrind/cast-enum-with-dtor.rs
@@ -0,0 +1,44 @@
+// 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.
+
+#![allow(dead_code)]
+
+// check dtor calling order when casting enums.
+
+use std::sync::atomic;
+use std::sync::atomic::Ordering;
+use std::mem;
+
+enum E {
+    A = 0,
+    B = 1,
+    C = 2
+}
+
+static FLAG: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT;
+
+impl Drop for E {
+    fn drop(&mut self) {
+        // avoid dtor loop
+        unsafe { mem::forget(mem::replace(self, E::B)) };
+
+        FLAG.store(FLAG.load(Ordering::SeqCst)+1, Ordering::SeqCst);
+    }
+}
+
+fn main() {
+    assert_eq!(FLAG.load(Ordering::SeqCst), 0);
+    {
+        let e = E::C;
+        assert_eq!(e as u32, 2);
+        assert_eq!(FLAG.load(Ordering::SeqCst), 0);
+    }
+    assert_eq!(FLAG.load(Ordering::SeqCst), 1);
+}
diff --git a/src/test/run-pass/cast-rfc0401-vtable-kinds.rs b/src/test/run-pass/cast-rfc0401-vtable-kinds.rs
new file mode 100644
index 00000000000..e53d4af8e36
--- /dev/null
+++ b/src/test/run-pass/cast-rfc0401-vtable-kinds.rs
@@ -0,0 +1,55 @@
+// 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.
+
+// Check that you can cast between different pointers to trait objects
+// whose vtable have the same kind (both lengths, or both trait pointers).
+
+trait Foo<T> {
+    fn foo(&self, _: T) -> u32 { 42 }
+}
+
+trait Bar {
+    fn bar(&self) { println!("Bar!"); }
+}
+
+impl<T> Foo<T> for () {}
+impl Foo<u32> for u32 { fn foo(&self, _: u32) -> u32 { self+43 } }
+impl Bar for () {}
+
+unsafe fn fool<'a>(t: *const (Foo<u32>+'a)) -> u32 {
+    let bar : *const Bar = t as *const Bar;
+    let foo_e : *const Foo<u16> = t as *const _;
+    let r_1 = foo_e as *mut Foo<u32>;
+
+    (&*r_1).foo(0)*(&*(bar as *const Foo<u32>)).foo(0)
+}
+
+#[repr(C)]
+struct FooS<T:?Sized>(T);
+#[repr(C)]
+struct BarS<T:?Sized>(T);
+
+fn foo_to_bar<T:?Sized>(u: *const FooS<T>) -> *const BarS<T> {
+    u as *const BarS<T>
+}
+
+fn main() {
+    let x = 4u32;
+    let y : &Foo<u32> = &x;
+    let fl = unsafe { fool(y as *const Foo<u32>) };
+    assert_eq!(fl, (43+4)*(43+4));
+
+    let s = FooS([0,1,2]);
+    let u: &FooS<[u32]> = &s;
+    let u: *const FooS<[u32]> = u;
+    let bar_ref : *const BarS<[u32]> = foo_to_bar(u);
+    let z : &BarS<[u32]> = unsafe{&*bar_ref};
+    assert_eq!(&z.0, &[0,1,2]);
+}
diff --git a/src/test/run-pass/cast-rfc0401.rs b/src/test/run-pass/cast-rfc0401.rs
new file mode 100644
index 00000000000..7c64c34fae5
--- /dev/null
+++ b/src/test/run-pass/cast-rfc0401.rs
@@ -0,0 +1,170 @@
+// 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.
+
+#![allow(dead_code)]
+
+use std::vec;
+
+enum Simple {
+    A,
+    B,
+    C
+}
+
+enum Valued {
+    H8=163,
+    Z=0,
+    X=256,
+    H7=67,
+}
+
+enum ValuedSigned {
+    M1=-1,
+    P1=1
+}
+
+fn main()
+{
+    // coercion-cast
+    let mut it = vec![137].into_iter();
+    let itr: &mut vec::IntoIter<u32> = &mut it;
+    assert_eq!((itr as &mut Iterator<Item=u32>).next(), Some(137));
+    assert_eq!((itr as &mut Iterator<Item=u32>).next(), None);
+
+    assert_eq!(Some(4u32) as Option<u32>, Some(4u32));
+    assert_eq!((1u32,2u32) as (u32,u32), (1,2));
+
+    // this isn't prim-int-cast. Check that it works.
+    assert_eq!(false as bool, false);
+    assert_eq!(true as bool, true);
+
+    // numeric-cast
+    let l: u64 = 0x8090a0b0c0d0e0f0;
+    let lsz: usize = l as usize;
+    assert_eq!(l as u32, 0xc0d0e0f0);
+
+    // numeric-cast
+    assert_eq!(l as u8, 0xf0);
+    assert_eq!(l as i8,-0x10);
+    assert_eq!(l as u32, 0xc0d0e0f0);
+    assert_eq!(l as u32 as usize as u32, l as u32);
+    assert_eq!(l as i32,-0x3f2f1f10);
+    assert_eq!(l as i32 as isize as i32, l as i32);
+    assert_eq!(l as i64,-0x7f6f5f4f3f2f1f10);
+
+    assert_eq!(0 as f64, 0f64);
+    assert_eq!(1 as f64, 1f64);
+
+    assert_eq!(l as f64, 9264081114510712022f64);
+
+    assert_eq!(l as i64 as f64, -9182662959198838444f64);
+//  float overflow : needs fixing
+//  assert_eq!(l as f32 as i64 as u64, 9264082620822882088u64);
+//  assert_eq!(l as i64 as f32 as i64, 9182664080220408446i64);
+
+    assert_eq!(4294967040f32 as u32, 0xffffff00u32);
+    assert_eq!(1.844674407370955e19f64 as u64, 0xfffffffffffff800u64);
+
+    assert_eq!(9.223372036854775e18f64 as i64, 0x7ffffffffffffc00i64);
+    assert_eq!(-9.223372036854776e18f64 as i64, 0x8000000000000000u64 as i64);
+
+    // addr-ptr-cast/ptr-addr-cast (thin ptr)
+    let p: *const [u8; 1] = lsz as *const [u8; 1];
+    assert_eq!(p as usize, lsz);
+
+    // ptr-ptr-cast (thin ptr)
+    let w: *const () = p as *const ();
+    assert_eq!(w as usize, lsz);
+
+    // ptr-ptr-cast (fat->thin)
+    let u: *const [u8] = unsafe{&*p};
+    assert_eq!(u as *const u8, p as *const u8);
+    assert_eq!(u as *const u16, p as *const u16);
+
+    // ptr-ptr-cast (both vk=Length)
+    let mut l : [u8; 2] = [0,1];
+    let w: *mut [u16; 2] = &mut l as *mut [u8; 2] as *mut _;
+    let w: *mut [u16] = unsafe {&mut *w};
+    let w_u8 : *const [u8] = w as *const [u8];
+    assert_eq!(unsafe{&*w_u8}, &l);
+
+    let s: *mut str = w as *mut str;
+    let l_via_str = unsafe{&*(s as *const [u8])};
+    assert_eq!(&l, l_via_str);
+
+    // enum-cast
+    assert_eq!(Simple::A as u8, 0);
+    assert_eq!(Simple::B as u8, 1);
+
+    assert_eq!(Valued::H8 as i8, -93);
+    assert_eq!(Valued::H7 as i8, 67);
+    assert_eq!(Valued::Z as i8, 0);
+
+    assert_eq!(Valued::H8 as u8, 163);
+    assert_eq!(Valued::H7 as u8, 67);
+    assert_eq!(Valued::Z as u8, 0);
+
+    assert_eq!(Valued::H8 as u16, 163);
+    assert_eq!(Valued::Z as u16, 0);
+    assert_eq!(Valued::H8 as u16, 163);
+    assert_eq!(Valued::Z as u16, 0);
+
+    assert_eq!(ValuedSigned::M1 as u16, 65535);
+    assert_eq!(ValuedSigned::M1 as i16, -1);
+    assert_eq!(ValuedSigned::P1 as u16, 1);
+    assert_eq!(ValuedSigned::P1 as i16, 1);
+
+    // prim-int-cast
+    assert_eq!(false as u16, 0);
+    assert_eq!(true as u16, 1);
+    assert_eq!(false as i64, 0);
+    assert_eq!(true as i64, 1);
+    assert_eq!('a' as u32, 0x61);
+    assert_eq!('a' as u16, 0x61);
+    assert_eq!('a' as u8, 0x61);
+    assert_eq!('א' as u8, 0xd0);
+    assert_eq!('א' as u16, 0x5d0);
+    assert_eq!('א' as u32, 0x5d0);
+    assert_eq!('🐵' as u8, 0x35);
+    assert_eq!('🐵' as u16, 0xf435);
+    assert_eq!('🐵' as u32, 0x1f435);
+    assert_eq!('英' as i16, -0x7d0f);
+    assert_eq!('英' as u16, 0x82f1);
+
+    // u8-char-cast
+    assert_eq!(0x61 as char, 'a');
+    assert_eq!(0u8 as char, '\0');
+    assert_eq!(0xd7 as char, '×');
+
+    // array-ptr-cast
+    let x = [1,2,3];
+    let first : *const u32 = &x[0];
+
+    assert_eq!(first, &x as *const _);
+    assert_eq!(first, &x as *const u32);
+
+    // fptr-addr-cast
+    fn foo() {
+        println!("foo!");
+    }
+    fn bar() {
+        println!("bar!");
+    }
+
+    assert!(foo as usize != bar as usize);
+
+    assert_eq!(foo as i16, foo as usize as i16);
+
+    // fptr-ptr-cast
+
+    assert_eq!(foo as *const u8 as usize, foo as usize);
+    assert!(foo as *const u32 != first);
+}
+fn foo() { }
diff --git a/src/test/run-pass/fat-ptr-cast.rs b/src/test/run-pass/fat-ptr-cast.rs
index b7513da99c8..91637d111fe 100644
--- a/src/test/run-pass/fat-ptr-cast.rs
+++ b/src/test/run-pass/fat-ptr-cast.rs
@@ -32,13 +32,12 @@ fn main() {
     // Test conversion to an address (usize).
     let a: *const [i32; 3] = &[1, 2, 3];
     let b: *const [i32] = a;
-    assert!(a as usize == b as usize);
+    assert!(a as usize == b as *const () as usize);
 
     // And conversion to a void pointer/address for trait objects too.
     let a: *mut Foo = &mut Bar;
     let b = a as *mut ();
-    let c = a as usize;
-
+    let c = a as *const () as usize;
     let d = unsafe {
         let r: raw::TraitObject = mem::transmute(a);
         r.data
@@ -46,4 +45,5 @@ fn main() {
 
     assert!(b == d);
     assert!(c == d as usize);
+
 }
diff --git a/src/test/run-pass/supported-cast.rs b/src/test/run-pass/supported-cast.rs
index 811b9dce4bc..a47ae52f590 100644
--- a/src/test/run-pass/supported-cast.rs
+++ b/src/test/run-pass/supported-cast.rs
@@ -181,7 +181,6 @@ pub fn main() {
 
   println!("{:?}", true as isize);
   println!("{:?}", true as usize);
-  println!("{:?}", true as *const libc::FILE);
   println!("{:?}", true as i8);
   println!("{:?}", true as i16);
   println!("{:?}", true as i32);
@@ -190,8 +189,6 @@ pub fn main() {
   println!("{:?}", true as u16);
   println!("{:?}", true as u32);
   println!("{:?}", true as u64);
-  println!("{:?}", true as f32);
-  println!("{:?}", true as f64);
 
   println!("{:?}", 1f32 as isize);
   println!("{:?}", 1f32 as usize);