add new deprecated_mode lint pass

It will warn you if you use the default mode for something that
is expensive to copy, and it will warn you if you use any explicit
mode other than copy.  So you should migrate over to using the
default mode for most things (and borrowed pointers when you don't
want to copy) and copy mode for things you really wanted to copy.
This commit is contained in:
Niko Matsakis 2012-07-25 06:41:56 -07:00
parent 99674dc52b
commit cc8086a045
2 changed files with 161 additions and 21 deletions

View File

@ -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);

View File

@ -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 {