about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-07-16 20:17:57 -0700
committerNiko Matsakis <niko@alum.mit.edu>2012-07-16 20:18:18 -0700
commit0e42004babc7b965a10056084a8ae76c72140a44 (patch)
treedd0484f1854d0c059b61516ee42812f16751fd94
parentd809336d0f5f4efa11336878736aeefe1fbae895 (diff)
downloadrust-0e42004babc7b965a10056084a8ae76c72140a44.tar.gz
rust-0e42004babc7b965a10056084a8ae76c72140a44.zip
introduce an owned kind for data that contains no borrowed ptrs
-rw-r--r--src/libsyntax/ast.rs1
-rw-r--r--src/libsyntax/fold.rs2
-rw-r--r--src/libsyntax/parse/parser.rs17
-rw-r--r--src/libsyntax/parse/token.rs2
-rw-r--r--src/libsyntax/print/pprust.rs1
-rw-r--r--src/libsyntax/visit.rs2
-rw-r--r--src/rustc/metadata/tydecode.rs1
-rw-r--r--src/rustc/metadata/tyencode.rs1
-rw-r--r--src/rustc/middle/kind.rs31
-rw-r--r--src/rustc/middle/resolve3.rs3
-rw-r--r--src/rustc/middle/trans/base.rs103
-rw-r--r--src/rustc/middle/trans/closure.rs18
-rw-r--r--src/rustc/middle/trans/foreign.rs6
-rw-r--r--src/rustc/middle/trans/impl.rs2
-rw-r--r--src/rustc/middle/ty.rs154
-rw-r--r--src/rustc/middle/typeck/check/method.rs3
-rw-r--r--src/rustc/middle/typeck/check/vtable.rs3
-rw-r--r--src/rustc/middle/typeck/collect.rs1
-rw-r--r--src/rustc/util/ppaux.rs1
-rw-r--r--src/test/compile-fail/block-arg-used-as-lambda-with-illegal-cap.rs4
-rw-r--r--src/test/compile-fail/cap-clause-illegal-cap.rs4
-rw-r--r--src/test/compile-fail/kindck-owned.rs19
-rw-r--r--src/test/run-pass/alignment-gep-tup-like-1.rs2
-rw-r--r--src/test/run-pass/close-over-big-then-small-data.rs2
-rw-r--r--src/test/run-pass/fixed-point-bind-unique.rs4
25 files changed, 245 insertions, 142 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index db70ad12b6e..0e70a58b33b 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -62,6 +62,7 @@ enum ty_param_bound {
     bound_copy,
     bound_send,
     bound_const,
+    bound_owned,
     bound_trait(@ty),
 }
 
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 965caacf07a..53bb9510acd 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -135,7 +135,7 @@ fn fold_fn_decl(decl: ast::fn_decl, fld: ast_fold) -> ast::fn_decl {
 
 fn fold_ty_param_bound(tpb: ty_param_bound, fld: ast_fold) -> ty_param_bound {
     alt tpb {
-      bound_copy | bound_send | bound_const { tpb }
+      bound_copy | bound_send | bound_const | bound_owned { tpb }
       bound_trait(ty) { bound_trait(fld.fold_ty(ty)) }
     }
 }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index fa0361e797b..dcc8f7430ca 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -16,7 +16,8 @@ import dvec::{dvec, extensions};
 import vec::{push};
 import ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute,
              bitand, bitor, bitxor, blk, blk_check_mode, bound_const,
-             bound_copy, bound_send, bound_trait, box, by_copy, by_move,
+             bound_copy, bound_send, bound_trait, bound_owned,
+             box, by_copy, by_move,
              by_mutbl_ref, by_ref, by_val, capture_clause, capture_item,
              carg_base, carg_ident, cdir_dir_mod, cdir_src_mod,
              cdir_view_item, checked_expr, claimed_expr, class_immutable,
@@ -1935,12 +1936,16 @@ class parser {
         let ident = self.parse_ident();
         if self.eat(token::COLON) {
             while self.token != token::COMMA && self.token != token::GT {
-                if self.eat_keyword(~"send") { push(bounds, bound_send); }
-                else if self.eat_keyword(~"copy") { push(bounds, bound_copy) }
+                if self.eat_keyword(~"send") {
+                    push(bounds, bound_send); }
+                else if self.eat_keyword(~"copy") {
+                    push(bounds, bound_copy) }
                 else if self.eat_keyword(~"const") {
-                    push(bounds, bound_const)
-                }
-                else { push(bounds, bound_trait(self.parse_ty(false))); }
+                    push(bounds, bound_const);
+                } else if self.eat_keyword(~"owned") {
+                    push(bounds, bound_owned);
+                } else {
+                    push(bounds, bound_trait(self.parse_ty(false))); }
             }
         }
         ret {ident: ident, id: self.get_id(), bounds: @bounds};
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index eb8c5b65fa5..949b078d8f0 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -313,7 +313,7 @@ fn restricted_keyword_table() -> hashmap<~str, ()> {
         ~"if", ~"iface", ~"impl", ~"import",
         ~"let", ~"log", ~"loop",
         ~"mod", ~"mut",
-        ~"new",
+        ~"new", ~"owned",
         ~"pure", ~"ret",
         ~"true", ~"trait", ~"type",
         ~"unchecked", ~"unsafe",
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 1b9b0ce7f96..937a46a05d3 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1377,6 +1377,7 @@ fn print_bounds(s: ps, bounds: @~[ast::ty_param_bound]) {
               ast::bound_copy { word(s.s, ~"copy"); }
               ast::bound_send { word(s.s, ~"send"); }
               ast::bound_const { word(s.s, ~"const"); }
+              ast::bound_owned { word(s.s, ~"owned"); }
               ast::bound_trait(t) { print_type(s, t); }
             }
         }
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 3e61f3706f9..b6822186b25 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -246,7 +246,7 @@ fn visit_ty_params<E>(tps: ~[ty_param], e: E, v: vt<E>) {
         for vec::each(*tp.bounds) |bound| {
             alt bound {
               bound_trait(t) { v.visit_ty(t, e, v); }
-              bound_copy | bound_send | bound_const { }
+              bound_copy | bound_send | bound_const | bound_owned { }
             }
         }
     }
diff --git a/src/rustc/metadata/tydecode.rs b/src/rustc/metadata/tydecode.rs
index 28056838a7a..d6084d308d3 100644
--- a/src/rustc/metadata/tydecode.rs
+++ b/src/rustc/metadata/tydecode.rs
@@ -417,6 +417,7 @@ fn parse_bounds(st: @pstate, conv: conv_did) -> @~[ty::param_bound] {
           'S' { ty::bound_send }
           'C' { ty::bound_copy }
           'K' { ty::bound_const }
+          'O' { ty::bound_owned }
           'I' { ty::bound_trait(parse_ty(st, conv)) }
           '.' { break; }
         });
