diff --git a/src/rustc/middle/lint.rs b/src/rustc/middle/lint.rs index f5ca8cfd543..7de4c0f73b2 100644 --- a/src/rustc/middle/lint.rs +++ b/src/rustc/middle/lint.rs @@ -7,7 +7,8 @@ import syntax::codemap::span; import std::map::{map,hashmap,int_hash,hash_from_strs}; import std::smallintmap::{map,smallintmap}; import io::writer_util; -import syntax::print::pprust::expr_to_str; +import util::ppaux::{ty_to_str}; +import syntax::print::pprust::{expr_to_str, mode_to_str}; export lint, ctypes, unused_imports, while_true, path_statement, old_vecs; export unrecognized_warning, non_implicitly_copyable_typarams; export vecs_not_implicitly_copyable, implicit_copies; @@ -47,6 +48,7 @@ enum lint { unrecognized_warning, non_implicitly_copyable_typarams, vecs_not_implicitly_copyable, + deprecated_mode, } // This is pretty unfortunate. We really want some sort of "deriving Enum" @@ -61,6 +63,7 @@ fn int_to_lint(i: int) -> lint { 5 { unrecognized_warning } 6 { non_implicitly_copyable_typarams } 7 { vecs_not_implicitly_copyable } + 8 { deprecated_mode } } } @@ -119,8 +122,12 @@ fn get_lint_dict() -> lint_dict { (~"implicit_copies", @{lint: implicit_copies, desc: ~"implicit copies of non implicitly copyable data", - default: warn}) + default: warn}), + (~"deprecated_mode", + @{lint: deprecated_mode, + desc: ~"warn about deprecated uses of modes", + default: ignore}) ]; hash_from_strs(v) } @@ -411,10 +418,56 @@ fn check_item_path_statement(cx: ty::ctxt, it: @ast::item) { visit::visit_item(it, (), visit); } +fn check_fn(tcx: ty::ctxt, fk: visit::fn_kind, decl: ast::fn_decl, + _body: ast::blk, span: span, id: ast::node_id) { + #debug["lint check_fn fk=%? id=%?", fk, id]; + let fn_ty = ty::node_id_to_type(tcx, id); + alt check ty::get(fn_ty).struct { + ty::ty_fn(fn_ty) { + let mut counter = 0; + do vec::iter2(fn_ty.inputs, decl.inputs) |arg_ty, arg_ast| { + counter += 1; + #debug["arg %d, ty=%s, mode=%s", + counter, + ty_to_str(tcx, arg_ty.ty), + mode_to_str(arg_ast.mode)]; + alt arg_ast.mode { + ast::expl(ast::by_copy) => { + /* always allow by-copy */ + } + + ast::expl(_) => { + tcx.sess.span_lint( + deprecated_mode, id, id, + span, + #fmt["argument %d uses an explicit mode", counter]); + } + + ast::infer(_) { + let kind = ty::type_kind(tcx, arg_ty.ty); + if !ty::kind_is_safe_for_default_mode(kind) { + tcx.sess.span_lint( + deprecated_mode, id, id, + span, + #fmt["argument %d uses the default mode \ + but shouldn't", + counter]); + } + } + } + } + } + } +} + fn check_crate(tcx: ty::ctxt, crate: @ast::crate) { let v = visit::mk_simple_visitor(@{ - visit_item: fn@(it: @ast::item) { check_item(it, tcx); } + visit_item: + |it| check_item(it, tcx), + visit_fn: + |fk, decl, body, span, id| check_fn(tcx, fk, decl, body, + span, id), with *visit::default_simple_visitor() }); visit::visit_crate(*crate, (), v); diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index bb175be3903..b6b1873b1dc 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -110,6 +110,7 @@ export ty_fn_args; export kind, kind_implicitly_copyable, kind_send_copy, kind_copyable; export kind_noncopyable, kind_const; export kind_can_be_copied, kind_can_be_sent, kind_can_be_implicitly_copied; +export kind_is_safe_for_default_mode; export kind_is_owned; export proto_kind, kind_lteq, type_kind; export operators; @@ -1371,19 +1372,22 @@ fn type_needs_unwind_cleanup_(cx: ctxt, ty: t, enum kind { kind_(u32) } /// can be copied (implicitly or explicitly) -const KIND_MASK_COPY : u32 = 0b00000000000000000000000000000001_u32; +const KIND_MASK_COPY : u32 = 0b000000000000000000000000001_u32; /// can be sent: no shared box, borrowed ptr (must imply OWNED) -const KIND_MASK_SEND : u32 = 0b00000000000000000000000000000010_u32; +const KIND_MASK_SEND : u32 = 0b000000000000000000000000010_u32; /// is owned (no borrowed ptrs) -const KIND_MASK_OWNED : u32 = 0b00000000000000000000000000000100_u32; +const KIND_MASK_OWNED : u32 = 0b000000000000000000000000100_u32; /// is deeply immutable -const KIND_MASK_CONST : u32 = 0b00000000000000000000000000001000_u32; +const KIND_MASK_CONST : u32 = 0b000000000000000000000001000_u32; /// can be implicitly copied (must imply COPY) -const KIND_MASK_IMPLICIT : u32 = 0b00000000000000000000000000010000_u32; +const KIND_MASK_IMPLICIT : u32 = 0b000000000000000000000010000_u32; + +/// safe for default mode (subset of KIND_MASK_IMPLICIT) +const KIND_MASK_DEFAULT_MODE : u32 = 0b000000000000000000000100000_u32; fn kind_noncopyable() -> kind { kind_(0u32) @@ -1397,10 +1401,22 @@ 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_SEND) } +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_SEND) +} + + fn kind_send_copy() -> kind { kind_(KIND_MASK_COPY | KIND_MASK_SEND) } @@ -1426,7 +1442,7 @@ fn remove_const(k: kind) -> kind { } fn remove_implicit(k: kind) -> kind { - k - kind_(KIND_MASK_IMPLICIT) + k - kind_(KIND_MASK_IMPLICIT | KIND_MASK_DEFAULT_MODE) } fn remove_send(k: kind) -> kind { @@ -1462,6 +1478,10 @@ pure fn kind_can_be_implicitly_copied(k: kind) -> bool { *k & KIND_MASK_IMPLICIT == KIND_MASK_IMPLICIT } +pure fn kind_is_safe_for_default_mode(k: kind) -> bool { + *k & KIND_MASK_DEFAULT_MODE == KIND_MASK_DEFAULT_MODE +} + pure fn kind_can_be_copied(k: kind) -> bool { *k & KIND_MASK_COPY == KIND_MASK_COPY } @@ -1478,9 +1498,9 @@ fn proto_kind(p: proto) -> kind { alt p { ast::proto_any { kind_noncopyable() } ast::proto_block { kind_noncopyable() } - ast::proto_box { kind_implicitly_copyable() | kind_owned() } + ast::proto_box { kind_safe_for_default_mode() | kind_owned() } ast::proto_uniq { kind_send_copy() | kind_owned() } - ast::proto_bare { kind_implicitly_sendable() | kind_const() | + ast::proto_bare { kind_safe_for_default_mode_send() | kind_const() | kind_owned() } } } @@ -1539,11 +1559,11 @@ fn type_kind(cx: ctxt, ty: t) -> kind { // Insert a default in case we loop back on self recursively. cx.kind_cache.insert(ty, kind_top()); - let result = alt get(ty).struct { + let mut result = alt get(ty).struct { // Scalar and unique types are sendable, constant, and owned ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_ptr(_) { - kind_implicitly_sendable() | kind_const() | kind_owned() + kind_safe_for_default_mode_send() | kind_const() | kind_owned() } // Implicit copyability of strs is configurable @@ -1561,14 +1581,14 @@ fn type_kind(cx: ctxt, ty: t) -> kind { // Those with refcounts raise noncopyable to copyable, // lower sendable to copyable. Therefore just set result to copyable. ty_box(tm) { - remove_send(mutable_type_kind(cx, tm) | kind_implicitly_copyable()) + remove_send(mutable_type_kind(cx, tm) | kind_safe_for_default_mode()) } // Iface instances are (for now) like shared boxes, basically - ty_trait(_, _) { kind_implicitly_copyable() | kind_owned() } + ty_trait(_, _) { kind_safe_for_default_mode() | kind_owned() } // Region pointers are copyable but NOT owned nor sendable - ty_rptr(_, _) { kind_implicitly_copyable() } + ty_rptr(_, _) { kind_safe_for_default_mode() } // Unique boxes and vecs have the kind of their contained type, // but unique boxes can't be implicitly copyable. @@ -1587,10 +1607,10 @@ fn type_kind(cx: ctxt, ty: t) -> kind { // 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_send(kind_implicitly_copyable() | mutable_type_kind(cx, tm)) + remove_send(kind_safe_for_default_mode() | mutable_type_kind(cx, tm)) } ty_evec(tm, vstore_slice(_)) { - remove_owned_send(kind_implicitly_copyable() | + remove_owned_send(kind_safe_for_default_mode() | mutable_type_kind(cx, tm)) } ty_evec(tm, vstore_fixed(_)) { @@ -1599,13 +1619,13 @@ fn type_kind(cx: ctxt, ty: t) -> kind { // All estrs are copyable; uniques and interiors are sendable. ty_estr(vstore_box) { - kind_implicitly_copyable() | kind_const() | kind_owned() + kind_safe_for_default_mode() | kind_const() | kind_owned() } ty_estr(vstore_slice(_)) { - kind_implicitly_copyable() | kind_const() + kind_safe_for_default_mode() | kind_const() } ty_estr(vstore_fixed(_)) { - kind_implicitly_sendable() | kind_const() | kind_owned() + kind_safe_for_default_mode_send() | kind_const() | kind_owned() } // Records lower to the lowest of their members. @@ -1676,10 +1696,77 @@ fn type_kind(cx: ctxt, ty: t) -> kind { } }; + // 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 -= kind_(KIND_MASK_DEFAULT_MODE); + } + } + cx.kind_cache.insert(ty, result); ret result; } +/// 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 { + alt get(ty).struct { + 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_fn(_) => { + 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_class(did, substs) { + let flds = class_items_as_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, 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_var(_) | ty_var_integral(_) { + cx.sess.bug(~"Asked to compute kind of a type variable"); + } + ty_type | ty_opaque_closure_ptr(_) | ty_opaque_box | ty_unboxed_vec(_) { + cx.sess.bug(~"Asked to compute kind of fictitious type"); + } + } +} + // True if instantiating an instance of `r_ty` requires an instance of `r_ty`. fn is_instantiable(cx: ctxt, r_ty: t) -> bool {