about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/liballoc/boxed.rs8
-rw-r--r--src/liballoc/rc.rs4
-rw-r--r--src/libcore/intrinsics.rs7
-rw-r--r--src/libcore/marker.rs8
-rw-r--r--src/libcore/mem.rs34
-rw-r--r--src/libcore/nonzero.rs5
-rw-r--r--src/libcore/ops.rs34
-rw-r--r--src/librustc/diagnostics.rs3
-rw-r--r--src/librustc/metadata/common.rs2
-rw-r--r--src/librustc/metadata/csearch.rs8
-rw-r--r--src/librustc/metadata/decoder.rs10
-rw-r--r--src/librustc/metadata/encoder.rs10
-rw-r--r--src/librustc/middle/expr_use_visitor.rs4
-rw-r--r--src/librustc/middle/lang_items.rs3
-rw-r--r--src/librustc/middle/traits/error_reporting.rs56
-rw-r--r--src/librustc/middle/traits/mod.rs10
-rw-r--r--src/librustc/middle/traits/select.rs243
-rw-r--r--src/librustc/middle/traits/util.rs21
-rw-r--r--src/librustc/middle/ty.rs52
-rw-r--r--src/librustc_trans/trans/expr.rs200
-rw-r--r--src/librustc_trans/trans/glue.rs4
-rw-r--r--src/librustc_trans/trans/intrinsic.rs26
-rw-r--r--src/librustc_typeck/check/coercion.rs262
-rw-r--r--src/librustc_typeck/check/method/mod.rs4
-rw-r--r--src/librustc_typeck/check/mod.rs86
-rw-r--r--src/librustc_typeck/check/regionck.rs4
-rw-r--r--src/librustc_typeck/check/vtable.rs186
-rw-r--r--src/librustc_typeck/check/wf.rs4
-rw-r--r--src/librustc_typeck/coherence/mod.rs172
-rw-r--r--src/librustc_typeck/coherence/orphan.rs7
-rw-r--r--src/librustc_typeck/diagnostics.rs12
-rw-r--r--src/test/compile-fail/destructure-trait-ref.rs2
-rw-r--r--src/test/compile-fail/dst-bad-coercions.rs14
-rw-r--r--src/test/compile-fail/issue-19692.rs2
-rw-r--r--src/test/compile-fail/issue-20261.rs1
-rw-r--r--src/test/compile-fail/issue-22034.rs3
-rw-r--r--src/test/compile-fail/object-lifetime-default-elision.rs2
-rw-r--r--src/test/compile-fail/object-lifetime-default-from-box-error.rs4
-rw-r--r--src/test/compile-fail/regions-close-over-type-parameter-multiple.rs2
-rw-r--r--src/test/compile-fail/regions-trait-object-subtyping.rs2
40 files changed, 1013 insertions, 508 deletions
diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs
index a0d60be3000..757c799d85c 100644
--- a/src/liballoc/boxed.rs
+++ b/src/liballoc/boxed.rs
@@ -62,6 +62,11 @@ use core::ops::{Deref, DerefMut};
 use core::ptr::{Unique};
 use core::raw::{TraitObject};
 
+#[cfg(not(stage0))] // SNAP c64d671
+use core::marker::Unsize;
+#[cfg(not(stage0))] // SNAP c64d671
+use core::ops::CoerceUnsized;
+
 /// A value that represents the heap. This is the default place that the `box`
 /// keyword allocates into when no place is supplied.
 ///
@@ -390,3 +395,6 @@ impl<'a,A,R> FnOnce<A> for Box<FnBox<A,Output=R>+Send+'a> {
         self.call_box(args)
     }
 }
+
+#[cfg(not(stage0))] // SNAP c64d671
+impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs
index 015d0330ed7..a1b5e6e6baf 100644
--- a/src/liballoc/rc.rs
+++ b/src/liballoc/rc.rs
@@ -173,9 +173,9 @@ use core::intrinsics::assume;
 use heap::deallocate;
 
 struct RcBox<T> {
-    value: T,
     strong: Cell<usize>,
-    weak: Cell<usize>
+    weak: Cell<usize>,
+    value: T
 }
 
 /// A reference-counted pointer type over an immutable value.
diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs
index 100b7e70591..d94b8884112 100644
--- a/src/libcore/intrinsics.rs
+++ b/src/libcore/intrinsics.rs
@@ -193,6 +193,13 @@ extern "rust-intrinsic" {
     pub fn min_align_of<T>() -> usize;
     pub fn pref_align_of<T>() -> usize;
 
+    #[cfg(not(stage0))]
+    pub fn size_of_val<T: ?Sized>(_: &T) -> usize;
+    #[cfg(not(stage0))]
+    pub fn min_align_of_val<T: ?Sized>(_: &T) -> usize;
+    #[cfg(not(stage0))]
+    pub fn drop_in_place<T: ?Sized>(_: *mut T);
+
     /// Gets a static string slice containing the name of a type.
     pub fn type_name<T: ?Sized>() -> &'static str;
 
diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs
index 3aaedaeb813..968f68a78a7 100644
--- a/src/libcore/marker.rs
+++ b/src/libcore/marker.rs
@@ -53,6 +53,14 @@ pub trait Sized {
     // Empty.
 }
 
+/// Types that can be "unsized" to a dynamically sized type.
+#[unstable(feature = "core")]
+#[cfg(not(stage0))] // SNAP c64d671
+#[lang="unsize"]
+pub trait Unsize<T> {
+    // Empty.
+}
+
 /// Types that can be copied by simply copying bits (i.e. `memcpy`).
 ///
 /// By default, variable bindings have 'move semantics.' In other
diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs
index a149af3a440..bb94cd886d7 100644
--- a/src/libcore/mem.rs
+++ b/src/libcore/mem.rs
@@ -86,6 +86,22 @@ pub fn size_of<T>() -> usize {
     unsafe { intrinsics::size_of::<T>() }
 }
 
+/// Returns the size of the type that `val` points to in bytes.
+///
+/// # Examples
+///
+/// ```
+/// use std::mem;
+///
+/// assert_eq!(4, mem::size_of_val(&5i32));
+/// ```
+#[cfg(not(stage0))] // SNAP c64d671
+#[inline]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub fn size_of_val<T: ?Sized>(val: &T) -> usize {
+    unsafe { intrinsics::size_of_val(val) }
+}
+
 /// Returns the size of the type that `_val` points to in bytes.
 ///
 /// # Examples
@@ -95,6 +111,7 @@ pub fn size_of<T>() -> usize {
 ///
 /// assert_eq!(4, mem::size_of_val(&5i32));
 /// ```
+#[cfg(stage0)] // SNAP c64d671
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn size_of_val<T>(_val: &T) -> usize {
@@ -118,6 +135,22 @@ pub fn min_align_of<T>() -> usize {
     unsafe { intrinsics::min_align_of::<T>() }
 }
 
+/// Returns the ABI-required minimum alignment of the type of the value that `val` points to
+///
+/// # Examples
+///
+/// ```
+/// use std::mem;
+///
+/// assert_eq!(4, mem::min_align_of_val(&5i32));
+/// ```
+#[cfg(not(stage0))] // SNAP c64d671
+#[inline]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
+    unsafe { intrinsics::min_align_of_val(val) }
+}
+
 /// Returns the ABI-required minimum alignment of the type of the value that `_val` points to
 ///
 /// # Examples
@@ -127,6 +160,7 @@ pub fn min_align_of<T>() -> usize {
 ///
 /// assert_eq!(4, mem::min_align_of_val(&5i32));
 /// ```
+#[cfg(stage0)] // SNAP c64d671
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn min_align_of_val<T>(_val: &T) -> usize {
diff --git a/src/libcore/nonzero.rs b/src/libcore/nonzero.rs
index 13b6468105d..85957382826 100644
--- a/src/libcore/nonzero.rs
+++ b/src/libcore/nonzero.rs
@@ -12,6 +12,8 @@
 
 use marker::Sized;
 use ops::Deref;
+#[cfg(not(stage0))] // SNAP c64d671
+use ops::CoerceUnsized;
 
 /// Unsafe trait to indicate what types are usable with the NonZero struct
 pub unsafe trait Zeroable {}
@@ -54,3 +56,6 @@ impl<T: Zeroable> Deref for NonZero<T> {
         inner
     }
 }
+
+#[cfg(not(stage0))] // SNAP c64d671
+impl<T: Zeroable+CoerceUnsized<U>, U: Zeroable> CoerceUnsized<NonZero<U>> for NonZero<T> {}
diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs
index 55c4264b10c..1a2473fda41 100644
--- a/src/libcore/ops.rs
+++ b/src/libcore/ops.rs
@@ -70,6 +70,9 @@
 use marker::Sized;
 use fmt;
 
+#[cfg(not(stage0))] // SNAP c64d671
+use marker::Unsize;
+
 /// The `Drop` trait is used to run some code when a value goes out of scope. This
 /// is sometimes called a 'destructor'.
 ///
@@ -1207,3 +1210,34 @@ mod impls {
         }
     }
 }
+
+/// Trait that indicates that this is a pointer or a wrapper for one,
+/// where unsizing can be performed on the pointee.
+#[unstable(feature = "core")]
+#[cfg(not(stage0))] // SNAP c64d671
+#[lang="coerce_unsized"]
+pub trait CoerceUnsized<T> {
+    // Empty.
+}
+
+#[cfg(not(stage0))] // SNAP c64d671
+impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
+#[cfg(not(stage0))] // SNAP c64d671
+impl<'a, 'b: 'a, T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b mut T {}
+#[cfg(not(stage0))] // SNAP c64d671
+impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for &'a mut T {}
+#[cfg(not(stage0))] // SNAP c64d671
+impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a mut T {}
+
+#[cfg(not(stage0))] // SNAP c64d671
+impl<'a, 'b: 'a, T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
+#[cfg(not(stage0))] // SNAP c64d671
+impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a T {}
+
+#[cfg(not(stage0))] // SNAP c64d671
+impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
+#[cfg(not(stage0))] // SNAP c64d671
+impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {}
+
+#[cfg(not(stage0))] // SNAP c64d671
+impl<T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 6690e6831af..c8788f76081 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -807,6 +807,9 @@ register_diagnostics! {
     E0017,
     E0019,
     E0022,
+    E0038,
+    E0079, // enum variant: expected signed integer constant
+    E0080, // enum variant: constant evaluation error
     E0109,
     E0110,
     E0134,
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index b6202084296..f410626714f 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -259,3 +259,5 @@ pub const tag_codemap_filemap: usize = 0xa2;
 pub const tag_item_super_predicates: usize = 0xa3;
 
 pub const tag_defaulted_trait: usize = 0xa4;
+
+pub const tag_impl_coerce_unsized_kind: usize = 0xa5;
diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs
index 6caefec4878..8a35c012004 100644
--- a/src/librustc/metadata/csearch.rs
+++ b/src/librustc/metadata/csearch.rs
@@ -279,6 +279,14 @@ pub fn get_impl_polarity<'tcx>(tcx: &ty::ctxt<'tcx>,
     decoder::get_impl_polarity(&*cdata, def.node)
 }
 
+pub fn get_custom_coerce_unsized_kind<'tcx>(tcx: &ty::ctxt<'tcx>,
+                                            def: ast::DefId)
+                                            -> Option<ty::CustomCoerceUnsized> {
+    let cstore = &tcx.sess.cstore;
+    let cdata = cstore.get_crate_data(def.krate);
+    decoder::get_custom_coerce_unsized_kind(&*cdata, def.node)
+}
+
 // Given a def_id for an impl, return the trait it implements,
 // if there is one.
 pub fn get_impl_trait<'tcx>(tcx: &ty::ctxt<'tcx>,
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index 382dc437bdc..2f2a5d31c9f 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -489,6 +489,16 @@ pub fn get_impl_polarity<'tcx>(cdata: Cmd,
     }
 }
 
+pub fn get_custom_coerce_unsized_kind<'tcx>(cdata: Cmd,
+                                            id: ast::NodeId)
+                                            -> Option<ty::CustomCoerceUnsized> {
+    let item_doc = lookup_item(id, cdata.data());
+    reader::maybe_get_doc(item_doc, tag_impl_coerce_unsized_kind).map(|kind_doc| {
+        let mut decoder = reader::Decoder::new(kind_doc);
+        Decodable::decode(&mut decoder).unwrap()
+    })
+}
+
 pub fn get_impl_trait<'tcx>(cdata: Cmd,
                             id: ast::NodeId,
                             tcx: &ty::ctxt<'tcx>)
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index afc71f83975..86f33257e09 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -1219,6 +1219,16 @@ fn encode_info_for_item(ecx: &EncodeContext,
         encode_attributes(rbml_w, &item.attrs);
         encode_unsafety(rbml_w, unsafety);
         encode_polarity(rbml_w, polarity);
+
+        match tcx.custom_coerce_unsized_kinds.borrow().get(&local_def(item.id)) {
+            Some(&kind) => {
+                rbml_w.start_tag(tag_impl_coerce_unsized_kind);
+                kind.encode(rbml_w);
+                rbml_w.end_tag();
+            }
+            None => {}
+        }
+
         match ty.node {
             ast::TyPath(None, ref path) if path.segments.len() == 1 => {
                 let name = path.segments.last().unwrap().identifier.name;
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 0458bd70346..7366ad94534 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -890,10 +890,6 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
             }
         };
 
-        debug!("walk_autoref: expr.id={} cmt_base={}",
-               expr.id,
-               cmt_base.repr(self.tcx()));
-
         match *autoref {
             ty::AutoPtr(r, m) => {
                 self.delegate.borrow(expr.id,
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index c2865e33849..273cd6b4f85 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -261,11 +261,14 @@ lets_do_this! {
 
     SendTraitLangItem,               "send",                    send_trait;
     SizedTraitLangItem,              "sized",                   sized_trait;
+    UnsizeTraitLangItem,             "unsize",                  unsize_trait;
     CopyTraitLangItem,               "copy",                    copy_trait;
     SyncTraitLangItem,               "sync",                    sync_trait;
 
     DropTraitLangItem,               "drop",                    drop_trait;
 
+    CoerceUnsizedTraitLangItem,      "coerce_unsized",          coerce_unsized_trait;
+
     AddTraitLangItem,                "add",                     add_trait;
     SubTraitLangItem,                "sub",                     sub_trait;
     MulTraitLangItem,                "mul",                     mul_trait;
diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs
index 79d08cd825d..2b82987480d 100644
--- a/src/librustc/middle/traits/error_reporting.rs
+++ b/src/librustc/middle/traits/error_reporting.rs
@@ -15,8 +15,12 @@ use super::{
     Obligation,
     ObligationCauseCode,
     OutputTypeParameterMismatch,
+    TraitNotObjectSafe,
     PredicateObligation,
     SelectionError,
+    ObjectSafetyViolation,
+    MethodViolationCode,
+    object_safety_violations,
 };
 
 use fmt_macros::{Parser, Piece, Position};
@@ -252,6 +256,54 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                     note_obligation_cause(infcx, obligation);
             }
         }
+
+        TraitNotObjectSafe(did) => {
+            span_err!(infcx.tcx.sess, obligation.cause.span, E0038,
+                "cannot convert to a trait object because trait `{}` is not object-safe",
+                ty::item_path_str(infcx.tcx, did));
+
+            for violation in object_safety_violations(infcx.tcx, did) {
+                match violation {
+                    ObjectSafetyViolation::SizedSelf => {
+                        infcx.tcx.sess.span_note(
+                            obligation.cause.span,
+                            "the trait cannot require that `Self : Sized`");
+                    }
+
+                    ObjectSafetyViolation::SupertraitSelf => {
+                        infcx.tcx.sess.span_note(
+                            obligation.cause.span,
+                            "the trait cannot use `Self` as a type parameter \
+                            in the supertrait listing");
+                    }
+
+                    ObjectSafetyViolation::Method(method,
+                            MethodViolationCode::StaticMethod) => {
+                        infcx.tcx.sess.span_note(
+                            obligation.cause.span,
+                            &format!("method `{}` has no receiver",
+                                    method.name.user_string(infcx.tcx)));
+                    }
+
+                    ObjectSafetyViolation::Method(method,
+                            MethodViolationCode::ReferencesSelf) => {
+                        infcx.tcx.sess.span_note(
+                            obligation.cause.span,
+                            &format!("method `{}` references the `Self` type \
+                                    in its arguments or return type",
+                                    method.name.user_string(infcx.tcx)));
+                    }
+
+                    ObjectSafetyViolation::Method(method,
+                            MethodViolationCode::Generic) => {
+                        infcx.tcx.sess.span_note(
+                            obligation.cause.span,
+                            &format!("method `{}` has generic type parameters",
+                                    method.name.user_string(infcx.tcx)));
+                    }
+                }
+            }
+        }
     }
 }
 
@@ -403,10 +455,6 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
                        "only the last field of a struct or enum variant \
                        may have a dynamically sized type")
         }
-        ObligationCauseCode::ObjectSized => {
-            span_note!(tcx.sess, cause_span,
-                       "only sized types can be made into objects");
-        }
         ObligationCauseCode::SharedStatic => {
             span_note!(tcx.sess, cause_span,
                        "shared static variables must have a type that implements `Sync`");
diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs
index b221c4bb685..fe61bb5e4ea 100644
--- a/src/librustc/middle/traits/mod.rs
+++ b/src/librustc/middle/traits/mod.rs
@@ -28,6 +28,7 @@ use util::ppaux::Repr;
 
 pub use self::error_reporting::report_fulfillment_errors;
 pub use self::error_reporting::report_overflow_error;
+pub use self::error_reporting::report_selection_error;
 pub use self::error_reporting::suggest_new_overflow_limit;
 pub use self::coherence::orphan_check;
 pub use self::coherence::overlapping_impls;
@@ -48,6 +49,7 @@ pub use self::select::{MethodMatchedData}; // intentionally don't export variant
 pub use self::util::elaborate_predicates;
 pub use self::util::get_vtable_index_of_object_method;
 pub use self::util::trait_ref_for_builtin_bound;
+pub use self::util::predicate_for_trait_def;
 pub use self::util::supertraits;
 pub use self::util::Supertraits;
 pub use self::util::supertrait_def_ids;
@@ -121,9 +123,6 @@ pub enum ObligationCauseCode<'tcx> {
     // Types of fields (other than the last) in a struct must be sized.
     FieldSized,
 
-    // Only Sized types can be made into objects
-    ObjectSized,
-
     // static items must have `Sync` type
     SharedStatic,
 
@@ -159,6 +158,7 @@ pub enum SelectionError<'tcx> {
     OutputTypeParameterMismatch(ty::PolyTraitRef<'tcx>,
                                 ty::PolyTraitRef<'tcx>,
                                 ty::type_err<'tcx>),
+    TraitNotObjectSafe(ast::DefId),
 }
 
 pub struct FulfillmentError<'tcx> {
@@ -536,7 +536,9 @@ impl<'tcx, N> Vtable<'tcx, N> {
         }
     }
 
-    pub fn map_nested<M, F>(&self, op: F) -> Vtable<'tcx, M> where F: FnMut(&N) -> M {
+    pub fn map_nested<M, F>(&self, op: F) -> Vtable<'tcx, M> where
+        F: FnMut(&N) -> M,
+    {
         match *self {
             VtableImpl(ref i) => VtableImpl(i.map_nested(op)),
             VtableDefaultImpl(ref t) => VtableDefaultImpl(t.map_nested(op)),
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index e199bb17bf9..2541bba5d9a 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -25,6 +25,8 @@ use super::{PredicateObligation, TraitObligation, ObligationCause};
 use super::report_overflow_error;
 use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation};
 use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch};
+use super::{ObjectCastObligation, Obligation};
+use super::TraitNotObjectSafe;
 use super::Selection;
 use super::SelectionResult;
 use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure,
@@ -35,7 +37,7 @@ use super::util;
 
 use middle::fast_reject;
 use middle::subst::{Subst, Substs, TypeSpace, VecPerParamSpace};
-use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty};
+use middle::ty::{self, AsPredicate, RegionEscape, ToPolyTraitRef, Ty};
 use middle::infer;
 use middle::infer::{InferCtxt, TypeFreshener};
 use middle::ty_fold::TypeFoldable;
@@ -207,6 +209,8 @@ enum SelectionCandidate<'tcx> {
 
     BuiltinObjectCandidate,
 
+    BuiltinUnsizeCandidate,
+
     ErrorCandidate,
 }
 
@@ -904,6 +908,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 try!(self.assemble_builtin_bound_candidates(bound, stack, &mut candidates));
             }
 
+            None if self.tcx().lang_items.unsize_trait() ==
+                    Some(obligation.predicate.def_id()) => {
+                self.assemble_candidates_for_unsizing(obligation, &mut candidates);
+            }
+
             Some(ty::BoundSend) |
             Some(ty::BoundSync) |
             None => {
@@ -1356,6 +1365,64 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }).unwrap();
     }
 
+    /// Search for unsizing that might apply to `obligation`.
+    fn assemble_candidates_for_unsizing(&mut self,
+                                        obligation: &TraitObligation<'tcx>,
+                                        candidates: &mut SelectionCandidateSet<'tcx>) {
+        // TODO is it Ok to skip the binder here?
+        let source = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
+        let target = self.infcx.shallow_resolve(obligation.predicate.0.input_types()[0]);
+
+        debug!("assemble_candidates_for_unsizing(source={}, target={})",
+               source.repr(self.tcx()), target.repr(self.tcx()));
+
+        let may_apply = match (&source.sty, &target.sty) {
+            // Trait+Kx+'a -> Trait+Ky+'b (upcasts).
+            (&ty::ty_trait(ref data_a), &ty::ty_trait(ref data_b)) => {
+                // Upcasts permit two things:
+                //
+                // 1. Dropping builtin bounds, e.g. `Foo+Send` to `Foo`
+                // 2. Tightening the region bound, e.g. `Foo+'a` to `Foo+'b` if `'a : 'b`
+                //
+                // Note that neither of these changes requires any
+                // change at runtime.  Eventually this will be
+                // generalized.
+                //
+                // We always upcast when we can because of reason
+                // #2 (region bounds).
+                data_a.principal.def_id() == data_a.principal.def_id() &&
+                data_a.bounds.builtin_bounds.is_superset(&data_b.bounds.builtin_bounds)
+            }
+
+            // T -> Trait.
+            (_, &ty::ty_trait(_)) => true,
+
+            // Ambiguous handling is below T -> Trait, because inference
+            // variables can still implement Unsize<Trait> and nested
+            // obligations will have the final say (likely deferred).
+            (&ty::ty_infer(ty::TyVar(_)), _) |
+            (_, &ty::ty_infer(ty::TyVar(_))) => {
+                debug!("assemble_candidates_for_unsizing: ambiguous");
+                candidates.ambiguous = true;
+                false
+            }
+
+            // [T; n] -> [T].
+            (&ty::ty_vec(_, Some(_)), &ty::ty_vec(_, None)) => true,
+
+            // Struct<T> -> Struct<U>.
+            (&ty::ty_struct(def_id_a, _), &ty::ty_struct(def_id_b, _)) => {
+                def_id_a == def_id_b
+            }
+
+            _ => false
+        };
+
+        if may_apply {
+            candidates.vec.push(BuiltinUnsizeCandidate);
+        }
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // WINNOW
     //
@@ -1427,6 +1494,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 &ClosureCandidate(..) |
                 &FnPointerCandidate(..) |
                 &BuiltinObjectCandidate(..) |
+                &&BuiltinUnsizeCandidate(..) |
                 &DefaultImplObjectCandidate(..) |
                 &BuiltinCandidate(..) => {
                     // We have a where-clause so don't go around looking
@@ -1855,11 +1923,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                                                   obligation.recursion_depth + 1,
                                                   &skol_ty);
                 let skol_obligation =
-                    try!(util::predicate_for_trait_def(self.tcx(),
-                                                       derived_cause.clone(),
-                                                       trait_def_id,
-                                                       obligation.recursion_depth + 1,
-                                                       normalized_ty));
+                    util::predicate_for_trait_def(self.tcx(),
+                                                  derived_cause.clone(),
+                                                  trait_def_id,
+                                                  obligation.recursion_depth + 1,
+                                                  normalized_ty,
+                                                  vec![]);
                 obligations.push(skol_obligation);
                 Ok(self.infcx().plug_leaks(skol_map, snapshot, &obligations))
             })
@@ -1949,6 +2018,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 self.confirm_projection_candidate(obligation);
                 Ok(VtableParam(Vec::new()))
             }
+
+            BuiltinUnsizeCandidate => {
+                let data = try!(self.confirm_builtin_unsize_candidate(obligation));
+                Ok(VtableBuiltin(data))
+            }
         }
     }
 
@@ -2322,6 +2396,159 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
     }
 
+    fn confirm_builtin_unsize_candidate(&mut self,
+                                        obligation: &TraitObligation<'tcx>,)
+                                        -> Result<VtableBuiltinData<PredicateObligation<'tcx>>,
+                                                  SelectionError<'tcx>> {
+        let tcx = self.tcx();
+
+        // TODO is this skip_binder Ok?
+        let source = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
+        let target = self.infcx.shallow_resolve(obligation.predicate.0.input_types()[0]);
+
+        debug!("confirm_builtin_unsize_candidate(source={}, target={})",
+               source.repr(tcx), target.repr(tcx));
+
+        let mut nested = vec![];
+        match (&source.sty, &target.sty) {
+            // Trait+Kx+'a -> Trait+Ky+'b (upcasts).
+            (&ty::ty_trait(ref data_a), &ty::ty_trait(ref data_b)) => {
+                // See assemble_candidates_for_unsizing for more info.
+                let bounds = ty::ExistentialBounds {
+                    region_bound: data_b.bounds.region_bound,
+                    builtin_bounds: data_b.bounds.builtin_bounds,
+                    projection_bounds: data_a.bounds.projection_bounds.clone(),
+                };
+
+                let new_trait = ty::mk_trait(tcx, data_a.principal.clone(), bounds);
+                let origin = infer::Misc(obligation.cause.span);
+                if self.infcx.sub_types(false, origin, new_trait, target).is_err() {
+                    return Err(Unimplemented);
+                }
+
+                // Register one obligation for 'a: 'b.
+                let cause = ObligationCause::new(obligation.cause.span,
+                                                 obligation.cause.body_id,
+                                                 ObjectCastObligation(target));
+                let outlives = ty::OutlivesPredicate(data_a.bounds.region_bound,
+                                                     data_b.bounds.region_bound);
+                nested.push(Obligation::with_depth(cause,
+                                                   obligation.recursion_depth + 1,
+                                                   ty::Binder(outlives).as_predicate()));
+            }
+
+            // T -> Trait.
+            (_, &ty::ty_trait(ref data)) => {
+                let object_did = data.principal_def_id();
+                if !object_safety::is_object_safe(tcx, object_did) {
+                    return Err(TraitNotObjectSafe(object_did));
+                }
+
+                let cause = ObligationCause::new(obligation.cause.span,
+                                                 obligation.cause.body_id,
+                                                 ObjectCastObligation(target));
+                let mut push = |predicate| {
+                    nested.push(Obligation::with_depth(cause.clone(),
+                                                       obligation.recursion_depth + 1,
+                                                       predicate));
+                };
+
+                // Create the obligation for casting from T to Trait.
+                push(data.principal_trait_ref_with_self_ty(tcx, source).as_predicate());
+
+                // We can only make objects from sized types.
+                let mut builtin_bounds = data.bounds.builtin_bounds;
+                builtin_bounds.insert(ty::BoundSized);
+
+                // Create additional obligations for all the various builtin
+                // bounds attached to the object cast. (In other words, if the
+                // object type is Foo+Send, this would create an obligation
+                // for the Send check.)
+                for bound in &builtin_bounds {
+                    if let Ok(tr) = util::trait_ref_for_builtin_bound(tcx, bound, source) {
+                        push(tr.as_predicate());
+                    } else {
+                        return Err(Unimplemented);
+                    }
+                }
+
+                // Create obligations for the projection predicates.
+                for bound in data.projection_bounds_with_self_ty(tcx, source) {
+                    push(bound.as_predicate());
+                }
+
+                // If the type is `Foo+'a`, ensures that the type
+                // being cast to `Foo+'a` outlives `'a`:
+                let outlives = ty::OutlivesPredicate(source,
+                                                     data.bounds.region_bound);
+                push(ty::Binder(outlives).as_predicate());
+            }
+
+            // [T; n] -> [T].
+            (&ty::ty_vec(a, Some(_)), &ty::ty_vec(b, None)) => {
+                let origin = infer::Misc(obligation.cause.span);
+                if self.infcx.sub_types(false, origin, a, b).is_err() {
+                    return Err(Unimplemented);
+                }
+            }
+
+            // Struct<T> -> Struct<U>.
+            (&ty::ty_struct(def_id, substs_a), &ty::ty_struct(_, substs_b)) => {
+                let fields = ty::lookup_struct_fields(tcx, def_id).iter().map(|f| {
+                    ty::lookup_field_type_unsubstituted(tcx, def_id, f.id)
+                }).collect::<Vec<_>>();
+
+                // The last field of the structure has to exist and be a
+                // type parameter (for now, to avoid tracking edge cases).
+                let i = if let Some(&ty::ty_param(p)) = fields.last().map(|ty| &ty.sty) {
+                    assert!(p.space == TypeSpace);
+                    p.idx as usize
+                } else {
+                    return Err(Unimplemented);
+                };
+
+                // Replace the type parameter chosen for unsizing with
+                // ty_err and ensure it does not affect any other fields.
+                // This could be checked after type collection for any struct
+                // with a potentially unsized trailing field.
+                let mut new_substs = substs_a.clone();
+                new_substs.types.get_mut_slice(TypeSpace)[i] = tcx.types.err;
+                for &ty in fields.init() {
+                    if ty::type_is_error(ty.subst(tcx, &new_substs)) {
+                        return Err(Unimplemented);
+                    }
+                }
+
+                // Extract T and U from Struct<T> and Struct<U>.
+                let inner_source = *substs_a.types.get(TypeSpace, i);
+                let inner_target = *substs_b.types.get(TypeSpace, i);
+
+                // Check that all the source structure with the unsized
+                // type parameter is a subtype of the target.
+                new_substs.types.get_mut_slice(TypeSpace)[i] = inner_target;
+                let new_struct = ty::mk_struct(tcx, def_id, tcx.mk_substs(new_substs));
+                let origin = infer::Misc(obligation.cause.span);
+                if self.infcx.sub_types(false, origin, new_struct, target).is_err() {
+                    return Err(Unimplemented);
+                }
+
+                // Construct the nested T: Unsize<U> predicate.
+                nested.push(util::predicate_for_trait_def(tcx,
+                    obligation.cause.clone(),
+                    obligation.predicate.def_id(),
+                    obligation.recursion_depth + 1,
+                    inner_source,
+                    vec![inner_target]));
+            }
+
+            _ => unreachable!()
+        };
+
+        Ok(VtableBuiltinData {
+            nested: VecPerParamSpace::new(nested, vec![], vec![])
+        })
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Matching
     //
@@ -2683,6 +2910,7 @@ impl<'tcx> Repr<'tcx> for SelectionCandidate<'tcx> {
             ErrorCandidate => format!("ErrorCandidate"),
             BuiltinCandidate(b) => format!("BuiltinCandidate({:?})", b),
             BuiltinObjectCandidate => format!("BuiltinObjectCandidate"),
+            BuiltinUnsizeCandidate => format!("BuiltinUnsizeCandidate"),
             ParamCandidate(ref a) => format!("ParamCandidate({})", a.repr(tcx)),
             ImplCandidate(a) => format!("ImplCandidate({})", a.repr(tcx)),
             DefaultImplCandidate(t) => format!("DefaultImplCandidate({:?})", t),
@@ -2756,7 +2984,8 @@ impl<'tcx> EvaluationResult<'tcx> {
         match *self {
             EvaluatedToOk |
             EvaluatedToAmbig |
-            EvaluatedToErr(OutputTypeParameterMismatch(..)) =>
+            EvaluatedToErr(OutputTypeParameterMismatch(..)) |
+            EvaluatedToErr(TraitNotObjectSafe(_)) =>
                 true,
 
             EvaluatedToErr(Unimplemented) =>
diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs
index a6b2359c2b8..ea4bc029ed6 100644
--- a/src/librustc/middle/traits/util.rs
+++ b/src/librustc/middle/traits/util.rs
@@ -356,13 +356,13 @@ pub fn predicate_for_trait_ref<'tcx>(
     cause: ObligationCause<'tcx>,
     trait_ref: ty::TraitRef<'tcx>,
     recursion_depth: usize)
-    -> Result<PredicateObligation<'tcx>, ErrorReported>
+    -> PredicateObligation<'tcx>
 {
-    Ok(Obligation {
+    Obligation {
         cause: cause,
         recursion_depth: recursion_depth,
         predicate: trait_ref.as_predicate(),
-    })
+    }
 }
 
 pub fn predicate_for_trait_def<'tcx>(
@@ -370,13 +370,14 @@ pub fn predicate_for_trait_def<'tcx>(
     cause: ObligationCause<'tcx>,
     trait_def_id: ast::DefId,
     recursion_depth: usize,
-    param_ty: Ty<'tcx>)
-    -> Result<PredicateObligation<'tcx>, ErrorReported>
+    param_ty: Ty<'tcx>,
+    ty_params: Vec<Ty<'tcx>>)
+    -> PredicateObligation<'tcx>
 {
     let trait_ref = ty::TraitRef {
         def_id: trait_def_id,
-        substs: tcx.mk_substs(Substs::empty().with_self_ty(param_ty))
-    };
+        substs: tcx.mk_substs(Substs::new_trait(ty_params, vec![], param_ty))
+    });
     predicate_for_trait_ref(cause, trait_ref, recursion_depth)
 }
 
@@ -389,7 +390,7 @@ pub fn predicate_for_builtin_bound<'tcx>(
     -> Result<PredicateObligation<'tcx>, ErrorReported>
 {
     let trait_ref = try!(trait_ref_for_builtin_bound(tcx, builtin_bound, param_ty));
-    predicate_for_trait_ref(cause, trait_ref, recursion_depth)
+    Ok(predicate_for_trait_ref(cause, trait_ref, recursion_depth))
 }
 
 /// Cast a trait reference into a reference to one of its super
@@ -561,6 +562,10 @@ impl<'tcx> Repr<'tcx> for super::SelectionError<'tcx> {
                         a.repr(tcx),
                         b.repr(tcx),
                         c.repr(tcx)),
+
+            super::TraitNotObjectSafe(ref tr) =>
+                format!("TraitNotObjectSafe({})",
+                        tr.repr(tcx))
         }
     }
 }
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index e445b6bb300..961f8a79324 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -404,6 +404,12 @@ pub enum AutoRef<'tcx> {
     AutoUnsafe(ast::Mutability),
 }
 
+#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)]
+pub enum CustomCoerceUnsized {
+    /// Records the index of the field being coerced.
+    Struct(usize)
+}
+
 #[derive(Clone, Copy, RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Debug)]
 pub struct param_index {
     pub space: subst::ParamSpace,
@@ -818,6 +824,9 @@ pub struct ctxt<'tcx> {
 
     /// Maps Expr NodeId's to their constant qualification.
     pub const_qualif_map: RefCell<NodeMap<check_const::ConstQualif>>,
+
+    /// Caches CoerceUnsized kinds for impls on custom types.
+    pub custom_coerce_unsized_kinds: RefCell<DefIdMap<CustomCoerceUnsized>>,
 }
 
 impl<'tcx> ctxt<'tcx> {
@@ -2809,6 +2818,7 @@ pub fn mk_ctxt<'tcx>(s: Session,
         type_impls_copy_cache: RefCell::new(HashMap::new()),
         type_impls_sized_cache: RefCell::new(HashMap::new()),
         const_qualif_map: RefCell::new(NodeMap()),
+        custom_coerce_unsized_kinds: RefCell::new(DefIdMap()),
    }
 }
 
@@ -5351,6 +5361,26 @@ pub fn trait_impl_polarity<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
      }
 }
 
+pub fn custom_coerce_unsized_kind<'tcx>(cx: &ctxt<'tcx>, did: ast::DefId)
+                                        -> CustomCoerceUnsized {
+    memoized(&cx.custom_coerce_unsized_kinds, did, |did: DefId| {
+        let (kind, src) = if did.krate != ast::LOCAL_CRATE {
+            (csearch::get_custom_coerce_unsized_kind(cx, did), "external")
+        } else {
+            (None, "local")
+        };
+
+        match kind {
+            Some(kind) => kind,
+            None => {
+                cx.sess.bug(&format!("custom_coerce_unsized_kind: \
+                                      {} impl `{}` is missing its kind",
+                                     src, item_path_str(cx, did)));
+            }
+        }
+    })
+}
+
 pub fn impl_or_trait_item<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
                                 -> ImplOrTraitItem<'tcx> {
     lookup_locally_or_in_crate_store("impl_or_trait_items",
@@ -6013,19 +6043,27 @@ pub fn lookup_repr_hints(tcx: &ctxt, did: DefId) -> Rc<Vec<attr::ReprAttr>> {
 }
 
 // Look up a field ID, whether or not it's local
+pub fn lookup_field_type_unsubstituted<'tcx>(tcx: &ctxt<'tcx>,
+                                             struct_id: DefId,
+                                             id: DefId)
+                                             -> Ty<'tcx> {
+    if id.krate == ast::LOCAL_CRATE {
+        node_id_to_type(tcx, id.node)
+    } else {
+        let mut tcache = tcx.tcache.borrow_mut();
+        tcache.entry(id).or_insert_with(|| csearch::get_field_type(tcx, struct_id, id)).ty
+    }
+}
+
+
+// Look up a field ID, whether or not it's local
 // Takes a list of type substs in case the struct is generic
 pub fn lookup_field_type<'tcx>(tcx: &ctxt<'tcx>,
                                struct_id: DefId,
                                id: DefId,
                                substs: &Substs<'tcx>)
                                -> Ty<'tcx> {
-    let ty = if id.krate == ast::LOCAL_CRATE {
-        node_id_to_type(tcx, id.node)
-    } else {
-        let mut tcache = tcx.tcache.borrow_mut();
-        tcache.entry(id).or_insert_with(|| csearch::get_field_type(tcx, struct_id, id)).ty
-    };
-    ty.subst(tcx, substs)
+    lookup_field_type_unsubstituted(tcx, struct_id, id).subst(tcx, substs)
 }
 
 // Look up the list of field names and IDs for a given struct.
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 7be1f3813d7..0343571562f 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -56,8 +56,10 @@ use back::abi;
 use llvm::{self, ValueRef};
 use middle::check_const;
 use middle::def;
+use middle::lang_items::CoerceUnsizedTraitLangItem;
 use middle::mem_categorization::Typer;
-use middle::subst::{self, Substs};
+use middle::subst::{Subst, Substs, VecPerParamSpace};
+use middle::traits;
 use trans::{_match, adt, asm, base, callee, closure, consts, controlflow};
 use trans::base::*;
 use trans::build::*;
@@ -304,7 +306,7 @@ pub fn unsized_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>,
                                 source: Ty<'tcx>,
                                 target: Ty<'tcx>,
                                 old_info: Option<ValueRef>,
-                                param_substs: &'tcx subst::Substs<'tcx>)
+                                param_substs: &'tcx Substs<'tcx>)
                                 -> ValueRef {
     let (source, target) = ty::struct_lockstep_tails(ccx.tcx(), source, target);
     match (&source.sty, &target.sty) {
@@ -390,81 +392,155 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             // (You might think there is a more elegant way to do this than a
             // skip_reborrows bool, but then you remember that the borrow checker exists).
             if skip_reborrows == 0 && adj.autoref.is_some() {
-                datum = unpack_datum!(bcx, apply_autoref(bcx, expr, datum));
+                if !type_is_sized(bcx.tcx(), datum.ty) {
+                    // Arrange cleanup
+                    let lval = unpack_datum!(bcx,
+                        datum.to_lvalue_datum(bcx, "ref_fat_ptr", expr.id));
+                    datum = unpack_datum!(bcx, ref_fat_ptr(bcx, lval));
+                } else {
+                    datum = unpack_datum!(bcx, auto_ref(bcx, datum, expr));
+                }
             }
 
             if let Some(target) = adj.unsize {
-                datum = unpack_datum!(bcx, unsize_pointer(bcx, datum,
-                                                          bcx.monomorphize(&target)));
+                // We do not arrange cleanup ourselves; if we already are an
+                // L-value, then cleanup will have already been scheduled (and
+                // the `datum.to_rvalue_datum` call below will emit code to zero
+                // the drop flag when moving out of the L-value). If we are an
+                // R-value, then we do not need to schedule cleanup.
+                let source_datum = unpack_datum!(bcx,
+                    datum.to_rvalue_datum(bcx, "__coerce_source"));
+
+                let target = bcx.monomorphize(&target);
+                let llty = type_of::type_of(bcx.ccx(), target);
+
+                // HACK(eddyb) get around issues with lifetime intrinsics.
+                let scratch = alloca_no_lifetime(bcx, llty, "__coerce_target");
+                let target_datum = Datum::new(scratch, target,
+                                              Rvalue::new(ByRef));
+                bcx = coerce_unsized(bcx, expr.span, source_datum, target_datum);
+                datum = Datum::new(scratch, target,
+                                   RvalueExpr(Rvalue::new(ByRef)));
             }
         }
     }
     debug!("after adjustments, datum={}", datum.to_string(bcx.ccx()));
-    return DatumBlock::new(bcx, datum);
+    DatumBlock::new(bcx, datum)
+}
 
-    fn apply_autoref<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                 expr: &ast::Expr,
-                                 datum: Datum<'tcx, Expr>)
-                                 -> DatumBlock<'blk, 'tcx, Expr> {
-        let mut bcx = bcx;
+fn coerce_unsized<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+                              span: codemap::Span,
+                              source: Datum<'tcx, Rvalue>,
+                              target: Datum<'tcx, Rvalue>)
+                              -> Block<'blk, 'tcx> {
+    let mut bcx = bcx;
+    debug!("coerce_unsized({} -> {})",
+           source.to_string(bcx.ccx()),
+           target.to_string(bcx.ccx()));
+
+    match (&source.ty.sty, &target.ty.sty) {
+        (&ty::ty_uniq(a), &ty::ty_uniq(b)) |
+        (&ty::ty_rptr(_, ty::mt { ty: a, .. }), &ty::ty_rptr(_, ty::mt { ty: b, .. })) |
+        (&ty::ty_rptr(_, ty::mt { ty: a, .. }), &ty::ty_ptr(ty::mt { ty: b, .. })) |
+        (&ty::ty_ptr(ty::mt { ty: a, .. }), &ty::ty_ptr(ty::mt { ty: b, .. })) => {
+            let (inner_source, inner_target) = (a, b);
+
+            let (base, old_info) = if !type_is_sized(bcx.tcx(), inner_source) {
+                // Normally, the source is a thin pointer and we are
+                // adding extra info to make a fat pointer. The exception
+                // is when we are upcasting an existing object fat pointer
+                // to use a different vtable. In that case, we want to
+                // load out the original data pointer so we can repackage
+                // it.
+                (Load(bcx, get_dataptr(bcx, source.val)),
+                Some(Load(bcx, get_len(bcx, source.val))))
+            } else {
+                let val = if source.kind.is_by_ref() {
+                    load_ty(bcx, source.val, source.ty)
+                } else {
+                    source.val
+                };
+                (val, None)
+            };
 
-        if !type_is_sized(bcx.tcx(), datum.ty) {
-            // Arrange cleanup
-            let lval = unpack_datum!(bcx,
-                datum.to_lvalue_datum(bcx, "ref_fat_ptr", expr.id));
-            ref_fat_ptr(bcx, lval)
-        } else {
-            auto_ref(bcx, datum, expr)
+            let info = unsized_info(bcx.ccx(), inner_source, inner_target,
+                                    old_info, bcx.fcx.param_substs);
+
+            // Compute the base pointer. This doesn't change the pointer value,
+            // but merely its type.
+            let ptr_ty = type_of::in_memory_type_of(bcx.ccx(), inner_target).ptr_to();
+            let base = PointerCast(bcx, base, ptr_ty);
+
+            Store(bcx, base, get_dataptr(bcx, target.val));
+            Store(bcx, info, get_len(bcx, target.val));
         }
-    }
 
-    fn unsize_pointer<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                  datum: Datum<'tcx, Expr>,
-                                  target: Ty<'tcx>)
-                                  -> DatumBlock<'blk, 'tcx, Expr> {
-        let mut bcx = bcx;
-        let unsized_ty = ty::deref(target, true)
-            .expect("expr::unsize got non-pointer target type").ty;
-        debug!("unsize_lvalue(unsized_ty={})", unsized_ty.repr(bcx.tcx()));
-
-        // We do not arrange cleanup ourselves; if we already are an
-        // L-value, then cleanup will have already been scheduled (and
-        // the `datum.to_rvalue_datum` call below will emit code to zero
-        // the drop flag when moving out of the L-value). If we are an
-        // R-value, then we do not need to schedule cleanup.
-        let datum = unpack_datum!(bcx, datum.to_rvalue_datum(bcx, "__unsize_ref"));
-
-        let pointee_ty = ty::deref(datum.ty, true)
-            .expect("expr::unsize got non-pointer datum type").ty;
-        let (base, old_info) = if !type_is_sized(bcx.tcx(), pointee_ty) {
-            // Normally, the source is a thin pointer and we are
-            // adding extra info to make a fat pointer. The exception
-            // is when we are upcasting an existing object fat pointer
-            // to use a different vtable. In that case, we want to
-            // load out the original data pointer so we can repackage
-            // it.
-            (Load(bcx, get_dataptr(bcx, datum.val)),
-             Some(Load(bcx, get_len(bcx, datum.val))))
-        } else {
-            (datum.val, None)
-        };
+        // This can be extended to enums and tuples in the future.
+        // (&ty::ty_enum(def_id_a, substs_a), &ty::ty_enum(def_id_b, substs_b)) |
+        (&ty::ty_struct(def_id_a, substs_a), &ty::ty_struct(def_id_b, substs_b)) => {
+            assert_eq!(def_id_a, def_id_b);
 
-        let info = unsized_info(bcx.ccx(), pointee_ty, unsized_ty,
-                                old_info, bcx.fcx.param_substs);
+            // The target is already by-ref because it's to be written to.
+            let source = unpack_datum!(bcx, source.to_ref_datum(bcx));
+            assert!(target.kind.is_by_ref());
 
-        // Compute the base pointer. This doesn't change the pointer value,
-        // but merely its type.
-        let ptr_ty = type_of::in_memory_type_of(bcx.ccx(), unsized_ty).ptr_to();
-        let base = PointerCast(bcx, base, ptr_ty);
+            let trait_substs = Substs::erased(VecPerParamSpace::new(vec![target.ty],
+                                                                    vec![source.ty],
+                                                                    Vec::new()));
+            let trait_ref = ty::Binder(Rc::new(ty::TraitRef {
+                def_id: langcall(bcx, Some(span), "coercion",
+                                 CoerceUnsizedTraitLangItem),
+                substs: bcx.tcx().mk_substs(trait_substs)
+            }));
+
+            let kind = match fulfill_obligation(bcx.ccx(), span, trait_ref) {
+                traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => {
+                    ty::custom_coerce_unsized_kind(bcx.tcx(), impl_def_id)
+                }
+                vtable => {
+                    bcx.sess().span_bug(span, &format!("invalid CoerceUnsized vtable: {}",
+                                                       vtable.repr(bcx.tcx())));
+                }
+            };
 
-        let llty = type_of::type_of(bcx.ccx(), target);
-        // HACK(eddyb) get around issues with lifetime intrinsics.
-        let scratch = alloca_no_lifetime(bcx, llty, "__fat_ptr");
-        Store(bcx, base, get_dataptr(bcx, scratch));
-        Store(bcx, info, get_len(bcx, scratch));
+            let repr_source = adt::represent_type(bcx.ccx(), source.ty);
+            let repr_target = adt::represent_type(bcx.ccx(), target.ty);
+            let fields = ty::lookup_struct_fields(bcx.tcx(), def_id_a);
 
-        DatumBlock::new(bcx, Datum::new(scratch, target, RvalueExpr(Rvalue::new(ByRef))))
+            let coerce_index = match kind {
+                ty::CustomCoerceUnsized::Struct(i) => i
+            };
+            assert!(coerce_index < fields.len());
+
+            for (i, field) in fields.iter().enumerate() {
+                let ll_source = adt::trans_field_ptr(bcx, &repr_source, source.val, 0, i);
+                let ll_target = adt::trans_field_ptr(bcx, &repr_target, target.val, 0, i);
+
+                let ty = ty::lookup_field_type_unsubstituted(bcx.tcx(),
+                                                             def_id_a,
+                                                             field.id);
+                let field_source = ty.subst(bcx.tcx(), substs_a);
+                let field_target = ty.subst(bcx.tcx(), substs_b);
+
+                // If this is the field we need to coerce, recurse on it.
+                if i == coerce_index {
+                    coerce_unsized(bcx, span,
+                                   Datum::new(ll_source, field_source,
+                                              Rvalue::new(ByRef)),
+                                   Datum::new(ll_target, field_target,
+                                              Rvalue::new(ByRef)));
+                } else {
+                    // Otherwise, simply copy the data from the source.
+                    assert_eq!(field_source, field_target);
+                    memcpy_ty(bcx, ll_target, ll_source, field_source);
+                }
+            }
+        }
+        _ => bcx.sess().bug(&format!("coerce_unsized: invalid coercion {} -> {}",
+                                     source.ty.repr(bcx.tcx()),
+                                     target.ty.repr(bcx.tcx())))
     }
+    bcx
 }
 
 /// Translates an expression in "lvalue" mode -- meaning that it returns a reference to the memory
@@ -1178,7 +1254,7 @@ fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 pub fn trans_def_fn_unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                          ref_expr: &ast::Expr,
                                          def: def::Def,
-                                         param_substs: &'tcx subst::Substs<'tcx>)
+                                         param_substs: &'tcx Substs<'tcx>)
                                          -> Datum<'tcx, Rvalue> {
     let _icx = push_ctxt("trans_def_datum_unadjusted");
 
diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs
index a2a9e89ff63..fd1f22e1d9d 100644
--- a/src/librustc_trans/trans/glue.rs
+++ b/src/librustc_trans/trans/glue.rs
@@ -392,8 +392,8 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     bcx.fcx.pop_and_trans_custom_cleanup_scope(bcx, contents_scope)
 }
 
-fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, info: ValueRef)
-                                     -> (ValueRef, ValueRef) {
+pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, info: ValueRef)
+                                         -> (ValueRef, ValueRef) {
     debug!("calculate size of DST: {}; with lost info: {}",
            bcx.ty_to_string(t), bcx.val_to_string(info));
     if type_is_sized(bcx.tcx(), t) {
diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs
index 4b1bdd63dd7..951d30c4fb8 100644
--- a/src/librustc_trans/trans/intrinsic.rs
+++ b/src/librustc_trans/trans/intrinsic.rs
@@ -326,10 +326,31 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             let lltp_ty = type_of::type_of(ccx, tp_ty);
             C_uint(ccx, machine::llsize_of_alloc(ccx, lltp_ty))
         }
+        (_, "size_of_val") => {
+            let tp_ty = *substs.types.get(FnSpace, 0);
+            if !type_is_sized(tcx, tp_ty) {
+                let info = Load(bcx, expr::get_len(bcx, llargs[0]));
+                let (llsize, _) = glue::size_and_align_of_dst(bcx, tp_ty, info);
+                llsize
+            } else {
+                let lltp_ty = type_of::type_of(ccx, tp_ty);
+                C_uint(ccx, machine::llsize_of_alloc(ccx, lltp_ty))
+            }
+        }
         (_, "min_align_of") => {
             let tp_ty = *substs.types.get(FnSpace, 0);
             C_uint(ccx, type_of::align_of(ccx, tp_ty))
         }
+        (_, "min_align_of_val") => {
+            let tp_ty = *substs.types.get(FnSpace, 0);
+            if !type_is_sized(tcx, tp_ty) {
+                let info = Load(bcx, expr::get_len(bcx, llargs[0]));
+                let (_, llalign) = glue::size_and_align_of_dst(bcx, tp_ty, info);
+                llalign
+            } else {
+                C_uint(ccx, type_of::align_of(ccx, tp_ty))
+            }
+        }
         (_, "pref_align_of") => {
             let tp_ty = *substs.types.get(FnSpace, 0);
             let lltp_ty = type_of::type_of(ccx, tp_ty);
@@ -351,6 +372,11 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             bcx = src.store_to(bcx, llargs[0]);
             C_nil(ccx)
         }
+        (_, "drop_in_place") => {
+            let tp_ty = *substs.types.get(FnSpace, 0);
+            glue::drop_ty(bcx, llargs[0], tp_ty, call_debug_location);
+            C_nil(ccx)
+        }
         (_, "type_name") => {
             let tp_ty = *substs.types.get(FnSpace, 0);
             let ty_name = token::intern_and_get_ident(&ty_to_string(ccx.tcx(), tp_ty));
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 28df1c21595..d33553f3859 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -61,25 +61,24 @@
 //! we may want to adjust precisely when coercions occur.
 
 use check::{autoderef, FnCtxt, NoPreference, PreferMutLvalue, UnresolvedTypeAction};
-use check::vtable;
 
 use middle::infer::{self, Coercion};
-use middle::subst;
-use middle::traits;
+use middle::traits::{self, ObligationCause};
+use middle::traits::{predicate_for_trait_def, report_selection_error};
 use middle::ty::{AutoDerefRef, AdjustDerefRef};
 use middle::ty::{self, mt, Ty};
 use middle::ty_relate::RelateResult;
 use util::common::indent;
-use util::ppaux;
 use util::ppaux::Repr;
 
-use std::cell::Cell;
+use std::cell::RefCell;
+use std::collections::VecDeque;
 use syntax::ast;
 
 struct Coerce<'a, 'tcx: 'a> {
     fcx: &'a FnCtxt<'a, 'tcx>,
     origin: infer::TypeOrigin,
-    unsizing_obligation: Cell<Option<Ty<'tcx>>>
+    unsizing_obligations: RefCell<Vec<traits::PredicateObligation<'tcx>>>,
 }
 
 type CoerceResult<'tcx> = RelateResult<'tcx, Option<ty::AutoAdjustment<'tcx>>>;
@@ -94,15 +93,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         Ok(None) // No coercion required.
     }
 
-    fn outlives(&self,
-                origin: infer::SubregionOrigin<'tcx>,
-                a: ty::Region,
-                b: ty::Region)
-                -> RelateResult<'tcx, ()> {
-        infer::mk_subr(self.fcx.infcx(), origin, b, a);
-        Ok(())
-    }
-
     fn unpack_actual_value<T, F>(&self, a: Ty<'tcx>, f: F) -> T where
         F: FnOnce(Ty<'tcx>) -> T,
     {
@@ -248,51 +238,94 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
     // or &mut [T, ..n] -> &mut [T]
     // or &Concrete -> &Trait, etc.
     fn coerce_unsized(&self,
-                      a: Ty<'tcx>,
-                      b: Ty<'tcx>)
+                      source: Ty<'tcx>,
+                      target: Ty<'tcx>)
                       -> CoerceResult<'tcx> {
-        debug!("coerce_unsized(a={}, b={})",
-               a.repr(self.tcx()),
-               b.repr(self.tcx()));
+        debug!("coerce_unsized(source={}, target={})",
+               source.repr(self.tcx()),
+               target.repr(self.tcx()));
+
+        let traits = (self.tcx().lang_items.unsize_trait(),
+                      self.tcx().lang_items.coerce_unsized_trait());
+        let (unsize_did, coerce_unsized_did) = if let (Some(u), Some(cu)) = traits {
+            (u, cu)
+        } else {
+            debug!("Missing Unsize or CoerceUnsized traits");
+            return Err(ty::terr_mismatch);
+        };
 
         // Note, we want to avoid unnecessary unsizing. We don't want to coerce to
         // a DST unless we have to. This currently comes out in the wash since
         // we can't unify [T] with U. But to properly support DST, we need to allow
-        // that, at which point we will need extra checks on b here.
+        // that, at which point we will need extra checks on the target here.
 
-        let (reborrow, target) = match (&a.sty, &b.sty) {
+        // Handle reborrows before selecting `Source: CoerceUnsized<Target>`.
+        let (source, reborrow) = match (&source.sty, &target.sty) {
             (&ty::ty_rptr(_, mt_a), &ty::ty_rptr(_, mt_b)) => {
-                if let Some(target) = self.unsize_ty(mt_a.ty, mt_b.ty) {
-                    try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl));
-
-                    let coercion = Coercion(self.origin.span());
-                    let r_borrow = self.fcx.infcx().next_region_var(coercion);
-                    let region = self.tcx().mk_region(r_borrow);
-                    (Some(ty::AutoPtr(region, mt_b.mutbl)), target)
-                } else {
-                    return Err(ty::terr_mismatch);
-                }
+                try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl));
+
+                let coercion = Coercion(self.origin.span());
+                let r_borrow = self.fcx.infcx().next_region_var(coercion);
+                let region = self.tcx().mk_region(r_borrow);
+                (mt_a.ty, Some(ty::AutoPtr(region, mt_b.mutbl)))
             }
             (&ty::ty_rptr(_, mt_a), &ty::ty_ptr(mt_b)) => {
-                if let Some(target) = self.unsize_ty(mt_a.ty, mt_b.ty) {
-                    try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl));
-                    (Some(ty::AutoUnsafe(mt_b.mutbl)), target)
-                } else {
-                    return Err(ty::terr_mismatch);
-                }
+                try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl));
+                (mt_a.ty, Some(ty::AutoUnsafe(mt_b.mutbl)))
             }
-            (&ty::ty_uniq(t_a), &ty::ty_uniq(t_b)) => {
-                if let Some(target) = self.unsize_ty(t_a, t_b) {
-                    (None, ty::mk_uniq(self.tcx(), target))
-                } else {
+            _ => (source, None)
+        };
+        let source = ty::adjust_ty_for_autoref(self.tcx(), source, reborrow);
+
+        let mut selcx = traits::SelectionContext::new(self.fcx.infcx(), self.fcx);
+
+        // Use a FIFO queue for this custom fulfillment procedure.
+        let mut queue = VecDeque::new();
+        let mut leftover_predicates = vec![];
+
+        // Create an obligation for `Source: CoerceUnsized<Target>`.
+        let cause = ObligationCause::misc(self.origin.span(), self.fcx.body_id);
+        queue.push_back(predicate_for_trait_def(self.tcx(), cause, coerce_unsized_did,
+                                                0, source, vec![target]));
+
+        // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
+        // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
+        // inference might unify those two inner type variables later.
+        let traits = [coerce_unsized_did, unsize_did];
+        while let Some(obligation) = queue.pop_front() {
+            let trait_ref =  match obligation.predicate {
+                ty::Predicate::Trait(ref tr) if traits.contains(&tr.def_id()) => {
+                    tr.clone()
+                }
+                _ => {
+                    leftover_predicates.push(obligation);
+                    continue;
+                }
+            };
+            match selcx.select(&obligation.with(trait_ref)) {
+                // Uncertain or unimplemented.
+                Ok(None) | Err(traits::Unimplemented) => {
                     return Err(ty::terr_mismatch);
                 }
+
+                // Object safety violations or miscellaneous.
+                Err(err) => {
+                    report_selection_error(self.fcx.infcx(), &obligation, &err);
+                    // Treat this like an obligation and follow through
+                    // with the unsizing - the lack of a coercion should
+                    // be silent, as it causes a type mismatch later.
+                }
+
+                Ok(Some(vtable)) => {
+                    vtable.map_move_nested(|o| queue.push_back(o));
+                }
             }
-            _ => return Err(ty::terr_mismatch)
-        };
+        }
+
+        let mut obligations = self.unsizing_obligations.borrow_mut();
+        assert!(obligations.is_empty());
+        *obligations = leftover_predicates;
 
-        let target = ty::adjust_ty_for_autoref(self.tcx(), target, reborrow);
-        try!(self.subtype(target, b));
         let adjustment = AutoDerefRef {
             autoderefs: if reborrow.is_some() { 1 } else { 0 },
             autoref: reborrow,
@@ -302,108 +335,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         Ok(Some(AdjustDerefRef(adjustment)))
     }
 
-    // Takes a type and returns an unsized version.
-    // E.g., `[T, ..n]` -> `[T]`.
-    fn unsize_ty(&self,
-                 ty_a: Ty<'tcx>,
-                 ty_b: Ty<'tcx>)
-                 -> Option<Ty<'tcx>> {
-        let tcx = self.tcx();
-
-        self.unpack_actual_value(ty_a, |a| self.unpack_actual_value(ty_b, |b| {
-            debug!("unsize_ty(a={}, b={})", a.repr(self.tcx()), b.repr(self.tcx()));
-            match (&a.sty, &b.sty) {
-                (&ty::ty_vec(t_a, Some(_)), &ty::ty_vec(_, None)) => {
-                    Some(ty::mk_vec(tcx, t_a, None))
-                }
-                (&ty::ty_trait(ref data_a), &ty::ty_trait(ref data_b)) => {
-                    // Upcasts permit two things:
-                    //
-                    // 1. Dropping builtin bounds, e.g. `Foo+Send` to `Foo`
-                    // 2. Tightening the region bound, e.g. `Foo+'a` to `Foo+'b` if `'a : 'b`
-                    //
-                    // Note that neither of these changes requires any
-                    // change at runtime.  Eventually this will be
-                    // generalized.
-                    //
-                    // We always upcast when we can because of reason
-                    // #2 (region bounds).
-                    if data_a.bounds.builtin_bounds.is_superset(&data_b.bounds.builtin_bounds) {
-                        // construct a type `a1` which is a version of
-                        // `a` using the upcast bounds from `b`
-                        let bounds_a1 = ty::ExistentialBounds {
-                            // From type b
-                            region_bound: data_b.bounds.region_bound,
-                            builtin_bounds: data_b.bounds.builtin_bounds,
-
-                            // From type a
-                            projection_bounds: data_a.bounds.projection_bounds.clone(),
-                        };
-                        let ty_a1 = ty::mk_trait(tcx, data_a.principal.clone(), bounds_a1);
-
-                        // relate `a1` to `b`
-                        let result = self.fcx.infcx().commit_if_ok(|_| {
-                            // it's ok to upcast from Foo+'a to Foo+'b so long as 'a : 'b
-                            try!(self.outlives(infer::RelateObjectBound(self.origin.span()),
-                                               data_a.bounds.region_bound,
-                                               data_b.bounds.region_bound));
-                            self.subtype(ty_a1, ty_b)
-                        });
-
-                        // if that was successful, we have a coercion
-                        match result {
-                            Ok(_) => Some(ty_b),
-                            Err(_) => None,
-                        }
-                    } else {
-                        None
-                    }
-                }
-                (_, &ty::ty_trait(_)) => {
-                    assert!(self.unsizing_obligation.get().is_none());
-                    self.unsizing_obligation.set(Some(a));
-                    Some(ty_b)
-                }
-                (&ty::ty_struct(did_a, substs_a), &ty::ty_struct(did_b, substs_b))
-                  if did_a == did_b => {
-                    debug!("unsizing a struct");
-                    // Try unsizing each type param in turn to see if we end up with ty_b.
-                    let ty_substs_a = substs_a.types.get_slice(subst::TypeSpace);
-                    let ty_substs_b = substs_b.types.get_slice(subst::TypeSpace);
-                    assert!(ty_substs_a.len() == ty_substs_b.len());
-
-                    let tps = ty_substs_a.iter().zip(ty_substs_b.iter()).enumerate();
-                    for (i, (tp_a, tp_b)) in tps {
-                        if self.subtype(*tp_a, *tp_b).is_ok() {
-                            continue;
-                        }
-                        if let Some(new_tp) = self.unsize_ty(tp_a, tp_b) {
-                            // Check that the whole types match.
-                            let mut new_substs = substs_a.clone();
-                            new_substs.types.get_mut_slice(subst::TypeSpace)[i] = new_tp;
-                            let ty = ty::mk_struct(tcx, did_a, tcx.mk_substs(new_substs));
-                            if self.subtype(ty, ty_b).is_err() {
-                                debug!("Unsized type parameter '{}', but still \
-                                        could not match types {} and {}",
-                                        ppaux::ty_to_string(tcx, tp_a),
-                                        ppaux::ty_to_string(tcx, ty),
-                                        ppaux::ty_to_string(tcx, ty_b));
-                                // We can only unsize a single type parameter, so
-                                // if we unsize one and it doesn't give us the
-                                // type we want, then we won't succeed later.
-                                break;
-                            }
-
-                            return Some(ty);
-                        }
-                    }
-                    None
-                }
-                _ => None
-            }
-        }))
-    }
-
     fn coerce_from_fn_pointer(&self,
                            a: Ty<'tcx>,
                            fn_ty_a: &'tcx ty::BareFnTy<'tcx>,
@@ -496,41 +427,24 @@ pub fn mk_assignty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                              b: Ty<'tcx>)
                              -> RelateResult<'tcx, ()> {
     debug!("mk_assignty({} -> {})", a.repr(fcx.tcx()), b.repr(fcx.tcx()));
-    let (adjustment, unsizing_obligation) = try!(indent(|| {
+    let mut unsizing_obligations = vec![];
+    let adjustment = try!(indent(|| {
         fcx.infcx().commit_if_ok(|_| {
             let coerce = Coerce {
                 fcx: fcx,
                 origin: infer::ExprAssignable(expr.span),
-                unsizing_obligation: Cell::new(None)
+                unsizing_obligations: RefCell::new(vec![])
             };
-            Ok((try!(coerce.coerce(expr, a, b)),
-                coerce.unsizing_obligation.get()))
+            let adjustment = try!(coerce.coerce(expr, a, b));
+            unsizing_obligations = coerce.unsizing_obligations.into_inner();
+            Ok(adjustment)
         })
     }));
 
     if let Some(AdjustDerefRef(auto)) = adjustment {
-        if let (Some(source), Some(target)) = (unsizing_obligation, auto.unsize) {
-            let target = ty::deref(target, true)
-                            .expect("coercion: unsizing got non-pointer target type").ty;
-            let target = ty::struct_tail(fcx.tcx(), target);
-            if let ty::ty_trait(ref ty_trait) = target.sty {
-                vtable::check_object_safety(fcx.tcx(), ty_trait, expr.span);
-
-                // If the type is `Foo+'a`, ensures that the type
-                // being cast to `Foo+'a` implements `Foo`:
-                vtable::register_object_cast_obligations(fcx,
-                                                         expr.span,
-                                                         ty_trait,
-                                                         source);
-
-                // If the type is `Foo+'a`, ensures that the type
-                // being cast to `Foo+'a` outlives `'a`:
-                let cause = traits::ObligationCause {
-                    span: expr.span,
-                    body_id: fcx.body_id,
-                    code: traits::ObjectCastObligation(source)
-                };
-                fcx.register_region_obligation(source, ty_trait.bounds.region_bound, cause);
+        if auto.unsize.is_some() {
+            for obligation in unsizing_obligations {
+                fcx.register_predicate(obligation);
             }
         }
     }
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index c070df6b593..fb2ad444005 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -12,8 +12,6 @@
 
 use astconv::AstConv;
 use check::FnCtxt;
-use check::vtable;
-use check::vtable::select_new_fcx_obligations;
 use middle::def;
 use middle::privacy::{AllPublic, DependsOn, LastPrivate, LastMod};
 use middle::subst;
@@ -233,7 +231,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     // FIXME(#18653) -- Try to resolve obligations, giving us more
     // typing information, which can sometimes be needed to avoid
     // pathological region inference failures.
-    vtable::select_new_fcx_obligations(fcx);
+    fcx.select_new_obligations();
 
     // Insert any adjustments needed (always an autoref of some mutability).
     match self_expr {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 3769e9fa0f3..554f3d4b5a0 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -93,7 +93,7 @@ use middle::pat_util::{self, pat_id_map};
 use middle::privacy::{AllPublic, LastMod};
 use middle::region::{self, CodeExtent};
 use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace};
-use middle::traits;
+use middle::traits::{self, report_fulfillment_errors};
 use middle::ty::{FnSig, GenericPredicates, TypeScheme};
 use middle::ty::{Disr, ParamTy, ParameterEnvironment};
 use middle::ty::{self, HasProjectionTypes, RegionEscape, ToPolyTraitRef, Ty};
@@ -129,7 +129,6 @@ use syntax::visit::{self, Visitor};
 mod assoc;
 pub mod dropck;
 pub mod _match;
-pub mod vtable;
 pub mod writeback;
 pub mod regionck;
 pub mod coercion;
@@ -525,9 +524,9 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
             let fcx = check_fn(ccx, fn_ty.unsafety, fn_id, &fn_sig,
                                decl, fn_id, body, &inh);
 
-            vtable::select_all_fcx_obligations_and_apply_defaults(&fcx);
+            fcx.select_all_obligations_and_apply_defaults();
             upvar::closure_analyze_fn(&fcx, fn_id, decl, body);
-            vtable::select_all_fcx_obligations_or_error(&fcx);
+            fcx.select_all_obligations_or_error();
             fcx.check_casts();
             regionck::regionck_fn(&fcx, fn_id, fn_span, decl, body);
             writeback::resolve_type_vars_in_fn(&fcx, decl, body);
@@ -1290,7 +1289,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         // If not, try resolving any new fcx obligations that have cropped up.
-        vtable::select_new_fcx_obligations(self);
+        self.select_new_obligations();
         ty = self.infcx().resolve_type_vars_if_possible(&ty);
         if !ty::type_has_ty_infer(ty) {
             debug!("resolve_type_vars_if_possible: ty={}", ty.repr(self.tcx()));
@@ -1301,7 +1300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // possible. This can help substantially when there are
         // indirect dependencies that don't seem worth tracking
         // precisely.
-        vtable::select_fcx_obligations_where_possible(self);
+        self.select_obligations_where_possible();
         ty = self.infcx().resolve_type_vars_if_possible(&ty);
 
         debug!("resolve_type_vars_if_possible: ty={}", ty.repr(self.tcx()));
@@ -1817,6 +1816,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         deferred_cast_checks.clear();
     }
+
+    fn select_all_obligations_and_apply_defaults(&self) {
+        debug!("select_all_obligations_and_apply_defaults");
+
+        self.select_obligations_where_possible();
+        self.default_type_parameters();
+        self.select_obligations_where_possible();
+    }
+
+    fn select_all_obligations_or_error(&self) {
+        debug!("select_all_obligations_or_error");
+
+        // upvar inference should have ensured that all deferred call
+        // resolutions are handled by now.
+        assert!(self.inh.deferred_call_resolutions.borrow().is_empty());
+
+        self.select_all_obligations_and_apply_defaults();
+        let mut fulfillment_cx = self.inh.fulfillment_cx.borrow_mut();
+        match fulfillment_cx.select_all_or_error(self.infcx(), self) {
+            Ok(()) => { }
+            Err(errors) => { report_fulfillment_errors(self.infcx(), &errors); }
+        }
+    }
+
+    /// Select as many obligations as we can at present.
+    fn select_obligations_where_possible(&self) {
+        match
+            self.inh.fulfillment_cx
+            .borrow_mut()
+            .select_where_possible(self.infcx(), self)
+        {
+            Ok(()) => { }
+            Err(errors) => { report_fulfillment_errors(self.infcx(), &errors); }
+        }
+    }
+
+    /// Try to select any fcx obligation that we haven't tried yet, in an effort
+    /// to improve inference. You could just call
+    /// `select_obligations_where_possible` except that it leads to repeated
+    /// work.
+    fn select_new_obligations(&self) {
+        match
+            self.inh.fulfillment_cx
+            .borrow_mut()
+            .select_new_obligations(self.infcx(), self)
+        {
+            Ok(()) => { }
+            Err(errors) => { report_fulfillment_errors(self.infcx(), &errors); }
+        }
+    }
+
 }
 
 impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> {
@@ -1880,11 +1930,7 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
     for autoderefs in 0..fcx.tcx().sess.recursion_limit.get() {
         let resolved_t = match unresolved_type_action {
             UnresolvedTypeAction::Error => {
-                let resolved_t = structurally_resolved_type(fcx, sp, t);
-                if ty::type_is_error(resolved_t) {
-                    return (resolved_t, autoderefs, None);
-                }
-                resolved_t
+                structurally_resolved_type(fcx, sp, t)
             }
             UnresolvedTypeAction::Ignore => {
                 // We can continue even when the type cannot be resolved
@@ -1894,6 +1940,9 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>,
                 fcx.resolve_type_vars_if_possible(t)
             }
         };
+        if ty::type_is_error(resolved_t) {
+            return (resolved_t, autoderefs, None);
+        }
 
         match should_stop(resolved_t, autoderefs) {
             Some(x) => return (resolved_t, autoderefs, Some(x)),
@@ -2263,7 +2312,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         // an "opportunistic" vtable resolution of any trait bounds on
         // the call. This helps coercions.
         if check_blocks {
-            vtable::select_new_fcx_obligations(fcx);
+            fcx.select_new_obligations();
         }
 
         // For variadic functions, we don't have a declared type for all of
@@ -4059,7 +4108,7 @@ fn check_const_with_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 
     check_expr_with_hint(fcx, e, declty);
     demand::coerce(fcx, e.span, declty, e);
-    vtable::select_all_fcx_obligations_or_error(fcx);
+    fcx.select_all_obligations_or_error();
     fcx.check_casts();
     regionck::regionck_expr(fcx, e);
     writeback::resolve_type_vars_in_expr(fcx, e);
@@ -4928,6 +4977,14 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
             "breakpoint" => (0, Vec::new(), ty::mk_nil(tcx)),
             "size_of" |
             "pref_align_of" | "min_align_of" => (1, Vec::new(), ccx.tcx.types.usize),
+            "size_of_val" |  "min_align_of_val" => {
+                (1, vec![
+                    ty::mk_imm_rptr(tcx,
+                                    tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
+                                                                  ty::BrAnon(0))),
+                                    param(ccx, 0))
+                 ], ccx.tcx.types.usize)
+            }
             "init" | "init_dropped" => (1, Vec::new(), param(ccx, 0)),
             "uninit" => (1, Vec::new(), param(ccx, 0)),
             "forget" => (1, vec!( param(ccx, 0) ), ty::mk_nil(tcx)),
@@ -4943,6 +5000,9 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
                   ),
                ty::mk_nil(tcx))
             }
+            "drop_in_place" => {
+                (1, vec![ty::mk_mut_ptr(tcx, param(ccx, 0))], ty::mk_nil(tcx))
+            }
             "needs_drop" => (1, Vec::new(), ccx.tcx.types.bool),
 
             "type_name" => (1, Vec::new(), ty::mk_str_slice(tcx, tcx.mk_region(ty::ReStatic),
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 19cb570c82d..337088c313d 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -85,8 +85,6 @@
 use astconv::AstConv;
 use check::dropck;
 use check::FnCtxt;
-use check::vtable;
-use middle::free_region::FreeRegionMap;
 use middle::implicator;
 use middle::mem_categorization as mc;
 use middle::region::CodeExtent;
@@ -312,7 +310,7 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
         // region checking can introduce new pending obligations
         // which, when processed, might generate new region
         // obligations. So make sure we process those.
-        vtable::select_all_fcx_obligations_or_error(self.fcx);
+        self.fcx.select_all_obligations_or_error();
 
         // Make a copy of the region obligations vec because we'll need
         // to be able to borrow the fulfillment-cx below when projecting.
diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs
deleted file mode 100644
index a9094fce57c..00000000000
--- a/src/librustc_typeck/check/vtable.rs
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use check::FnCtxt;
-use middle::traits::{self, ObjectSafetyViolation, MethodViolationCode};
-use middle::traits::{Obligation, ObligationCause};
-use middle::traits::report_fulfillment_errors;
-use middle::ty::{self, Ty, AsPredicate};
-use syntax::codemap::Span;
-use util::ppaux::{Repr, UserString};
-
-
-// Check that a trait is 'object-safe'. This should be checked whenever a trait object
-// is created (by casting or coercion, etc.). A trait is object-safe if all its
-// methods are object-safe. A trait method is object-safe if it does not take
-// self by value, has no type parameters and does not use the `Self` type, except
-// in self position.
-pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
-                                 object_trait: &ty::TyTrait<'tcx>,
-                                 span: Span)
-{
-    let trait_def_id = object_trait.principal_def_id();
-
-    if traits::is_object_safe(tcx, trait_def_id) {
-        return;
-    }
-
-    span_err!(tcx.sess, span, E0038,
-              "cannot convert to a trait object because trait `{}` is not object-safe",
-              ty::item_path_str(tcx, trait_def_id));
-
-    let violations = traits::object_safety_violations(tcx, trait_def_id);
-    for violation in violations {
-        match violation {
-            ObjectSafetyViolation::SizedSelf => {
-                tcx.sess.span_note(
-                    span,
-                    "the trait cannot require that `Self : Sized`");
-            }
-
-            ObjectSafetyViolation::SupertraitSelf => {
-                tcx.sess.span_note(
-                    span,
-                    "the trait cannot use `Self` as a type parameter \
-                     in the supertrait listing");
-            }
-
-            ObjectSafetyViolation::Method(method, MethodViolationCode::StaticMethod) => {
-                tcx.sess.span_note(
-                    span,
-                    &format!("method `{}` has no receiver",
-                             method.name.user_string(tcx)));
-            }
-
-            ObjectSafetyViolation::Method(method, MethodViolationCode::ReferencesSelf) => {
-                tcx.sess.span_note(
-                    span,
-                    &format!("method `{}` references the `Self` type \
-                              in its arguments or return type",
-                             method.name.user_string(tcx)));
-            }
-
-            ObjectSafetyViolation::Method(method, MethodViolationCode::Generic) => {
-                tcx.sess.span_note(
-                    span,
-                    &format!("method `{}` has generic type parameters",
-                             method.name.user_string(tcx)));
-            }
-        }
-    }
-}
-
-pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                                  span: Span,
-                                                  object_trait: &ty::TyTrait<'tcx>,
-                                                  referent_ty: Ty<'tcx>)
-                                                  -> ty::PolyTraitRef<'tcx>
-{
-    // We can only make objects from sized types.
-    fcx.register_builtin_bound(
-        referent_ty,
-        ty::BoundSized,
-        traits::ObligationCause::new(span, fcx.body_id, traits::ObjectSized));
-
-    // This is just for better error reporting. Kinda goofy. The object type stuff
-    // needs some refactoring so there is a more convenient type to pass around.
-    let object_trait_ty =
-        ty::mk_trait(fcx.tcx(),
-                     object_trait.principal.clone(),
-                     object_trait.bounds.clone());
-
-    debug!("register_object_cast_obligations: referent_ty={} object_trait_ty={}",
-           referent_ty.repr(fcx.tcx()),
-           object_trait_ty.repr(fcx.tcx()));
-
-    let cause = ObligationCause::new(span,
-                                     fcx.body_id,
-                                     traits::ObjectCastObligation(object_trait_ty));
-
-    // Create the obligation for casting from T to Trait.
-    let object_trait_ref =
-        object_trait.principal_trait_ref_with_self_ty(fcx.tcx(), referent_ty);
-    let object_obligation =
-        Obligation::new(cause.clone(), object_trait_ref.as_predicate());
-    fcx.register_predicate(object_obligation);
-
-    // Create additional obligations for all the various builtin
-    // bounds attached to the object cast. (In other words, if the
-    // object type is Foo+Send, this would create an obligation
-    // for the Send check.)
-    for builtin_bound in &object_trait.bounds.builtin_bounds {
-        fcx.register_builtin_bound(
-            referent_ty,
-            builtin_bound,
-            cause.clone());
-    }
-
-    // Create obligations for the projection predicates.
-    let projection_bounds =
-        object_trait.projection_bounds_with_self_ty(fcx.tcx(), referent_ty);
-    for projection_bound in &projection_bounds {
-        let projection_obligation =
-            Obligation::new(cause.clone(), projection_bound.as_predicate());
-        fcx.register_predicate(projection_obligation);
-    }
-
-    object_trait_ref
-}
-
-pub fn select_all_fcx_obligations_and_apply_defaults(fcx: &FnCtxt) {
-    debug!("select_all_fcx_obligations_and_apply_defaults");
-
-    select_fcx_obligations_where_possible(fcx);
-    fcx.default_type_parameters();
-    select_fcx_obligations_where_possible(fcx);
-}
-
-pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
-    debug!("select_all_fcx_obligations_or_error");
-
-    // upvar inference should have ensured that all deferred call
-    // resolutions are handled by now.
-    assert!(fcx.inh.deferred_call_resolutions.borrow().is_empty());
-
-    select_all_fcx_obligations_and_apply_defaults(fcx);
-    let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut();
-    let r = fulfillment_cx.select_all_or_error(fcx.infcx(), fcx);
-    match r {
-        Ok(()) => { }
-        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
-    }
-}
-
-/// Select as many obligations as we can at present.
-pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt)
-{
-    match
-        fcx.inh.fulfillment_cx
-        .borrow_mut()
-        .select_where_possible(fcx.infcx(), fcx)
-    {
-        Ok(()) => { }
-        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
-    }
-}
-
-/// Try to select any fcx obligation that we haven't tried yet, in an effort to improve inference.
-/// You could just call `select_fcx_obligations_where_possible` except that it leads to repeated
-/// work.
-pub fn select_new_fcx_obligations(fcx: &FnCtxt) {
-    match
-        fcx.inh.fulfillment_cx
-        .borrow_mut()
-        .select_new_obligations(fcx.infcx(), fcx)
-    {
-        Ok(()) => { }
-        Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
-    }
-}
diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs
index c2209ba2dc6..79736c08f37 100644
--- a/src/librustc_typeck/check/wf.rs
+++ b/src/librustc_typeck/check/wf.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use astconv::AstConv;
-use check::{FnCtxt, Inherited, blank_fn_ctxt, vtable, regionck};
+use check::{FnCtxt, Inherited, blank_fn_ctxt, regionck};
 use constrained_type_params::{identify_constrained_type_params, Parameter};
 use CrateCtxt;
 use middle::region;
@@ -151,7 +151,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
         let inh = Inherited::new(ccx.tcx, param_env);
         let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(type_scheme.ty), item.id);
         f(self, &fcx);
-        vtable::select_all_fcx_obligations_or_error(&fcx);
+        fcx.select_all_obligations_or_error();
         regionck::regionck_item(&fcx, item);
     }
 
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index 4dc1596b1ff..c6e0bb676f7 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -16,7 +16,9 @@
 // mappings. That mapping code resides here.
 
 
+use middle::lang_items::UnsizeTraitLangItem;
 use middle::subst::{self, Subst};
+use middle::traits;
 use middle::ty::RegionEscape;
 use middle::ty::{ImplContainer, ImplOrTraitItemId, ConstTraitItemId};
 use middle::ty::{MethodTraitItemId, TypeTraitItemId, ParameterEnvironment};
@@ -28,8 +30,7 @@ use middle::ty::{ty_uint, ty_closure, ty_uniq, ty_bare_fn};
 use middle::ty::ty_projection;
 use middle::ty;
 use CrateCtxt;
-use middle::infer::InferCtxt;
-use middle::infer::new_infer_ctxt;
+use middle::infer::{self, InferCtxt, new_infer_ctxt};
 use std::cell::RefCell;
 use std::rc::Rc;
 use syntax::ast::{Crate, DefId};
@@ -133,6 +134,10 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
 
         // Check to make sure implementations of `Copy` are legal.
         self.check_implementations_of_copy();
+
+        // Check to make sure implementations of `CoerceUnsized` are legal
+        // and collect the necessary information from them.
+        self.check_implementations_of_coerce_unsized();
     }
 
     fn check_implementation(&self, item: &Item, opt_trait: Option<&TraitRef>) {
@@ -419,6 +424,169 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
             }
         });
     }
+
+    /// Process implementations of the built-in trait `CoerceUnsized`.
+    fn check_implementations_of_coerce_unsized(&self) {
+        let tcx = self.crate_context.tcx;
+        let coerce_unsized_trait = match tcx.lang_items.coerce_unsized_trait() {
+            Some(id) => id,
+            None => return,
+        };
+        let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) {
+            Ok(id) => id,
+            Err(err) => {
+                tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
+            }
+        };
+
+        let trait_impls = match tcx.trait_impls
+                                   .borrow()
+                                   .get(&coerce_unsized_trait)
+                                   .cloned() {
+            None => {
+                debug!("check_implementations_of_coerce_unsized(): no types \
+                        with implementations of `CoerceUnsized` found");
+                return
+            }
+            Some(found_impls) => found_impls
+        };
+
+        // Clone first to avoid a double borrow error.
+        let trait_impls = trait_impls.borrow().clone();
+
+        for &impl_did in &trait_impls {
+            debug!("check_implementations_of_coerce_unsized: impl_did={}",
+                   impl_did.repr(tcx));
+
+            if impl_did.krate != ast::LOCAL_CRATE {
+                debug!("check_implementations_of_coerce_unsized(): impl not \
+                        in this crate");
+                continue
+            }
+
+            let source = self.get_self_type_for_implementation(impl_did).ty;
+            let trait_ref = ty::impl_id_to_trait_ref(self.crate_context.tcx,
+                                                     impl_did.node);
+            let target = *trait_ref.substs.types.get(subst::TypeSpace, 0);
+            debug!("check_implementations_of_coerce_unsized: {} -> {} (bound)",
+                   source.repr(tcx), target.repr(tcx));
+
+            let span = tcx.map.span(impl_did.node);
+            let param_env = ParameterEnvironment::for_item(tcx, impl_did.node);
+            let source = source.subst(tcx, &param_env.free_substs);
+            let target = target.subst(tcx, &param_env.free_substs);
+            assert!(!source.has_escaping_regions());
+
+            debug!("check_implementations_of_coerce_unsized: {} -> {} (free)",
+                   source.repr(tcx), target.repr(tcx));
+
+            let infcx = new_infer_ctxt(tcx);
+
+            let check_mutbl = |mt_a: ty::mt<'tcx>, mt_b: ty::mt<'tcx>,
+                               mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| {
+                if (mt_a.mutbl, mt_b.mutbl) == (ast::MutImmutable, ast::MutMutable) {
+                    infcx.report_mismatched_types(span, mk_ptr(mt_b.ty),
+                                                  target, &ty::terr_mutability);
+                }
+                (mt_a.ty, mt_b.ty, unsize_trait, None)
+            };
+            let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) {
+                (&ty::ty_uniq(a), &ty::ty_uniq(b)) => (a, b, unsize_trait, None),
+
+                (&ty::ty_rptr(r_a, mt_a), &ty::ty_rptr(r_b, mt_b)) => {
+                    infer::mk_subr(&infcx, infer::RelateObjectBound(span), *r_b, *r_a);
+                    check_mutbl(mt_a, mt_b, &|ty| ty::mk_imm_rptr(tcx, r_b, ty))
+                }
+
+                (&ty::ty_rptr(_, mt_a), &ty::ty_ptr(mt_b)) |
+                (&ty::ty_ptr(mt_a), &ty::ty_ptr(mt_b)) => {
+                    check_mutbl(mt_a, mt_b, &|ty| ty::mk_imm_ptr(tcx, ty))
+                }
+
+                (&ty::ty_struct(def_id_a, substs_a), &ty::ty_struct(def_id_b, substs_b)) => {
+                    if def_id_a != def_id_b {
+                        let source_path = ty::item_path_str(tcx, def_id_a);
+                        let target_path = ty::item_path_str(tcx, def_id_b);
+                        span_err!(tcx.sess, span, E0373,
+                                  "the trait `CoerceUnsized` may only be implemented \
+                                   for a coercion between structures with the same \
+                                   definition; expected {}, found {}",
+                                  source_path, target_path);
+                        continue;
+                    }
+
+                    let origin = infer::Misc(span);
+                    let fields = ty::lookup_struct_fields(tcx, def_id_a);
+                    let diff_fields = fields.iter().enumerate().filter_map(|(i, f)| {
+                        let ty = ty::lookup_field_type_unsubstituted(tcx, def_id_a, f.id);
+                        let (a, b) = (ty.subst(tcx, substs_a), ty.subst(tcx, substs_b));
+                        if infcx.sub_types(false, origin, b, a).is_ok() {
+                            None
+                        } else {
+                            Some((i, a, b))
+                        }
+                    }).collect::<Vec<_>>();
+
+                    if diff_fields.is_empty() {
+                        span_err!(tcx.sess, span, E0374,
+                                  "the trait `CoerceUnsized` may only be implemented \
+                                   for a coercion between structures with one field \
+                                   being coerced, none found");
+                        continue;
+                    } else if diff_fields.len() > 1 {
+                        span_err!(tcx.sess, span, E0375,
+                                  "the trait `CoerceUnsized` may only be implemented \
+                                   for a coercion between structures with one field \
+                                   being coerced, but {} fields need coercions: {}",
+                                   diff_fields.len(), diff_fields.iter().map(|&(i, a, b)| {
+                                        let name = fields[i].name;
+                                        format!("{} ({} to {})",
+                                                if name == token::special_names::unnamed_field {
+                                                    i.to_string()
+                                                } else {
+                                                    token::get_name(name).to_string()
+                                                },
+                                                a.repr(tcx),
+                                                b.repr(tcx))
+                                   }).collect::<Vec<_>>().connect(", "));
+                        continue;
+                    }
+
+                    let (i, a, b) = diff_fields[0];
+                    let kind = ty::CustomCoerceUnsized::Struct(i);
+                    (a, b, coerce_unsized_trait, Some(kind))
+                }
+
+                _ => {
+                    span_err!(tcx.sess, span, E0376,
+                              "the trait `CoerceUnsized` may only be implemented \
+                               for a coercion between structures");
+                    continue;
+                }
+            };
+
+            let mut fulfill_cx = traits::FulfillmentContext::new();
+
+            // Register an obligation for `A: Trait<B>`.
+            let cause = traits::ObligationCause::misc(span, impl_did.node);
+            let predicate = traits::predicate_for_trait_def(tcx, cause, trait_def_id,
+                                                            0, source, vec![target]);
+            fulfill_cx.register_predicate_obligation(&infcx, predicate);
+
+            // Check that all transitive obligations are satisfied.
+            if let Err(errors) = fulfill_cx.select_all_or_error(&infcx, &param_env) {
+                traits::report_fulfillment_errors(&infcx, &errors);
+            }
+
+            // Finally, resolve all regions. This catches wily misuses of lifetime
+            // parameters.
+            infcx.resolve_regions_and_report_errors(impl_did.node);
+
+            if let Some(kind) = kind {
+                tcx.custom_coerce_unsized_kinds.borrow_mut().insert(impl_did, kind);
+            }
+        }
+    }
 }
 
 fn enforce_trait_manually_implementable(tcx: &ty::ctxt, sp: Span, trait_def_id: ast::DefId) {
diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs
index b450e6b398a..c75a0d816a8 100644
--- a/src/librustc_typeck/coherence/orphan.rs
+++ b/src/librustc_typeck/coherence/orphan.rs
@@ -316,12 +316,17 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> {
                     }
                 }
 
-                // Disallow *all* explicit impls of `Sized` for now.
+                // Disallow *all* explicit impls of `Sized` and `Unsize` for now.
                 if Some(trait_def_id) == self.tcx.lang_items.sized_trait() {
                     span_err!(self.tcx.sess, item.span, E0322,
                               "explicit impls for the `Sized` trait are not permitted");
                     return;
                 }
+                if Some(trait_def_id) == self.tcx.lang_items.unsize_trait() {
+                    span_err!(self.tcx.sess, item.span, E0323,
+                              "explicit impls for the `Unsize` trait are not permitted");
+                    return;
+                }
             }
             ast::ItemDefaultImpl(..) => {
                 // "Trait" impl
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index f9565d528e8..ac10a4e7121 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -445,10 +445,20 @@ register_diagnostics! {
     E0325, // implemented an associated type when another trait item expected
     E0326, // associated const implemented with different type from trait
     E0327, // referred to method instead of constant in match pattern
+    E0328, // cannot implement Unsize explicitly
     E0366, // dropck forbid specialization to concrete type or region
     E0367, // dropck forbid specialization to predicate not in struct/enum
     E0368, // binary operation `<op>=` cannot be applied to types
     E0369, // binary operation `<op>` cannot be applied to types
     E0371, // impl Trait for Trait is illegal
-    E0372  // impl Trait for Trait where Trait is not object safe
+    E0372, // impl Trait for Trait where Trait is not object safe
+    E0373, // the trait `CoerceUnsized` may only be implemented for a coercion
+           // between structures with the same definition
+    E0374, // the trait `CoerceUnsized` may only be implemented for a coercion
+           // between structures with one field being coerced, none found
+    E0375, // the trait `CoerceUnsized` may only be implemented for a coercion
+           // between structures with one field being coerced, but multiple
+           // fields need coercions
+    E0376  // the trait `CoerceUnsized` may only be implemented for a coercion
+           // between structures
 }
diff --git a/src/test/compile-fail/destructure-trait-ref.rs b/src/test/compile-fail/destructure-trait-ref.rs
index 4161cce2843..3f455e148a0 100644
--- a/src/test/compile-fail/destructure-trait-ref.rs
+++ b/src/test/compile-fail/destructure-trait-ref.rs
@@ -35,7 +35,7 @@ fn main() {
     // 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 type `Box<T>` cannot be dereferenced
+    let box x = box 1 as Box<T>; //~ ERROR the trait `core::marker::Sized` is not implemented
 
     // n > m
     let &&x = &1 as &T;
diff --git a/src/test/compile-fail/dst-bad-coercions.rs b/src/test/compile-fail/dst-bad-coercions.rs
index b30eada162b..b7a07e48799 100644
--- a/src/test/compile-fail/dst-bad-coercions.rs
+++ b/src/test/compile-fail/dst-bad-coercions.rs
@@ -33,18 +33,4 @@ pub fn main() {
     let x: &mut T = &S; //~ ERROR mismatched types
     let x: *mut T = &S; //~ ERROR mismatched types
     let x: *mut S = &S; //~ ERROR mismatched types
-
-    // The below four sets of tests test that we cannot implicitly deref a *-ptr
-    // during a coercion.
-    let x: *const S = &S;
-    let y: *const T = x;  //~ ERROR mismatched types
-
-    let x: *mut S = &mut S;
-    let y: *mut T = x;  //~ ERROR mismatched types
-
-    let x: *const Foo<S> = &Foo {f: S};
-    let y: *const Foo<T> = x;  //~ ERROR mismatched types
-
-    let x: *mut Foo<S> = &mut Foo {f: S};
-    let y: *mut Foo<T> = x;  //~ ERROR mismatched types
 }
diff --git a/src/test/compile-fail/issue-19692.rs b/src/test/compile-fail/issue-19692.rs
index 7794c34a04b..7b84ba0343a 100644
--- a/src/test/compile-fail/issue-19692.rs
+++ b/src/test/compile-fail/issue-19692.rs
@@ -12,7 +12,7 @@ struct Homura;
 
 fn akemi(homura: Homura) {
     let Some(ref madoka) = Some(homura.kaname()); //~ ERROR does not implement any method
-    madoka.clone();
+    madoka.clone(); //~ ERROR the type of this value must be known in this context
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/issue-20261.rs b/src/test/compile-fail/issue-20261.rs
index 33e00f9a823..42fd856ad87 100644
--- a/src/test/compile-fail/issue-20261.rs
+++ b/src/test/compile-fail/issue-20261.rs
@@ -12,6 +12,5 @@ fn main() {
     for (ref i,) in [].iter() { //~ ERROR: type mismatch resolving
         i.clone();
         //~^ ERROR: the type of this value must be known in this context
-        //~| ERROR: reached the recursion limit while auto-dereferencing
     }
 }
diff --git a/src/test/compile-fail/issue-22034.rs b/src/test/compile-fail/issue-22034.rs
index c084a94d55e..8b258180e83 100644
--- a/src/test/compile-fail/issue-22034.rs
+++ b/src/test/compile-fail/issue-22034.rs
@@ -14,6 +14,7 @@ fn main() {
     let ptr: *mut () = 0 as *mut _;
     let _: &mut Fn() = unsafe {
         &mut *(ptr as *mut Fn())
-        //~^ ERROR illegal cast
+        //~^ ERROR the trait `core::ops::Fn<()>` is not implemented
+        //~| ERROR the trait `core::ops::FnOnce<()>` is not implemented
     };
 }
diff --git a/src/test/compile-fail/object-lifetime-default-elision.rs b/src/test/compile-fail/object-lifetime-default-elision.rs
index 4fba45e2a66..75ee0bdc9c7 100644
--- a/src/test/compile-fail/object-lifetime-default-elision.rs
+++ b/src/test/compile-fail/object-lifetime-default-elision.rs
@@ -81,7 +81,7 @@ fn load3<'a,'b>(ss: &'a SomeTrait) -> &'b SomeTrait {
     // which fails to type check.
 
     ss
-        //~^ ERROR lifetime of the source pointer does not outlive lifetime bound
+        //~^ ERROR lifetime bound not satisfied
         //~| ERROR cannot infer
 }
 
diff --git a/src/test/compile-fail/object-lifetime-default-from-box-error.rs b/src/test/compile-fail/object-lifetime-default-from-box-error.rs
index 7fae530984f..dd94dfe1e08 100644
--- a/src/test/compile-fail/object-lifetime-default-from-box-error.rs
+++ b/src/test/compile-fail/object-lifetime-default-from-box-error.rs
@@ -25,7 +25,7 @@ fn load(ss: &mut SomeStruct) -> Box<SomeTrait> {
     // `Box<SomeTrait>` defaults to a `'static` bound, so this return
     // is illegal.
 
-    ss.r //~ ERROR lifetime of the source pointer does not outlive lifetime bound
+    ss.r //~ ERROR lifetime bound not satisfied
 }
 
 fn store(ss: &mut SomeStruct, b: Box<SomeTrait>) {
@@ -38,7 +38,7 @@ fn store(ss: &mut SomeStruct, b: Box<SomeTrait>) {
 fn store1<'b>(ss: &mut SomeStruct, b: Box<SomeTrait+'b>) {
     // Here we override the lifetimes explicitly, and so naturally we get an error.
 
-    ss.r = b; //~ ERROR lifetime of the source pointer does not outlive lifetime bound
+    ss.r = b; //~ ERROR lifetime bound not satisfied
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-close-over-type-parameter-multiple.rs b/src/test/compile-fail/regions-close-over-type-parameter-multiple.rs
index 10b883d4dc8..c5cf43e355d 100644
--- a/src/test/compile-fail/regions-close-over-type-parameter-multiple.rs
+++ b/src/test/compile-fail/regions-close-over-type-parameter-multiple.rs
@@ -27,7 +27,7 @@ fn make_object_good2<'a,'b,A:SomeTrait+'a+'b>(v: A) -> Box<SomeTrait+'b> {
 
 fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<SomeTrait+'c> {
     // A outlives 'a AND 'b...but not 'c.
-    box v as Box<SomeTrait+'a> //~ ERROR lifetime of the source pointer does not outlive
+    box v as Box<SomeTrait+'a> //~ ERROR lifetime bound not satisfied
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-trait-object-subtyping.rs b/src/test/compile-fail/regions-trait-object-subtyping.rs
index f3722690ef8..b4e527972e4 100644
--- a/src/test/compile-fail/regions-trait-object-subtyping.rs
+++ b/src/test/compile-fail/regions-trait-object-subtyping.rs
@@ -22,7 +22,7 @@ fn foo2<'a:'b,'b>(x: &'b mut (Dummy+'a)) -> &'b mut (Dummy+'b) {
 
 fn foo3<'a,'b>(x: &'a mut Dummy) -> &'b mut Dummy {
     // Without knowing 'a:'b, we can't coerce
-    x //~ ERROR lifetime of the source pointer does not outlive
+    x //~ ERROR lifetime bound not satisfied
      //~^ ERROR cannot infer
 }