1
Fork 0

auto merge of #4848 : nikomatsakis/rust/issue-4821-bad-kind-computation, r=catamorphism

...ear

values to be copied.  Rewrite kind computation so that instead of directly
computing the kind it computes what kinds of values are present in the type,
and then derive kinds based on that. I find this easier to think about.

Fixes #4821.

r? @catamorphism
This commit is contained in:
bors 2013-02-09 10:20:55 -08:00
commit d90abd5879
31 changed files with 612 additions and 579 deletions

View file

@ -457,7 +457,7 @@ pub fn parse_source(name: ~str, j: &json::Json) -> @Source {
} }
match *j { match *j {
json::Object(j) => { json::Object(ref j) => {
let mut url = match j.find(&~"url") { let mut url = match j.find(&~"url") {
Some(&json::String(u)) => copy u, Some(&json::String(u)) => copy u,
_ => die!(~"needed 'url' field in source") _ => die!(~"needed 'url' field in source")
@ -563,7 +563,7 @@ pub fn load_one_source_package(src: @Source, p: &json::Object) {
let mut tags = ~[]; let mut tags = ~[];
match p.find(&~"tags") { match p.find(&~"tags") {
Some(&json::List(js)) => { Some(&json::List(ref js)) => {
for js.each |j| { for js.each |j| {
match *j { match *j {
json::String(ref j) => tags.grow(1u, 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; } if !os::path_exists(&pkgfile) { return; }
let pkgstr = io::read_whole_file_str(&pkgfile); let pkgstr = io::read_whole_file_str(&pkgfile);
match json::from_str(pkgstr.get()) { match json::from_str(pkgstr.get()) {
Ok(json::List(js)) => { Ok(json::List(ref js)) => {
for js.each |j| { for js.each |j| {
match *j { match *j {
json::Object(p) => { json::Object(ref p) => {
load_one_source_package(src, p); load_one_source_package(src, *p);
} }
_ => { _ => {
warn(~"malformed source json: " + src.name + warn(~"malformed source json: " + src.name +

View file

@ -220,6 +220,16 @@ pub pure fn connect(v: &[~str], sep: &str) -> ~str {
s 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 /// Given a string, make a new string with repeated copies of it
pub pure fn repeat(ss: &str, nn: uint) -> ~str { pub pure fn repeat(ss: &str, nn: uint) -> ~str {
let mut acc = ~""; let mut acc = ~"";
@ -2667,6 +2677,17 @@ mod tests {
t(~[~"hi"], ~" ", ~"hi"); t(~[~"hi"], ~" ", ~"hi");
} }
#[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] #[test]
fn test_repeat() { fn test_repeat() {
assert repeat(~"x", 4) == ~"xxxx"; assert repeat(~"x", 4) == ~"xxxx";

View file

@ -15,7 +15,6 @@ use middle::freevars;
use middle::lint::{non_implicitly_copyable_typarams, implicit_copies}; use middle::lint::{non_implicitly_copyable_typarams, implicit_copies};
use middle::liveness; use middle::liveness;
use middle::pat_util; use middle::pat_util;
use middle::ty::{Kind, kind_copyable, kind_noncopyable, kind_const};
use middle::ty; use middle::ty;
use middle::typeck; use middle::typeck;
use middle; use middle;
@ -61,26 +60,6 @@ use syntax::{visit, ast_util};
pub const try_adding: &str = "Try adding a move"; 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 rval_map = HashMap<node_id, ()>;
pub type ctx = { pub type ctx = {
@ -119,11 +98,11 @@ type check_fn = fn@(ctx, @freevar_entry);
// closure. // closure.
fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) { fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
fn check_for_uniq(cx: ctx, fv: @freevar_entry) { fn check_for_uniq(cx: ctx, fv: @freevar_entry) {
// all captured data must be sendable, regardless of whether it is // all captured data must be owned, regardless of whether it is
// moved in or copied in. Note that send implies owned. // moved in or copied in.
let id = ast_util::def_id_of_def(fv.def).node; let id = ast_util::def_id_of_def(fv.def).node;
let var_t = ty::node_id_to_type(cx.tcx, id); 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 that only immutable variables are implicitly copied in
check_imm_free_var(cx, fv.def, fv.span); 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); visit::visit_ty(aty, cx, v);
} }
pub fn check_bounds(cx: ctx, id: node_id, sp: span, pub fn check_bounds(cx: ctx,
ty: ty::t, bounds: ty::param_bounds) { _type_parameter_id: node_id,
let kind = ty::type_kind(cx.tcx, ty); sp: span,
let p_kind = ty::param_bounds_to_kind(bounds); ty: ty::t,
if !ty::kind_lteq(p_kind, kind) { bounds: ty::param_bounds)
// If the only reason the kind check fails is because the {
// argument type isn't implicitly copyable, consult the warning let kind = ty::type_contents(cx.tcx, ty);
// settings to figure out what to do. let mut missing = ~[];
let implicit = ty::kind_implicitly_copyable() - ty::kind_copyable(); for bounds.each |bound| {
if ty::kind_lteq(p_kind, kind | implicit) { match *bound {
cx.tcx.sess.span_lint( ty::bound_trait(_) => {
non_implicitly_copyable_typarams, /* Not our job, checking in typeck */
id, cx.current_item, sp, }
~"instantiating copy type parameter with a \
not implicitly copyable type"); ty::bound_copy => {
} else { if !kind.is_copy(cx.tcx) {
cx.tcx.sess.span_err( missing.push("Copy");
sp, }
~"instantiating a type parameter with an incompatible type " + }
~"(needs `" + kind_to_str(p_kind) +
~"`, got `" + kind_to_str(kind) + ty::bound_durable => {
~"`, missing `" + kind_to_str(p_kind - kind) + ~"`)"); 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 { 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) { fn check_copy(cx: ctx, ty: ty::t, sp: span, reason: &str) {
let k = ty::type_kind(cx.tcx, ty); debug!("type_contents(%s)=%s",
if !ty::kind_can_be_copied(k) { ty_to_str(cx.tcx, ty),
cx.tcx.sess.span_err(sp, ~"copying a noncopyable value"); 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)); cx.tcx.sess.span_note(sp, fmt!("%s", reason));
} }
} }
pub fn check_send(cx: ctx, ty: ty::t, sp: span) -> bool { pub fn check_owned(cx: ctx, ty: ty::t, sp: span) -> bool {
if !ty::kind_can_be_sent(ty::type_kind(cx.tcx, ty)) { if !ty::type_is_owned(cx.tcx, ty) {
cx.tcx.sess.span_err(sp, ~"not a sendable value"); cx.tcx.sess.span_err(
sp, fmt!("value has non-owned type `%s`",
ty_to_str(cx.tcx, ty)));
false false
} else { } else {
true true
@ -360,7 +369,7 @@ pub fn check_send(cx: ctx, ty: ty::t, sp: span) -> bool {
// note: also used from middle::typeck::regionck! // note: also used from middle::typeck::regionck!
pub fn check_durable(tcx: ty::ctxt, ty: ty::t, sp: span) -> bool { 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 { match ty::get(ty).sty {
ty::ty_param(*) => { ty::ty_param(*) => {
tcx.sess.span_err(sp, ~"value may contain borrowed \ 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( pub fn check_cast_for_escaping_regions(
cx: ctx, cx: ctx,
source: @expr, source: @expr,
target: @expr) { target: @expr)
{
// Determine what type we are casting to; if it is not an trait, then no // Determine what type we are casting to; if it is not an trait, then no
// worries. // worries.
let target_ty = ty::expr_ty(cx.tcx, target); 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 { match ty::get(target_ty).sty {
ty::ty_trait(_, _, ty::vstore_uniq) => { ty::ty_trait(_, _, ty::vstore_uniq) => {
let source_ty = ty::expr_ty(cx.tcx, source); let source_ty = ty::expr_ty(cx.tcx, source);
let source_kind = ty::type_kind(cx.tcx, source_ty); if !ty::type_is_owned(cx.tcx, source_ty) {
if !ty::kind_can_be_copied(source_kind) { cx.tcx.sess.span_err(
cx.tcx.sess.span_err(target.span, target.span,
~"uniquely-owned trait objects must be copyable");
}
if !ty::kind_can_be_sent(source_kind) {
cx.tcx.sess.span_err(target.span,
~"uniquely-owned trait objects must be sendable"); ~"uniquely-owned trait objects must be sendable");
} }
} }

View file

@ -925,8 +925,8 @@ fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: ast::fn_decl,
ast::infer(_) => { ast::infer(_) => {
if tcx.legacy_modes { if tcx.legacy_modes {
let kind = ty::type_kind(tcx, arg_ty.ty); let kind = ty::type_contents(tcx, arg_ty.ty);
if !ty::kind_is_safe_for_default_mode(kind) { if !kind.is_safe_for_default_mode(tcx) {
tcx.sess.span_lint( tcx.sess.span_lint(
deprecated_mode, id, id, deprecated_mode, id, id,
span, span,

View file

@ -374,7 +374,7 @@ impl VisitContext {
* not implicitly copyable. * 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 MoveInWhole
} else { } else {
Read Read
@ -495,7 +495,7 @@ impl VisitContext {
// moves-by-default: // moves-by-default:
let consume_with = with_fields.any(|tf| { let consume_with = with_fields.any(|tf| {
!fields.any(|f| f.node.ident == tf.ident) && !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 { if consume_with {
@ -830,7 +830,7 @@ impl VisitContext {
let fvar_ty = ty::node_id_to_type(self.tcx, fvar_def_id); let fvar_ty = ty::node_id_to_type(self.tcx, fvar_def_id);
debug!("fvar_def_id=%? fvar_ty=%s", debug!("fvar_def_id=%? fvar_ty=%s",
fvar_def_id, ppaux::ty_to_str(self.tcx, fvar_ty)); 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 CapMove
} else { } else {
CapCopy CapCopy

View file

@ -26,6 +26,7 @@ use session::Session;
use util::ppaux::{note_and_explain_region, bound_region_to_str}; 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::{region_to_str, explain_region, vstore_to_str};
use util::ppaux::{ty_to_str, tys_to_str}; use util::ppaux::{ty_to_str, tys_to_str};
use util::common::{indenter};
use core::cast; use core::cast;
use core::cmp; use core::cmp;
@ -39,6 +40,7 @@ use core::result;
use core::to_bytes; use core::to_bytes;
use core::uint; use core::uint;
use core::vec; use core::vec;
use core::hashmap::linear::LinearMap;
use std::oldmap::HashMap; use std::oldmap::HashMap;
use std::{oldmap, oldsmallintmap}; use std::{oldmap, oldsmallintmap};
use syntax::ast::*; use syntax::ast::*;
@ -259,7 +261,7 @@ struct ctxt_ {
short_names_cache: HashMap<t, @~str>, short_names_cache: HashMap<t, @~str>,
needs_drop_cache: HashMap<t, bool>, needs_drop_cache: HashMap<t, bool>,
needs_unwind_cleanup_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>, ast_ty_to_ty_cache: HashMap<@ast::Ty, ast_ty_to_ty_cache_entry>,
enum_var_cache: HashMap<def_id, @~[VariantInfo]>, enum_var_cache: HashMap<def_id, @~[VariantInfo]>,
trait_method_cache: HashMap<def_id, @~[method]>, 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. /// A polytype.
/// ///
/// - `bounds`: The list of bounds for each type parameter. The length of the /// - `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(), short_names_cache: new_ty_hash(),
needs_drop_cache: new_ty_hash(), needs_drop_cache: new_ty_hash(),
needs_unwind_cleanup_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(), ast_ty_to_ty_cache: HashMap(),
enum_var_cache: HashMap(), enum_var_cache: HashMap(),
trait_method_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 { pub fn type_is_sequence(ty: t) -> bool {
match get(ty).sty { match get(ty).sty {
ty_estr(_) | ty_evec(_, _) => true, ty_estr(_) | ty_evec(_, _) => true,
@ -1777,467 +1744,491 @@ fn type_needs_unwind_cleanup_(cx: ctxt, ty: t,
return needs_unwind_cleanup; return needs_unwind_cleanup;
} }
pub enum Kind { kind_(u32) } /**
* Type contents is how the type checker reasons about kinds.
/// can be copied (implicitly or explicitly) * They track what kinds of things are found within a type. You can
const KIND_MASK_COPY : u32 = 0b000000000000000000000000001_u32; * 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
/// no shared box, borrowed ptr (must imply DURABLE) * a type tends to rule that type *out* from various kinds. For example,
const KIND_MASK_OWNED : u32 = 0b000000000000000000000000010_u32; * a type that contains a borrowed pointer is not sendable.
*
/// is durable (no borrowed ptrs) * The reason we compute type contents and not kinds is that it is
const KIND_MASK_DURABLE : u32 = 0b000000000000000000000000100_u32; * easier for me (nmatsakis) to think about what is contained within
* a type than to think about what is *not* contained within a type.
/// is deeply immutable */
const KIND_MASK_CONST : u32 = 0b000000000000000000000001000_u32; pub struct TypeContents {
bits: u32
/// can be implicitly copied (must imply COPY)
const KIND_MASK_IMPLICIT : u32 = 0b000000000000000000000010000_u32;
/// safe for default mode (subset of KIND_MASK_IMPLICIT)
const KIND_MASK_DEFAULT_MODE : u32 = 0b000000000000000000000100000_u32;
pub fn kind_noncopyable() -> Kind {
kind_(0u32)
} }
pub fn kind_copyable() -> Kind { pub impl TypeContents {
kind_(KIND_MASK_COPY) fn intersects(&self, tc: TypeContents) -> bool {
} (self.bits & tc.bits) != 0
}
pub fn kind_implicitly_copyable() -> Kind { fn is_copy(&self, cx: ctxt) -> bool {
kind_(KIND_MASK_IMPLICIT | KIND_MASK_COPY) !self.intersects(TypeContents::noncopyable(cx))
} }
fn kind_safe_for_default_mode() -> Kind { static fn noncopyable(_cx: ctxt) -> TypeContents {
// similar to implicit copy, but always includes vectors and strings TC_DTOR + TC_BORROWED_MUT + TC_ONCE_CLOSURE + TC_OWNED_CLOSURE +
kind_(KIND_MASK_DEFAULT_MODE | KIND_MASK_IMPLICIT | KIND_MASK_COPY) TC_EMPTY_ENUM
} }
fn kind_implicitly_sendable() -> Kind { fn is_durable(&self, cx: ctxt) -> bool {
kind_(KIND_MASK_IMPLICIT | KIND_MASK_COPY | KIND_MASK_OWNED) !self.intersects(TypeContents::nondurable(cx))
} }
fn kind_safe_for_default_mode_send() -> Kind { static fn nondurable(_cx: ctxt) -> TypeContents {
// similar to implicit copy, but always includes vectors and strings TC_BORROWED_POINTER
kind_(KIND_MASK_DEFAULT_MODE | KIND_MASK_IMPLICIT | }
KIND_MASK_COPY | KIND_MASK_OWNED)
}
fn is_owned(&self, cx: ctxt) -> bool {
!self.intersects(TypeContents::nonowned(cx))
}
fn kind_owned_copy() -> Kind { static fn nonowned(_cx: ctxt) -> TypeContents {
kind_(KIND_MASK_COPY | KIND_MASK_OWNED) TC_MANAGED + TC_BORROWED_POINTER
} }
fn kind_owned_only() -> Kind { fn is_const(&self, cx: ctxt) -> bool {
kind_(KIND_MASK_OWNED) !self.intersects(TypeContents::nonconst(cx))
} }
pub fn kind_const() -> Kind { static fn nonconst(_cx: ctxt) -> TypeContents {
kind_(KIND_MASK_CONST) TC_MUTABLE
} }
fn kind_durable() -> Kind { fn moves_by_default(&self, cx: ctxt) -> bool {
kind_(KIND_MASK_DURABLE) self.intersects(TypeContents::nonimplicitly_copyable(cx))
} }
fn kind_top() -> Kind { static fn nonimplicitly_copyable(cx: ctxt) -> TypeContents {
kind_(0xffffffffu32) let base = TypeContents::noncopyable(cx) + TC_OWNED_POINTER;
} if cx.vecs_implicitly_copyable {base} else {base + TC_OWNED_SLICE}
}
fn remove_const(k: Kind) -> Kind { fn is_safe_for_default_mode(&self, cx: ctxt) -> bool {
k - kind_const() !self.intersects(TypeContents::nondefault_mode(cx))
} }
fn remove_implicit(k: Kind) -> Kind { static fn nondefault_mode(cx: ctxt) -> TypeContents {
k - kind_(KIND_MASK_IMPLICIT | KIND_MASK_DEFAULT_MODE) let tc = TypeContents::nonimplicitly_copyable(cx);
} tc + TC_BIG + TC_OWNED_SLICE // disregard cx.vecs_implicitly_copyable
fn remove_owned(k: Kind) -> Kind {
k - kind_(KIND_MASK_OWNED)
}
fn remove_durable_owned(k: Kind) -> Kind {
k - kind_(KIND_MASK_DURABLE) - kind_(KIND_MASK_OWNED)
}
fn remove_copyable(k: Kind) -> Kind {
k - kind_(KIND_MASK_COPY | KIND_MASK_DEFAULT_MODE)
}
impl Kind : ops::BitAnd<Kind,Kind> {
pure fn bitand(&self, other: &Kind) -> Kind {
unsafe {
lower_kind(*self, *other)
}
} }
} }
impl Kind : ops::BitOr<Kind,Kind> { impl TypeContents : ops::Add<TypeContents,TypeContents> {
pure fn bitor(&self, other: &Kind) -> Kind { pure fn add(&self, other: &TypeContents) -> TypeContents {
unsafe { TypeContents {bits: self.bits | other.bits}
raise_kind(*self, *other)
}
} }
} }
impl Kind : ops::Sub<Kind,Kind> { impl TypeContents : ops::Sub<TypeContents,TypeContents> {
pure fn sub(&self, other: &Kind) -> Kind { pure fn sub(&self, other: &TypeContents) -> TypeContents {
unsafe { TypeContents {bits: self.bits & !other.bits}
kind_(**self & !**other)
}
} }
} }
// Using these query functions is preferable to direct comparison or matching impl TypeContents : ToStr {
// against the kind constants, as we may modify the kind hierarchy in the pure fn to_str(&self) -> ~str {
// future. fmt!("TypeContents(%s)", u32::to_str_radix(self.bits, 2))
pub pure fn kind_can_be_implicitly_copied(k: Kind) -> bool {
*k & KIND_MASK_IMPLICIT == KIND_MASK_IMPLICIT
}
pub pure fn kind_is_safe_for_default_mode(k: Kind) -> bool {
*k & KIND_MASK_DEFAULT_MODE == KIND_MASK_DEFAULT_MODE
}
pub pure fn kind_can_be_copied(k: Kind) -> bool {
*k & KIND_MASK_COPY == KIND_MASK_COPY
}
pub pure fn kind_can_be_sent(k: Kind) -> bool {
*k & KIND_MASK_OWNED == KIND_MASK_OWNED
}
pub pure fn kind_is_durable(k: Kind) -> bool {
*k & KIND_MASK_DURABLE == KIND_MASK_DURABLE
}
pure fn kind_is_const(k: Kind) -> bool {
*k & KIND_MASK_CONST == KIND_MASK_CONST
}
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(),
};
let kind = match cty.region {
re_static => kind | kind_durable(),
_ => kind - kind_owned_only() - kind_durable()
};
match cty.onceness {
ast::Once => kind - kind_implicitly_copyable(),
ast::Many => kind
} }
} }
pub fn kind_lteq(a: Kind, b: Kind) -> bool { /// Constant for a type containing nothing of interest.
*a & *b == *a const TC_NONE: TypeContents = TypeContents{bits:0b0000_00000000};
/// Contains a borrowed value with a lifetime other than static
const TC_BORROWED_POINTER: TypeContents = TypeContents{bits:0b0000_00000001};
/// Contains an owned pointer (~T) but not slice of some kind
const TC_OWNED_POINTER: TypeContents = TypeContents{bits:0b000000000010};
/// Contains an owned slice
const TC_OWNED_SLICE: TypeContents = TypeContents{bits:0b000000000100};
/// Contains a ~fn() or a ~Trait, which is non-copyable.
const TC_OWNED_CLOSURE: TypeContents = TypeContents{bits:0b000000001000};
/// Type with a destructor
const TC_DTOR: TypeContents = TypeContents{bits:0b000000010000};
/// Contains a managed value
const TC_MANAGED: TypeContents = TypeContents{bits:0b000000100000};
/// &mut with any region
const TC_BORROWED_MUT: TypeContents = TypeContents{bits:0b000001000000};
/// Mutable content, whether owned or by ref
const TC_MUTABLE: TypeContents = TypeContents{bits:0b000010000000};
/// Mutable content, whether owned or by ref
const TC_ONCE_CLOSURE: TypeContents = TypeContents{bits:0b000100000000};
/// Something we estimate to be "big"
const TC_BIG: TypeContents = TypeContents{bits:0b001000000000};
/// An enum with no variants.
const TC_EMPTY_ENUM: TypeContents = TypeContents{bits:0b010000000000};
/// All possible contents.
const TC_ALL: TypeContents = TypeContents{bits:0b011111111111};
pub fn type_is_copyable(cx: ctxt, t: ty::t) -> bool {
type_contents(cx, t).is_copy(cx)
} }
fn lower_kind(a: Kind, b: Kind) -> Kind { pub fn type_is_durable(cx: ctxt, t: ty::t) -> bool {
kind_(*a & *b) type_contents(cx, t).is_durable(cx)
} }
fn raise_kind(a: Kind, b: Kind) -> Kind { pub fn type_is_owned(cx: ctxt, t: ty::t) -> bool {
kind_(*a | *b) type_contents(cx, t).is_owned(cx)
} }
#[test] pub fn type_is_const(cx: ctxt, t: ty::t) -> bool {
fn test_kinds() { type_contents(cx, t).is_const(cx)
// 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());
} }
// Return the most permissive kind that a composite object containing a field pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
// with the given mutability can have. let ty_id = type_id(ty);
// This is used to prevent objects containing mutable state from being match cx.tc_cache.find(&ty_id) {
// implicitly copied and to compute whether things have const kind. Some(tc) => { return *tc; }
fn mutability_kind(m: mutability) -> Kind { None => {}
match (m) {
m_mutbl => remove_const(remove_implicit(kind_top())),
m_const => remove_implicit(kind_top()),
m_imm => kind_top()
}
}
fn mutable_type_kind(cx: ctxt, ty: mt) -> Kind {
lower_kind(mutability_kind(ty.mutbl), type_kind(cx, ty.ty))
}
pub fn type_kind(cx: ctxt, ty: t) -> Kind {
type_kind_ext(cx, ty, false)
}
// 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 */ }
} }
// Insert a default in case we loop back on self recursively. let mut cache = LinearMap::new();
cx.kind_cache.insert(ty, kind_top()); let result = tc_ty(cx, ty, &mut cache);
cx.tc_cache.insert(ty_id, result);
return result;
let mut result = match /*bad*/copy get(ty).sty { fn tc_ty(cx: ctxt,
// Scalar and unique types are sendable, constant, and owned ty: t,
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | cache: &mut LinearMap<uint, TypeContents>) -> TypeContents
ty_bare_fn(_) | ty_ptr(_) => { {
kind_safe_for_default_mode_send() | kind_const() | kind_durable() // 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:
// Implicit copyability of strs is configurable //
ty_estr(vstore_uniq) => { // struct List { next: ~Option<List>, ... }
if cx.vecs_implicitly_copyable { //
kind_implicitly_sendable() | kind_const() | kind_durable() // When computing the type contents of such a type, we wind up deeply
} else { // recursing as we go. So when we encounter the recursive reference
kind_owned_copy() | kind_const() | kind_durable() // 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);
ty_closure(ref c) => { debug!("computing contents of %s", ty_to_str(cx, ty));
closure_kind(c) let _i = indenter();
}
// Those with refcounts raise noncopyable to copyable, let mut result = match get(ty).sty {
// lower sendable to copyable. Therefore just set result to copyable. // Scalar and unique types are sendable, constant, and owned
ty_box(tm) => { ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
remove_owned(mutable_type_kind(cx, tm) | kind_safe_for_default_mode()) ty_bare_fn(_) | ty_ptr(_) => {
} TC_NONE
}
// XXX: This is wrong for ~Trait and &Trait! ty_estr(vstore_uniq) => {
ty_trait(_, _, _) => kind_safe_for_default_mode() | kind_durable(), TC_OWNED_SLICE
}
// Static region pointers are copyable and sendable, but not owned ty_closure(ref c) => {
ty_rptr(re_static, mt) => closure_contents(c)
kind_safe_for_default_mode() | mutable_type_kind(cx, mt), }
ty_rptr(_, mt) => { ty_box(mt) => {
if mt.mutbl == ast::m_mutbl { TC_MANAGED + nonowned(tc_mt(cx, mt, cache))
// Mutable region pointers are noncopyable }
kind_noncopyable()
} else {
// General region pointers are copyable but NOT owned nor sendable
kind_safe_for_default_mode()
}
}
// Unique boxes and vecs have the kind of their contained type, ty_trait(_, _, vstore_uniq) => {
// but unique boxes can't be implicitly copyable. TC_OWNED_CLOSURE
ty_uniq(tm) => remove_implicit(mutable_type_kind(cx, tm)), }
// Implicit copyability of vecs is configurable ty_trait(_, _, vstore_box) => {
ty_evec(tm, vstore_uniq) => { TC_MANAGED
if cx.vecs_implicitly_copyable { }
mutable_type_kind(cx, tm)
} else {
remove_implicit(mutable_type_kind(cx, tm))
}
}
// Slices, refcounted evecs are copyable; uniques depend on the their ty_trait(_, _, vstore_slice(r)) => {
// contained type, but aren't implicitly copyable. Fixed vectors have borrowed_contents(r, m_imm)
// 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)
}
// All estrs are copyable; uniques and interiors are sendable. ty_rptr(r, mt) => {
ty_estr(vstore_box) => { borrowed_contents(r, mt.mutbl) +
kind_safe_for_default_mode() | kind_const() | kind_durable() nonowned(tc_mt(cx, mt, cache))
} }
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()
}
// Records lower to the lowest of their members. ty_uniq(mt) => {
ty_rec(flds) => { TC_OWNED_POINTER + tc_mt(cx, mt, cache)
let mut lowest = kind_top(); }
for flds.each |f| {
lowest = lower_kind(lowest, mutable_type_kind(cx, f.mt));
}
lowest
}
ty_struct(did, ref substs) => { ty_evec(mt, vstore_uniq) => {
// Structs are sendable if all their fields are sendable, TC_OWNED_SLICE + tc_mt(cx, mt, cache)
// 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
}
// Tuples lower to the lowest of their members. ty_evec(mt, vstore_box) => {
ty_tup(tys) => { TC_MANAGED + nonowned(tc_mt(cx, mt, cache))
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_evec(mt, vstore_slice(r)) => {
ty_enum(did, ref substs) => { borrowed_contents(r, mt.mutbl) +
let mut lowest = kind_top(); nonowned(tc_mt(cx, mt, cache))
let variants = enum_variants(cx, did); }
if variants.is_empty() {
lowest = kind_owned_only() | kind_durable(); ty_evec(mt, vstore_fixed(_)) => {
} else { tc_mt(cx, mt, cache)
for variants.each |variant| { }
for variant.args.each |aty| {
// Perform any type parameter substitutions. ty_estr(vstore_box) => {
let arg_ty = subst(cx, substs, *aty); TC_MANAGED
lowest = lower_kind(lowest, type_kind(cx, arg_ty)); }
if lowest == kind_noncopyable() { break; }
ty_estr(vstore_slice(r)) => {
borrowed_contents(r, m_imm)
}
ty_estr(vstore_fixed(_)) => {
TC_NONE
}
ty_rec(ref flds) => {
flds.foldl(
TC_NONE,
|tc, f| tc + tc_mt(cx, f.mt, cache))
}
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
} }
} }
}
lowest
}
ty_param(p) => { ty_tup(ref tys) => {
// We only ever ask for the kind of types that are defined in the tys.foldl(TC_NONE, |tc, ty| *tc + tc_ty(cx, *ty, cache))
// 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_kind(cx.ty_param_bounds.get(&p.def_id.node)) 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))
})
}
}
// self is a special type parameter that can only appear in traits; it ty_param(p) => {
// is never bounded in any way, hence it has the bottom kind. // We only ever ask for the kind of types that are defined in
ty_self => kind_noncopyable(), // 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;
ty_infer(_) => { param_bounds_to_contents(
if allow_ty_var { cx, cx.ty_param_bounds.get(&p.def_id.node))
kind_top() }
} else {
cx.sess.bug(~"Asked to compute kind of a type variable")
}
}
ty_type | ty_opaque_closure_ptr(_) ty_self => {
| ty_opaque_box | ty_unboxed_vec(_) | ty_err => { // Currently, self is not bounded, so we must assume the
cx.sess.bug(~"Asked to compute kind of fictitious type"); // worst. But in the future we should examine the super
} // traits.
}; //
// FIXME(#4678)---self should just be a ty param
TC_ALL
}
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");
}
};
// arbitrary threshold to prevent by-value copying of big records
if kind_is_safe_for_default_mode(result) {
if type_size(cx, ty) > 4 { if type_size(cx, ty) > 4 {
result = result - kind_(KIND_MASK_DEFAULT_MODE); result = result + TC_BIG;
}
debug!("result = %s", result.to_str());
cache.insert(ty_id, result);
return result;
}
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)
}
fn borrowed_contents(region: ty::Region,
mutbl: ast::mutability) -> TypeContents
{
let mc = if mutbl == m_mutbl {
TC_MUTABLE + TC_BORROWED_MUT
} else {
TC_NONE
};
let rc = if region != ty::re_static {
TC_BORROWED_POINTER
} else {
TC_NONE
};
mc + rc
}
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.
*/
let mask = TC_MUTABLE.bits | TC_BORROWED_POINTER.bits;
TypeContents {bits: pointee.bits & mask}
}
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
}
});
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
}
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_estr(vstore_fixed(n)) => {
n
}
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_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_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");
}
} }
} }
cx.kind_cache.insert(ty, result);
return result;
} }
pub fn type_implicitly_moves(cx: ctxt, ty: t) -> bool { pub fn type_moves_by_default(cx: ctxt, ty: t) -> bool {
let kind = type_kind(cx, ty); type_contents(cx, ty).moves_by_default(cx)
!(kind_can_be_copied(kind) && kind_can_be_implicitly_copied(kind))
}
/// 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(t, vstore_fixed(n)) => {
type_size(cx, t.ty) * n
}
ty_estr(vstore_fixed(n)) => {
n
}
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_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_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");
}
}
} }
// True if instantiating an instance of `r_ty` requires an instance of `r_ty`. // True if instantiating an instance of `r_ty` requires an instance of `r_ty`.
@ -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 { impl FnSig : to_bytes::IterBytes {
pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
to_bytes::iter_bytes_2(&self.inputs, 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) } 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: // Local Variables:
// mode: rust // mode: rust
// fill-column: 78; // fill-column: 78;

View file

@ -24,13 +24,13 @@ use metadata::cstore::{CStore, iter_crate_data};
use metadata::decoder::{dl_def, dl_field, dl_impl}; use metadata::decoder::{dl_def, dl_field, dl_impl};
use middle::resolve::{Impl, MethodInfo}; use middle::resolve::{Impl, MethodInfo};
use middle::ty::{ProvidedMethodSource, ProvidedMethodInfo, bound_copy, get}; 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::{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_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_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_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_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::{type_is_ty_var};
use middle::ty; use middle::ty;
use middle::typeck::CrateCtxt; use middle::typeck::CrateCtxt;
@ -578,11 +578,10 @@ pub impl CoherenceChecker {
for bounds.each |bound| { for bounds.each |bound| {
match *bound { match *bound {
bound_copy => { bound_copy => {
let kind = type_kind_ext( if !ty::type_is_copyable(
self.inference_context.tcx, self.inference_context.tcx,
resolved_ty, resolved_ty)
true); {
if !kind_can_be_copied(kind) {
might_unify = false; might_unify = false;
break; break;
} }

View file

@ -832,7 +832,7 @@ pub impl Decoder: serialize::Decoder {
fn read_owned_vec<T>(&self, f: fn(uint) -> T) -> T { fn read_owned_vec<T>(&self, f: fn(uint) -> T) -> T {
debug!("read_owned_vec()"); debug!("read_owned_vec()");
let len = match *self.peek() { let len = match *self.peek() {
List(list) => list.len(), List(ref list) => list.len(),
_ => die!(~"not a list"), _ => die!(~"not a list"),
}; };
let res = f(len); let res = f(len);

View file

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
fn foo<T>() { fn foo<T>() {
1u.bar::<T>(); //~ ERROR: missing `copy` 1u.bar::<T>(); //~ ERROR: does not fulfill `Copy`
} }
trait bar { trait bar {

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: copying a noncopyable value
struct foo { struct foo {
i: int, 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);
}

View file

@ -34,7 +34,7 @@ fn main() {
let mut res = foo(x); let mut res = foo(x);
let mut v = ~[mut]; 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); assert (v.len() == 2);
} }

View file

@ -20,6 +20,6 @@ impl C : Drop {
fn main() { fn main() {
let c = C{ x: 2}; 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); error!("%?", d.x);
} }

View file

@ -12,7 +12,7 @@ fn foo(_x: @uint) {}
fn main() { fn main() {
let x = @3u; let x = @3u;
let _ = fn~() { 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 not a sendable value let _ = fn~(copy x) { foo(x); }; //~ ERROR value has non-owned type `@uint`
let _ = fn~(move x) { foo(x); }; //~ ERROR not a sendable value let _ = fn~(move x) { foo(x); }; //~ ERROR value has non-owned type `@uint`
} }

View file

@ -18,12 +18,12 @@ fn copy2<T: Copy &static>(t: T) -> fn@() -> T {
fn main() { fn main() {
let x = &3; let x = &3;
copy2(&x); //~ ERROR missing `&static` copy2(&x); //~ ERROR does not fulfill `&static`
copy2(@3); copy2(@3);
copy2(@&x); //~ ERROR missing `&static` copy2(@&x); //~ ERROR does not fulfill `&static`
copy2(fn@() {}); copy2(fn@() {});
copy2(fn~() {}); //~ ERROR missing `copy` copy2(fn~() {}); //~ ERROR does not fulfill `Copy`
copy2(fn&() {}); //~ ERROR missing `&static` copy2(fn&() {}); //~ ERROR does not fulfill `&static`
} }

View file

@ -68,5 +68,5 @@ impl r : Drop {
fn main() { fn main() {
let x = r { x: () }; 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
} }

View file

@ -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() {}

View file

@ -29,7 +29,7 @@ fn main() {
do task::spawn { do task::spawn {
let mut y = None; let mut y = None;
*x <-> y; //~ ERROR not a sendable value *x <-> y; //~ ERROR value has non-owned type
log(error, y); log(error, y);
} }
} }

View file

@ -42,14 +42,14 @@ fn r2(x:@mut int) -> r2 {
fn main() { fn main() {
foo({f: 3}); foo({f: 3});
foo({mut f: 3}); //~ ERROR missing `const` foo({mut f: 3}); //~ ERROR does not fulfill `Const`
foo(~[1]); foo(~[1]);
foo(~[mut 1]); //~ ERROR missing `const` foo(~[mut 1]); //~ ERROR does not fulfill `Const`
foo(~1); foo(~1);
foo(~mut 1); //~ ERROR missing `const` foo(~mut 1); //~ ERROR does not fulfill `Const`
foo(@1); foo(@1);
foo(@mut 1); //~ ERROR missing `const` foo(@mut 1); //~ ERROR does not fulfill `Const`
foo(r(1)); // this is okay now. foo(r(1)); // this is okay now.
foo(r2(@mut 1)); //~ ERROR missing `const` foo(r2(@mut 1)); //~ ERROR does not fulfill `Const`
foo({f: {mut f: 1}}); //~ ERROR missing `const` foo({f: {mut f: 1}}); //~ ERROR does not fulfill `Const`
} }

View file

@ -13,6 +13,6 @@ fn main() {
let y : *libc::c_void = x as *libc::c_void; let y : *libc::c_void = x as *libc::c_void;
unsafe { unsafe {
let _z = copy *y; let _z = copy *y;
//~^ ERROR copying a noncopyable value //~^ ERROR copying a value of non-copyable type
} }
} }

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: copying a noncopyable value
// Test that a class with a non-copyable field can't be // Test that a class with a non-copyable field can't be
// copied // copied
struct bar { 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);
}

View file

@ -11,7 +11,7 @@
fn main() { fn main() {
let x = Some(private::exclusive(false)); let x = Some(private::exclusive(false));
match x { 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; } do z.with |b| { assert !*b; }
} }
None => die!() None => die!()

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: copying a noncopyable value
struct r { struct r {
i: @mut int, i: @mut int,
} }
@ -31,7 +29,7 @@ fn main() {
{ {
// Can't do this copy // Can't do this copy
let x = ~~~{y: r(i)}; 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(debug, x);
} }
log(error, *i); log(error, *i);

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: copying a noncopyable value
struct my_resource { struct my_resource {
x: int, x: int,
} }
@ -29,7 +27,7 @@ fn my_resource(x: int) -> my_resource {
fn main() { fn main() {
{ {
let a = {x: 0, y: my_resource(20)}; 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)); log(error, (a, b));
} }
} }

View file

@ -24,6 +24,6 @@ impl Foo : Drop {
fn main() { fn main() {
let a = Foo { x: 3 }; let a = Foo { x: 3 };
let _ = [ a, ..5 ]; //~ ERROR copying a noncopyable value let _ = [ a, ..5 ]; //~ ERROR copying a value of non-copyable type
} }

View file

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
trait Foo { trait Foo {
fn f(); fn f(&self);
} }
struct Bar { struct Bar {
@ -21,14 +21,14 @@ impl Bar : Drop {
} }
impl Bar : Foo { impl Bar : Foo {
fn f() { fn f(&self) {
io::println("hi"); io::println("hi");
} }
} }
fn main() { fn main() {
let x = ~Bar { x: 10 }; let x = ~Bar { x: 10 };
let y = (move x) as ~Foo; //~ ERROR uniquely-owned trait objects must be copyable let y: ~Foo = x as ~Foo;
let _z = copy y; let _z = copy y; //~ ERROR copying a value of non-copyable type
} }

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: copying a noncopyable value
struct r { struct r {
b:bool, b:bool,
} }
@ -20,6 +18,6 @@ impl r : Drop {
fn main() { fn main() {
let i = move ~r { b: true }; 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); log(debug, i);
} }

View file

@ -13,5 +13,5 @@ fn f<T: Owned>(_i: T) {
fn main() { fn main() {
let i = ~@100; let i = ~@100;
f(move i); //~ ERROR missing `owned` f(move i); //~ ERROR does not fulfill `Owned`
} }

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: copying a noncopyable value
struct r { struct r {
i: @mut int, 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() { fn main() {
@ -29,6 +27,8 @@ fn main() {
let r1 = move ~[~r { i: i1 }]; let r1 = move ~[~r { i: i1 }];
let r2 = move ~[~r { i: i2 }]; let r2 = move ~[~r { i: i2 }];
f(copy r1, copy r2); 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, (r2, *i1));
log(debug, (r1, *i2)); log(debug, (r1, *i2));
} }

View file

@ -25,6 +25,6 @@ fn foo(i:int, j: @~str) -> foo {
fn main() { fn main() {
let cat = ~"kitty"; let cat = ~"kitty";
let (_, ch) = pipes::stream(); //~ ERROR missing `owned` let (_, ch) = pipes::stream(); //~ ERROR does not fulfill `Owned`
ch.send(foo(42, @(move cat))); //~ ERROR missing `owned` ch.send(foo(42, @(move cat))); //~ ERROR does not fulfill `Owned`
} }

View file

@ -26,8 +26,6 @@ pub fn main() {
let x = ~S { x: 3 }; let x = ~S { x: 3 };
let y = x as ~Foo; let y = x as ~Foo;
y.f(); y.f();
y.f();
y.f();
} }

View file

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
trait Foo { trait Foo {
fn f(); fn f(&self) -> int;
} }
struct Bar { struct Bar {
@ -17,16 +17,14 @@ struct Bar {
} }
impl Bar : Foo { impl Bar : Foo {
fn f() { fn f(&self) -> int {
io::println("hi"); self.x
} }
} }
pub fn main() { pub fn main() {
let x = ~Bar { x: 10 }; let x = ~Bar { x: 10 };
let y = x as ~Foo; let y = x as ~Foo;
let z = copy y; assert y.f() == 10;
y.f();
z.f();
} }