diff --git a/src/rustc/metadata/tyencode.rs b/src/rustc/metadata/tyencode.rs
index 9a6ddaf7c59..99e8f1cb611 100644
--- a/src/rustc/metadata/tyencode.rs
+++ b/src/rustc/metadata/tyencode.rs
@@ -350,6 +350,7 @@ fn enc_bounds(w: io::writer, cx: @ctxt, bs: @~[ty::param_bound]) {
           ty::bound_send { w.write_char('S'); }
           ty::bound_copy { w.write_char('C'); }
           ty::bound_const { w.write_char('K'); }
+          ty::bound_owned { w.write_char('O'); }
           ty::bound_trait(tp) {
             w.write_char('I');
             enc_ty(w, cx, tp);
diff --git a/src/rustc/middle/kind.rs b/src/rustc/middle/kind.rs
index 7d89b4c701a..7c7b5eb49f7 100644
--- a/src/rustc/middle/kind.rs
+++ b/src/rustc/middle/kind.rs
@@ -1,8 +1,7 @@
 import syntax::{visit, ast_util};
 import syntax::ast::*;
 import syntax::codemap::span;
-import ty::{kind, kind_sendable, kind_copyable, kind_noncopyable, kind_const,
-           operators};
+import ty::{kind, kind_copyable, kind_noncopyable, kind_const, operators};
 import driver::session::session;
 import std::map::hashmap;
 import util::ppaux::{ty_to_str, tys_to_str};
@@ -21,6 +20,7 @@ import lint::{non_implicitly_copyable_typarams,implicit_copies};
 //  copy: Things that can be copied.
 //  const: Things thare are deeply immutable. They are guaranteed never to
 //    change, and can be safely shared without copying between tasks.
+//  owned: Things that do not contain borrowed pointers.
 //
 // Send includes scalar types as well as classes and unique types containing
 // only sendable types.
@@ -41,15 +41,21 @@ import lint::{non_implicitly_copyable_typarams,implicit_copies};
 
 fn kind_to_str(k: kind) -> ~str {
     let mut kinds = ~[];
+
     if ty::kind_lteq(kind_const(), k) {
         vec::push(kinds, ~"const");
     }
+
     if ty::kind_can_be_copied(k) {
         vec::push(kinds, ~"copy");
     }
+
     if ty::kind_can_be_sent(k) {
         vec::push(kinds, ~"send");
+    } else if ty::kind_is_owned(k) {
+        vec::push(kinds, ~"owned");
     }
+
     str::connect(kinds, ~" ")
 }
 
@@ -93,8 +99,8 @@ fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
     fn check_for_uniq(cx: ctx, id: node_id, fv: option<@freevar_entry>,
                       is_move: bool, var_t: ty::t, sp: span) {
         // all captured data must be sendable, regardless of whether it is
-        // moved in or copied in
-        check_send(cx, var_t, sp);
+        // moved in or copied in.  Note that send implies owned.
+        if !check_send(cx, var_t, sp) { ret; }
 
         // copied in data must be copyable, but moved in data can be anything
         let is_implicit = fv.is_some();
@@ -108,6 +114,9 @@ fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
 
     fn check_for_box(cx: ctx, id: node_id, fv: option<@freevar_entry>,
                      is_move: bool, var_t: ty::t, sp: span) {
+        // all captured data must be owned
+        if !check_owned(cx, var_t, sp) { ret; }
+
         // copied in data must be copyable, but moved in data can be anything
         let is_implicit = fv.is_some();
         if !is_move { check_copy(cx, id, var_t, sp, is_implicit); }
@@ -422,9 +431,21 @@ fn check_copy(cx: ctx, id: node_id, ty: ty::t, sp: span,
     }
 }
 
-fn check_send(cx: ctx, ty: ty::t, sp: span) {
+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");
+        false
+    } else {
+        true
+    }
+}
+
+fn check_owned(cx: ctx, ty: ty::t, sp: span) -> bool {
+    if !ty::kind_is_owned(ty::type_kind(cx.tcx, ty)) {
+        cx.tcx.sess.span_err(sp, ~"not an owned value");
+        false
+    } else {
+        true
     }
 }
 
diff --git a/src/rustc/middle/resolve3.rs b/src/rustc/middle/resolve3.rs
index 9e742f38bac..7a021549b92 100644
--- a/src/rustc/middle/resolve3.rs
+++ b/src/rustc/middle/resolve3.rs
@@ -4,6 +4,7 @@ import metadata::cstore::find_use_stmt_cnum;
 import metadata::decoder::{def_like, dl_def, dl_field, dl_impl};
 import middle::lint::{error, ignore, level, unused_imports, warn};
 import syntax::ast::{_mod, arm, blk, bound_const, bound_copy, bound_trait};
+import syntax::ast::{bound_owned};
 import syntax::ast::{bound_send, capture_clause, class_ctor, class_dtor};
 import syntax::ast::{class_member, class_method, crate, crate_num, decl_item};
 import syntax::ast::{def, def_arg, def_binding, def_class, def_const, def_fn};
@@ -3181,7 +3182,7 @@ class Resolver {
         for type_parameters.each |type_parameter| {
             for (*type_parameter.bounds).each |bound| {
                 alt bound {
-                    bound_copy | bound_send | bound_const {
+                    bound_copy | bound_send | bound_const | bound_owned {
                         // Nothing to do.
                     }
                     bound_trait(interface_type) {
diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs
index 212ba14a516..256c3b0dd11 100644
--- a/src/rustc/middle/trans/base.rs
+++ b/src/rustc/middle/trans/base.rs
@@ -1373,23 +1373,23 @@ fn move_val(cx: block, action: copy_action, dst: ValueRef,
     let tcx = cx.tcx();
     let mut cx = cx;
     if ty::type_is_scalar(t) {
-        if src.kind == owned { src_val = Load(cx, src_val); }
+        if src.kind == lv_owned { src_val = Load(cx, src_val); }
         Store(cx, src_val, dst);
         ret cx;
     } else if ty::type_is_nil(t) || ty::type_is_bot(t) {
         ret cx;
     } else if ty::type_is_boxed(t) || ty::type_is_unique(t) {
-        if src.kind == owned { src_val = Load(cx, src_val); }
+        if src.kind == lv_owned { src_val = Load(cx, src_val); }
         if action == DROP_EXISTING { cx = drop_ty(cx, dst, t); }
         Store(cx, src_val, dst);
-        if src.kind == owned { ret zero_mem(cx, src.val, t); }
+        if src.kind == lv_owned { ret zero_mem(cx, src.val, t); }
         // If we're here, it must be a temporary.
         revoke_clean(cx, src_val);
         ret cx;
     } else if type_is_structural_or_param(t) {
         if action == DROP_EXISTING { cx = drop_ty(cx, dst, t); }
         memmove_ty(cx, dst, src_val, t);
-        if src.kind == owned { ret zero_mem(cx, src_val, t); }
+        if src.kind == lv_owned { ret zero_mem(cx, src_val, t); }
         // If we're here, it must be a temporary.
         revoke_clean(cx, src_val);
         ret cx;
@@ -1403,8 +1403,8 @@ fn store_temp_expr(cx: block, action: copy_action, dst: ValueRef,
     -> block {
     let _icx = cx.insn_ctxt(~"trans_temp_expr");
     // Lvals in memory are not temporaries. Copy them.
-    if src.kind != temporary && !last_use {
-        let v = if src.kind == owned {
+    if src.kind != lv_temporary && !last_use {
+        let v = if src.kind == lv_owned {
                     load_if_immediate(cx, src.val, t)
                 } else {
                     src.val
@@ -1518,7 +1518,7 @@ fn trans_addr_of(cx: block, e: @ast::expr, dest: dest) -> block {
     let mut {bcx, val, kind} = trans_temp_lval(cx, e);
     let ety = expr_ty(cx, e);
     let is_immediate = ty::type_is_immediate(ety);
-    if (kind == temporary && is_immediate) || kind == owned_imm {
+    if (kind == lv_temporary && is_immediate) || kind == lv_owned_imm {
         val = do_spill(bcx, val, ety);
     }
     ret store_in_dest(bcx, val, dest);
@@ -1696,7 +1696,7 @@ fn trans_assign_op(bcx: block, ex: @ast::expr, op: ast::binop,
     let _icx = bcx.insn_ctxt(~"trans_assign_op");
     let t = expr_ty(bcx, src);
     let lhs_res = trans_lval(bcx, dst);
-    assert (lhs_res.kind == owned);
+    assert (lhs_res.kind == lv_owned);
 
     // A user-defined operator method
     alt bcx.ccx().maps.method_map.find(ex.id) {
@@ -1720,7 +1720,7 @@ fn trans_assign_op(bcx: block, ex: @ast::expr, op: ast::binop,
             arg_exprs(~[src]), save_in(target));
 
         ret move_val(bcx, DROP_EXISTING, lhs_res.val,
-                     {bcx: bcx, val: target, kind: owned},
+                     {bcx: bcx, val: target, kind: lv_owned},
                      dty);
       }
       _ {}
@@ -1947,9 +1947,9 @@ fn trans_loop(cx:block, body: ast::blk) -> block {
 }
 
 enum lval_kind {
-    temporary, //< Temporary value passed by value if of immediate type
-    owned,     //< Non-temporary value passed by pointer
-    owned_imm, //< Non-temporary value passed by value
+    lv_temporary, //< Temporary value passed by value if of immediate type
+    lv_owned,     //< Non-temporary value passed by pointer
+    lv_owned_imm, //< Non-temporary value passed by value
 }
 type local_var_result = {val: ValueRef, kind: lval_kind};
 type lval_result = {bcx: block, val: ValueRef, kind: lval_kind};
@@ -1972,10 +1972,10 @@ fn lval_from_local_var(bcx: block, r: local_var_result) -> lval_result {
 }
 
 fn lval_owned(bcx: block, val: ValueRef) -> lval_result {
-    ret {bcx: bcx, val: val, kind: owned};
+    ret {bcx: bcx, val: val, kind: lv_owned};
 }
 fn lval_temp(bcx: block, val: ValueRef) -> lval_result {
-    ret {bcx: bcx, val: val, kind: temporary};
+    ret {bcx: bcx, val: val, kind: lv_temporary};
 }
 
 fn lval_no_env(bcx: block, val: ValueRef, kind: lval_kind)
@@ -2355,7 +2355,7 @@ fn lval_static_fn_inner(bcx: block, fn_id: ast::def_id, id: ast::node_id,
             val = PointerCast(bcx, val, T_ptr(type_of_fn_from_ty(
                 ccx, node_id_type(bcx, id))));
         }
-        ret {bcx: bcx, val: val, kind: owned, env: null_env};
+        ret {bcx: bcx, val: val, kind: lv_owned, env: null_env};
     }
 
     let mut val = if fn_id.crate == ast::local_crate {
@@ -2376,7 +2376,7 @@ fn lval_static_fn_inner(bcx: block, fn_id: ast::def_id, id: ast::node_id,
           ast::extern_fn {
             // Extern functions are just opaque pointers
             let val = PointerCast(bcx, val, T_ptr(T_i8()));
-            ret lval_no_env(bcx, val, owned_imm);
+            ret lval_no_env(bcx, val, lv_owned_imm);
           }
           _ { /* fall through */ }
         }
@@ -2384,7 +2384,7 @@ fn lval_static_fn_inner(bcx: block, fn_id: ast::def_id, id: ast::node_id,
       _ { /* fall through */ }
     }
 
-    ret {bcx: bcx, val: val, kind: owned, env: null_env};
+    ret {bcx: bcx, val: val, kind: lv_owned, env: null_env};
 }
 
 fn lookup_discriminant(ccx: @crate_ctxt, vid: ast::def_id) -> ValueRef {
@@ -2415,15 +2415,15 @@ fn trans_local_var(cx: block, def: ast::def) -> local_var_result {
     fn take_local(table: hashmap<ast::node_id, local_val>,
                   id: ast::node_id) -> local_var_result {
         alt table.find(id) {
-          some(local_mem(v)) { {val: v, kind: owned} }
-          some(local_imm(v)) { {val: v, kind: owned_imm} }
+          some(local_mem(v)) { {val: v, kind: lv_owned} }
+          some(local_imm(v)) { {val: v, kind: lv_owned_imm} }
           r { fail(~"take_local: internal error"); }
         }
     }
     alt def {
       ast::def_upvar(nid, _, _) {
         assert (cx.fcx.llupvars.contains_key(nid));
-        ret { val: cx.fcx.llupvars.get(nid), kind: owned };
+        ret { val: cx.fcx.llupvars.get(nid), kind: lv_owned };
       }
       ast::def_arg(nid, _) {
         assert (cx.fcx.llargs.contains_key(nid));
@@ -2439,7 +2439,7 @@ fn trans_local_var(cx: block, def: ast::def) -> local_var_result {
           none { cx.sess().bug(~"trans_local_var: reference to self \
                                  out of context"); }
         };
-        ret {val: slf, kind: owned};
+        ret {val: slf, kind: lv_owned};
       }
       _ {
         cx.sess().unimpl(#fmt("unsupported def type in trans_local_def: %?",
@@ -2478,16 +2478,17 @@ fn trans_var(cx: block, def: ast::def, id: ast::node_id)-> lval_maybe_callee {
             let lldiscrim_gv = lookup_discriminant(ccx, vid);
             let lldiscrim = Load(cx, lldiscrim_gv);
             Store(cx, lldiscrim, lldiscrimptr);
-            ret lval_no_env(cx, llenumptr, temporary);
+            ret lval_no_env(cx, llenumptr, lv_temporary);
         }
       }
       ast::def_const(did) {
         if did.crate == ast::local_crate {
-            ret lval_no_env(cx, get_item_val(ccx, did.node), owned);
+            ret lval_no_env(cx, get_item_val(ccx, did.node), lv_owned);
         } else {
             let tp = node_id_type(cx, id);
             let val = trans_external_path(ccx, did, tp);
-            ret lval_no_env(cx, load_if_immediate(cx, val, tp), owned_imm);
+            ret lval_no_env(cx, load_if_immediate(cx, val, tp),
+                            lv_owned_imm);
         }
       }
       _ {
@@ -2538,7 +2539,7 @@ fn trans_rec_field_inner(bcx: block, val: ValueRef, ty: ty::t,
     }
     else { GEPi(bcx, val, ~[0u, ix]) };
 
-    ret {bcx: bcx, val: val, kind: owned};
+    ret {bcx: bcx, val: val, kind: lv_owned};
 }
 
 
@@ -2717,7 +2718,7 @@ fn lval_maybe_callee_to_lval(c: lval_maybe_callee, sp: span) -> lval_result {
         let llfnty = llvm::LLVMGetElementType(val_ty(c.val));
         let llfn = create_real_fn_pair(c.bcx, llfnty, c.val,
                                        null_env_ptr(c.bcx));
-        {bcx: c.bcx, val: llfn, kind: temporary}
+        {bcx: c.bcx, val: llfn, kind: lv_temporary}
       }
     }
 }
@@ -2863,7 +2864,7 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
           ast::expr_loop_body(blk) {
             let scratch = alloc_ty(cx, expr_ty(cx, blk));
             let bcx = trans_loop_body(cx, e, ret_flag, save_in(scratch));
-            {bcx: bcx, val: scratch, kind: temporary}
+            {bcx: bcx, val: scratch, kind: lv_temporary}
           }
         }
       }
@@ -2881,7 +2882,7 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
       let {bcx, val} = lval_result_to_result(lv, e_ty);
       let {bcx, val, ty: e_ty} =
           autoderef(bcx, e.id, val, e_ty, derefs);
-      {lv: {bcx: bcx, val: val, kind: temporary},
+      {lv: {bcx: bcx, val: val, kind: lv_temporary},
        e_ty: e_ty}
     };
 
@@ -2904,14 +2905,14 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
         alt arg_mode {
           ast::by_ref | ast::by_mutbl_ref {
             // Ensure that the value is spilled into memory:
-            if lv.kind != owned && ty::type_is_immediate(e_ty) {
+            if lv.kind != lv_owned && ty::type_is_immediate(e_ty) {
                 val = do_spill_noroot(bcx, val);
             }
           }
 
           ast::by_val {
             // Ensure that the value is not spilled into memory:
-            if lv.kind == owned || !ty::type_is_immediate(e_ty) {
+            if lv.kind == lv_owned || !ty::type_is_immediate(e_ty) {
                 val = Load(bcx, val);
             }
           }
@@ -2921,15 +2922,15 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
             let alloc = alloc_ty(bcx, arg.ty);
             let move_out = arg_mode == ast::by_move ||
                 ccx.maps.last_use_map.contains_key(e.id);
-            if lv.kind == temporary { revoke_clean(bcx, val); }
-            if lv.kind == owned || !ty::type_is_immediate(arg.ty) {
+            if lv.kind == lv_temporary { revoke_clean(bcx, val); }
+            if lv.kind == lv_owned || !ty::type_is_immediate(arg.ty) {
                 memmove_ty(bcx, alloc, val, arg.ty);
                 if move_out && ty::type_needs_drop(ccx.tcx, arg.ty) {
                     bcx = zero_mem(bcx, val, arg.ty);
                 }
             } else { Store(bcx, val, alloc); }
             val = alloc;
-            if lv.kind != temporary && !move_out {
+            if lv.kind != lv_temporary && !move_out {
                 bcx = take_ty(bcx, val, arg.ty);
             }
 
@@ -2977,9 +2978,9 @@ fn adapt_borrowed_value(lv: lval_result,
       ty::ty_estr(_) | ty::ty_evec(_, _) {
         let ccx = bcx.ccx();
         let val = alt lv.kind {
-          temporary { lv.val }
-          owned { load_if_immediate(bcx, lv.val, e_ty) }
-          owned_imm { lv.val }
+          lv_temporary { lv.val }
+          lv_owned { load_if_immediate(bcx, lv.val, e_ty) }
+          lv_owned_imm { lv.val }
         };
 
         let unit_ty = ty::sequence_element_type(ccx.tcx, e_ty);
@@ -3150,7 +3151,7 @@ fn trans_call_inner(
           }
           is_closure {
             // It's a closure. Have to fetch the elements
-            if f_res.kind == owned {
+            if f_res.kind == lv_owned {
                 faddr = load_if_immediate(bcx, faddr, fn_expr_ty);
             }
             let pair = faddr;
@@ -3418,17 +3419,17 @@ fn trans_temp_lval(bcx: block, e: @ast::expr) -> lval_result {
         let ty = expr_ty(bcx, e);
         if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
             bcx = trans_expr(bcx, e, ignore);
-            ret {bcx: bcx, val: C_nil(), kind: temporary};
+            ret {bcx: bcx, val: C_nil(), kind: lv_temporary};
         } else if ty::type_is_immediate(ty) {
             let cell = empty_dest_cell();
             bcx = trans_expr(bcx, e, by_val(cell));
             add_clean_temp(bcx, *cell, ty);
-            ret {bcx: bcx, val: *cell, kind: temporary};
+            ret {bcx: bcx, val: *cell, kind: lv_temporary};
         } else {
             let scratch = alloc_ty(bcx, ty);
             let bcx = trans_expr_save_in(bcx, e, scratch);
             add_clean_temp(bcx, scratch, ty);
-            ret {bcx: bcx, val: scratch, kind: temporary};
+            ret {bcx: bcx, val: scratch, kind: lv_temporary};
         }
     }
 }
@@ -3442,9 +3443,9 @@ fn trans_temp_expr(bcx: block, e: @ast::expr) -> result {
 
 fn load_value_from_lval_result(lv: lval_result, ty: ty::t) -> ValueRef {
     alt lv.kind {
-      temporary { lv.val }
-      owned { load_if_immediate(lv.bcx, lv.val, ty) }
-      owned_imm { lv.val }
+      lv_temporary { lv.val }
+      lv_owned { load_if_immediate(lv.bcx, lv.val, ty) }
+      lv_owned_imm { lv.val }
     }
 }
 
@@ -3521,7 +3522,7 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
 
         let _icx = bcx.insn_ctxt(~"root_value_expr");
         add_root_cleanup(bcx, scope_id, root_loc, ty);
-        let lv = {bcx: bcx, val: root_loc, kind: owned};
+        let lv = {bcx: bcx, val: root_loc, kind: lv_owned};
         lval_result_to_dps(lv, ty, false, dest)
       }
     };
@@ -3647,7 +3648,7 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
             assert dest == ignore;
             let src_r = trans_temp_lval(bcx, src);
             let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst);
-            assert kind == owned;
+            assert kind == lv_owned;
             let is_last_use =
                 bcx.ccx().maps.last_use_map.contains_key(src.id);
             ret store_temp_expr(bcx, DROP_EXISTING, addr, src_r,
@@ -3658,14 +3659,14 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
             assert dest == ignore;
             let src_r = trans_temp_lval(bcx, src);
             let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst);
-            assert kind == owned;
+            assert kind == lv_owned;
             ret move_val(bcx, DROP_EXISTING, addr, src_r,
                          expr_ty(bcx, src));
           }
           ast::expr_swap(dst, src) {
             assert dest == ignore;
             let lhs_res = trans_lval(bcx, dst);
-            assert lhs_res.kind == owned;
+            assert lhs_res.kind == lv_owned;
             let rhs_res = trans_lval(lhs_res.bcx, src);
             let t = expr_ty(bcx, src);
             let tmp_alloc = alloc_ty(rhs_res.bcx, t);
@@ -3728,7 +3729,7 @@ fn lval_to_dps(bcx: block, e: @ast::expr, dest: dest) -> block {
     let last_use_map = bcx.ccx().maps.last_use_map;
     let ty = expr_ty(bcx, e);
     let lv = trans_lval(bcx, e);
-    let last_use = (lv.kind == owned && last_use_map.contains_key(e.id));
+    let last_use = (lv.kind == lv_owned && last_use_map.contains_key(e.id));
     #debug["is last use (%s) = %b, %d", expr_to_str(e), last_use,
            lv.kind as int];
     lval_result_to_dps(lv, ty, last_use, dest)
@@ -3740,7 +3741,7 @@ fn lval_result_to_dps(lv: lval_result, ty: ty::t,
     let ccx = bcx.ccx();
     alt dest {
       by_val(cell) {
-        if kind == temporary {
+        if kind == lv_temporary {
             revoke_clean(bcx, val);
             *cell = val;
         } else if last_use {
@@ -3749,7 +3750,7 @@ fn lval_result_to_dps(lv: lval_result, ty: ty::t,
                 bcx = zero_mem(bcx, val, ty);
             }
         } else {
-            if kind == owned { val = Load(bcx, val); }
+            if kind == lv_owned { val = Load(bcx, val); }
             let {bcx: cx, val} = take_ty_immediate(bcx, val, ty);
             *cell = val;
             bcx = cx;
@@ -4788,7 +4789,7 @@ fn trans_class_ctor(ccx: @crate_ctxt, path: path, decl: ast::fn_decl,
 
   // Translate the body of the ctor
   bcx = trans_block(bcx_top, body, ignore);
-  let lval_res = {bcx: bcx, val: selfptr, kind: owned};
+  let lval_res = {bcx: bcx, val: selfptr, kind: lv_owned};
   // Generate the return expression
   bcx = store_temp_expr(bcx, INIT, fcx.llretptr, lval_res,
                         rslt_ty, true);
diff --git a/src/rustc/middle/trans/closure.rs b/src/rustc/middle/trans/closure.rs
index 22d16efbae5..dfeecabeb10 100644
--- a/src/rustc/middle/trans/closure.rs
+++ b/src/rustc/middle/trans/closure.rs
@@ -216,31 +216,31 @@ fn store_environment(bcx: block,
         let bound_data = GEPi(bcx, llbox,
              ~[0u, abi::box_field_body, i]);
         alt bv {
-          env_copy(val, ty, owned) {
+          env_copy(val, ty, lv_owned) {
             let val1 = load_if_immediate(bcx, val, ty);
             bcx = base::copy_val(bcx, INIT, bound_data, val1, ty);
           }
-          env_copy(val, ty, owned_imm) {
+          env_copy(val, ty, lv_owned_imm) {
             bcx = base::copy_val(bcx, INIT, bound_data, val, ty);
           }
-          env_copy(_, _, temporary) {
+          env_copy(_, _, lv_temporary) {
             fail ~"cannot capture temporary upvar";
           }
           env_move(val, ty, kind) {
             let src = {bcx:bcx, val:val, kind:kind};
             bcx = move_val(bcx, INIT, bound_data, src, ty);
           }
-          env_ref(val, ty, owned) {
+          env_ref(val, ty, lv_owned) {
             #debug["> storing %s into %s",
                    val_str(bcx.ccx().tn, val),
                    val_str(bcx.ccx().tn, bound_data)];
             Store(bcx, val, bound_data);
           }
-          env_ref(val, ty, owned_imm) {
+          env_ref(val, ty, lv_owned_imm) {
             let addr = do_spill_noroot(bcx, val);
             Store(bcx, addr, bound_data);
           }
-          env_ref(_, _, temporary) {
+          env_ref(_, _, lv_temporary) {
             fail ~"cannot capture temporary upvar";
           }
         }
@@ -289,7 +289,7 @@ fn build_closure(bcx0: block,
             vec::push(env_vals, env_move(lv.val, ty, lv.kind));
           }
           capture::cap_drop {
-            assert lv.kind == owned;
+            assert lv.kind == lv_owned;
             bcx = drop_ty(bcx, lv.val, ty);
             bcx = zero_mem(bcx, lv.val, ty);
           }
@@ -303,9 +303,9 @@ fn build_closure(bcx0: block,
         let nil_ret = PointerCast(bcx, our_ret, T_ptr(T_nil()));
         vec::push(env_vals,
                   env_ref(flagptr,
-                          ty::mk_mut_ptr(tcx, ty::mk_bool(tcx)), owned));
+                          ty::mk_mut_ptr(tcx, ty::mk_bool(tcx)), lv_owned));
         vec::push(env_vals,
-                  env_ref(nil_ret, ty::mk_nil_ptr(tcx), owned));
+                  env_ref(nil_ret, ty::mk_nil_ptr(tcx), lv_owned));
     }
     ret store_environment(bcx, env_vals, ck);
 }
diff --git a/src/rustc/middle/trans/foreign.rs b/src/rustc/middle/trans/foreign.rs
index fca21eb3a8e..21ffefc5456 100644
--- a/src/rustc/middle/trans/foreign.rs
+++ b/src/rustc/middle/trans/foreign.rs
@@ -881,7 +881,7 @@ fn trans_intrinsic(ccx: @crate_ctxt, decl: ValueRef, item: @ast::foreign_item,
         let tp_ty = substs.tys[0];
         let src = {bcx: bcx,
                    val: get_param(decl, first_real_arg + 1u),
-                   kind: owned };
+                   kind: lv_owned};
         bcx = move_val(bcx, DROP_EXISTING,
                        get_param(decl, first_real_arg),
                        src,
@@ -891,7 +891,7 @@ fn trans_intrinsic(ccx: @crate_ctxt, decl: ValueRef, item: @ast::foreign_item,
         let tp_ty = substs.tys[0];
         let src = {bcx: bcx,
                    val: get_param(decl, first_real_arg + 1u),
-                   kind: owned };
+                   kind: lv_owned};
         bcx = move_val(bcx, INIT,
                        get_param(decl, first_real_arg),
                        src,
@@ -982,7 +982,7 @@ fn trans_intrinsic(ccx: @crate_ctxt, decl: ValueRef, item: @ast::foreign_item,
                                |bcx| lval_no_env(
                                    bcx,
                                    get_param(decl, first_real_arg),
-                                   temporary),
+                                   lv_temporary),
                                arg_vals(~[frameaddress_val]), ignore);
       }
     }
diff --git a/src/rustc/middle/trans/impl.rs b/src/rustc/middle/trans/impl.rs
index 8ea0583b81b..5fc1b204e0e 100644
--- a/src/rustc/middle/trans/impl.rs
+++ b/src/rustc/middle/trans/impl.rs
@@ -162,7 +162,7 @@ fn trans_trait_callee(bcx: block, val: ValueRef,
     let vtable = PointerCast(bcx, vtable,
                              T_ptr(T_array(T_ptr(llfty), n_method + 1u)));
     let mptr = Load(bcx, GEPi(bcx, vtable, ~[0u, n_method]));
-    {bcx: bcx, val: mptr, kind: owned, env: env}
+    {bcx: bcx, val: mptr, kind: lv_owned, env: env}
 }
 
 fn find_vtable_in_fn_ctxt(ps: param_substs, n_param: uint, n_bound: uint)
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index 0e724321b3e..0ff07554505 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -107,9 +107,10 @@ export tbox_has_flag;
 export ty_var_id;
 export ty_to_def_id;
 export ty_fn_args;
-export kind, kind_implicitly_copyable, kind_sendable, kind_copyable;
+export kind, kind_implicitly_copyable, kind_send_copy, kind_copyable;
 export kind_noncopyable, kind_const;
 export kind_can_be_copied, kind_can_be_sent, kind_can_be_implicitly_copied;
+export kind_is_owned;
 export proto_kind, kind_lteq, type_kind;
 export operators;
 export type_err, terr_vstore_kind;
@@ -144,7 +145,8 @@ export closure_kind;
 export ck_block;
 export ck_box;
 export ck_uniq;
-export param_bound, param_bounds, bound_copy, bound_send, bound_trait;
+export param_bound, param_bounds, bound_copy, bound_owned;
+export bound_send, bound_trait;
 export param_bounds_to_kind;
 export default_arg_mode_for_ty;
 export item_path;
@@ -409,6 +411,7 @@ enum type_err {
 
 enum param_bound {
     bound_copy,
+    bound_owned,
     bound_send,
     bound_const,
     bound_trait(t),
@@ -451,8 +454,15 @@ fn param_bounds_to_kind(bounds: param_bounds) -> kind {
           bound_copy {
             kind = raise_kind(kind, kind_implicitly_copyable());
           }
-          bound_send { kind = raise_kind(kind, kind_send_only()); }
-          bound_const { kind = raise_kind(kind, kind_const()); }
+          bound_owned {
+            kind = raise_kind(kind, kind_owned());
+          }
+          bound_send {
+            kind = raise_kind(kind, kind_send_only() | kind_owned());
+          }
+          bound_const {
+            kind = raise_kind(kind, kind_const());
+          }
           bound_trait(_) {}
         }
     }
@@ -1303,11 +1313,20 @@ fn type_needs_unwind_cleanup_(cx: ctxt, ty: t,
 
 enum kind { kind_(u32) }
 
-// *ALL* implicitly copiable things must be copiable
-const KIND_MASK_COPY     : u32 = 0b00000000000000000000000000000001u32;
-const KIND_MASK_SEND     : u32 = 0b00000000000000000000000000000010u32;
-const KIND_MASK_CONST    : u32 = 0b00000000000000000000000000000100u32;
-const KIND_MASK_IMPLICIT : u32 = 0b00000000000000000000000000001000u32;
+/// can be copied (implicitly or explicitly)
+const KIND_MASK_COPY     : u32 = 0b00000000000000000000000000000001_u32;
+
+/// can be sent: no shared box, borrowed ptr (must imply OWNED)
+const KIND_MASK_SEND     : u32 = 0b00000000000000000000000000000010_u32;
+
+/// is owned (no borrowed ptrs)
+const KIND_MASK_OWNED    : u32 = 0b00000000000000000000000000000100_u32;
+
+/// is deeply immutable
+const KIND_MASK_CONST    : u32 = 0b00000000000000000000000000001000_u32;
+
+/// can be implicitly copied (must imply COPY)
+const KIND_MASK_IMPLICIT : u32 = 0b00000000000000000000000000010000_u32;
 
 fn kind_noncopyable() -> kind {
     kind_(0u32)
@@ -1325,7 +1344,7 @@ fn kind_implicitly_sendable() -> kind {
     kind_(KIND_MASK_IMPLICIT | KIND_MASK_COPY | KIND_MASK_SEND)
 }
 
-fn kind_sendable() -> kind {
+fn kind_send_copy() -> kind {
     kind_(KIND_MASK_COPY | KIND_MASK_SEND)
 }
 
@@ -1337,6 +1356,10 @@ fn kind_const() -> kind {
     kind_(KIND_MASK_CONST)
 }
 
+fn kind_owned() -> kind {
+    kind_(KIND_MASK_OWNED)
+}
+
 fn kind_top() -> kind {
     kind_(0xffffffffu32)
 }
@@ -1349,6 +1372,14 @@ fn remove_implicit(k: kind) -> kind {
     k - kind_(KIND_MASK_IMPLICIT)
 }
 
+fn remove_send(k: kind) -> kind {
+    k - kind_(KIND_MASK_SEND)
+}
+
+fn remove_owned_send(k: kind) -> kind {
+    k - kind_(KIND_MASK_OWNED) - kind_(KIND_MASK_SEND)
+}
+
 fn remove_copyable(k: kind) -> kind {
     k - kind_(KIND_MASK_COPY)
 }
@@ -1371,24 +1402,29 @@ impl operators for kind {
 // against the kind constants, as we may modify the kind hierarchy in the
 // future.
 pure fn kind_can_be_implicitly_copied(k: kind) -> bool {
-    *k & KIND_MASK_IMPLICIT != 0u32
+    *k & KIND_MASK_IMPLICIT == KIND_MASK_IMPLICIT
 }
 
 pure fn kind_can_be_copied(k: kind) -> bool {
-    *k & KIND_MASK_COPY != 0u32
+    *k & KIND_MASK_COPY == KIND_MASK_COPY
 }
 
 pure fn kind_can_be_sent(k: kind) -> bool {
-    *k & KIND_MASK_SEND != 0u32
+    *k & KIND_MASK_SEND == KIND_MASK_SEND
+}
+
+pure fn kind_is_owned(k: kind) -> bool {
+    *k & KIND_MASK_OWNED == KIND_MASK_OWNED
 }
 
 fn proto_kind(p: proto) -> kind {
     alt p {
       ast::proto_any { kind_noncopyable() }
       ast::proto_block { kind_noncopyable() }
-      ast::proto_box { kind_implicitly_copyable() }
-      ast::proto_uniq { kind_sendable() }
-      ast::proto_bare { kind_implicitly_sendable() | kind_const() }
+      ast::proto_box { kind_implicitly_copyable() | kind_owned() }
+      ast::proto_uniq { kind_send_copy() | kind_owned() }
+      ast::proto_bare { kind_implicitly_sendable() | kind_const() |
+                           kind_owned() }
     }
 }
 
@@ -1408,16 +1444,16 @@ fn raise_kind(a: kind, b: kind) -> kind {
 fn test_kinds() {
     // The kind "lattice" is defined by the subset operation on the
     // set of permitted operations.
-    assert kind_lteq(kind_sendable(), kind_sendable());
-    assert kind_lteq(kind_copyable(), kind_sendable());
+    assert kind_lteq(kind_send_copy(), kind_send_copy());
+    assert kind_lteq(kind_copyable(), kind_send_copy());
     assert kind_lteq(kind_copyable(), kind_copyable());
-    assert kind_lteq(kind_noncopyable(), kind_sendable());
+    assert kind_lteq(kind_noncopyable(), kind_send_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_sendable(), kind_implicitly_sendable());
-    assert !kind_lteq(kind_sendable(), kind_implicitly_copyable());
+    assert kind_lteq(kind_send_copy(), kind_implicitly_sendable());
+    assert !kind_lteq(kind_send_copy(), kind_implicitly_copyable());
     assert !kind_lteq(kind_copyable(), kind_send_only());
 }
 
@@ -1447,66 +1483,73 @@ fn type_kind(cx: ctxt, ty: t) -> kind {
     cx.kind_cache.insert(ty, kind_top());
 
     let result = alt get(ty).struct {
-      // Scalar and unique types are sendable
+      // Scalar and unique types are sendable, constant, and owned
       ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
-      ty_ptr(_) { kind_implicitly_sendable() | kind_const() }
+      ty_ptr(_) {
+        kind_implicitly_sendable() | kind_const() | kind_owned()
+      }
+
       // Implicit copyability of strs is configurable
       ty_estr(vstore_uniq) {
         if cx.vecs_implicitly_copyable {
-            kind_implicitly_sendable() | kind_const()
-        } else { kind_sendable() | kind_const() }
+            kind_implicitly_sendable() | kind_const() | kind_owned()
+        } else {
+            kind_send_copy() | kind_const() | kind_owned()
+        }
       }
+
+      // functions depend on the protocol
       ty_fn(f) { proto_kind(f.proto) }
 
       // Those with refcounts raise noncopyable to copyable,
       // lower sendable to copyable. Therefore just set result to copyable.
       ty_box(tm) {
-        if tm.mutbl == ast::m_mutbl {
-            kind_implicitly_copyable()
-        }
-        else {
-            let k = type_kind(cx, tm.ty);
-            if kind_lteq(kind_const(), k) {
-                kind_implicitly_copyable() | kind_const()
-            }
-            else { kind_implicitly_copyable() }
-        }
+        remove_send(mutable_type_kind(cx, tm) | kind_implicitly_copyable())
       }
-      ty_trait(_, _) { kind_implicitly_copyable() }
+
+      // Iface instances are (for now) like shared boxes, basically
+      ty_trait(_, _) { kind_implicitly_copyable() | kind_owned() }
+
+      // Region pointers are copyable but NOT owned nor sendable
       ty_rptr(_, _) { kind_implicitly_copyable() }
 
       // 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_uniq(tm) { remove_implicit(mutable_type_kind(cx, tm)) }
+
       // 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)) }
+          } else {
+              remove_implicit(mutable_type_kind(cx, tm))
+          }
       }
 
       // 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) |
+      ty_evec(tm, vstore_box) {
+        remove_send(kind_implicitly_copyable() | mutable_type_kind(cx, tm))
+      }
       ty_evec(tm, vstore_slice(_)) {
-        if kind_lteq(kind_const(), type_kind(cx, tm.ty)) {
-            kind_implicitly_copyable() | kind_const()
-        }
-        else {
-            kind_implicitly_copyable()
-        }
+        remove_owned_send(kind_implicitly_copyable() |
+                          mutable_type_kind(cx, tm))
       }
       ty_evec(tm, vstore_fixed(_)) {
         mutable_type_kind(cx, tm)
       }
 
       // All estrs are copyable; uniques and interiors are sendable.
-      ty_estr(vstore_box) |
-      ty_estr(vstore_slice(_)) { kind_implicitly_copyable() | kind_const() }
-      ty_estr(vstore_fixed(_)) { kind_implicitly_sendable() | kind_const() }
+      ty_estr(vstore_box) {
+        kind_implicitly_copyable() | kind_const() | kind_owned()
+      }
+      ty_estr(vstore_slice(_)) {
+        kind_implicitly_copyable() | kind_const()
+      }
+      ty_estr(vstore_fixed(_)) {
+        kind_implicitly_sendable() | kind_const() | kind_owned()
+      }
 
       // Records lower to the lowest of their members.
       ty_rec(flds) {
@@ -1516,6 +1559,7 @@ fn type_kind(cx: ctxt, ty: t) -> kind {
         }
         lowest
       }
+
       ty_class(did, substs) {
         // Classes are sendable if all their fields are sendable,
         // likewise for copyable...
@@ -1532,18 +1576,20 @@ fn type_kind(cx: ctxt, ty: t) -> kind {
         }
         lowest
       }
+
       // 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
       }
+
       // Enums lower to the lowest of their variants.
       ty_enum(did, substs) {
         let mut lowest = kind_top();
         let variants = enum_variants(cx, did);
         if vec::len(*variants) == 0u {
-            lowest = kind_send_only();
+            lowest = kind_send_only() | kind_owned();
         } else {
             for vec::each(*variants) |variant| {
                 for variant.args.each |aty| {
@@ -1556,11 +1602,15 @@ fn type_kind(cx: ctxt, ty: t) -> kind {
         }
         lowest
       }
+
       ty_param(_, did) {
         param_bounds_to_kind(cx.ty_param_bounds.get(did.node))
       }
-      // FIXME (#2663): is self ever const?
+
+      // self is a special type parameter that can only appear in ifaces; it
+      // is never bounded in any way, hence it has the bottom kind.
       ty_self { kind_noncopyable() }
+
       ty_var(_) | ty_var_integral(_) {
         cx.sess.bug(~"Asked to compute kind of a type variable");
       }
diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs
index 5627625daa3..05290d176ae 100644
--- a/src/rustc/middle/typeck/check/method.rs
+++ b/src/rustc/middle/typeck/check/method.rs
@@ -169,7 +169,8 @@ class lookup {
         let bounds = tcx.ty_param_bounds.get(did.node);
         for vec::each(*bounds) |bound| {
             let (iid, bound_substs) = alt bound {
-              ty::bound_copy | ty::bound_send | ty::bound_const {
+              ty::bound_copy | ty::bound_send | ty::bound_const |
+              ty::bound_owned {
                 again; /* ok */
               }
               ty::bound_trait(bound_t) {
diff --git a/src/rustc/middle/typeck/check/vtable.rs b/src/rustc/middle/typeck/check/vtable.rs
index a4dd6bae933..e38b1a39e13 100644
--- a/src/rustc/middle/typeck/check/vtable.rs
+++ b/src/rustc/middle/typeck/check/vtable.rs
@@ -68,7 +68,8 @@ fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
         let mut n_bound = 0u;
         for vec::each(*tcx.ty_param_bounds.get(did.node)) |bound| {
             alt bound {
-              ty::bound_send | ty::bound_copy | ty::bound_const {
+              ty::bound_send | ty::bound_copy | ty::bound_const |
+              ty::bound_owned {
                 /* ignore */
               }
               ty::bound_trait(ity) {
diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs
index 68253a51ba6..0f4eb6d2e82 100644
--- a/src/rustc/middle/typeck/collect.rs
+++ b/src/rustc/middle/typeck/collect.rs
@@ -572,6 +572,7 @@ fn ty_param_bounds(ccx: @crate_ctxt,
               ast::bound_send { ~[ty::bound_send] }
               ast::bound_copy { ~[ty::bound_copy] }
               ast::bound_const { ~[ty::bound_const] }
+              ast::bound_owned { ~[ty::bound_owned] }
               ast::bound_trait(t) {
                 let ity = ast_ty_to_ty(ccx, empty_rscope, t);
                 alt ty::get(ity).struct {
diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs
index cbcd4b31e53..4d433939b37 100644
--- a/src/rustc/util/ppaux.rs
+++ b/src/rustc/util/ppaux.rs
@@ -57,7 +57,6 @@ fn re_scope_id_to_str(cx: ctxt, node_id: ast::node_id) -> ~str {
         }
       }
       none {
-        // FIXME(#2586)
         #fmt["<unknown-%d>", node_id]
       }
       _ { cx.sess.bug(
diff --git a/src/test/compile-fail/block-arg-used-as-lambda-with-illegal-cap.rs b/src/test/compile-fail/block-arg-used-as-lambda-with-illegal-cap.rs
index 2abaabc3ef8..f8688ee2bd2 100644
--- a/src/test/compile-fail/block-arg-used-as-lambda-with-illegal-cap.rs
+++ b/src/test/compile-fail/block-arg-used-as-lambda-with-illegal-cap.rs
@@ -1,11 +1,9 @@
-// error-pattern: copying a noncopyable value
-
 fn to_lambda1(f: fn@(uint) -> uint) -> fn@(uint) -> uint {
     ret f;
 }
 
 fn to_lambda2(b: fn(uint) -> uint) -> fn@(uint) -> uint {
-    ret to_lambda1({|x| b(x)});
+    ret to_lambda1({|x| b(x)}); //~ ERROR not an owned value
 }
 
 fn main() {
diff --git a/src/test/compile-fail/cap-clause-illegal-cap.rs b/src/test/compile-fail/cap-clause-illegal-cap.rs
index 89a19cb89db..c13cc37f4b7 100644
--- a/src/test/compile-fail/cap-clause-illegal-cap.rs
+++ b/src/test/compile-fail/cap-clause-illegal-cap.rs
@@ -1,6 +1,8 @@
 // error-pattern: copying a noncopyable value
 
-fn to_lambda2(b: fn(uint) -> uint) -> fn@(uint) -> uint {
+class foo { let x: int; new(x: int) { self.x = x; } drop { } }
+
+fn to_lambda2(b: foo) -> fn@(uint) -> uint {
     // test case where copy clause specifies a value that is not used
     // in fn@ body, but value is illegal to copy:
     ret fn@(u: uint, copy b) -> uint { 22u };
diff --git a/src/test/compile-fail/kindck-owned.rs b/src/test/compile-fail/kindck-owned.rs
new file mode 100644
index 00000000000..7392d3183fe
--- /dev/null
+++ b/src/test/compile-fail/kindck-owned.rs
@@ -0,0 +1,19 @@
+fn copy1<T: copy>(t: T) -> fn@() -> T {
+    fn@() -> T { t } //~ ERROR not an owned value
+}
+
+fn copy2<T: copy owned>(t: T) -> fn@() -> T {
+    fn@() -> T { t }
+}
+
+fn main() {
+    let x = &3;
+    copy2(&x); //~ ERROR instantiating a type parameter with an incompatible type
+
+    copy2(@3);
+    copy2(@&x); //~ ERROR instantiating a type parameter with an incompatible type
+
+    copy2(fn@() {});
+    copy2(fn~() {}); //~ WARNING instantiating copy type parameter with a not implicitly copyable type
+    copy2(fn&() {}); //~ ERROR instantiating a type parameter with an incompatible type
+}
diff --git a/src/test/run-pass/alignment-gep-tup-like-1.rs b/src/test/run-pass/alignment-gep-tup-like-1.rs
index 0e25b86c90d..04f07001810 100644
--- a/src/test/run-pass/alignment-gep-tup-like-1.rs
+++ b/src/test/run-pass/alignment-gep-tup-like-1.rs
@@ -2,7 +2,7 @@ type pair<A,B> = {
     a: A, b: B
 };
 
-fn f<A:copy>(a: A, b: u16) -> fn@() -> (A, u16) {
+fn f<A:copy owned>(a: A, b: u16) -> fn@() -> (A, u16) {
     fn@() -> (A, u16) { (a, b) }
 }
 
diff --git a/src/test/run-pass/close-over-big-then-small-data.rs b/src/test/run-pass/close-over-big-then-small-data.rs
index 5fa3f7facc6..20f4a8055e8 100644
--- a/src/test/run-pass/close-over-big-then-small-data.rs
+++ b/src/test/run-pass/close-over-big-then-small-data.rs
@@ -6,7 +6,7 @@ type pair<A,B> = {
     a: A, b: B
 };
 
-fn f<A:copy>(a: A, b: u16) -> fn@() -> (A, u16) {
+fn f<A:copy owned>(a: A, b: u16) -> fn@() -> (A, u16) {
     fn@() -> (A, u16) { (a, b) }
 }
 
diff --git a/src/test/run-pass/fixed-point-bind-unique.rs b/src/test/run-pass/fixed-point-bind-unique.rs
index be9354d015a..029af017f65 100644
--- a/src/test/run-pass/fixed-point-bind-unique.rs
+++ b/src/test/run-pass/fixed-point-bind-unique.rs
@@ -1,8 +1,8 @@
-fn fix_help<A, B: send>(f: extern fn(fn@(A) -> B, A) -> B, x: A) -> B {
+fn fix_help<A: owned, B: send>(f: extern fn(fn@(A) -> B, A) -> B, x: A) -> B {
     ret f({|a|fix_help(f, a)}, x);
 }
 
-fn fix<A, B: send>(f: extern fn(fn@(A) -> B, A) -> B) -> fn@(A) -> B {
+fn fix<A: owned, B: send>(f: extern fn(fn@(A) -> B, A) -> B) -> fn@(A) -> B {
     ret {|a|fix_help(f, a)};
 }