Do not allow moving out of obj fields, properly check move inits

Closes #591
This commit is contained in:
Marijn Haverbeke 2011-07-13 10:23:13 +02:00
parent 729fa81d3b
commit 6914d32acc
1 changed files with 85 additions and 87 deletions

View File

@ -47,13 +47,14 @@ fn check_crate(@ty::ctxt tcx, &@ast::crate crate) {
auto v =
@rec(visit_fn=bind visit_fn(cx, _, _, _, _, _, _, _),
visit_item=bind visit_item(cx, _, _, _),
visit_expr=bind visit_expr(cx, _, _, _)
visit_expr=bind visit_expr(cx, _, _, _),
visit_decl=bind visit_decl(cx, _, _, _)
with *visit::default_visitor[scope]());
visit::visit_crate(*crate, [], visit::mk_vt(v));
tcx.sess.abort_if_errors();
}
fn visit_fn(@ctx cx, &ast::_fn f, &ast::ty_param[] tp, &span sp,
fn visit_fn(&@ctx cx, &ast::_fn f, &ast::ty_param[] tp, &span sp,
&fn_ident name, ast::node_id id, &scope sc, &vt[scope] v) {
visit::visit_fn_decl(f.decl, sc, v);
for (ast::arg arg_ in f.decl.inputs) {
@ -62,7 +63,7 @@ fn visit_fn(@ctx cx, &ast::_fn f, &ast::ty_param[] tp, &span sp,
v.visit_block(f.body, [], v);
}
fn visit_item(@ctx cx, &@ast::item i, &scope sc, &vt[scope] v) {
fn visit_item(&@ctx cx, &@ast::item i, &scope sc, &vt[scope] v) {
alt (i.node) {
case (ast::item_obj(?o, _, _)) {
for (ast::obj_field f in o.fields) {
@ -74,21 +75,21 @@ fn visit_item(@ctx cx, &@ast::item i, &scope sc, &vt[scope] v) {
visit::visit_item(i, sc, v);
}
fn visit_expr(@ctx cx, &@ast::expr ex, &scope sc, &vt[scope] v) {
fn visit_expr(&@ctx cx, &@ast::expr ex, &scope sc, &vt[scope] v) {
auto handled = true;
alt (ex.node) {
case (ast::expr_call(?f, ?args)) {
ast::expr_call(?f, ?args) {
check_call(*cx, f, args, sc);
handled = false;
}
case (ast::expr_be(?cl)) {
ast::expr_be(?cl) {
check_tail_call(*cx, cl);
visit::visit_expr(cl, sc, v);
}
case (ast::expr_alt(?input, ?arms)) {
ast::expr_alt(?input, ?arms) {
check_alt(*cx, input, arms, sc, v);
}
case (ast::expr_put(?val)) {
ast::expr_put(?val) {
alt (val) {
case (some(?ex)) {
auto root = expr_root(*cx, ex, false);
@ -102,36 +103,50 @@ fn visit_expr(@ctx cx, &@ast::expr ex, &scope sc, &vt[scope] v) {
case (_) { }
}
}
case (ast::expr_for_each(?decl, ?call, ?block)) {
ast::expr_for_each(?decl, ?call, ?block) {
check_for_each(*cx, decl, call, block, sc, v);
}
case (ast::expr_for(?decl, ?seq, ?block)) {
ast::expr_for(?decl, ?seq, ?block) {
check_for(*cx, decl, seq, block, sc, v);
}
case (ast::expr_path(?pt)) {
ast::expr_path(?pt) {
check_var(*cx, ex, pt, ex.id, false, sc);
handled = false;
}
case (ast::expr_swap(?lhs, ?rhs)) {
ast::expr_swap(?lhs, ?rhs) {
check_lval(cx, lhs, sc, v);
check_lval(cx, rhs, sc, v);
handled = false;
}
case (ast::expr_move(?dest, ?src)) {
ast::expr_move(?dest, ?src) {
check_assign(cx, dest, src, sc, v);
check_move_rhs(cx, src, sc, v);
}
case (ast::expr_assign(?dest, ?src)) {
ast::expr_assign(?dest, ?src) | ast::expr_assign_op(_, ?dest, ?src) {
check_assign(cx, dest, src, sc, v);
}
case (ast::expr_assign_op(_, ?dest, ?src)) {
check_assign(cx, dest, src, sc, v);
}
case (_) { handled = false; }
_ { handled = false; }
}
if (!handled) { visit::visit_expr(ex, sc, v); }
}
fn visit_decl(&@ctx cx, &@ast::decl d, &scope sc, &vt[scope] v) {
visit::visit_decl(d, sc, v);
alt (d.node) {
ast::decl_local(?loc) {
alt (loc.node.init) {
some(?init) {
if (init.op == ast::init_move) {
check_move_rhs(cx, init.expr, sc, v);
}
}
none {}
}
}
_ {}
}
}
fn check_call(&ctx cx, &@ast::expr f, &(@ast::expr)[] args, &scope sc) ->
rec(vec[node_id] root_vars, vec[ty::t] unsafe_ts) {
auto fty = ty::expr_ty(*cx.tcx, f);
@ -265,20 +280,17 @@ fn check_alt(&ctx cx, &@ast::expr input, &ast::arm[] arms, &scope sc,
&vt[scope] v) {
visit::visit_expr(input, sc, v);
auto root = expr_root(cx, input, true);
auto roots =
alt (path_def_id(cx, root.ex)) {
case (some(?did)) { [did._1] }
case (_) { [] }
auto roots = alt (path_def_id(cx, root.ex)) {
some(?did) { [did._1] }
_ { [] }
};
let vec[ty::t] forbidden_tp =
alt (inner_mut(root.ds)) { case (some(?t)) { [t] } case (_) { [] } };
alt (inner_mut(root.ds)) { some(?t) { [t] } _ { [] } };
for (ast::arm a in arms) {
auto dnums = arm_defnums(a);
auto new_sc = sc;
if (vec::len(dnums) > 0u) {
new_sc =
sc +
[@rec(root_vars=roots,
new_sc = sc + [@rec(root_vars=roots,
block_defnum=dnums.(0),
bindings=dnums,
tys=forbidden_tp,
@ -331,26 +343,20 @@ fn check_for(&ctx cx, &@ast::local local, &@ast::expr seq, &ast::block block,
visit::visit_expr(seq, sc, v);
auto defnum = local.node.id;
auto root = expr_root(cx, seq, false);
auto root_def =
alt (path_def_id(cx, root.ex)) {
case (some(?did)) { [did._1] }
case (_) { [] }
auto root_def = alt (path_def_id(cx, root.ex)) {
some(?did) { [did._1] }
_ { [] }
};
auto unsafe =
alt (inner_mut(root.ds)) { case (some(?t)) { [t] } case (_) { [] } };
// If this is a mutable vector, don't allow it to be touched.
auto unsafe = alt (inner_mut(root.ds)) { some(?t) { [t] } _ { [] } };
// If this is a mutable vector, don't allow it to be touched.
auto seq_t = ty::expr_ty(*cx.tcx, seq);
alt (ty::struct(*cx.tcx, seq_t)) {
case (ty::ty_vec(?mt)) {
ty::ty_vec(?mt) | ty::ty_ivec(?mt) {
if (mt.mut != ast::imm) { unsafe = [seq_t]; }
}
case (ty::ty_str) { /* no-op */ }
case (ty::ty_ivec(?mt)) {
if (mt.mut != ast::imm) { unsafe = [seq_t]; }
}
case (ty::ty_istr) { /* no-op */ }
case (_) {
ty::ty_str | ty::ty_istr { /* no-op */ }
_ {
cx.tcx.sess.span_unimpl(seq.span, "unknown seq type " +
util::ppaux::ty_to_str(*cx.tcx, seq_t));
}
@ -424,6 +430,13 @@ fn check_lval(&@ctx cx, &@ast::expr dest, &scope sc, &vt[scope] v) {
fn check_move_rhs(&@ctx cx, &@ast::expr src, &scope sc, &vt[scope] v) {
alt (src.node) {
case (ast::expr_path(?p)) {
alt (cx.tcx.def_map.get(src.id)) {
ast::def_obj_field(_) {
cx.tcx.sess.span_err
(src.span, "may not move out of an obj field");
}
_ {}
}
check_lval(cx, src, sc, v);
}
case (_) {
@ -550,14 +563,12 @@ fn expr_root(&ctx cx, @ast::expr ex, bool autoderef) ->
maybe_auto_unbox(cx, ty::expr_ty(*cx.tcx, base));
alt (ty::struct(*cx.tcx, auto_unbox.t)) {
case (ty::ty_vec(?mt)) {
vec::push(ds,
rec(mut=mt.mut != ast::imm,
vec::push(ds, rec(mut=mt.mut != ast::imm,
kind=index,
outer_t=auto_unbox.t));
}
case (ty::ty_ivec(?mt)) {
vec::push(ds,
rec(mut=mt.mut != ast::imm,
vec::push(ds, rec(mut=mt.mut != ast::imm,
kind=index,
outer_t=auto_unbox.t));
}
@ -616,22 +627,16 @@ fn ty_can_unsafely_include(&ctx cx, ty::t needle, ty::t haystack, bool mut) ->
fn helper(&ty::ctxt tcx, ty::t needle, ty::t haystack, bool mut) -> bool {
if (needle == haystack) { ret true; }
alt (ty::struct(tcx, haystack)) {
case (ty::ty_tag(_, ?ts)) {
ty::ty_tag(_, ?ts) {
for (ty::t t in ts) {
if (helper(tcx, needle, t, mut)) { ret true; }
}
ret false;
}
case (ty::ty_box(?mt)) {
ty::ty_box(?mt) | ty::ty_vec(?mt) | ty::ty_ptr(?mt) {
ret helper(tcx, needle, mt.ty, get_mut(mut, mt));
}
case (ty::ty_vec(?mt)) {
ret helper(tcx, needle, mt.ty, get_mut(mut, mt));
}
case (ty::ty_ptr(?mt)) {
ret helper(tcx, needle, mt.ty, get_mut(mut, mt));
}
case (ty::ty_tup(?mts)) {
ty::ty_tup(?mts) {
for (ty::mt mt in mts) {
if (helper(tcx, needle, mt.ty, get_mut(mut, mt))) {
ret true;
@ -639,7 +644,7 @@ fn ty_can_unsafely_include(&ctx cx, ty::t needle, ty::t haystack, bool mut) ->
}
ret false;
}
case (ty::ty_rec(?fields)) {
ty::ty_rec(?fields) {
for (ty::field f in fields) {
if (helper(tcx, needle, f.mt.ty, get_mut(mut, f.mt))) {
ret true;
@ -647,21 +652,17 @@ fn ty_can_unsafely_include(&ctx cx, ty::t needle, ty::t haystack, bool mut) ->
}
ret false;
}
case (
// These may contain anything.
ty::ty_fn(_, _, _, _, _)) {
ty::ty_fn(_, _, _, _, _) {
ret true;
}
case (ty::ty_obj(_)) { ret true; }
case (
ty::ty_obj(_) { ret true; }
// A type param may include everything, but can only be
// treated as opaque downstream, and is thus safe unless we
// saw mutable fields, in which case the whole thing can be
// overwritten.
ty::ty_param(_)) {
ret mut;
}
case (_) { ret false; }
ty::ty_param(_) { ret mut; }
_ { ret false; }
}
}
ret helper(*cx.tcx, needle, haystack, mut);
@ -669,18 +670,15 @@ fn ty_can_unsafely_include(&ctx cx, ty::t needle, ty::t haystack, bool mut) ->
fn def_is_local(&ast::def d, bool objfields_count) -> bool {
ret alt (d) {
case (ast::def_local(_)) { true }
case (ast::def_arg(_)) { true }
case (ast::def_obj_field(_)) { objfields_count }
case (ast::def_binding(_)) { true }
case (_) { false }
ast::def_local(_) | ast::def_arg(_) | ast::def_binding(_) { true }
ast::def_obj_field(_) { objfields_count }
_ { false }
};
}
fn fty_args(&ctx cx, ty::t fty) -> ty::arg[] {
ret alt (ty::struct(*cx.tcx, ty::type_autoderef(*cx.tcx, fty))) {
case (ty::ty_fn(_, ?args, _, _, _)) { args }
case (ty::ty_native_fn(_, ?args, _)) { args }
ty::ty_fn(_, ?args, _, _, _) | ty::ty_native_fn(_, ?args, _) { args }
};
}
// Local Variables: