about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-03-17 13:29:48 +0000
committerbors <bors@rust-lang.org>2015-03-17 13:29:48 +0000
commitc64d671671aea2e44ee7fc6eb00ee75fc30ed7b9 (patch)
tree058ec26f2e5ae48d415b7730c5f2d51df9b369a0
parent31ba21228e0e539a665ce14ab3a176e30e57f822 (diff)
parent277b4f035aa7e42330aabbc243a8fcb5cf4cc8bd (diff)
downloadrust-c64d671671aea2e44ee7fc6eb00ee75fc30ed7b9.tar.gz
rust-c64d671671aea2e44ee7fc6eb00ee75fc30ed7b9.zip
Auto merge of #23423 - nikomatsakis:issue-18737-trait-subtyping, r=nrc
This upcast coercion currently never requires vtable changes. It should be generalized. 

This is a [breaking-change] -- if you have an impl on an object type like `impl SomeTrait`, then this will no longer be applicable to object types like `SomeTrait+Send`. In the standard library, this primarily affected `Any`, and this PR adds impls for `Any+Send` as to keep the API the same in practice. An alternate workaround is to use UFCS form or standalone fns. For more details, see <https://github.com/rust-lang/rust/issues/18737#issuecomment-78450798>.

r? @nrc 
-rw-r--r--src/liballoc/boxed.rs11
-rw-r--r--src/libcore/any.rs26
-rw-r--r--src/librustc/middle/astencode.rs12
-rw-r--r--src/librustc/middle/expr_use_visitor.rs36
-rw-r--r--src/librustc/middle/infer/combine.rs15
-rw-r--r--src/librustc/middle/infer/equate.rs18
-rw-r--r--src/librustc/middle/infer/glb.rs10
-rw-r--r--src/librustc/middle/infer/lub.rs10
-rw-r--r--src/librustc/middle/infer/sub.rs15
-rw-r--r--src/librustc/middle/ty.rs7
-rw-r--r--src/librustc/middle/ty_fold.rs1
-rw-r--r--src/librustc/util/ppaux.rs6
-rw-r--r--src/librustc_trans/trans/consts.rs8
-rw-r--r--src/librustc_trans/trans/expr.rs91
-rw-r--r--src/librustc_typeck/check/coercion.rs34
-rw-r--r--src/librustc_typeck/check/method/confirm.rs4
-rw-r--r--src/librustc_typeck/check/method/probe.rs5
-rw-r--r--src/librustc_typeck/check/mod.rs1
-rw-r--r--src/test/compile-fail/borrowck-consume-unsize-vec.rs22
-rw-r--r--src/test/compile-fail/borrowck-consume-upcast-box.rs24
-rw-r--r--src/test/compile-fail/retslot-cast.rs14
21 files changed, 248 insertions, 122 deletions
diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs
index 451237d7596..50935e6404d 100644
--- a/src/liballoc/boxed.rs
+++ b/src/liballoc/boxed.rs
@@ -241,7 +241,7 @@ pub trait BoxAny {
     /// Returns the boxed value if it is of type `T`, or
     /// `Err(Self)` if it isn't.
     #[stable(feature = "rust1", since = "1.0.0")]
-    fn downcast<T: 'static>(self) -> Result<Box<T>, Self>;
+    fn downcast<T: 'static>(self) -> Result<Box<T>, Box<Any>>;
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -264,6 +264,15 @@ impl BoxAny for Box<Any> {
     }
 }
 
+#[cfg(not(stage0))]
+#[stable(feature = "rust1", since = "1.0.0")]
+impl BoxAny for Box<Any+Send> {
+    #[inline]
+    fn downcast<T: 'static>(self) -> Result<Box<T>, Box<Any>> {
+        <Box<Any>>::downcast(self)
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: fmt::Display + ?Sized> fmt::Display for Box<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
diff --git a/src/libcore/any.rs b/src/libcore/any.rs
index 462b6771b4a..6d3fac4c68d 100644
--- a/src/libcore/any.rs
+++ b/src/libcore/any.rs
@@ -71,6 +71,7 @@
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
+use marker::Send;
 use mem::transmute;
 use option::Option::{self, Some, None};
 use raw::TraitObject;
@@ -154,6 +155,31 @@ impl Any {
     }
 }
 
+#[cfg(not(stage0))]
+impl Any+Send {
+    /// Forwards to the method defined on the type `Any`.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    #[inline]
+    pub fn is<T: 'static>(&self) -> bool {
+        Any::is::<T>(self)
+    }
+
+    /// Forwards to the method defined on the type `Any`.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    #[inline]
+    pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
+        Any::downcast_ref::<T>(self)
+    }
+
+    /// Forwards to the method defined on the type `Any`.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    #[inline]
+    pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
+        Any::downcast_mut::<T>(self)
+    }
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////
 // TypeID and its methods
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index ed1d876d836..f705cc8ed99 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -1102,6 +1102,11 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
                         this.emit_enum_variant_arg(1, |this| Ok(this.emit_ty(ecx, self_ty)))
                     })
                 }
+                ty::UnsizeUpcast(target_ty) => {
+                    this.emit_enum_variant("UnsizeUpcast", 3, 1, |this| {
+                        this.emit_enum_variant_arg(0, |this| Ok(this.emit_ty(ecx, target_ty)))
+                    })
+                }
             }
         });
     }
@@ -1707,7 +1712,7 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
     fn read_unsize_kind<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
                                 -> ty::UnsizeKind<'tcx> {
         self.read_enum("UnsizeKind", |this| {
-            let variants = &["UnsizeLength", "UnsizeStruct", "UnsizeVtable"];
+            let variants = &["UnsizeLength", "UnsizeStruct", "UnsizeVtable", "UnsizeUpcast"];
             this.read_enum_variant(variants, |this, i| {
                 Ok(match i {
                     0 => {
@@ -1741,6 +1746,11 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
                             this.read_enum_variant_arg(1, |this| Ok(this.read_ty(dcx))).unwrap();
                         ty::UnsizeVtable(ty_trait, self_ty)
                     }
+                    3 => {
+                        let target_ty =
+                            this.read_enum_variant_arg(0, |this| Ok(this.read_ty(dcx))).unwrap();
+                        ty::UnsizeUpcast(target_ty)
+                    }
                     _ => panic!("bad enum variant for ty::UnsizeKind")
                 })
             })
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index a1e38a1c8bd..c0d51f5675c 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -857,28 +857,13 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                     n: uint) {
         debug!("walk_autoref expr={}", expr.repr(self.tcx()));
 
-        // Match for unique trait coercions first, since we don't need the
-        // call to cat_expr_autoderefd.
-        match *autoref {
-            ty::AutoUnsizeUniq(ty::UnsizeVtable(..)) |
-            ty::AutoUnsize(ty::UnsizeVtable(..)) => {
-                assert!(n == 1, format!("Expected exactly 1 deref with Uniq \
-                                         AutoRefs, found: {}", n));
-                let cmt_unadjusted =
-                    return_if_err!(self.mc.cat_expr_unadjusted(expr));
-                self.delegate_consume(expr.id, expr.span, cmt_unadjusted);
-                return;
-            }
-            _ => {}
-        }
-
-        let cmt_derefd = return_if_err!(
-            self.mc.cat_expr_autoderefd(expr, n));
-        debug!("walk_adjustment: cmt_derefd={}",
-               cmt_derefd.repr(self.tcx()));
-
         match *autoref {
             ty::AutoPtr(r, m, _) => {
+                let cmt_derefd = return_if_err!(
+                    self.mc.cat_expr_autoderefd(expr, n));
+                debug!("walk_adjustment: cmt_derefd={}",
+                       cmt_derefd.repr(self.tcx()));
+
                 self.delegate.borrow(expr.id,
                                      expr.span,
                                      cmt_derefd,
@@ -886,7 +871,16 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
                                      ty::BorrowKind::from_mutbl(m),
                                      AutoRef);
             }
-            ty::AutoUnsizeUniq(_) | ty::AutoUnsize(_) | ty::AutoUnsafe(..) => {}
+            ty::AutoUnsize(_) |
+            ty::AutoUnsizeUniq(_) => {
+                assert!(n == 1, format!("Expected exactly 1 deref with Uniq \
+                                         AutoRefs, found: {}", n));
+                let cmt_unadjusted =
+                    return_if_err!(self.mc.cat_expr_unadjusted(expr));
+                self.delegate_consume(expr.id, expr.span, cmt_unadjusted);
+            }
+            ty::AutoUnsafe(..) => {
+            }
         }
     }
 
diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs
index 9add236d54b..438b61f918a 100644
--- a/src/librustc/middle/infer/combine.rs
+++ b/src/librustc/middle/infer/combine.rs
@@ -314,9 +314,18 @@ pub trait Combine<'tcx> : Sized {
     }
 
     fn builtin_bounds(&self,
-                      a: ty::BuiltinBounds,
-                      b: ty::BuiltinBounds)
-                      -> cres<'tcx, ty::BuiltinBounds>;
+                      a: BuiltinBounds,
+                      b: BuiltinBounds)
+                      -> cres<'tcx, BuiltinBounds>
+    {
+        // Two sets of builtin bounds are only relatable if they are
+        // precisely the same (but see the coercion code).
+        if a != b {
+            Err(ty::terr_builtin_bounds(expected_found(self, a, b)))
+        } else {
+            Ok(a)
+        }
+    }
 
     fn trait_refs(&self,
                   a: &ty::TraitRef<'tcx>,
diff --git a/src/librustc/middle/infer/equate.rs b/src/librustc/middle/infer/equate.rs
index 7194e20b0cf..2f9f8532b6c 100644
--- a/src/librustc/middle/infer/equate.rs
+++ b/src/librustc/middle/infer/equate.rs
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use middle::ty::{BuiltinBounds};
 use middle::ty::{self, Ty};
 use middle::ty::TyVar;
 use middle::infer::combine::*;
@@ -73,23 +72,6 @@ impl<'f, 'tcx> Combine<'tcx> for Equate<'f, 'tcx> {
         }
     }
 
