about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcargo/cargo.rc10
-rw-r--r--src/libcore/str.rs21
-rw-r--r--src/librustc/middle/kind.rs129
-rw-r--r--src/librustc/middle/lint.rs4
-rw-r--r--src/librustc/middle/moves.rs6
-rw-r--r--src/librustc/middle/ty.rs823
-rw-r--r--src/librustc/middle/typeck/coherence.rs11
-rw-r--r--src/libstd/json.rs2
-rw-r--r--src/test/compile-fail/bad-method-typaram-kind.rs2
-rw-r--r--src/test/compile-fail/copy-a-resource.rs9
-rw-r--r--src/test/compile-fail/issue-2548.rs2
-rw-r--r--src/test/compile-fail/issue-2823.rs2
-rw-r--r--src/test/compile-fail/kindck-nonsendable-1.rs6
-rw-r--r--src/test/compile-fail/kindck-owned.rs8
-rw-r--r--src/test/compile-fail/liveness-unused.rs2
-rw-r--r--src/test/compile-fail/moves-based-on-type-cyclic-types-issue-4821.rs34
-rw-r--r--src/test/compile-fail/no-send-res-ports.rs2
-rw-r--r--src/test/compile-fail/non-const.rs12
-rw-r--r--src/test/compile-fail/non-copyable-void.rs2
-rw-r--r--src/test/compile-fail/noncopyable-class.rs8
-rw-r--r--src/test/compile-fail/noncopyable-match-pattern.rs2
-rw-r--r--src/test/compile-fail/pinned-deep-copy.rs4
-rw-r--r--src/test/compile-fail/record-with-resource.rs4
-rw-r--r--src/test/compile-fail/repeat-to-run-dtor-twice.rs2
-rw-r--r--src/test/compile-fail/unique-object-noncopyable.rs8
-rw-r--r--src/test/compile-fail/unique-pinned-nocopy.rs4
-rw-r--r--src/test/compile-fail/unique-unique-kind.rs2
-rw-r--r--src/test/compile-fail/unique-vec-res.rs6
-rw-r--r--src/test/compile-fail/unsendable-class.rs4
-rw-r--r--src/test/run-pass/explicit-self-objects-uniq.rs2
-rw-r--r--src/test/run-pass/unique-object.rs10
31 files changed, 588 insertions, 555 deletions
diff --git a/src/libcargo/cargo.rc b/src/libcargo/cargo.rc
index cff44ed5ef2..0a6582c8460 100644
--- a/src/libcargo/cargo.rc
+++ b/src/libcargo/cargo.rc
@@ -457,7 +457,7 @@ pub fn parse_source(name: ~str, j: &json::Json) -> @Source {
     }
 
     match *j {
-        json::Object(j) => {
+        json::Object(ref j) => {
             let mut url = match j.find(&~"url") {
                 Some(&json::String(u)) => copy u,
                 _ => die!(~"needed 'url' field in source")
@@ -563,7 +563,7 @@ pub fn load_one_source_package(src: @Source, p: &json::Object) {
 
     let mut tags = ~[];
     match p.find(&~"tags") {
-        Some(&json::List(js)) => {
+        Some(&json::List(ref js)) => {
           for js.each |j| {
                 match *j {
                     json::String(ref j) => tags.grow(1u, j),
@@ -635,11 +635,11 @@ pub fn load_source_packages(c: &Cargo, src: @Source) {
     if !os::path_exists(&pkgfile) { return; }
     let pkgstr = io::read_whole_file_str(&pkgfile);
     match json::from_str(pkgstr.get()) {
-        Ok(json::List(js)) => {
+        Ok(json::List(ref js)) => {
           for js.each |j| {
                 match *j {
-                    json::Object(p) => {
-                        load_one_source_package(src, p);
+                    json::Object(ref p) => {
+                        load_one_source_package(src, *p);
                     }
                     _ => {
                         warn(~"malformed source json: " + src.name +
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index 9b25f92ede2..47e68401485 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -220,6 +220,16 @@ pub pure fn connect(v: &[~str], sep: &str) -> ~str {
     s
 }
 
+/// Concatenate a vector of strings, placing a given separator between each
+pub pure fn connect_slices(v: &[&str], sep: &str) -> ~str {
+    let mut s = ~"", first = true;
+    for vec::each(v) |ss| {
+        if first { first = false; } else { unsafe { push_str(&mut s, sep); } }
+        unsafe { push_str(&mut s, *ss) };
+    }
+    s
+}
+
 /// Given a string, make a new string with repeated copies of it
 pub pure fn repeat(ss: &str, nn: uint) -> ~str {
     let mut acc = ~"";
@@ -2668,6 +2678,17 @@ mod tests {
     }
 
     #[test]
+    fn test_connect_slices() {
+        fn t(v: &[&str], sep: &str, s: &str) {
+            assert connect_slices(v, sep) == s.to_str();
+        }
+        t(["you", "know", "I'm", "no", "good"],
+          " ", "you know I'm no good");
+        t([], " ", "");
+        t(["hi"], " ", "hi");
+    }
+
+    #[test]
     fn test_repeat() {
         assert repeat(~"x", 4) == ~"xxxx";
         assert repeat(~"hi", 4) == ~"hihihihi";
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index 35d5595fefd..a949b40bf75 100644
--- a/src/librustc/middle/kind.rs
+++ b/src/librustc/middle/kind.rs
@@ -15,7 +15,6 @@ use middle::freevars;
 use middle::lint::{non_implicitly_copyable_typarams, implicit_copies};
 use middle::liveness;
 use middle::pat_util;
-use middle::ty::{Kind, kind_copyable, kind_noncopyable, kind_const};
 use middle::ty;
 use middle::typeck;
 use middle;
@@ -61,26 +60,6 @@ use syntax::{visit, ast_util};
 
 pub const try_adding: &str = "Try adding a move";
 
-pub fn kind_to_str(k: Kind) -> ~str {
-    let mut kinds = ~[];
-
-    if ty::kind_lteq(kind_const(), k) {
-        kinds.push(~"const");
-    }
-
-    if ty::kind_can_be_copied(k) {
-        kinds.push(~"copy");
-    }
-
-    if ty::kind_can_be_sent(k) {
-        kinds.push(~"owned");
-    } else if ty::kind_is_durable(k) {
-        kinds.push(~"&static");
-    }
-
-    str::connect(kinds, ~" ")
-}
-
 pub type rval_map = HashMap<node_id, ()>;
 
 pub type ctx = {
@@ -119,11 +98,11 @@ type check_fn = fn@(ctx, @freevar_entry);
 // closure.
 fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
     fn check_for_uniq(cx: ctx, fv: @freevar_entry) {
-        // all captured data must be sendable, regardless of whether it is
-        // moved in or copied in.  Note that send implies owned.
+        // all captured data must be owned, regardless of whether it is
+        // moved in or copied in.
         let id = ast_util::def_id_of_def(fv.def).node;
         let var_t = ty::node_id_to_type(cx.tcx, id);
-        if !check_send(cx, var_t, fv.span) { return; }
+        if !check_owned(cx, var_t, fv.span) { return; }
 
         // check that only immutable variables are implicitly copied in
         check_imm_free_var(cx, fv.def, fv.span);
@@ -281,30 +260,54 @@ fn check_ty(aty: @Ty, cx: ctx, v: visit::vt<ctx>) {
     visit::visit_ty(aty, cx, v);
 }
 
-pub fn check_bounds(cx: ctx, id: node_id, sp: span,
-                    ty: ty::t, bounds: ty::param_bounds) {
-    let kind = ty::type_kind(cx.tcx, ty);
-    let p_kind = ty::param_bounds_to_kind(bounds);
-    if !ty::kind_lteq(p_kind, kind) {
-        // If the only reason the kind check fails is because the
-        // argument type isn't implicitly copyable, consult the warning
-        // settings to figure out what to do.
-        let implicit = ty::kind_implicitly_copyable() - ty::kind_copyable();
-        if ty::kind_lteq(p_kind, kind | implicit) {
-            cx.tcx.sess.span_lint(
-                non_implicitly_copyable_typarams,
-                id, cx.current_item, sp,
-                ~"instantiating copy type parameter with a \
-                 not implicitly copyable type");
-        } else {
-            cx.tcx.sess.span_err(
-                sp,
-                ~"instantiating a type parameter with an incompatible type " +
-                ~"(needs `" + kind_to_str(p_kind) +
-                ~"`, got `" + kind_to_str(kind) +
-                ~"`, missing `" + kind_to_str(p_kind - kind) + ~"`)");
+pub fn check_bounds(cx: ctx,
+                    _type_parameter_id: node_id,
+                    sp: span,
+                    ty: ty::t,
+                    bounds: ty::param_bounds)
+{
+    let kind = ty::type_contents(cx.tcx, ty);
+    let mut missing = ~[];
+    for bounds.each |bound| {
+        match *bound {
+            ty::bound_trait(_) => {
+                /* Not our job, checking in typeck */
+            }
+
+            ty::bound_copy => {
+                if !kind.is_copy(cx.tcx) {
+                    missing.push("Copy");
+                }
+            }
+
+            ty::bound_durable => {
+                if !kind.is_durable(cx.tcx) {
+                    missing.push("&static");
+                }
+            }
+
+            ty::bound_owned => {
+                if !kind.is_owned(cx.tcx) {
+                    missing.push("Owned");
+                }
+            }
+
+            ty::bound_const => {
+                if !kind.is_const(cx.tcx) {
+                    missing.push("Const");
+                }
+            }
         }
     }
+
+    if !missing.is_empty() {
+        cx.tcx.sess.span_err(
+            sp,
+            fmt!("instantiating a type parameter with an incompatible type \
+                  `%s`, which does not fulfill `%s`",
+                 ty_to_str(cx.tcx, ty),
+                 str::connect_slices(missing, " ")));
+    }
 }
 
 fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
@@ -342,16 +345,22 @@ fn check_imm_free_var(cx: ctx, def: def, sp: span) {
 }
 
 fn check_copy(cx: ctx, ty: ty::t, sp: span, reason: &str) {
-    let k = ty::type_kind(cx.tcx, ty);
-    if !ty::kind_can_be_copied(k) {
-        cx.tcx.sess.span_err(sp, ~"copying a noncopyable value");
+    debug!("type_contents(%s)=%s",
+           ty_to_str(cx.tcx, ty),
+           ty::type_contents(cx.tcx, ty).to_str());
+    if !ty::type_is_copyable(cx.tcx, ty) {
+        cx.tcx.sess.span_err(
+            sp, fmt!("copying a value of non-copyable type `%s`",
+                     ty_to_str(cx.tcx, ty)));
         cx.tcx.sess.span_note(sp, fmt!("%s", reason));
     }
 }
 
-pub fn check_send(cx: ctx, ty: ty::t, sp: span) -> bool {
-    if !ty::kind_can_be_sent(ty::type_kind(cx.tcx, ty)) {
-        cx.tcx.sess.span_err(sp, ~"not a sendable value");
+pub fn check_owned(cx: ctx, ty: ty::t, sp: span) -> bool {
+    if !ty::type_is_owned(cx.tcx, ty) {
+        cx.tcx.sess.span_err(
+            sp, fmt!("value has non-owned type `%s`",
+                     ty_to_str(cx.tcx, ty)));
         false
     } else {
         true
@@ -360,7 +369,7 @@ pub fn check_send(cx: ctx, ty: ty::t, sp: span) -> bool {
 
 // note: also used from middle::typeck::regionck!
 pub fn check_durable(tcx: ty::ctxt, ty: ty::t, sp: span) -> bool {
-    if !ty::kind_is_durable(ty::type_kind(tcx, ty)) {
+    if !ty::type_is_durable(tcx, ty) {
         match ty::get(ty).sty {
           ty::ty_param(*) => {
             tcx.sess.span_err(sp, ~"value may contain borrowed \
@@ -403,8 +412,8 @@ pub fn check_durable(tcx: ty::ctxt, ty: ty::t, sp: span) -> bool {
 pub fn check_cast_for_escaping_regions(
     cx: ctx,
     source: @expr,
-    target: @expr) {
-
+    target: @expr)
+{
     // Determine what type we are casting to; if it is not an trait, then no
     // worries.
     let target_ty = ty::expr_ty(cx.tcx, target);
@@ -450,13 +459,9 @@ pub fn check_kind_bounds_of_cast(cx: ctx, source: @expr, target: @expr) {
     match ty::get(target_ty).sty {
         ty::ty_trait(_, _, ty::vstore_uniq) => {
             let source_ty = ty::expr_ty(cx.tcx, source);
-            let source_kind = ty::type_kind(cx.tcx, source_ty);
-            if !ty::kind_can_be_copied(source_kind) {
-                cx.tcx.sess.span_err(target.span,
-                    ~"uniquely-owned trait objects must be copyable");
-            }
-            if !ty::kind_can_be_sent(source_kind) {
-                cx.tcx.sess.span_err(target.span,
+            if !ty::type_is_owned(cx.tcx, source_ty) {
+                cx.tcx.sess.span_err(
+                    target.span,
                     ~"uniquely-owned trait objects must be sendable");
             }
         }
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index 51bbdfdc19c..843b01bdb35 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -925,8 +925,8 @@ fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: ast::fn_decl,
 
                     ast::infer(_) => {
                         if tcx.legacy_modes {
-                            let kind = ty::type_kind(tcx, arg_ty.ty);
-                            if !ty::kind_is_safe_for_default_mode(kind) {
+                            let kind = ty::type_contents(tcx, arg_ty.ty);
+                            if !kind.is_safe_for_default_mode(tcx) {
                                 tcx.sess.span_lint(
                                     deprecated_mode, id, id,
                                     span,
diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs
index aaa3156e27c..0d32bb7ecf6 100644
--- a/src/librustc/middle/moves.rs
+++ b/src/librustc/middle/moves.rs
@@ -374,7 +374,7 @@ impl VisitContext {
          * not implicitly copyable.
          */
 
-        let result = if ty::type_implicitly_moves(self.tcx, ty) {
+        let result = if ty::type_moves_by_default(self.tcx, ty) {
             MoveInWhole
         } else {
             Read
@@ -495,7 +495,7 @@ impl VisitContext {
                     // moves-by-default:
                     let consume_with = with_fields.any(|tf| {
                         !fields.any(|f| f.node.ident == tf.ident) &&
-                            ty::type_implicitly_moves(self.tcx, tf.mt.ty)
+                            ty::type_moves_by_default(self.tcx, tf.mt.ty)
                     });
 
                     if consume_with {
@@ -830,7 +830,7 @@ impl VisitContext {
                 let fvar_ty = ty::node_id_to_type(self.tcx, fvar_def_id);
                 debug!("fvar_def_id=%? fvar_ty=%s",
                        fvar_def_id, ppaux::ty_to_str(self.tcx, fvar_ty));
-                let mode = if ty::type_implicitly_moves(self.tcx, fvar_ty) {
+                let mode = if ty::type_moves_by_default(self.tcx, fvar_ty) {
                     CapMove
                 } else {
                     CapCopy
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 6455c5da98d..2a43fc8a601 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -26,6 +26,7 @@ use session::Session;
 use util::ppaux::{note_and_explain_region, bound_region_to_str};
 use util::ppaux::{region_to_str, explain_region, vstore_to_str};
 use util::ppaux::{ty_to_str, tys_to_str};
+use util::common::{indenter};
 
 use core::cast;
 use core::cmp;
@@ -39,6 +40,7 @@ use core::result;
 use core::to_bytes;
 use core::uint;
 use core::vec;
+use core::hashmap::linear::LinearMap;
 use std::oldmap::HashMap;
 use std::{oldmap, oldsmallintmap};
 use syntax::ast::*;
@@ -259,7 +261,7 @@ struct ctxt_ {
     short_names_cache: HashMap<t, @~str>,
     needs_drop_cache: HashMap<t, bool>,
     needs_unwind_cleanup_cache: HashMap<t, bool>,
-    kind_cache: HashMap<t, Kind>,
+    mut tc_cache: LinearMap<uint, TypeContents>,
     ast_ty_to_ty_cache: HashMap<@ast::Ty, ast_ty_to_ty_cache_entry>,
     enum_var_cache: HashMap<def_id, @~[VariantInfo]>,
     trait_method_cache: HashMap<def_id, @~[method]>,
@@ -737,37 +739,6 @@ pub impl RegionVid : to_bytes::IterBytes {
     }
 }
 
-pub fn kind_to_param_bounds(kind: Kind) -> param_bounds {
-    let mut bounds = ~[];
-    if kind_can_be_copied(kind) { bounds.push(bound_copy); }
-    if kind_can_be_sent(kind) { bounds.push(bound_owned); }
-    else if kind_is_durable(kind) { bounds.push(bound_durable); }
-    if kind_is_const(kind) { bounds.push(bound_const); }
-    return @bounds;
-}
-
-pub fn param_bounds_to_kind(bounds: param_bounds) -> Kind {
-    let mut kind = kind_noncopyable();
-    for vec::each(*bounds) |bound| {
-        match *bound {
-          bound_copy => {
-            kind = raise_kind(kind, kind_implicitly_copyable());
-          }
-          bound_durable => {
-            kind = raise_kind(kind, kind_durable());
-          }
-          bound_owned => {
-            kind = raise_kind(kind, kind_owned_only() | kind_durable());
-          }
-          bound_const => {
-            kind = raise_kind(kind, kind_const());
-          }
-          bound_trait(_) => ()
-        }
-    }
-    kind
-}
-
 /// A polytype.
 ///
 /// - `bounds`: The list of bounds for each type parameter.  The length of the
@@ -851,7 +822,7 @@ pub fn mk_ctxt(s: session::Session,
         short_names_cache: new_ty_hash(),
         needs_drop_cache: new_ty_hash(),
         needs_unwind_cleanup_cache: new_ty_hash(),
-        kind_cache: new_ty_hash(),
+        tc_cache: LinearMap::new(),
         ast_ty_to_ty_cache: HashMap(),
         enum_var_cache: HashMap(),
         trait_method_cache: HashMap(),
@@ -1507,10 +1478,6 @@ pub fn type_is_structural(ty: t) -> bool {
     }
 }
 
-pub fn type_is_copyable(cx: ctxt, ty: t) -> bool {
-    return kind_can_be_copied(type_kind(cx, ty));
-}
-
 pub fn type_is_sequence(ty: t) -> bool {
     match get(ty).sty {
       ty_estr(_) | ty_evec(_, _) => true,
@@ -1777,469 +1744,493 @@ fn type_needs_unwind_cleanup_(cx: ctxt, ty: t,
     return needs_unwind_cleanup;
 }
 
-pub enum Kind { kind_(u32) }
-
-/// can be copied (implicitly or explicitly)
-const KIND_MASK_COPY         : u32 = 0b000000000000000000000000001_u32;
+/**
+ * Type contents is how the type checker reasons about kinds.
+ * They track what kinds of things are found within a type.  You can
+ * think of them as kind of an "anti-kind".  They track the kinds of values
+ * and thinks that are contained in types.  Having a larger contents for
+ * a type tends to rule that type *out* from various kinds.  For example,
+ * a type that contains a borrowed pointer is not sendable.
+ *
+ * The reason we compute type contents and not kinds is that it is
+ * easier for me (nmatsakis) to think about what is contained within
+ * a type than to think about what is *not* contained within a type.
+ */
+pub struct TypeContents {
+    bits: u32
+}
 
-/// no shared box, borrowed ptr (must imply DURABLE)
-const KIND_MASK_OWNED        : u32 = 0b000000000000000000000000010_u32;
+pub impl TypeContents {
+    fn intersects(&self, tc: TypeContents) -> bool {
+        (self.bits & tc.bits) != 0
+    }
 
-/// is durable (no borrowed ptrs)
-const KIND_MASK_DURABLE      : u32 = 0b000000000000000000000000100_u32;
+    fn is_copy(&self, cx: ctxt) -> bool {
+        !self.intersects(TypeContents::noncopyable(cx))
+    }
 
-/// is deeply immutable
-const KIND_MASK_CONST        : u32 = 0b000000000000000000000001000_u32;
+    static fn noncopyable(_cx: ctxt) -> TypeContents {
+        TC_DTOR + TC_BORROWED_MUT + TC_ONCE_CLOSURE + TC_OWNED_CLOSURE +
+            TC_EMPTY_ENUM
+    }
 
-/// can be implicitly copied (must imply COPY)
-const KIND_MASK_IMPLICIT     : u32 = 0b000000000000000000000010000_u32;
+    fn is_durable(&self, cx: ctxt) -> bool {
+        !self.intersects(TypeContents::nondurable(cx))
+    }
 
-/// safe for default mode (subset of KIND_MASK_IMPLICIT)
-const KIND_MASK_DEFAULT_MODE : u32 = 0b000000000000000000000100000_u32;
+    static fn nondurable(_cx: ctxt) -> TypeContents {
+        TC_BORROWED_POINTER
+    }
 
-pub fn kind_noncopyable() -> Kind {
-    kind_(0u32)
-}
+    fn is_owned(&self, cx: ctxt) -> bool {
+        !self.intersects(TypeContents::nonowned(cx))
+    }
 
-pub fn kind_copyable() -> Kind {
-    kind_(KIND_MASK_COPY)
-}
+    static fn nonowned(_cx: ctxt) -> TypeContents {
+        TC_MANAGED + TC_BORROWED_POINTER
+    }
 
-pub fn kind_implicitly_copyable() -> Kind {
-    kind_(KIND_MASK_IMPLICIT | KIND_MASK_COPY)
-}
+    fn is_const(&self, cx: ctxt) -> bool {
+        !self.intersects(TypeContents::nonconst(cx))
+    }
 
-fn kind_safe_for_default_mode() -> Kind {
-    // similar to implicit copy, but always includes vectors and strings
-    kind_(KIND_MASK_DEFAULT_MODE | KIND_MASK_IMPLICIT | KIND_MASK_COPY)
-}
+    static fn nonconst(_cx: ctxt) -> TypeContents {
+        TC_MUTABLE
+    }
 
-fn kind_implicitly_sendable() -> Kind {
-    kind_(KIND_MASK_IMPLICIT | KIND_MASK_COPY | KIND_MASK_OWNED)
-}
+    fn moves_by_default(&self, cx: ctxt) -> bool {
+        self.intersects(TypeContents::nonimplicitly_copyable(cx))
+    }
 
-fn kind_safe_for_default_mode_send() -> Kind {
-    // similar to implicit copy, but always includes vectors and strings
-    kind_(KIND_MASK_DEFAULT_MODE | KIND_MASK_IMPLICIT |
-          KIND_MASK_COPY | KIND_MASK_OWNED)
-}
+    static fn nonimplicitly_copyable(cx: ctxt) -> TypeContents {
+        let base = TypeContents::noncopyable(cx) + TC_OWNED_POINTER;
+        if cx.vecs_implicitly_copyable {base} else {base + TC_OWNED_SLICE}
+    }
 
+    fn is_safe_for_default_mode(&self, cx: ctxt) -> bool {
+        !self.intersects(TypeContents::nondefault_mode(cx))
+    }
 
-fn kind_owned_copy() -> Kind {
-    kind_(KIND_MASK_COPY | KIND_MASK_OWNED)
+    static fn nondefault_mode(cx: ctxt) -> TypeContents {
+        let tc = TypeContents::nonimplicitly_copyable(cx);
+        tc + TC_BIG + TC_OWNED_SLICE // disregard cx.vecs_implicitly_copyable
+    }
 }
 
-fn kind_owned_only() -> Kind {
-    kind_(KIND_MASK_OWNED)
+impl TypeContents : ops::Add<TypeContents,TypeContents> {
+    pure fn add(&self, other: &TypeContents) -> TypeContents {
+        TypeContents {bits: self.bits | other.bits}
+    }
 }
 
-pub fn kind_const() -> Kind {
-    kind_(KIND_MASK_CONST)
+impl TypeContents : ops::Sub<TypeContents,TypeContents> {
+    pure fn sub(&self, other: &TypeContents) -> TypeContents {
+        TypeContents {bits: self.bits & !other.bits}
+    }
 }
 
-fn kind_durable() -> Kind {
-    kind_(KIND_MASK_DURABLE)
+impl TypeContents : ToStr {
+    pure fn to_str(&self) -> ~str {
+        fmt!("TypeContents(%s)", u32::to_str_radix(self.bits, 2))
+    }
 }
 
-fn kind_top() -> Kind {
-    kind_(0xffffffffu32)
-}
+/// Constant for a type containing nothing of interest.
+const TC_NONE: TypeContents =             TypeContents{bits:0b0000_00000000};
 
-fn remove_const(k: Kind) -> Kind {
-    k - kind_const()
-}
+/// Contains a borrowed value with a lifetime other than static
+const TC_BORROWED_POINTER: TypeContents = TypeContents{bits:0b0000_00000001};
 
-fn remove_implicit(k: Kind) -> Kind {
-    k - kind_(KIND_MASK_IMPLICIT | KIND_MASK_DEFAULT_MODE)
-}
+/// Contains an owned pointer (~T) but not slice of some kind
+const TC_OWNED_POINTER: TypeContents =    TypeContents{bits:0b000000000010};
 
-fn remove_owned(k: Kind) -> Kind {
-    k - kind_(KIND_MASK_OWNED)
-}
+/// Contains an owned slice
+const TC_OWNED_SLICE: TypeContents =      TypeContents{bits:0b000000000100};
 
-fn remove_durable_owned(k: Kind) -> Kind {
-    k - kind_(KIND_MASK_DURABLE) - kind_(KIND_MASK_OWNED)
-}
+/// Contains a ~fn() or a ~Trait, which is non-copyable.
+const TC_OWNED_CLOSURE: TypeContents =    TypeContents{bits:0b000000001000};
 
-fn remove_copyable(k: Kind) -> Kind {
-    k - kind_(KIND_MASK_COPY | KIND_MASK_DEFAULT_MODE)
-}
+/// Type with a destructor
+const TC_DTOR: TypeContents =             TypeContents{bits:0b000000010000};
 
-impl Kind : ops::BitAnd<Kind,Kind> {
-    pure fn bitand(&self, other: &Kind) -> Kind {
-        unsafe {
-            lower_kind(*self, *other)
-        }
-    }
-}
+/// Contains a managed value
+const TC_MANAGED: TypeContents =          TypeContents{bits:0b000000100000};
 
-impl Kind : ops::BitOr<Kind,Kind> {
-    pure fn bitor(&self, other: &Kind) -> Kind {
-        unsafe {
-            raise_kind(*self, *other)
-        }
-    }
-}
+/// &mut with any region
+const TC_BORROWED_MUT: TypeContents =     TypeContents{bits:0b000001000000};
 
-impl Kind : ops::Sub<Kind,Kind> {
-    pure fn sub(&self, other: &Kind) -> Kind {
-        unsafe {
-            kind_(**self & !**other)
-        }
-    }
-}
+/// Mutable content, whether owned or by ref
+const TC_MUTABLE: TypeContents =          TypeContents{bits:0b000010000000};
 
-// Using these query functions is preferable to direct comparison or matching
-// against the kind constants, as we may modify the kind hierarchy in the
-// future.
-pub pure fn kind_can_be_implicitly_copied(k: Kind) -> bool {
-    *k & KIND_MASK_IMPLICIT == KIND_MASK_IMPLICIT
-}
+/// Mutable content, whether owned or by ref
+const TC_ONCE_CLOSURE: TypeContents =     TypeContents{bits:0b000100000000};
 
-pub pure fn kind_is_safe_for_default_mode(k: Kind) -> bool {
-    *k & KIND_MASK_DEFAULT_MODE == KIND_MASK_DEFAULT_MODE
-}
+/// Something we estimate to be "big"
+const TC_BIG: TypeContents =              TypeContents{bits:0b001000000000};
 
-pub pure fn kind_can_be_copied(k: Kind) -> bool {
-    *k & KIND_MASK_COPY == KIND_MASK_COPY
-}
+/// An enum with no variants.
+const TC_EMPTY_ENUM: TypeContents =       TypeContents{bits:0b010000000000};
 
-pub pure fn kind_can_be_sent(k: Kind) -> bool {
-    *k & KIND_MASK_OWNED == KIND_MASK_OWNED
-}
+/// All possible contents.
+const TC_ALL: TypeContents =              TypeContents{bits:0b011111111111};
 
-pub pure fn kind_is_durable(k: Kind) -> bool {
-    *k & KIND_MASK_DURABLE == KIND_MASK_DURABLE
+pub fn type_is_copyable(cx: ctxt, t: ty::t) -> bool {
+    type_contents(cx, t).is_copy(cx)
 }
 
-pure fn kind_is_const(k: Kind) -> bool {
-    *k & KIND_MASK_CONST == KIND_MASK_CONST
+pub fn type_is_durable(cx: ctxt, t: ty::t) -> bool {
+    type_contents(cx, t).is_durable(cx)
 }
 
-fn closure_kind(cty: &ClosureTy) -> Kind {
-    let kind = match cty.sigil {
-        ast::BorrowedSigil => kind_implicitly_copyable(),
-        ast::ManagedSigil => kind_implicitly_copyable(),
-        ast::OwnedSigil => kind_owned_only() | kind_durable(),
-    };
+pub fn type_is_owned(cx: ctxt, t: ty::t) -> bool {
+    type_contents(cx, t).is_owned(cx)
+}
 
-    let kind = match cty.region {
-        re_static => kind | kind_durable(),
-        _ => kind - kind_owned_only() - kind_durable()
-    };
+pub fn type_is_const(cx: ctxt, t: ty::t) -> bool {
+    type_contents(cx, t).is_const(cx)
+}
 
-    match cty.onceness {
-        ast::Once => kind - kind_implicitly_copyable(),
-        ast::Many => kind
+pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
+    let ty_id = type_id(ty);
+    match cx.tc_cache.find(&ty_id) {
+        Some(tc) => { return *tc; }
+        None => {}
     }
-}
 
-pub fn kind_lteq(a: Kind, b: Kind) -> bool {
-    *a & *b == *a
-}
+    let mut cache = LinearMap::new();
+    let result = tc_ty(cx, ty, &mut cache);
+    cx.tc_cache.insert(ty_id, result);
+    return result;
 
-fn lower_kind(a: Kind, b: Kind) -> Kind {
-    kind_(*a & *b)
-}
+    fn tc_ty(cx: ctxt,
+             ty: t,
+             cache: &mut LinearMap<uint, TypeContents>) -> TypeContents
+    {
+        // Subtle: Note that we are *not* using cx.tc_cache here but rather a
+        // private cache for this walk.  This is needed in the case of cyclic
+        // types like:
+        //
+        //     struct List { next: ~Option<List>, ... }
+        //
+        // When computing the type contents of such a type, we wind up deeply
+        // recursing as we go.  So when we encounter the recursive reference
+        // to List, we temporarily use TC_NONE as its contents.  Later we'll
+        // patch up the cache with the correct value, once we've computed it
+        // (this is basically a co-inductive process, if that helps).  So in
+        // the end we'll compute TC_OWNED_POINTER, in this case.
+        //
+        // The problem is, as we are doing the computation, we will also
+        // compute an *intermediate* contents for, e.g., Option<List> of
+        // TC_NONE.  This is ok during the computation of List itself, but if
+        // we stored this intermediate value into cx.tc_cache, then later
+        // requests for the contents of Option<List> would also yield TC_NONE
+        // which is incorrect.  This value was computed based on the crutch
+        // value for the type contents of list.  The correct value is
+        // TC_OWNED_POINTER.  This manifested as issue #4821.
+        let ty_id = type_id(ty);
+        match cache.find(&ty_id) {
+            Some(tc) => { return *tc; }
+            None => {}
+        }
+        cache.insert(ty_id, TC_NONE);
+
+        debug!("computing contents of %s", ty_to_str(cx, ty));
+        let _i = indenter();
+
+        let mut result = match get(ty).sty {
+            // Scalar and unique types are sendable, constant, and owned
+            ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
+            ty_bare_fn(_) | ty_ptr(_) => {
+                TC_NONE
+            }
 
-fn raise_kind(a: Kind, b: Kind) -> Kind {
-    kind_(*a | *b)
-}
+            ty_estr(vstore_uniq) => {
+                TC_OWNED_SLICE
+            }
 
-#[test]
-fn test_kinds() {
-    // The kind "lattice" is defined by the subset operation on the
-    // set of permitted operations.
-    assert kind_lteq(kind_owned_copy(), kind_owned_copy());
-    assert kind_lteq(kind_copyable(), kind_owned_copy());
-    assert kind_lteq(kind_copyable(), kind_copyable());
-    assert kind_lteq(kind_noncopyable(), kind_owned_copy());
-    assert kind_lteq(kind_noncopyable(), kind_copyable());
-    assert kind_lteq(kind_noncopyable(), kind_noncopyable());
-    assert kind_lteq(kind_copyable(), kind_implicitly_copyable());
-    assert kind_lteq(kind_copyable(), kind_implicitly_sendable());
-    assert kind_lteq(kind_owned_copy(), kind_implicitly_sendable());
-    assert !kind_lteq(kind_owned_copy(), kind_implicitly_copyable());
-    assert !kind_lteq(kind_copyable(), kind_owned_only());
-}
+            ty_closure(ref c) => {
+                closure_contents(c)
+            }
 
-// Return the most permissive kind that a composite object containing a field
-// with the given mutability can have.
-// This is used to prevent objects containing mutable state from being
-// implicitly copied and to compute whether things have const kind.
-fn mutability_kind(m: mutability) -> Kind {
-    match (m) {
-      m_mutbl => remove_const(remove_implicit(kind_top())),
-      m_const => remove_implicit(kind_top()),
-      m_imm => kind_top()
-    }
-}
+            ty_box(mt) => {
+                TC_MANAGED + nonowned(tc_mt(cx, mt, cache))
+            }
 
-fn mutable_type_kind(cx: ctxt, ty: mt) -> Kind {
-    lower_kind(mutability_kind(ty.mutbl), type_kind(cx, ty.ty))
-}
+            ty_trait(_, _, vstore_uniq) => {
+                TC_OWNED_CLOSURE
+            }
 
-pub fn type_kind(cx: ctxt, ty: t) -> Kind {
-    type_kind_ext(cx, ty, false)
-}
+            ty_trait(_, _, vstore_box) => {
+                TC_MANAGED
+            }
 
-// If `allow_ty_var` is true, then this is a conservative assumption; we
-// assume that type variables *do* have all kinds.
-pub fn type_kind_ext(cx: ctxt, ty: t, allow_ty_var: bool) -> Kind {
-    match cx.kind_cache.find(&ty) {
-      Some(result) => return result,
-      None => {/* fall through */ }
-    }
+            ty_trait(_, _, vstore_slice(r)) => {
+                borrowed_contents(r, m_imm)
+            }
 
-    // Insert a default in case we loop back on self recursively.
-    cx.kind_cache.insert(ty, kind_top());
+            ty_rptr(r, mt) => {
+                borrowed_contents(r, mt.mutbl) +
+                    nonowned(tc_mt(cx, mt, cache))
+            }
 
-    let mut result = match /*bad*/copy get(ty).sty {
-      // Scalar and unique types are sendable, constant, and owned
-      ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
-      ty_bare_fn(_) | ty_ptr(_) => {
-        kind_safe_for_default_mode_send() | kind_const() | kind_durable()
-      }
+            ty_uniq(mt) => {
+                TC_OWNED_POINTER + tc_mt(cx, mt, cache)
+            }
 
-      // Implicit copyability of strs is configurable
-      ty_estr(vstore_uniq) => {
-        if cx.vecs_implicitly_copyable {
-            kind_implicitly_sendable() | kind_const() | kind_durable()
-        } else {
-            kind_owned_copy() | kind_const() | kind_durable()
-        }
-      }
+            ty_evec(mt, vstore_uniq) => {
+                TC_OWNED_SLICE + tc_mt(cx, mt, cache)
+            }
 
-      ty_closure(ref c) => {
-          closure_kind(c)
-      }
+            ty_evec(mt, vstore_box) => {
+                TC_MANAGED + nonowned(tc_mt(cx, mt, cache))
+            }
 
-      // Those with refcounts raise noncopyable to copyable,
-      // lower sendable to copyable. Therefore just set result to copyable.
-      ty_box(tm) => {
-        remove_owned(mutable_type_kind(cx, tm) | kind_safe_for_default_mode())
-      }
+            ty_evec(mt, vstore_slice(r)) => {
+                borrowed_contents(r, mt.mutbl) +
+                    nonowned(tc_mt(cx, mt, cache))
+            }
 
-      // XXX: This is wrong for ~Trait and &Trait!
-      ty_trait(_, _, _) => kind_safe_for_default_mode() | kind_durable(),
+            ty_evec(mt, vstore_fixed(_)) => {
+                tc_mt(cx, mt, cache)
+            }
 
-      // Static region pointers are copyable and sendable, but not owned
-      ty_rptr(re_static, mt) =>
-      kind_safe_for_default_mode() | mutable_type_kind(cx, mt),
+            ty_estr(vstore_box) => {
+                TC_MANAGED
+            }
 
-      ty_rptr(_, mt) => {
-        if mt.mutbl == ast::m_mutbl {
-            // Mutable region pointers are noncopyable
-            kind_noncopyable()
-        } else {
-            // General region pointers are copyable but NOT owned nor sendable
-            kind_safe_for_default_mode()
-        }
-      }
+            ty_estr(vstore_slice(r)) => {
+                borrowed_contents(r, m_imm)
+            }
 
-      // Unique boxes and vecs have the kind of their contained type,
-      // but unique boxes can't be implicitly copyable.
-      ty_uniq(tm) => remove_implicit(mutable_type_kind(cx, tm)),
+            ty_estr(vstore_fixed(_)) => {
+                TC_NONE
+            }
 
-      // Implicit copyability of vecs is configurable
-      ty_evec(tm, vstore_uniq) => {
-          if cx.vecs_implicitly_copyable {
-              mutable_type_kind(cx, tm)
-          } else {
-              remove_implicit(mutable_type_kind(cx, tm))
-          }
-      }
+            ty_rec(ref flds) => {
+                flds.foldl(
+                    TC_NONE,
+                    |tc, f| tc + tc_mt(cx, f.mt, cache))
+            }
 
-      // Slices, refcounted evecs are copyable; uniques depend on the their
-      // contained type, but aren't implicitly copyable.  Fixed vectors have
-      // the kind of the element they contain, taking mutability into account.
-      ty_evec(tm, vstore_box) => {
-        remove_owned(kind_safe_for_default_mode() | mutable_type_kind(cx, tm))
-      }
-      ty_evec(tm, vstore_slice(re_static)) => {
-        kind_safe_for_default_mode() | mutable_type_kind(cx, tm)
-      }
-      ty_evec(tm, vstore_slice(_)) => {
-        remove_durable_owned(kind_safe_for_default_mode() |
-                           mutable_type_kind(cx, tm))
-      }
-      ty_evec(tm, vstore_fixed(_)) => {
-        mutable_type_kind(cx, tm)
-      }
+            ty_struct(did, ref substs) => {
+                let flds = struct_fields(cx, did, substs);
+                let flds_tc = flds.foldl(
+                    TC_NONE,
+                    |tc, f| tc + tc_mt(cx, f.mt, cache));
+                if ty::has_dtor(cx, did) {
+                    flds_tc + TC_DTOR
+                } else {
+                    flds_tc
+                }
+            }
 
-      // All estrs are copyable; uniques and interiors are sendable.
-      ty_estr(vstore_box) => {
-        kind_safe_for_default_mode() | kind_const() | kind_durable()
-      }
-      ty_estr(vstore_slice(re_static)) => {
-        kind_safe_for_default_mode() | kind_owned_copy() | kind_const()
-      }
-      ty_estr(vstore_slice(_)) => {
-        kind_safe_for_default_mode() | kind_const()
-      }
-      ty_estr(vstore_fixed(_)) => {
-        kind_safe_for_default_mode_send() | kind_const() | kind_durable()
-      }
+            ty_tup(ref tys) => {
+                tys.foldl(TC_NONE, |tc, ty| *tc + tc_ty(cx, *ty, cache))
+            }
 
-      // Records lower to the lowest of their members.
-      ty_rec(flds) => {
-        let mut lowest = kind_top();
-        for flds.each |f| {
-            lowest = lower_kind(lowest, mutable_type_kind(cx, f.mt));
-        }
-        lowest
-      }
+            ty_enum(did, ref substs) => {
+                let variants = substd_enum_variants(cx, did, substs);
+                if variants.is_empty() {
+                    // we somewhat arbitrary declare that empty enums
+                    // are non-copyable
+                    TC_EMPTY_ENUM
+                } else {
+                    variants.foldl(TC_NONE, |tc, variant| {
+                        variant.args.foldl(
+                            *tc,
+                            |tc, arg_ty| *tc + tc_ty(cx, *arg_ty, cache))
+                    })
+                }
+            }
 
-      ty_struct(did, ref substs) => {
-        // Structs are sendable if all their fields are sendable,
-        // likewise for copyable...
-        // also factor out this code, copied from the records case
-        let mut lowest = kind_top();
-        let flds = struct_fields(cx, did, substs);
-        for flds.each |f| {
-            lowest = lower_kind(lowest, mutable_type_kind(cx, f.mt));
-        }
-        // ...but structs with dtors are never copyable (they can be
-        // sendable)
-        if ty::has_dtor(cx, did) {
-           lowest = remove_copyable(lowest);
-        }
-        lowest
-      }
+            ty_param(p) => {
+                // We only ever ask for the kind of types that are defined in
+                // the current crate; therefore, the only type parameters that
+                // could be in scope are those defined in the current crate.
+                // If this assertion failures, it is likely because of a
+                // failure in the cross-crate inlining code to translate a
+                // def-id.
+                assert p.def_id.crate == ast::local_crate;
+
+                param_bounds_to_contents(
+                    cx, cx.ty_param_bounds.get(&p.def_id.node))
+            }
 
-      // Tuples lower to the lowest of their members.
-      ty_tup(tys) => {
-        let mut lowest = kind_top();
-        for tys.each |ty| { lowest = lower_kind(lowest, type_kind(cx, *ty)); }
-        lowest
-      }
+            ty_self => {
+                // Currently, self is not bounded, so we must assume the
+                // worst.  But in the future we should examine the super
+                // traits.
+                //
+                // FIXME(#4678)---self should just be a ty param
+                TC_ALL
+            }
 
-      // Enums lower to the lowest of their variants.
-      ty_enum(did, ref substs) => {
-        let mut lowest = kind_top();
-        let variants = enum_variants(cx, did);
-        if variants.is_empty() {
-            lowest = kind_owned_only() | kind_durable();
-        } else {
-            for variants.each |variant| {
-                for variant.args.each |aty| {
-                    // Perform any type parameter substitutions.
-                    let arg_ty = subst(cx, substs, *aty);
-                    lowest = lower_kind(lowest, type_kind(cx, arg_ty));
-                    if lowest == kind_noncopyable() { break; }
-                }
+            ty_infer(_) => {
+                // This occurs during coherence, but shouldn't occur at other
+                // times.
+                TC_ALL
+            }
+
+            ty_trait(_, _, vstore_fixed(_)) |
+            ty_type |
+            ty_opaque_closure_ptr(_) |
+            ty_opaque_box |
+            ty_unboxed_vec(_) |
+            ty_err => {
+                cx.sess.bug(~"Asked to compute contents of fictitious type");
             }
+        };
+
+        if type_size(cx, ty) > 4 {
+            result = result + TC_BIG;
         }
-        lowest
-      }
 
-      ty_param(p) => {
-          // We only ever ask for the kind of types that are defined in the
-          // current crate; therefore, the only type parameters that could be
-          // in scope are those defined in the current crate.  If this
-          // assertion failures, it is likely because of a failure in the
-          // cross-crate inlining code to translate a def-id.
-          assert p.def_id.crate == ast::local_crate;
+        debug!("result = %s", result.to_str());
 
-          param_bounds_to_kind(cx.ty_param_bounds.get(&p.def_id.node))
-      }
+        cache.insert(ty_id, result);
+        return result;
+    }
 
-      // self is a special type parameter that can only appear in traits; it
-      // is never bounded in any way, hence it has the bottom kind.
-      ty_self => kind_noncopyable(),
+    fn tc_mt(cx: ctxt,
+             mt: mt,
+             cache: &mut LinearMap<uint, TypeContents>) -> TypeContents
+    {
+        let mc = if mt.mutbl == m_mutbl {TC_MUTABLE} else {TC_NONE};
+        mc + tc_ty(cx, mt.ty, cache)
+    }
 
-      ty_infer(_) => {
-        if allow_ty_var {
-            kind_top()
+    fn borrowed_contents(region: ty::Region,
+                         mutbl: ast::mutability) -> TypeContents
+    {
+        let mc = if mutbl == m_mutbl {
+            TC_MUTABLE + TC_BORROWED_MUT
         } else {
-            cx.sess.bug(~"Asked to compute kind of a type variable")
-        }
-      }
+            TC_NONE
+        };
+        let rc = if region != ty::re_static {
+            TC_BORROWED_POINTER
+        } else {
+            TC_NONE
+        };
+        mc + rc
+    }
 
-      ty_type | ty_opaque_closure_ptr(_)
-      | ty_opaque_box | ty_unboxed_vec(_) | ty_err => {
-        cx.sess.bug(~"Asked to compute kind of fictitious type");
-      }
-    };
+    fn nonowned(pointee: TypeContents) -> TypeContents {
+        /*!
+         *
+         * Given a non-owning pointer to some type `T` with
+         * contents `pointee` (like `@T` or
+         * `&T`), returns the relevant bits that
+         * apply to the owner of the pointer.
+         */
 
-    // arbitrary threshold to prevent by-value copying of big records
-    if kind_is_safe_for_default_mode(result) {
-        if type_size(cx, ty) > 4 {
-            result = result - kind_(KIND_MASK_DEFAULT_MODE);
-        }
+        let mask = TC_MUTABLE.bits | TC_BORROWED_POINTER.bits;
+        TypeContents {bits: pointee.bits & mask}
     }
 
-    cx.kind_cache.insert(ty, result);
-    return result;
-}
+    fn closure_contents(cty: &ClosureTy) -> TypeContents {
+        let st = match cty.sigil {
+            ast::BorrowedSigil => TC_BORROWED_POINTER,
+            ast::ManagedSigil => TC_MANAGED,
+            ast::OwnedSigil => TC_OWNED_CLOSURE
+        };
+        let rt = borrowed_contents(cty.region, m_imm);
+        let ot = match cty.onceness {
+            ast::Once => TC_ONCE_CLOSURE,
+            ast::Many => TC_NONE
+        };
+        st + rt + ot
+    }
+
+    fn param_bounds_to_contents(cx: ctxt,
+                                bounds: param_bounds) -> TypeContents
+    {
+        debug!("param_bounds_to_contents()");
+        let _i = indenter();
+
+        let r = bounds.foldl(TC_ALL, |tc, bound| {
+            debug!("tc = %s, bound = %?", tc.to_str(), bound);
+            match *bound {
+                bound_copy => tc - TypeContents::nonimplicitly_copyable(cx),
+                bound_durable => tc - TypeContents::nondurable(cx),
+                bound_owned => tc - TypeContents::nonowned(cx),
+                bound_const => tc - TypeContents::nonconst(cx),
+                bound_trait(_) => *tc
+            }
+        });
 
-pub fn type_implicitly_moves(cx: ctxt, ty: t) -> bool {
-    let kind = type_kind(cx, ty);
-    !(kind_can_be_copied(kind) && kind_can_be_implicitly_copied(kind))
-}
+        debug!("result = %s", r.to_str());
+        return r;
+    }
 
-/// gives a rough estimate of how much space it takes to represent
-/// an instance of `ty`.  Used for the mode transition.
-fn type_size(cx: ctxt, ty: t) -> uint {
-    match /*bad*/copy get(ty).sty {
-      ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
-      ty_ptr(_) | ty_box(_) | ty_uniq(_) | ty_estr(vstore_uniq) |
-      ty_trait(*) | ty_rptr(*) | ty_evec(_, vstore_uniq) |
-      ty_evec(_, vstore_box) | ty_estr(vstore_box) => {
-        1
-      }
+    /// gives a rough estimate of how much space it takes to represent
+    /// an instance of `ty`.  Used for the mode transition.
+    fn type_size(cx: ctxt, ty: t) -> uint {
+        match /*bad*/copy get(ty).sty {
+          ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
+          ty_ptr(_) | ty_box(_) | ty_uniq(_) | ty_estr(vstore_uniq) |
+          ty_trait(*) | ty_rptr(*) | ty_evec(_, vstore_uniq) |
+          ty_evec(_, vstore_box) | ty_estr(vstore_box) => {
+            1
+          }
 
-      ty_evec(_, vstore_slice(_)) |
-      ty_estr(vstore_slice(_)) |
-      ty_bare_fn(*) |
-      ty_closure(*) => {
-        2
-      }
+          ty_evec(_, vstore_slice(_)) |
+          ty_estr(vstore_slice(_)) |
+          ty_bare_fn(*) |
+          ty_closure(*) => {
+            2
+          }
 
-      ty_evec(t, vstore_fixed(n)) => {
-        type_size(cx, t.ty) * n
-      }
+          ty_evec(t, vstore_fixed(n)) => {
+            type_size(cx, t.ty) * n
+          }
 
-      ty_estr(vstore_fixed(n)) => {
-        n
-      }
+          ty_estr(vstore_fixed(n)) => {
+            n
+          }
 
-      ty_rec(flds) => {
-        flds.foldl(0, |s, f| *s + type_size(cx, f.mt.ty))
-      }
+          ty_rec(flds) => {
+            flds.foldl(0, |s, f| *s + type_size(cx, f.mt.ty))
+          }
 
-      ty_struct(did, ref substs) => {
-        let flds = struct_fields(cx, did, substs);
-        flds.foldl(0, |s, f| *s + type_size(cx, f.mt.ty))
-      }
+          ty_struct(did, ref substs) => {
+            let flds = struct_fields(cx, did, substs);
+            flds.foldl(0, |s, f| *s + type_size(cx, f.mt.ty))
+          }
 
-      ty_tup(tys) => {
-        tys.foldl(0, |s, t| *s + type_size(cx, *t))
-      }
+          ty_tup(tys) => {
+            tys.foldl(0, |s, t| *s + type_size(cx, *t))
+          }
 
-      ty_enum(did, ref substs) => {
-        let variants = substd_enum_variants(cx, did, substs);
-        variants.foldl( // find max size of any variant
-            0,
-            |m, v| uint::max(*m,
-                             // find size of this variant:
-                             v.args.foldl(0, |s, a| *s + type_size(cx, *a))))
-      }
+          ty_enum(did, ref substs) => {
+            let variants = substd_enum_variants(cx, did, substs);
+            variants.foldl( // find max size of any variant
+                0,
+                |m, v| uint::max(
+                    *m,
+                    // find size of this variant:
+                    v.args.foldl(0, |s, a| *s + type_size(cx, *a))))
+          }
 
-      ty_param(_) | ty_self => {
-        1
-      }
+          ty_param(_) | ty_self => {
+            1
+          }
 
-      ty_infer(_) => {
-        cx.sess.bug(~"Asked to compute kind of a type variable");
-      }
-      ty_type | ty_opaque_closure_ptr(_)
-      | ty_opaque_box | ty_unboxed_vec(_) | ty_err => {
-        cx.sess.bug(~"Asked to compute kind of fictitious type");
-      }
+          ty_infer(_) => {
+            cx.sess.bug(~"Asked to compute kind of a type variable");
+          }
+          ty_type | ty_opaque_closure_ptr(_)
+          | ty_opaque_box | ty_unboxed_vec(_) | ty_err => {
+            cx.sess.bug(~"Asked to compute kind of fictitious type");
+          }
+        }
     }
 }
 
+pub fn type_moves_by_default(cx: ctxt, ty: t) -> bool {
+    type_contents(cx, ty).moves_by_default(cx)
+}
+
 // True if instantiating an instance of `r_ty` requires an instance of `r_ty`.
 pub fn is_instantiable(cx: ctxt, r_ty: t) -> bool {
     fn type_requires(cx: ctxt, seen: @mut ~[def_id],
@@ -2678,12 +2669,6 @@ impl arg : to_bytes::IterBytes {
     }
 }
 
-impl Kind : to_bytes::IterBytes {
-    pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
-        (**self).iter_bytes(lsb0, f)
-    }
-}
-
 impl FnSig : to_bytes::IterBytes {
     pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
         to_bytes::iter_bytes_2(&self.inputs,
@@ -4549,12 +4534,6 @@ impl param_bound : cmp::Eq {
     pure fn ne(&self, other: &param_bound) -> bool { !self.eq(other) }
 }
 
-impl Kind : cmp::Eq {
-    pure fn eq(&self, other: &Kind) -> bool { *(*self) == *(*other) }
-    pure fn ne(&self, other: &Kind) -> bool { *(*self) != *(*other) }
-}
-
-
 // Local Variables:
 // mode: rust
 // fill-column: 78;
diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs
index 9f28cb61520..1790bd19925 100644
--- a/src/librustc/middle/typeck/coherence.rs
+++ b/src/librustc/middle/typeck/coherence.rs
@@ -24,13 +24,13 @@ use metadata::cstore::{CStore, iter_crate_data};
 use metadata::decoder::{dl_def, dl_field, dl_impl};
 use middle::resolve::{Impl, MethodInfo};
 use middle::ty::{ProvidedMethodSource, ProvidedMethodInfo, bound_copy, get};
-use middle::ty::{kind_can_be_copied, lookup_item_type, param_bounds, subst};
+use middle::ty::{lookup_item_type, param_bounds, subst};
 use middle::ty::{substs, t, ty_bool, ty_bot, ty_box, ty_enum, ty_err};
 use middle::ty::{ty_estr, ty_evec, ty_float, ty_infer, ty_int, ty_nil};
 use middle::ty::{ty_opaque_box, ty_param, ty_param_bounds_and_ty, ty_ptr};
 use middle::ty::{ty_rec, ty_rptr, ty_self, ty_struct, ty_trait, ty_tup};
 use middle::ty::{ty_type, ty_uint, ty_uniq, ty_bare_fn, ty_closure};
-use middle::ty::{ty_opaque_closure_ptr, ty_unboxed_vec, type_kind_ext};
+use middle::ty::{ty_opaque_closure_ptr, ty_unboxed_vec};
 use middle::ty::{type_is_ty_var};
 use middle::ty;
 use middle::typeck::CrateCtxt;
@@ -578,11 +578,10 @@ pub impl CoherenceChecker {
                                 for bounds.each |bound| {
                                     match *bound {
                                         bound_copy => {
-                                            let kind = type_kind_ext(
+                                            if !ty::type_is_copyable(
                                                 self.inference_context.tcx,
-                                                resolved_ty,
-                                                true);
-                                            if !kind_can_be_copied(kind) {
+                                                resolved_ty)
+                                            {
                                                 might_unify = false;
                                                 break;
                                             }
diff --git a/src/libstd/json.rs b/src/libstd/json.rs
index 99c6c2f008d..80c64b214ba 100644
--- a/src/libstd/json.rs
+++ b/src/libstd/json.rs
@@ -832,7 +832,7 @@ pub impl Decoder: serialize::Decoder {
     fn read_owned_vec<T>(&self, f: fn(uint) -> T) -> T {
         debug!("read_owned_vec()");
         let len = match *self.peek() {
-            List(list) => list.len(),
+            List(ref list) => list.len(),
             _ => die!(~"not a list"),
         };
         let res = f(len);
diff --git a/src/test/compile-fail/bad-method-typaram-kind.rs b/src/test/compile-fail/bad-method-typaram-kind.rs
index b79e6ecb3fb..112423a073e 100644
--- a/src/test/compile-fail/bad-method-typaram-kind.rs
+++ b/src/test/compile-fail/bad-method-typaram-kind.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 fn foo<T>() {
-    1u.bar::<T>(); //~ ERROR: missing `copy`
+    1u.bar::<T>(); //~ ERROR: does not fulfill `Copy`
 }
 
 trait bar {
diff --git a/src/test/compile-fail/copy-a-resource.rs b/src/test/compile-fail/copy-a-resource.rs
index df2540be987..eb135e18893 100644
--- a/src/test/compile-fail/copy-a-resource.rs
+++ b/src/test/compile-fail/copy-a-resource.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern: copying a noncopyable value
-
 struct foo {
   i: int,
 }
@@ -24,4 +22,9 @@ fn foo(i:int) -> foo {
     }
 }
 
-fn main() { let x = move foo(10); let y = copy x; log(error, x); }
+fn main() {
+    let x = move foo(10);
+    let _y = copy x;
+    //~^ ERROR copying a value of non-copyable type `foo`
+    log(error, x);
+}
diff --git a/src/test/compile-fail/issue-2548.rs b/src/test/compile-fail/issue-2548.rs
index 2684289d70f..402e8ee831f 100644
--- a/src/test/compile-fail/issue-2548.rs
+++ b/src/test/compile-fail/issue-2548.rs
@@ -34,7 +34,7 @@ fn main() {
         let mut res = foo(x);
 
         let mut v = ~[mut];
-        v = move ~[mut (move res)] + v; //~ ERROR instantiating a type parameter with an incompatible type (needs `copy`, got `&static`, missing `copy`)
+        v = move ~[mut (move res)] + v; //~ ERROR does not fulfill `Copy`
         assert (v.len() == 2);
     }
 
diff --git a/src/test/compile-fail/issue-2823.rs b/src/test/compile-fail/issue-2823.rs
index 4ad349381da..f2ea4faf482 100644
--- a/src/test/compile-fail/issue-2823.rs
+++ b/src/test/compile-fail/issue-2823.rs
@@ -20,6 +20,6 @@ impl C : Drop {
 
 fn main() {
     let c = C{ x: 2};
-    let d = copy c; //~ ERROR copying a noncopyable value
+    let d = copy c; //~ ERROR copying a value of non-copyable type `C`
     error!("%?", d.x);
 }
diff --git a/src/test/compile-fail/kindck-nonsendable-1.rs b/src/test/compile-fail/kindck-nonsendable-1.rs
index dd1b3f03f05..f04740bae4c 100644
--- a/src/test/compile-fail/kindck-nonsendable-1.rs
+++ b/src/test/compile-fail/kindck-nonsendable-1.rs
@@ -12,7 +12,7 @@ fn foo(_x: @uint) {}
 
 fn main() {
     let x = @3u;
-    let _ = fn~() { foo(x); }; //~ ERROR not a sendable value
-    let _ = fn~(copy x) { foo(x); }; //~ ERROR not a sendable value
-    let _ = fn~(move x) { foo(x); }; //~ ERROR not a sendable value
+    let _ = fn~() { foo(x); }; //~ ERROR value has non-owned type `@uint`
+    let _ = fn~(copy x) { foo(x); }; //~ ERROR value has non-owned type `@uint`
+    let _ = fn~(move x) { foo(x); }; //~ ERROR value has non-owned type `@uint`
 }
\ No newline at end of file
diff --git a/src/test/compile-fail/kindck-owned.rs b/src/test/compile-fail/kindck-owned.rs
index bc6dd8f5dc9..65f6f837c83 100644
--- a/src/test/compile-fail/kindck-owned.rs
+++ b/src/test/compile-fail/kindck-owned.rs
@@ -18,12 +18,12 @@ fn copy2<T: Copy &static>(t: T) -> fn@() -> T {
 
 fn main() {
     let x = &3;
-    copy2(&x); //~ ERROR missing `&static`
+    copy2(&x); //~ ERROR does not fulfill `&static`
 
     copy2(@3);
-    copy2(@&x); //~ ERROR missing `&static`
+    copy2(@&x); //~ ERROR does not fulfill `&static`
 
     copy2(fn@() {});
-    copy2(fn~() {}); //~ ERROR missing `copy`
-    copy2(fn&() {}); //~ ERROR missing `&static`
+    copy2(fn~() {}); //~ ERROR does not fulfill `Copy`
+    copy2(fn&() {}); //~ ERROR does not fulfill `&static`
 }
diff --git a/src/test/compile-fail/liveness-unused.rs b/src/test/compile-fail/liveness-unused.rs
index 14eacb81cba..351a63f062d 100644
--- a/src/test/compile-fail/liveness-unused.rs
+++ b/src/test/compile-fail/liveness-unused.rs
@@ -68,5 +68,5 @@ impl r : Drop {
 
 fn main() {
     let x = r { x: () };
-    fn@(move x) { copy x; }; //~ ERROR copying a noncopyable value
+    fn@(move x) { copy x; }; //~ ERROR copying a value of non-copyable type
 }
diff --git a/src/test/compile-fail/moves-based-on-type-cyclic-types-issue-4821.rs b/src/test/compile-fail/moves-based-on-type-cyclic-types-issue-4821.rs
new file mode 100644
index 00000000000..4af3e1cbe86
--- /dev/null
+++ b/src/test/compile-fail/moves-based-on-type-cyclic-types-issue-4821.rs
@@ -0,0 +1,34 @@
+// Copyright 2012 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.
+
+// Test for a subtle failure computing kinds of cyclic types, in which
+// temporary kinds wound up being stored in a cache and used later.
+// See middle::ty::type_contents() for more information.
+
+extern mod std;
+use core::cmp::Ord;
+use core::option::swap_unwrap;
+
+struct List { key: int, next: Option<~List> }
+
+fn foo(node: ~List) -> int {
+    let r = match node.next {
+        Some(right) => consume(right),
+        None => 0
+    };
+    consume(node) + r //~ ERROR use of partially moved value: `node`
+}
+
+fn consume(v: ~List) -> int {
+    v.key
+}
+
+fn main() {}
+
diff --git a/src/test/compile-fail/no-send-res-ports.rs b/src/test/compile-fail/no-send-res-ports.rs
index 9a16ebda59d..d0c216fe231 100644
--- a/src/test/compile-fail/no-send-res-ports.rs
+++ b/src/test/compile-fail/no-send-res-ports.rs
@@ -29,7 +29,7 @@ fn main() {
 
     do task::spawn {
         let mut y = None;
-        *x <-> y; //~ ERROR not a sendable value
+        *x <-> y; //~ ERROR value has non-owned type
         log(error, y);
     }
 }
diff --git a/src/test/compile-fail/non-const.rs b/src/test/compile-fail/non-const.rs
index b8c7eb4a204..84f34d0e9bd 100644
--- a/src/test/compile-fail/non-const.rs
+++ b/src/test/compile-fail/non-const.rs
@@ -42,14 +42,14 @@ fn r2(x:@mut int) -> r2 {
 
 fn main() {
     foo({f: 3});
-    foo({mut f: 3}); //~ ERROR missing `const`
+    foo({mut f: 3}); //~ ERROR does not fulfill `Const`
     foo(~[1]);
-    foo(~[mut 1]); //~ ERROR missing `const`
+    foo(~[mut 1]); //~ ERROR does not fulfill `Const`
     foo(~1);
-    foo(~mut 1); //~ ERROR missing `const`
+    foo(~mut 1); //~ ERROR does not fulfill `Const`
     foo(@1);
-    foo(@mut 1); //~ ERROR missing `const`
+    foo(@mut 1); //~ ERROR does not fulfill `Const`
     foo(r(1)); // this is okay now.
-    foo(r2(@mut 1)); //~ ERROR missing `const`
-    foo({f: {mut f: 1}}); //~ ERROR missing `const`
+    foo(r2(@mut 1)); //~ ERROR does not fulfill `Const`
+    foo({f: {mut f: 1}}); //~ ERROR does not fulfill `Const`
 }
diff --git a/src/test/compile-fail/non-copyable-void.rs b/src/test/compile-fail/non-copyable-void.rs
index e96650938d2..d8192a24652 100644
--- a/src/test/compile-fail/non-copyable-void.rs
+++ b/src/test/compile-fail/non-copyable-void.rs
@@ -13,6 +13,6 @@ fn main() {
     let y : *libc::c_void = x as *libc::c_void;
     unsafe {
         let _z = copy *y;
-        //~^ ERROR copying a noncopyable value
+        //~^ ERROR copying a value of non-copyable type
     }
 }
diff --git a/src/test/compile-fail/noncopyable-class.rs b/src/test/compile-fail/noncopyable-class.rs
index 96a9347d2e9..360180ee455 100644
--- a/src/test/compile-fail/noncopyable-class.rs
+++ b/src/test/compile-fail/noncopyable-class.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern: copying a noncopyable value
-
 // Test that a class with a non-copyable field can't be
 // copied
 struct bar {
@@ -38,4 +36,8 @@ fn foo(i:int) -> foo {
     }
 }
 
-fn main() { let x = move foo(10); let y = copy x; log(error, x); }
+fn main() {
+    let x = move foo(10);
+    let _y = copy x; //~ ERROR copying a value of non-copyable type
+    log(error, x);
+}
diff --git a/src/test/compile-fail/noncopyable-match-pattern.rs b/src/test/compile-fail/noncopyable-match-pattern.rs
index 9b2d129a1a8..a123cf9fc6f 100644
--- a/src/test/compile-fail/noncopyable-match-pattern.rs
+++ b/src/test/compile-fail/noncopyable-match-pattern.rs
@@ -11,7 +11,7 @@
 fn main() {
     let x = Some(private::exclusive(false));
     match x {
-        Some(copy z) => { //~ ERROR copying a noncopyable value
+        Some(copy z) => { //~ ERROR copying a value of non-copyable type
             do z.with |b| { assert !*b; }
         }
         None => die!()
diff --git a/src/test/compile-fail/pinned-deep-copy.rs b/src/test/compile-fail/pinned-deep-copy.rs
index 16adb6f1925..80cf409f239 100644
--- a/src/test/compile-fail/pinned-deep-copy.rs
+++ b/src/test/compile-fail/pinned-deep-copy.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern: copying a noncopyable value
-
 struct r {
   i: @mut int,
 }
@@ -31,7 +29,7 @@ fn main() {
     {
         // Can't do this copy
         let x = ~~~{y: r(i)};
-        let z = copy x;
+        let _z = copy x; //~ ERROR copying a value of non-copyable type
         log(debug, x);
     }
     log(error, *i);
diff --git a/src/test/compile-fail/record-with-resource.rs b/src/test/compile-fail/record-with-resource.rs
index 95b0aba92fc..c9dc77ce4d2 100644
--- a/src/test/compile-fail/record-with-resource.rs
+++ b/src/test/compile-fail/record-with-resource.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern: copying a noncopyable value
-
 struct my_resource {
   x: int,
 }
@@ -29,7 +27,7 @@ fn my_resource(x: int) -> my_resource {
 fn main() {
     {
         let a = {x: 0, y: my_resource(20)};
-        let b = {x: 2,.. copy a};
+        let b = {x: 2,.. copy a}; //~ ERROR copying a value of non-copyable type
         log(error, (a, b));
     }
 }
diff --git a/src/test/compile-fail/repeat-to-run-dtor-twice.rs b/src/test/compile-fail/repeat-to-run-dtor-twice.rs
index d3205ef4f4b..58c07cc8ea6 100644
--- a/src/test/compile-fail/repeat-to-run-dtor-twice.rs
+++ b/src/test/compile-fail/repeat-to-run-dtor-twice.rs
@@ -24,6 +24,6 @@ impl Foo : Drop {
 
 fn main() {
     let a = Foo { x: 3 };
-    let _ = [ a, ..5 ];     //~ ERROR copying a noncopyable value
+    let _ = [ a, ..5 ];     //~ ERROR copying a value of non-copyable type
 }
 
diff --git a/src/test/compile-fail/unique-object-noncopyable.rs b/src/test/compile-fail/unique-object-noncopyable.rs
index 85ab64ce076..010161181f5 100644
--- a/src/test/compile-fail/unique-object-noncopyable.rs
+++ b/src/test/compile-fail/unique-object-noncopyable.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 trait Foo {
-    fn f();
+    fn f(&self);
 }
 
 struct Bar {
@@ -21,14 +21,14 @@ impl Bar : Drop {
 }
 
 impl Bar : Foo {
-    fn f() {
+    fn f(&self) {
         io::println("hi");
     }
 }
 
 fn main() {
     let x = ~Bar { x: 10 };
-    let y = (move x) as ~Foo;   //~ ERROR uniquely-owned trait objects must be copyable
-    let _z = copy y;
+    let y: ~Foo = x as ~Foo;
+    let _z = copy y; //~ ERROR copying a value of non-copyable type
 }
 
diff --git a/src/test/compile-fail/unique-pinned-nocopy.rs b/src/test/compile-fail/unique-pinned-nocopy.rs
index 9037290af4e..0211fd3c011 100644
--- a/src/test/compile-fail/unique-pinned-nocopy.rs
+++ b/src/test/compile-fail/unique-pinned-nocopy.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern: copying a noncopyable value
-
 struct r {
   b:bool,
 }
@@ -20,6 +18,6 @@ impl r : Drop {
 
 fn main() {
     let i = move ~r { b: true };
-    let j = copy i;
+    let _j = copy i; //~ ERROR copying a value of non-copyable type
     log(debug, i);
 }
diff --git a/src/test/compile-fail/unique-unique-kind.rs b/src/test/compile-fail/unique-unique-kind.rs
index e20971a63bd..25c42ab4add 100644
--- a/src/test/compile-fail/unique-unique-kind.rs
+++ b/src/test/compile-fail/unique-unique-kind.rs
@@ -13,5 +13,5 @@ fn f<T: Owned>(_i: T) {
 
 fn main() {
     let i = ~@100;
-    f(move i); //~ ERROR missing `owned`
+    f(move i); //~ ERROR does not fulfill `Owned`
 }
diff --git a/src/test/compile-fail/unique-vec-res.rs b/src/test/compile-fail/unique-vec-res.rs
index 1193861f19b..a97f47551e1 100644
--- a/src/test/compile-fail/unique-vec-res.rs
+++ b/src/test/compile-fail/unique-vec-res.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern: copying a noncopyable value
-
 struct r {
   i: @mut int,
 }
@@ -20,7 +18,7 @@ impl r : Drop {
     }
 }
 
-fn f<T>(+i: ~[T], +j: ~[T]) {
+fn f<T>(+_i: ~[T], +_j: ~[T]) {
 }
 
 fn main() {
@@ -29,6 +27,8 @@ fn main() {
     let r1 = move ~[~r { i: i1 }];
     let r2 = move ~[~r { i: i2 }];
     f(copy r1, copy r2);
+    //~^ ERROR copying a value of non-copyable type
+    //~^^ ERROR copying a value of non-copyable type
     log(debug, (r2, *i1));
     log(debug, (r1, *i2));
 }
diff --git a/src/test/compile-fail/unsendable-class.rs b/src/test/compile-fail/unsendable-class.rs
index b5bb05d5c69..e660884e40b 100644
--- a/src/test/compile-fail/unsendable-class.rs
+++ b/src/test/compile-fail/unsendable-class.rs
@@ -25,6 +25,6 @@ fn foo(i:int, j: @~str) -> foo {
 
 fn main() {
   let cat = ~"kitty";
-    let (_, ch) = pipes::stream(); //~ ERROR missing `owned`
-  ch.send(foo(42, @(move cat))); //~ ERROR missing `owned`
+    let (_, ch) = pipes::stream(); //~ ERROR does not fulfill `Owned`
+  ch.send(foo(42, @(move cat))); //~ ERROR does not fulfill `Owned`
 }
diff --git a/src/test/run-pass/explicit-self-objects-uniq.rs b/src/test/run-pass/explicit-self-objects-uniq.rs
index bf1776e598e..6e789b7ff1e 100644
--- a/src/test/run-pass/explicit-self-objects-uniq.rs
+++ b/src/test/run-pass/explicit-self-objects-uniq.rs
@@ -26,8 +26,6 @@ pub fn main() {
     let x = ~S { x: 3 };
     let y = x as ~Foo;
     y.f();
-    y.f();
-    y.f();
 }
 
 
diff --git a/src/test/run-pass/unique-object.rs b/src/test/run-pass/unique-object.rs
index 058d212eb69..4ad8ab38e4b 100644
--- a/src/test/run-pass/unique-object.rs
+++ b/src/test/run-pass/unique-object.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 trait Foo {
-    fn f();
+    fn f(&self) -> int;
 }
 
 struct Bar {
@@ -17,16 +17,14 @@ struct Bar {
 }
 
 impl Bar : Foo {
-    fn f() {
-        io::println("hi");
+    fn f(&self) -> int {
+        self.x
     }
 }
 
 pub fn main() {
     let x = ~Bar { x: 10 };
     let y = x as ~Foo;
-    let z = copy y;
-    y.f();
-    z.f();
+    assert y.f() == 10;
 }