Apply implicit copying for unsafe references to alt patterns

This commit is contained in:
Marijn Haverbeke 2011-09-13 12:14:30 +02:00
parent 7f94957721
commit 3e92f90952
7 changed files with 135 additions and 58 deletions

View File

@ -1,7 +1,7 @@
import syntax::{ast, ast_util};
import ast::{ident, fn_ident, node_id, def_id};
import mut::{expr_root, mut_field, inner_mut};
import mut::{expr_root, mut_field, deref, field, index, unbox};
import syntax::codemap::span;
import syntax::visit;
import visit::vt;
@ -21,7 +21,7 @@ type restrict =
span: span,
local_id: uint,
bindings: [node_id],
unsafe_ty: option::t<ty::t>,
unsafe_tys: [ty::t],
depends_on: [uint],
mutable ok: valid,
mutable given_up: bool};
@ -192,21 +192,17 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr], sc: scope) ->
}
}
let root_var = path_def_id(cx, root.ex);
let unsafe_t =
alt inner_mut(root.ds) { some(t) { some(t) } _ { none } };
restricts +=
[
// FIXME kludge
@{root_var: root_var,
node_id: arg_t.mode == ast::by_mut_ref ? 0 : arg.id,
ty: arg_t.ty,
span: arg.span,
local_id: cx.next_local,
bindings: [arg.id],
unsafe_ty: unsafe_t,
depends_on: deps(sc, root_var),
mutable ok: valid,
mutable given_up: arg_t.mode == ast::by_move}];
restricts += [@{root_var: root_var,
// FIXME kludge
node_id: arg_t.mode == ast::by_mut_ref ? 0 : arg.id,
ty: arg_t.ty,
span: arg.span,
local_id: cx.next_local,
bindings: [arg.id],
unsafe_tys: inner_mut(root.ds),
depends_on: deps(sc, root_var),
mutable ok: valid,
mutable given_up: arg_t.mode == ast::by_move}];
i += 1u;
}
let f_may_close =
@ -217,7 +213,7 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr], sc: scope) ->
if f_may_close {
let i = 0u;
for r in restricts {
if !option::is_none(r.unsafe_ty) && cant_copy(cx, r) {
if vec::len(r.unsafe_tys) > 0u && cant_copy(cx, r) {
cx.tcx.sess.span_err(f.span,
#fmt["function may alias with argument \
%u, which is not immutably rooted",
@ -228,8 +224,7 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr], sc: scope) ->
}
let j = 0u;
for r in restricts {
alt r.unsafe_ty {
some(ty) {
for ty in r.unsafe_tys {
let i = 0u;
for arg_t: ty::arg in arg_ts {
let mut_alias = arg_t.mode == ast::by_mut_ref;
@ -244,8 +239,6 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr], sc: scope) ->
}
i += 1u;
}
}
_ { }
}
j += 1u;
}
@ -279,24 +272,42 @@ fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope,
v.visit_expr(input, sc, v);
let root = expr_root(cx.tcx, input, true);
for a: ast::arm in arms {
let dnums = ast_util::pat_binding_ids(a.pats[0]);
let new_sc = sc;
if vec::len(dnums) > 0u {
let root_var = path_def_id(cx, root.ex);
// FIXME need to use separate restrict for each binding
new_sc = @(*sc + [@{root_var: root_var,
node_id: 0,
ty: ty::mk_int(cx.tcx),
span: a.pats[0].span,
local_id: cx.next_local,
bindings: dnums,
unsafe_ty: inner_mut(root.ds),
depends_on: deps(sc, root_var),
mutable ok: valid,
mutable given_up: false}]);
// FIXME handle other | patterns
let new_sc = *sc;
let root_var = path_def_id(cx, root.ex);
let pat_id_map = ast_util::pat_id_map(a.pats[0]);
type info = {id: node_id, mutable unsafe: [ty::t], span: span};
let binding_info: [info] = [];
for pat in a.pats {
for proot in *pattern_roots(cx.tcx, root.ds, pat) {
let canon_id = pat_id_map.get(proot.name);
// FIXME I wanted to use a block, but that hit a
// typestate bug.
fn match(x: info, canon: node_id) -> bool { x.id == canon }
alt vec::find(bind match(_, canon_id), binding_info) {
some(s) { s.unsafe += inner_mut(proot.ds); }
none. {
binding_info += [{id: canon_id,
mutable unsafe: inner_mut(proot.ds),
span: proot.span}];
}
}
}
}
for info in binding_info {
new_sc += [@{root_var: root_var,
node_id: info.id,
ty: ty::node_id_to_type(cx.tcx, info.id),
span: info.span,
local_id: cx.next_local,
bindings: [info.id],
unsafe_tys: info.unsafe,
depends_on: deps(sc, root_var),
mutable ok: valid,
mutable given_up: false}];
}
register_locals(cx, a.pats[0]);
visit::visit_arm(a, new_sc, v);
visit::visit_arm(a, @new_sc, v);
}
}
@ -323,7 +334,7 @@ fn check_for(cx: ctx, local: @ast::local, seq: @ast::expr, blk: ast::blk,
let elt_t;
alt ty::struct(cx.tcx, seq_t) {
ty::ty_vec(mt) {
if mt.mut != ast::imm { unsafe = some(seq_t); }
if mt.mut != ast::imm { unsafe = [seq_t]; }
elt_t = mt.ty;
}
ty::ty_str. { elt_t = ty::mk_mach(cx.tcx, ast::ty_u8); }
@ -337,7 +348,7 @@ fn check_for(cx: ctx, local: @ast::local, seq: @ast::expr, blk: ast::blk,
span: local.node.pat.span,
local_id: cx.next_local,
bindings: ast_util::pat_binding_ids(local.node.pat),
unsafe_ty: unsafe,
unsafe_tys: unsafe,
depends_on: deps(sc, root_var),
mutable ok: valid,
mutable given_up: false};
@ -354,16 +365,12 @@ fn check_var(cx: ctx, ex: @ast::expr, p: ast::path, id: ast::node_id,
alt cx.local_map.find(my_defnum) { some(local(id)) { id } _ { 0u } };
let var_t = ty::expr_ty(cx.tcx, ex);
for r: restrict in *sc {
// excludes variables introduced since the alias was made
if my_local_id < r.local_id {
alt r.unsafe_ty {
some(ty) {
for ty in r.unsafe_tys {
if ty_can_unsafely_include(cx, ty, var_t, assign) {
r.ok = val_taken(ex.span, p);
}
}
_ { }
}
} else if vec::member(my_defnum, r.bindings) {
test_scope(cx, sc, r, p);
@ -546,6 +553,49 @@ fn copy_is_expensive(tcx: ty::ctxt, ty: ty::t) -> bool {
ret score_ty(tcx, ty) > 8u;
}
type pattern_root = {id: node_id, name: ident, ds: @[deref], span: span};
fn pattern_roots(tcx: ty::ctxt, base: @[deref], pat: @ast::pat)
-> @[pattern_root] {
fn walk(tcx: ty::ctxt, base: [deref], pat: @ast::pat,
&set: [pattern_root]) {
alt pat.node {
ast::pat_wild. | ast::pat_lit(_) {}
ast::pat_bind(nm) {
set += [{id: pat.id, name: nm, ds: @base, span: pat.span}];
}
ast::pat_tag(_, ps) | ast::pat_tup(ps) {
let base = base + [@{mut: false, kind: field,
outer_t: ty::node_id_to_type(tcx, pat.id)}];
for p in ps { walk(tcx, base, p, set); }
}
ast::pat_rec(fs, _) {
let ty = ty::node_id_to_type(tcx, pat.id);
for f in fs {
let mut = ty::get_field(tcx, ty, f.ident).mt.mut != ast::imm;
let base = base + [@{mut: mut, kind: field, outer_t: ty}];
walk(tcx, base, f.pat, set);
}
}
ast::pat_box(p) {
let ty = ty::node_id_to_type(tcx, pat.id);
let mut = alt ty::struct(tcx, ty) {
ty::ty_box(mt) { mt.mut != ast::imm }
};
walk(tcx, base + [@{mut: mut, kind: unbox, outer_t: ty}], p, set);
}
}
}
let set = [];
walk(tcx, *base, pat, set);
ret @set;
}
fn inner_mut(ds: @[deref]) -> [ty::t] {
for d: deref in *ds { if d.mut { ret [d.outer_t]; } }
ret [];
}
// Local Variables:
// mode: rust
// fill-column: 78;

View File

@ -110,11 +110,6 @@ fn mut_field(ds: @[deref]) -> bool {
ret false;
}
fn inner_mut(ds: @[deref]) -> option::t<ty::t> {
for d: deref in *ds { if d.mut { ret some(d.outer_t); } }
ret none;
}
// Actual mut-checking pass
type mut_map = std::map::hashmap<node_id, ()>;

View File

@ -471,22 +471,37 @@ fn make_phi_bindings(bcx: @block_ctxt, map: [exit_node],
ids: ast_util::pat_id_map) -> bool {
let our_block = bcx.llbb as uint;
let success = true;
for each item: @{key: ast::ident, val: ast::node_id} in ids.items() {
for each @{key: name, val: node_id} in ids.items() {
let llbbs = [];
let vals = [];
for ex: exit_node in map {
if ex.to as uint == our_block {
alt assoc(item.key, ex.bound) {
alt assoc(name, ex.bound) {
some(val) { llbbs += [ex.from]; vals += [val]; }
none. { }
}
}
}
if vec::len(vals) > 0u {
let phi = Phi(bcx, val_ty(vals[0]), vals, llbbs);
bcx.fcx.lllocals.insert(item.val, phi);
let local = Phi(bcx, val_ty(vals[0]), vals, llbbs);
bcx.fcx.lllocals.insert(node_id, local);
} else { success = false; }
}
if success {
// Copy references that the alias analysis considered unsafe
for each @{val: node_id, _} in ids.items() {
if bcx_ccx(bcx).copy_map.contains_key(node_id) {
let local = bcx.fcx.lllocals.get(node_id);
let e_ty = ty::node_id_to_type(bcx_tcx(bcx), node_id);
let {bcx: abcx, val: alloc} = trans::alloc_ty(bcx, e_ty);
bcx = trans::copy_val(abcx, trans::INIT, alloc,
load_if_immediate(abcx, local, e_ty),
e_ty);
add_clean(bcx, alloc, e_ty);
bcx.fcx.lllocals.insert(node_id, alloc);
}
}
}
ret success;
}

View File

@ -45,6 +45,7 @@ export expr_ty_params_and_ty;
export fold_ty;
export field;
export field_idx;
export get_field;
export fm_general;
export get_element_type;
export hash_ty;
@ -1680,6 +1681,16 @@ fn field_idx(sess: session::session, sp: span, id: ast::ident,
sess.span_fatal(sp, "unknown field '" + id + "' of record");
}
fn get_field(tcx: ctxt, rec_ty: t, id: ast::ident) -> field {
alt struct(tcx, rec_ty) {
ty_rec(fields) {
alt vec::find({|f| str::eq(f.ident, id) }, fields) {
some(f) { ret f; }
}
}
}
}
fn method_idx(sess: session::session, sp: span, id: ast::ident,
meths: [method]) -> uint {
let i: uint = 0u;

View File

@ -1,8 +1,8 @@
// error-pattern:invalidate reference x
fn whoknows(x: @mutable int) { *x = 10; }
fn whoknows(x: @mutable {mutable x: int}) { x.x = 10; }
fn main() {
let box = @mutable 1;
let box = @mutable {mutable x: 1};
alt *box { x { whoknows(box); log_err x; } }
}

View File

@ -1,8 +1,8 @@
// error-pattern:invalidate reference i
tag foo { left(int); right(bool); }
tag foo { left({mutable x: int}); right(bool); }
fn main() {
let x = left(10);
let x = left({mutable x: 10});
alt x { left(i) { x = right(false); log i; } _ { } }
}

View File

@ -0,0 +1,6 @@
fn main() {
let x = @{mutable a: @10, b: @20};
alt x {
@{a, b} { assert *a == 10; (*x).a = @30; assert *a == 10; }
}
}