-    fn builtin_bounds(&self,
-                      a: BuiltinBounds,
-                      b: BuiltinBounds)
-                      -> cres<'tcx, BuiltinBounds>
-    {
-        // More bounds is a subtype of fewer bounds.
-        //
-        // e.g., fn:Copy() <: fn(), because the former is a function
-        // that only closes over copyable things, but the latter is
-        // any function at all.
-        if a != b {
-            Err(ty::terr_builtin_bounds(expected_found(self, a, b)))
-        } else {
-            Ok(a)
-        }
-    }
-
     fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
         debug!("{}.tys({}, {})", self.tag(),
                a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));
diff --git a/src/librustc/middle/infer/glb.rs b/src/librustc/middle/infer/glb.rs
index 33303808e84..ba073999ed1 100644
--- a/src/librustc/middle/infer/glb.rs
+++ b/src/librustc/middle/infer/glb.rs
@@ -14,7 +14,6 @@ use super::higher_ranked::HigherRankedRelations;
 use super::{cres};
 use super::Subtype;
 
-use middle::ty::{BuiltinBounds};
 use middle::ty::{self, Ty};
 use syntax::ast::{MutImmutable, MutMutable, Unsafety};
 use util::ppaux::mt_to_string;
@@ -94,15 +93,6 @@ impl<'f, 'tcx> Combine<'tcx> for Glb<'f, 'tcx> {
         }
     }
 
-    fn builtin_bounds(&self,
-                      a: ty::BuiltinBounds,
-                      b: ty::BuiltinBounds)
-                      -> cres<'tcx, ty::BuiltinBounds> {
-        // More bounds is a subtype of fewer bounds, so
-        // the GLB (mutual subtype) is the union.
-        Ok(a.union(b))
-    }
-
     fn regions(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ty::Region> {
         debug!("{}.regions({}, {})",
                self.tag(),
diff --git a/src/librustc/middle/infer/lub.rs b/src/librustc/middle/infer/lub.rs
index 3570effa9fa..37eace0b15b 100644
--- a/src/librustc/middle/infer/lub.rs
+++ b/src/librustc/middle/infer/lub.rs
@@ -14,7 +14,6 @@ use super::lattice::*;
 use super::{cres};
 use super::{Subtype};
 
-use middle::ty::{BuiltinBounds};
 use middle::ty::{self, Ty};
 use syntax::ast::{MutMutable, MutImmutable, Unsafety};
 use util::ppaux::mt_to_string;
@@ -89,15 +88,6 @@ impl<'f, 'tcx> Combine<'tcx> for Lub<'f, 'tcx> {
         }
     }
 
-    fn builtin_bounds(&self,
-                      a: ty::BuiltinBounds,
-                      b: ty::BuiltinBounds)
-                      -> cres<'tcx, ty::BuiltinBounds> {
-        // More bounds is a subtype of fewer bounds, so
-        // the LUB (mutual supertype) is the intersection.
-        Ok(a.intersection(b))
-    }
-
     fn regions(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ty::Region> {
         debug!("{}.regions({}, {})",
                self.tag(),
diff --git a/src/librustc/middle/infer/sub.rs b/src/librustc/middle/infer/sub.rs
index a92c960cd3a..8ab063686d4 100644
--- a/src/librustc/middle/infer/sub.rs
+++ b/src/librustc/middle/infer/sub.rs
@@ -14,7 +14,6 @@ use super::higher_ranked::HigherRankedRelations;
 use super::{Subtype};
 use super::type_variable::{SubtypeOf, SupertypeOf};
 
-use middle::ty::{BuiltinBounds};
 use middle::ty::{self, Ty};
 use middle::ty::TyVar;
 use util::ppaux::{Repr};
@@ -97,20 +96,6 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
         })
     }
 
-    fn builtin_bounds(&self, a: BuiltinBounds, b: BuiltinBounds)
-                      -> cres<'tcx, BuiltinBounds> {
-        // More bounds is a subtype of fewer bounds.
-        //
-        // e.g., fn:Copy() <: fn(), because the former is a function
-        // that only closes over copyable things, but the latter is
-        // any function at all.
-        if a.is_superset(&b) {
-            Ok(a)
-        } else {
-            Err(ty::terr_builtin_bounds(expected_found(self, a, b)))
-        }
-    }
-
     fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
         debug!("{}.tys({}, {})", self.tag(),
                a.repr(self.tcx()), b.repr(self.tcx()));
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index c3e1879fca8..8cc188e5df5 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -292,7 +292,8 @@ pub enum UnsizeKind<'tcx> {
     // An unsize coercion applied to the tail field of a struct.
     // The uint is the index of the type parameter which is unsized.
     UnsizeStruct(Box<UnsizeKind<'tcx>>, uint),
-    UnsizeVtable(TyTrait<'tcx>, /* the self type of the trait */ Ty<'tcx>)
+    UnsizeVtable(TyTrait<'tcx>, /* the self type of the trait */ Ty<'tcx>),
+    UnsizeUpcast(Ty<'tcx>),
 }
 
 #[derive(Clone, Debug)]
@@ -4631,6 +4632,9 @@ pub fn unsize_ty<'tcx>(cx: &ctxt<'tcx>,
         &UnsizeVtable(TyTrait { ref principal, ref bounds }, _) => {
             mk_trait(cx, principal.clone(), bounds.clone())
         }
+        &UnsizeUpcast(target_ty) => {
+            target_ty
+        }
     }
 }
 
@@ -6853,6 +6857,7 @@ impl<'tcx> Repr<'tcx> for UnsizeKind<'tcx> {
             UnsizeLength(n) => format!("UnsizeLength({})", n),
             UnsizeStruct(ref k, n) => format!("UnsizeStruct({},{})", k.repr(tcx), n),
             UnsizeVtable(ref a, ref b) => format!("UnsizeVtable({},{})", a.repr(tcx), b.repr(tcx)),
+            UnsizeUpcast(ref a) => format!("UnsizeUpcast({})", a.repr(tcx)),
         }
     }
 }
diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs
index f3a7c1ee6a0..f17ba78007b 100644
--- a/src/librustc/middle/ty_fold.rs
+++ b/src/librustc/middle/ty_fold.rs
@@ -480,6 +480,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::UnsizeKind<'tcx> {
                     },
                     self_ty.fold_with(folder))
             }
+            ty::UnsizeUpcast(t) => ty::UnsizeUpcast(t.fold_with(folder)),
         }
     }
 }
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 3a0b5832c9f..e9b02917040 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -1214,17 +1214,17 @@ impl<'tcx> Repr<'tcx> for ty::ExistentialBounds<'tcx> {
     fn repr(&self, tcx: &ctxt<'tcx>) -> String {
         let mut res = Vec::new();
 
-        let region_str = self.region_bound.user_string(tcx);
+        let region_str = self.region_bound.repr(tcx);
         if !region_str.is_empty() {
             res.push(region_str);
         }
 
         for bound in &self.builtin_bounds {
-            res.push(bound.user_string(tcx));
+            res.push(bound.repr(tcx));
         }
 
         for projection_bound in &self.projection_bounds {
-            res.push(projection_bound.user_string(tcx));
+            res.push(projection_bound.repr(tcx));
         }
 
         res.connect("+")
diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs
index 8dcabe0a94b..6ff230b7065 100644
--- a/src/librustc_trans/trans/consts.rs
+++ b/src/librustc_trans/trans/consts.rs
@@ -311,12 +311,16 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     llconst = addr_of(cx, llconst, "autoref", e.id);
                 }
                 Some(box ty::AutoUnsize(ref k)) => {
-                    let info = expr::unsized_info(cx, k, e.id, ty, param_substs,
-                        |t| ty::mk_imm_rptr(cx.tcx(), cx.tcx().mk_region(ty::ReStatic), t));
+                    let info =
+                        expr::unsized_info(
+                            cx, k, e.id, ty, param_substs,
+                            |t| ty::mk_imm_rptr(cx.tcx(), cx.tcx().mk_region(ty::ReStatic), t),
+                            || const_get_elt(cx, llconst, &[abi::FAT_PTR_EXTRA as u32]));
 
                     let unsized_ty = ty::unsize_ty(cx.tcx(), ty, k, e.span);
                     let ptr_ty = type_of::in_memory_type_of(cx, unsized_ty).ptr_to();
                     let base = ptrcast(llconst, ptr_ty);
+
                     let prev_const = cx.const_unsized().borrow_mut()
                                        .insert(base, llconst);
                     assert!(prev_const.is_none() || prev_const == Some(llconst));
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index ecdc7c06bb1..da2b5a43b48 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -287,18 +287,50 @@ pub fn get_dataptr(bcx: Block, fat_ptr: ValueRef) -> ValueRef {
 
 // Retrieve the information we are losing (making dynamic) in an unsizing
 // adjustment.
+//
 // When making a dtor, we need to do different things depending on the
 // ownership of the object.. mk_ty is a function for turning `unadjusted_ty`
 // into a type to be destructed. If we want to end up with a Box pointer,
 // then mk_ty should make a Box pointer (T -> Box<T>), if we want a
 // borrowed reference then it should be T -> &T.
-pub fn unsized_info<'a, 'tcx, F>(ccx: &CrateContext<'a, 'tcx>,
-                                 kind: &ty::UnsizeKind<'tcx>,
-                                 id: ast::NodeId,
-                                 unadjusted_ty: Ty<'tcx>,
-                                 param_substs: &'tcx subst::Substs<'tcx>,
-                                 mk_ty: F) -> ValueRef where
-    F: FnOnce(Ty<'tcx>) -> Ty<'tcx>,
+//
+// The `unadjusted_val` argument is a bit funny. It is intended
+// for use in an upcast, where the new vtable for an object will
+// be drived from the old one. Hence it is a pointer to the fat
+// pointer.
+pub fn unsized_info_bcx<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
+                                       kind: &ty::UnsizeKind<'tcx>,
+                                       id: ast::NodeId,
+                                       unadjusted_ty: Ty<'tcx>,
+                                       unadjusted_val: ValueRef, // see above (*)
+                                       param_substs: &'tcx subst::Substs<'tcx>,
+                                       mk_ty: F)
+                                       -> ValueRef
+    where F: FnOnce(Ty<'tcx>) -> Ty<'tcx>
+{
+    unsized_info(
+        bcx.ccx(),
+        kind,
+        id,
+        unadjusted_ty,
+        param_substs,
+        mk_ty,
+        || Load(bcx, GEPi(bcx, unadjusted_val, &[0, abi::FAT_PTR_EXTRA])))
+}
+
+// Same as `unsize_info_bcx`, but does not require a bcx -- instead it
+// takes an extra closure to compute the upcast vtable.
+pub fn unsized_info<'ccx, 'tcx, MK_TY, MK_UPCAST_VTABLE>(
+    ccx: &CrateContext<'ccx, 'tcx>,
+    kind: &ty::UnsizeKind<'tcx>,
+    id: ast::NodeId,
+    unadjusted_ty: Ty<'tcx>,
+    param_substs: &'tcx subst::Substs<'tcx>,
+    mk_ty: MK_TY,
+    mk_upcast_vtable: MK_UPCAST_VTABLE) // see notes above
+    -> ValueRef
+    where MK_TY: FnOnce(Ty<'tcx>) -> Ty<'tcx>,
+          MK_UPCAST_VTABLE: FnOnce() -> ValueRef,
 {
     // FIXME(#19596) workaround: `|t| t` causes monomorphization recursion
     fn identity<T>(t: T) -> T { t }
@@ -312,7 +344,8 @@ pub fn unsized_info<'a, 'tcx, F>(ccx: &CrateContext<'a, 'tcx>,
                 let ty_substs = substs.types.get_slice(subst::TypeSpace);
                 // The dtor for a field treats it like a value, so mk_ty
                 // should just be the identity function.
-                unsized_info(ccx, k, id, ty_substs[tp_index], param_substs, identity)
+                unsized_info(ccx, k, id, ty_substs[tp_index], param_substs,
+                             identity, mk_upcast_vtable)
             }
             _ => ccx.sess().bug(&format!("UnsizeStruct with bad sty: {}",
                                          unadjusted_ty.repr(ccx.tcx())))
@@ -330,6 +363,12 @@ pub fn unsized_info<'a, 'tcx, F>(ccx: &CrateContext<'a, 'tcx>,
             consts::ptrcast(meth::get_vtable(ccx, box_ty, trait_ref, param_substs),
                             Type::vtable_ptr(ccx))
         }
+        &ty::UnsizeUpcast(_) => {
+            // For now, upcasts are limited to changes in marker
+            // traits, and hence never actually require an actual
+            // change to the vtable.
+            mk_upcast_vtable()
+        }
     }
 }
 
@@ -338,7 +377,8 @@ pub fn unsized_info<'a, 'tcx, F>(ccx: &CrateContext<'a, 'tcx>,
 fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                  expr: &ast::Expr,
                                  datum: Datum<'tcx, Expr>)
-                                 -> DatumBlock<'blk, 'tcx, Expr> {
+                                 -> DatumBlock<'blk, 'tcx, Expr>
+{
     let mut bcx = bcx;
     let mut datum = datum;
     let adjustment = match bcx.tcx().adjustments.borrow().get(&expr.id).cloned() {
@@ -347,10 +387,10 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         }
         Some(adj) => { adj }
     };
-    debug!("unadjusted datum for expr {}: {}, adjustment={}",
+    debug!("unadjusted datum for expr {}: {} adjustment={:?}",
            expr.repr(bcx.tcx()),
            datum.to_string(bcx.ccx()),
-           adjustment.repr(bcx.tcx()));
+           adjustment);
     match adjustment {
         AdjustReifyFnPointer(_def_id) => {
             // FIXME(#19925) once fn item types are
@@ -434,7 +474,6 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 debug!("  AutoUnsize");
                 unpack_datum!(bcx, unsize_expr(bcx, expr, datum, k))
             }
-
             &ty::AutoUnsizeUniq(ty::UnsizeLength(len)) => {
                 debug!("  AutoUnsizeUniq(UnsizeLength)");
                 unpack_datum!(bcx, unsize_unique_vec(bcx, expr, datum, len))
@@ -459,16 +498,27 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         let unsized_ty = ty::unsize_ty(tcx, datum_ty, k, expr.span);
         debug!("unsized_ty={}", unsized_ty.repr(bcx.tcx()));
 
-        let info = unsized_info(bcx.ccx(), k, expr.id, datum_ty, bcx.fcx.param_substs,
-                                |t| ty::mk_imm_rptr(tcx, tcx.mk_region(ty::ReStatic), t));
+        let info = unsized_info_bcx(bcx, k, expr.id, datum_ty, datum.val, bcx.fcx.param_substs,
+                                    |t| ty::mk_imm_rptr(tcx, tcx.mk_region(ty::ReStatic), t));
 
         // Arrange cleanup
-        let lval = unpack_datum!(bcx,
-                                 datum.to_lvalue_datum(bcx, "into_fat_ptr", expr.id));
+        let lval = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "into_fat_ptr", expr.id));
+
         // 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, lval.val, ptr_ty);
+        let base = if !type_is_sized(bcx.tcx(), lval.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, lval.val))
+        } else {
+            lval.val
+        };
+        let base = PointerCast(bcx, base, ptr_ty);
 
         let llty = type_of::type_of(bcx.ccx(), unsized_ty);
         // HACK(eddyb) get around issues with lifetime intrinsics.
@@ -540,8 +590,8 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         let base = PointerCast(bcx, get_dataptr(bcx, scratch.val), llbox_ty.ptr_to());
         bcx = datum.store_to(bcx, base);
 
-        let info = unsized_info(bcx.ccx(), k, expr.id, unboxed_ty, bcx.fcx.param_substs,
-                                |t| ty::mk_uniq(tcx, t));
+        let info = unsized_info_bcx(bcx, k, expr.id, unboxed_ty, base, bcx.fcx.param_substs,
+                                    |t| ty::mk_uniq(tcx, t));
         Store(bcx, info, get_len(bcx, scratch.val));
 
         DatumBlock::new(bcx, scratch.to_expr_datum())
@@ -1373,8 +1423,7 @@ pub fn with_field_tys<'tcx, R, F>(tcx: &ty::ctxt<'tcx>,
                     let def = tcx.def_map.borrow()[node_id].full_def();
                     match def {
                         def::DefVariant(enum_id, variant_id, _) => {
-                            let variant_info = ty::enum_variant_with_id(
-                                tcx, enum_id, variant_id);
+                            let variant_info = ty::enum_variant_with_id(tcx, enum_id, variant_id);
                             let fields = struct_fields(tcx, variant_id, substs);
                             let fields = monomorphize::normalize_associated_type(tcx, &fields);
                             op(variant_info.disr_val, &fields[..])
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 04a3f423dce..4e42ec61011 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -334,15 +334,33 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     let ty = ty::mk_vec(tcx, t_a, None);
                     Some((ty, ty::UnsizeLength(len)))
                 }
-                (&ty::ty_trait(..), &ty::ty_trait(..)) => {
-                    None
+                (&ty::ty_trait(ref data_a), &ty::ty_trait(ref data_b)) => {
+                    // For now, we only support upcasts from
+                    // `Foo+Send` to `Foo` (really, any time there are
+                    // fewer builtin bounds then before). These are
+                    // convenient because they don't require any sort
+                    // of change to the vtable at runtime.
+                    if data_a.bounds.builtin_bounds != data_b.bounds.builtin_bounds &&
+                        data_a.bounds.builtin_bounds.is_superset(&data_b.bounds.builtin_bounds)
+                    {
+                        let bounds_a1 = ty::ExistentialBounds {
+                            region_bound: data_a.bounds.region_bound,
+                            builtin_bounds: data_b.bounds.builtin_bounds,
+                            projection_bounds: data_a.bounds.projection_bounds.clone(),
+                        };
+                        let ty_a1 = ty::mk_trait(tcx, data_a.principal.clone(), bounds_a1);
+                        match self.fcx.infcx().try(|_| self.subtype(ty_a1, ty_b)) {
+                            Ok(_) => Some((ty_b, ty::UnsizeUpcast(ty_b))),
+                            Err(_) => None,
+                        }
+                    } else {
+                        None
+                    }
                 }
-                (_, &ty::ty_trait(box ty::TyTrait { ref principal, ref bounds })) => {
-                    // FIXME what is the purpose of `ty`?
-                    let ty = ty::mk_trait(tcx, principal.clone(), bounds.clone());
-                    Some((ty, ty::UnsizeVtable(ty::TyTrait { principal: principal.clone(),
-                                                             bounds: bounds.clone() },
-                                               ty_a)))
+                (_, &ty::ty_trait(ref data)) => {
+                    Some((ty_b, ty::UnsizeVtable(ty::TyTrait { principal: data.principal.clone(),
+                                                               bounds: data.bounds.clone() },
+                                                 ty_a)))
                 }
                 (&ty::ty_struct(did_a, substs_a), &ty::ty_struct(did_b, substs_b))
                   if did_a == did_b => {
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 6ef6953f707..6aefcf5a47c 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -176,13 +176,13 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
             probe::AutoDeref(num) => {
                 ty::AutoDerefRef {
                     autoderefs: num,
-                    autoref: None
+                    autoref: None,
                 }
             }
             probe::AutoUnsizeLength(autoderefs, len) => {
                 ty::AutoDerefRef {
                     autoderefs: autoderefs,
-                    autoref: Some(ty::AutoUnsize(ty::UnsizeLength(len)))
+                    autoref: Some(ty::AutoUnsize(ty::UnsizeLength(len))),
                 }
             }
             probe::AutoRef(mutability, ref sub_adjustment) => {
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 1f7cc3bb647..bc581a6af41 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -933,7 +933,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
 
         return self.pick_method(step.self_ty).map(|r| self.adjust(r, adjustment.clone()));
 
-        fn consider_reborrow(ty: Ty, d: uint) -> PickAdjustment {
+        fn consider_reborrow<'tcx>(ty: Ty<'tcx>, d: uint) -> PickAdjustment {
             // Insert a `&*` or `&mut *` if this is a reference type:
             match ty.sty {
                 ty::ty_rptr(_, ref mt) => AutoRef(mt.mutbl, box AutoDeref(d+1)),
@@ -978,7 +978,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
     fn adjust(&mut self,
               result: PickResult<'tcx>,
               adjustment: PickAdjustment)
-              -> PickResult<'tcx> {
+              -> PickResult<'tcx>
+    {
         match result {
             Err(e) => Err(e),
             Ok(mut pick) => {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 41951ab2b62..31bee612b78 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1592,6 +1592,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                                       code: traits::ObjectCastObligation(self_ty) };
                 self.register_region_obligation(self_ty, ty_trait.bounds.region_bound, cause);
             }
+            ty::UnsizeUpcast(_) => { }
         }
     }
 
diff --git a/src/test/compile-fail/borrowck-consume-unsize-vec.rs b/src/test/compile-fail/borrowck-consume-unsize-vec.rs
new file mode 100644
index 00000000000..32490e0dc7d
--- /dev/null
+++ b/src/test/compile-fail/borrowck-consume-unsize-vec.rs
@@ -0,0 +1,22 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that we report an error if an upcast box is moved twice.
+
+fn consume(_: Box<[i32]>) {
+}
+
+fn foo(b: Box<[i32;5]>) {
+    consume(b);
+    consume(b); //~ ERROR use of moved value
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/borrowck-consume-upcast-box.rs b/src/test/compile-fail/borrowck-consume-upcast-box.rs
new file mode 100644
index 00000000000..5bcafa675c7
--- /dev/null
+++ b/src/test/compile-fail/borrowck-consume-upcast-box.rs
@@ -0,0 +1,24 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that we report an error if an upcast box is moved twice.
+
+trait Foo { fn dummy(&self); }
+
+fn consume(_: Box<Foo>) {
+}
+
+fn foo(b: Box<Foo+Send>) {
+    consume(b);
+    consume(b); //~ ERROR use of moved value
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/retslot-cast.rs b/src/test/compile-fail/retslot-cast.rs
index fafae0cb705..4ef07ecb438 100644
--- a/src/test/compile-fail/retslot-cast.rs
+++ b/src/test/compile-fail/retslot-cast.rs
@@ -12,9 +12,15 @@
 #![allow(warnings)]
 
 pub fn fail(x: Option<& (Iterator+Send)>) -> Option<&Iterator> {
-    // This call used to trigger an LLVM assertion because the return slot had type
-    // "Option<&Iterator>"* instead of "Option<&(Iterator+Send)>"*
-    inner(x)
+    // This call used to trigger an LLVM assertion because the return
+    // slot had type "Option<&Iterator>"* instead of
+    // "Option<&(Iterator+Send)>"* -- but this now yields a
+    // compilation error and I'm not sure how to create a comparable
+    // test. To ensure that this PARTICULAR failure doesn't occur
+    // again, though, I've left this test here, so if this ever starts
+    // to compile again, we can adjust the test appropriately (clearly
+    // it should never ICE...). -nmatsakis
+    inner(x) //~ ERROR mismatched types
 }
 
 pub fn inner(x: Option<& (Iterator+Send)>) -> Option<&(Iterator+Send)> {
@@ -22,4 +28,4 @@ pub fn inner(x: Option<& (Iterator+Send)>) -> Option<&(Iterator+Send)> {
 }
 
 #[rustc_error]
-fn main() {} //~ ERROR compilation successful
+fn main() {}