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:
commit
d90abd5879
31 changed files with 612 additions and 579 deletions
|
@ -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 +
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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,29 +260,53 @@ 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) {
|
||||||
|
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(
|
cx.tcx.sess.span_err(
|
||||||
sp,
|
sp,
|
||||||
~"instantiating a type parameter with an incompatible type " +
|
fmt!("instantiating a type parameter with an incompatible type \
|
||||||
~"(needs `" + kind_to_str(p_kind) +
|
`%s`, which does not fulfill `%s`",
|
||||||
~"`, got `" + kind_to_str(kind) +
|
ty_to_str(cx.tcx, ty),
|
||||||
~"`, missing `" + kind_to_str(p_kind - kind) + ~"`)");
|
str::connect_slices(missing, " ")));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,410 +1744,428 @@ 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 {
|
|
||||||
kind_(KIND_MASK_IMPLICIT | KIND_MASK_COPY)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kind_implicitly_sendable() -> Kind {
|
|
||||||
kind_(KIND_MASK_IMPLICIT | KIND_MASK_COPY | KIND_MASK_OWNED)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn kind_owned_copy() -> Kind {
|
|
||||||
kind_(KIND_MASK_COPY | KIND_MASK_OWNED)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kind_owned_only() -> Kind {
|
|
||||||
kind_(KIND_MASK_OWNED)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn kind_const() -> Kind {
|
|
||||||
kind_(KIND_MASK_CONST)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kind_durable() -> Kind {
|
|
||||||
kind_(KIND_MASK_DURABLE)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kind_top() -> Kind {
|
|
||||||
kind_(0xffffffffu32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_const(k: Kind) -> Kind {
|
|
||||||
k - kind_const()
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Kind,Kind> {
|
|
||||||
pure fn bitand(&self, other: &Kind) -> Kind {
|
|
||||||
unsafe {
|
|
||||||
lower_kind(*self, *other)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_copy(&self, cx: ctxt) -> bool {
|
||||||
|
!self.intersects(TypeContents::noncopyable(cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
static fn noncopyable(_cx: ctxt) -> TypeContents {
|
||||||
|
TC_DTOR + TC_BORROWED_MUT + TC_ONCE_CLOSURE + TC_OWNED_CLOSURE +
|
||||||
|
TC_EMPTY_ENUM
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_durable(&self, cx: ctxt) -> bool {
|
||||||
|
!self.intersects(TypeContents::nondurable(cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
static fn nondurable(_cx: ctxt) -> TypeContents {
|
||||||
|
TC_BORROWED_POINTER
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_owned(&self, cx: ctxt) -> bool {
|
||||||
|
!self.intersects(TypeContents::nonowned(cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
static fn nonowned(_cx: ctxt) -> TypeContents {
|
||||||
|
TC_MANAGED + TC_BORROWED_POINTER
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_const(&self, cx: ctxt) -> bool {
|
||||||
|
!self.intersects(TypeContents::nonconst(cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
static fn nonconst(_cx: ctxt) -> TypeContents {
|
||||||
|
TC_MUTABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
fn moves_by_default(&self, cx: ctxt) -> bool {
|
||||||
|
self.intersects(TypeContents::nonimplicitly_copyable(cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
static fn nonimplicitly_copyable(cx: ctxt) -> TypeContents {
|
||||||
|
let base = TypeContents::noncopyable(cx) + TC_OWNED_POINTER;
|
||||||
|
if cx.vecs_implicitly_copyable {base} else {base + TC_OWNED_SLICE}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_safe_for_default_mode(&self, cx: ctxt) -> bool {
|
||||||
|
!self.intersects(TypeContents::nondefault_mode(cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
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<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,
|
||||||
|
ty: t,
|
||||||
|
cache: &mut LinearMap<uint, TypeContents>) -> TypeContents
|
||||||
|
{
|
||||||
|
// Subtle: Note that we are *not* using cx.tc_cache here but rather a
|
||||||
|
// private cache for this walk. This is needed in the case of cyclic
|
||||||
|
// types like:
|
||||||
|
//
|
||||||
|
// struct List { next: ~Option<List>, ... }
|
||||||
|
//
|
||||||
|
// When computing the type contents of such a type, we wind up deeply
|
||||||
|
// recursing as we go. So when we encounter the recursive reference
|
||||||
|
// to List, we temporarily use TC_NONE as its contents. Later we'll
|
||||||
|
// patch up the cache with the correct value, once we've computed it
|
||||||
|
// (this is basically a co-inductive process, if that helps). So in
|
||||||
|
// the end we'll compute TC_OWNED_POINTER, in this case.
|
||||||
|
//
|
||||||
|
// The problem is, as we are doing the computation, we will also
|
||||||
|
// compute an *intermediate* contents for, e.g., Option<List> of
|
||||||
|
// TC_NONE. This is ok during the computation of List itself, but if
|
||||||
|
// we stored this intermediate value into cx.tc_cache, then later
|
||||||
|
// requests for the contents of Option<List> would also yield TC_NONE
|
||||||
|
// which is incorrect. This value was computed based on the crutch
|
||||||
|
// value for the type contents of list. The correct value is
|
||||||
|
// TC_OWNED_POINTER. This manifested as issue #4821.
|
||||||
|
let ty_id = type_id(ty);
|
||||||
|
match cache.find(&ty_id) {
|
||||||
|
Some(tc) => { return *tc; }
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
cache.insert(ty_id, TC_NONE);
|
||||||
|
|
||||||
|
debug!("computing contents of %s", ty_to_str(cx, ty));
|
||||||
|
let _i = indenter();
|
||||||
|
|
||||||
|
let mut result = match get(ty).sty {
|
||||||
// Scalar and unique types are sendable, constant, and owned
|
// Scalar and unique types are sendable, constant, and owned
|
||||||
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
|
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
|
||||||
ty_bare_fn(_) | ty_ptr(_) => {
|
ty_bare_fn(_) | ty_ptr(_) => {
|
||||||
kind_safe_for_default_mode_send() | kind_const() | kind_durable()
|
TC_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implicit copyability of strs is configurable
|
|
||||||
ty_estr(vstore_uniq) => {
|
ty_estr(vstore_uniq) => {
|
||||||
if cx.vecs_implicitly_copyable {
|
TC_OWNED_SLICE
|
||||||
kind_implicitly_sendable() | kind_const() | kind_durable()
|
|
||||||
} else {
|
|
||||||
kind_owned_copy() | kind_const() | kind_durable()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ty_closure(ref c) => {
|
ty_closure(ref c) => {
|
||||||
closure_kind(c)
|
closure_contents(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Those with refcounts raise noncopyable to copyable,
|
ty_box(mt) => {
|
||||||
// lower sendable to copyable. Therefore just set result to copyable.
|
TC_MANAGED + nonowned(tc_mt(cx, mt, cache))
|
||||||
ty_box(tm) => {
|
|
||||||
remove_owned(mutable_type_kind(cx, tm) | kind_safe_for_default_mode())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: This is wrong for ~Trait and &Trait!
|
ty_trait(_, _, vstore_uniq) => {
|
||||||
ty_trait(_, _, _) => kind_safe_for_default_mode() | kind_durable(),
|
TC_OWNED_CLOSURE
|
||||||
|
|
||||||
// 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_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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unique boxes and vecs have the kind of their contained type,
|
ty_trait(_, _, vstore_box) => {
|
||||||
// but unique boxes can't be implicitly copyable.
|
TC_MANAGED
|
||||||
ty_uniq(tm) => remove_implicit(mutable_type_kind(cx, tm)),
|
|
||||||
|
|
||||||
// Implicit copyability of vecs is configurable
|
|
||||||
ty_evec(tm, vstore_uniq) => {
|
|
||||||
if cx.vecs_implicitly_copyable {
|
|
||||||
mutable_type_kind(cx, tm)
|
|
||||||
} else {
|
|
||||||
remove_implicit(mutable_type_kind(cx, tm))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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_rptr(r, mt) => {
|
||||||
}
|
borrowed_contents(r, mt.mutbl) +
|
||||||
ty_evec(tm, vstore_slice(re_static)) => {
|
nonowned(tc_mt(cx, mt, cache))
|
||||||
kind_safe_for_default_mode() | mutable_type_kind(cx, tm)
|
}
|
||||||
}
|
|
||||||
ty_evec(tm, vstore_slice(_)) => {
|
ty_uniq(mt) => {
|
||||||
remove_durable_owned(kind_safe_for_default_mode() |
|
TC_OWNED_POINTER + tc_mt(cx, mt, cache)
|
||||||
mutable_type_kind(cx, tm))
|
}
|
||||||
}
|
|
||||||
ty_evec(tm, vstore_fixed(_)) => {
|
ty_evec(mt, vstore_uniq) => {
|
||||||
mutable_type_kind(cx, tm)
|
TC_OWNED_SLICE + tc_mt(cx, mt, cache)
|
||||||
|
}
|
||||||
|
|
||||||
|
ty_evec(mt, vstore_box) => {
|
||||||
|
TC_MANAGED + nonowned(tc_mt(cx, mt, cache))
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// All estrs are copyable; uniques and interiors are sendable.
|
|
||||||
ty_estr(vstore_box) => {
|
ty_estr(vstore_box) => {
|
||||||
kind_safe_for_default_mode() | kind_const() | kind_durable()
|
TC_MANAGED
|
||||||
}
|
|
||||||
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_estr(vstore_slice(r)) => {
|
||||||
ty_rec(flds) => {
|
borrowed_contents(r, m_imm)
|
||||||
let mut lowest = kind_top();
|
|
||||||
for flds.each |f| {
|
|
||||||
lowest = lower_kind(lowest, mutable_type_kind(cx, f.mt));
|
|
||||||
}
|
}
|
||||||
lowest
|
|
||||||
|
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) => {
|
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);
|
let flds = struct_fields(cx, did, substs);
|
||||||
for flds.each |f| {
|
let flds_tc = flds.foldl(
|
||||||
lowest = lower_kind(lowest, mutable_type_kind(cx, f.mt));
|
TC_NONE,
|
||||||
}
|
|tc, f| tc + tc_mt(cx, f.mt, cache));
|
||||||
// ...but structs with dtors are never copyable (they can be
|
|
||||||
// sendable)
|
|
||||||
if ty::has_dtor(cx, did) {
|
if ty::has_dtor(cx, did) {
|
||||||
lowest = remove_copyable(lowest);
|
flds_tc + TC_DTOR
|
||||||
}
|
|
||||||
lowest
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tuples lower to the lowest of their members.
|
|
||||||
ty_tup(tys) => {
|
|
||||||
let mut lowest = kind_top();
|
|
||||||
for tys.each |ty| { lowest = lower_kind(lowest, type_kind(cx, *ty)); }
|
|
||||||
lowest
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enums lower to the lowest of their variants.
|
|
||||||
ty_enum(did, ref substs) => {
|
|
||||||
let mut lowest = kind_top();
|
|
||||||
let variants = enum_variants(cx, did);
|
|
||||||
if variants.is_empty() {
|
|
||||||
lowest = kind_owned_only() | kind_durable();
|
|
||||||
} else {
|
} else {
|
||||||
for variants.each |variant| {
|
flds_tc
|
||||||
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_tup(ref tys) => {
|
||||||
|
tys.foldl(TC_NONE, |tc, ty| *tc + tc_ty(cx, *ty, cache))
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
lowest
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ty_param(p) => {
|
ty_param(p) => {
|
||||||
// We only ever ask for the kind of types that are defined in the
|
// We only ever ask for the kind of types that are defined in
|
||||||
// current crate; therefore, the only type parameters that could be
|
// the current crate; therefore, the only type parameters that
|
||||||
// in scope are those defined in the current crate. If this
|
// could be in scope are those defined in the current crate.
|
||||||
// assertion failures, it is likely because of a failure in the
|
// If this assertion failures, it is likely because of a
|
||||||
// cross-crate inlining code to translate a def-id.
|
// failure in the cross-crate inlining code to translate a
|
||||||
|
// def-id.
|
||||||
assert p.def_id.crate == ast::local_crate;
|
assert p.def_id.crate == ast::local_crate;
|
||||||
|
|
||||||
param_bounds_to_kind(cx.ty_param_bounds.get(&p.def_id.node))
|
param_bounds_to_contents(
|
||||||
|
cx, cx.ty_param_bounds.get(&p.def_id.node))
|
||||||
}
|
}
|
||||||
|
|
||||||
// self is a special type parameter that can only appear in traits; it
|
ty_self => {
|
||||||
// is never bounded in any way, hence it has the bottom kind.
|
// Currently, self is not bounded, so we must assume the
|
||||||
ty_self => kind_noncopyable(),
|
// worst. But in the future we should examine the super
|
||||||
|
// traits.
|
||||||
|
//
|
||||||
|
// FIXME(#4678)---self should just be a ty param
|
||||||
|
TC_ALL
|
||||||
|
}
|
||||||
|
|
||||||
ty_infer(_) => {
|
ty_infer(_) => {
|
||||||
if allow_ty_var {
|
// This occurs during coherence, but shouldn't occur at other
|
||||||
kind_top()
|
// times.
|
||||||
} else {
|
TC_ALL
|
||||||
cx.sess.bug(~"Asked to compute kind of a type variable")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ty_type | ty_opaque_closure_ptr(_)
|
ty_trait(_, _, vstore_fixed(_)) |
|
||||||
| ty_opaque_box | ty_unboxed_vec(_) | ty_err => {
|
ty_type |
|
||||||
cx.sess.bug(~"Asked to compute kind of fictitious 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;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.kind_cache.insert(ty, result);
|
debug!("result = %s", result.to_str());
|
||||||
|
|
||||||
|
cache.insert(ty_id, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_implicitly_moves(cx: ctxt, ty: t) -> bool {
|
fn tc_mt(cx: ctxt,
|
||||||
let kind = type_kind(cx, ty);
|
mt: mt,
|
||||||
!(kind_can_be_copied(kind) && kind_can_be_implicitly_copied(kind))
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
/// gives a rough estimate of how much space it takes to represent
|
fn borrowed_contents(region: ty::Region,
|
||||||
/// an instance of `ty`. Used for the mode transition.
|
mutbl: ast::mutability) -> TypeContents
|
||||||
fn type_size(cx: ctxt, ty: t) -> uint {
|
{
|
||||||
|
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 {
|
match /*bad*/copy get(ty).sty {
|
||||||
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
|
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
|
||||||
ty_ptr(_) | ty_box(_) | ty_uniq(_) | ty_estr(vstore_uniq) |
|
ty_ptr(_) | ty_box(_) | ty_uniq(_) | ty_estr(vstore_uniq) |
|
||||||
|
@ -2221,7 +2206,8 @@ fn type_size(cx: ctxt, ty: t) -> uint {
|
||||||
let variants = substd_enum_variants(cx, did, substs);
|
let variants = substd_enum_variants(cx, did, substs);
|
||||||
variants.foldl( // find max size of any variant
|
variants.foldl( // find max size of any variant
|
||||||
0,
|
0,
|
||||||
|m, v| uint::max(*m,
|
|m, v| uint::max(
|
||||||
|
*m,
|
||||||
// find size of this variant:
|
// find size of this variant:
|
||||||
v.args.foldl(0, |s, a| *s + type_size(cx, *a))))
|
v.args.foldl(0, |s, a| *s + type_size(cx, *a))))
|
||||||
}
|
}
|
||||||
|
@ -2238,6 +2224,11 @@ fn type_size(cx: ctxt, ty: t) -> uint {
|
||||||
cx.sess.bug(~"Asked to compute kind of fictitious type");
|
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`.
|
// 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: ¶m_bound) -> bool { !self.eq(other) }
|
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:
|
// Local Variables:
|
||||||
// mode: rust
|
// mode: rust
|
||||||
// fill-column: 78;
|
// fill-column: 78;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`
|
||||||
}
|
}
|
|
@ -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`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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!()
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue