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