Skip to content

Commit 3e92f90

Browse files
committed
Apply implicit copying for unsafe references to alt patterns
1 parent 7f94957 commit 3e92f90

File tree

7 files changed

+135
-58
lines changed

7 files changed

+135
-58
lines changed

src/comp/middle/alias.rs

Lines changed: 95 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
import syntax::{ast, ast_util};
33
import ast::{ident, fn_ident, node_id, def_id};
4-
import mut::{expr_root, mut_field, inner_mut};
4+
import mut::{expr_root, mut_field, deref, field, index, unbox};
55
import syntax::codemap::span;
66
import syntax::visit;
77
import visit::vt;
@@ -21,7 +21,7 @@ type restrict =
2121
span: span,
2222
local_id: uint,
2323
bindings: [node_id],
24-
unsafe_ty: option::t<ty::t>,
24+
unsafe_tys: [ty::t],
2525
depends_on: [uint],
2626
mutable ok: valid,
2727
mutable given_up: bool};
@@ -192,21 +192,17 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr], sc: scope) ->
192192
}
193193
}
194194
let root_var = path_def_id(cx, root.ex);
195-
let unsafe_t =
196-
alt inner_mut(root.ds) { some(t) { some(t) } _ { none } };
197-
restricts +=
198-
[
199-
// FIXME kludge
200-
@{root_var: root_var,
201-
node_id: arg_t.mode == ast::by_mut_ref ? 0 : arg.id,
202-
ty: arg_t.ty,
203-
span: arg.span,
204-
local_id: cx.next_local,
205-
bindings: [arg.id],
206-
unsafe_ty: unsafe_t,
207-
depends_on: deps(sc, root_var),
208-
mutable ok: valid,
209-
mutable given_up: arg_t.mode == ast::by_move}];
195+
restricts += [@{root_var: root_var,
196+
// FIXME kludge
197+
node_id: arg_t.mode == ast::by_mut_ref ? 0 : arg.id,
198+
ty: arg_t.ty,
199+
span: arg.span,
200+
local_id: cx.next_local,
201+
bindings: [arg.id],
202+
unsafe_tys: inner_mut(root.ds),
203+
depends_on: deps(sc, root_var),
204+
mutable ok: valid,
205+
mutable given_up: arg_t.mode == ast::by_move}];
210206
i += 1u;
211207
}
212208
let f_may_close =
@@ -217,7 +213,7 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr], sc: scope) ->
217213
if f_may_close {
218214
let i = 0u;
219215
for r in restricts {
220-
if !option::is_none(r.unsafe_ty) && cant_copy(cx, r) {
216+
if vec::len(r.unsafe_tys) > 0u && cant_copy(cx, r) {
221217
cx.tcx.sess.span_err(f.span,
222218
#fmt["function may alias with argument \
223219
%u, which is not immutably rooted",
@@ -228,8 +224,7 @@ fn check_call(cx: ctx, f: @ast::expr, args: [@ast::expr], sc: scope) ->
228224
}
229225
let j = 0u;
230226
for r in restricts {
231-
alt r.unsafe_ty {
232-
some(ty) {
227+
for ty in r.unsafe_tys {
233228
let i = 0u;
234229
for arg_t: ty::arg in arg_ts {
235230
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) ->
244239
}
245240
i += 1u;
246241
}
247-
}
248-
_ { }
249242
}
250243
j += 1u;
251244
}
@@ -279,24 +272,42 @@ fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope,
279272
v.visit_expr(input, sc, v);
280273
let root = expr_root(cx.tcx, input, true);
281274
for a: ast::arm in arms {
282-
let dnums = ast_util::pat_binding_ids(a.pats[0]);
283-
let new_sc = sc;
284-
if vec::len(dnums) > 0u {
285-
let root_var = path_def_id(cx, root.ex);
286-
// FIXME need to use separate restrict for each binding
287-
new_sc = @(*sc + [@{root_var: root_var,
288-
node_id: 0,
289-
ty: ty::mk_int(cx.tcx),
290-
span: a.pats[0].span,
291-
local_id: cx.next_local,
292-
bindings: dnums,
293-
unsafe_ty: inner_mut(root.ds),
294-
depends_on: deps(sc, root_var),
295-
mutable ok: valid,
296-
mutable given_up: false}]);
275+
// FIXME handle other | patterns
276+
let new_sc = *sc;
277+
let root_var = path_def_id(cx, root.ex);
278+
let pat_id_map = ast_util::pat_id_map(a.pats[0]);
279+
type info = {id: node_id, mutable unsafe: [ty::t], span: span};
280+
let binding_info: [info] = [];
281+
for pat in a.pats {
282+
for proot in *pattern_roots(cx.tcx, root.ds, pat) {
283+
let canon_id = pat_id_map.get(proot.name);
284+
// FIXME I wanted to use a block, but that hit a
285+
// typestate bug.
286+
fn match(x: info, canon: node_id) -> bool { x.id == canon }
287+
alt vec::find(bind match(_, canon_id), binding_info) {
288+
some(s) { s.unsafe += inner_mut(proot.ds); }
289+
none. {
290+
binding_info += [{id: canon_id,
291+
mutable unsafe: inner_mut(proot.ds),
292+
span: proot.span}];
293+
}
294+
}
295+
}
296+
}
297+
for info in binding_info {
298+
new_sc += [@{root_var: root_var,
299+
node_id: info.id,
300+
ty: ty::node_id_to_type(cx.tcx, info.id),
301+
span: info.span,
302+
local_id: cx.next_local,
303+
bindings: [info.id],
304+
unsafe_tys: info.unsafe,
305+
depends_on: deps(sc, root_var),
306+
mutable ok: valid,
307+
mutable given_up: false}];
297308
}
298309
register_locals(cx, a.pats[0]);
299-
visit::visit_arm(a, new_sc, v);
310+
visit::visit_arm(a, @new_sc, v);
300311
}
301312
}
302313

@@ -323,7 +334,7 @@ fn check_for(cx: ctx, local: @ast::local, seq: @ast::expr, blk: ast::blk,
323334
let elt_t;
324335
alt ty::struct(cx.tcx, seq_t) {
325336
ty::ty_vec(mt) {
326-
if mt.mut != ast::imm { unsafe = some(seq_t); }
337+
if mt.mut != ast::imm { unsafe = [seq_t]; }
327338
elt_t = mt.ty;
328339
}
329340
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,
337348
span: local.node.pat.span,
338349
local_id: cx.next_local,
339350
bindings: ast_util::pat_binding_ids(local.node.pat),
340-
unsafe_ty: unsafe,
351+
unsafe_tys: unsafe,
341352
depends_on: deps(sc, root_var),
342353
mutable ok: valid,
343354
mutable given_up: false};
@@ -354,16 +365,12 @@ fn check_var(cx: ctx, ex: @ast::expr, p: ast::path, id: ast::node_id,
354365
alt cx.local_map.find(my_defnum) { some(local(id)) { id } _ { 0u } };
355366
let var_t = ty::expr_ty(cx.tcx, ex);
356367
for r: restrict in *sc {
357-
358368
// excludes variables introduced since the alias was made
359369
if my_local_id < r.local_id {
360-
alt r.unsafe_ty {
361-
some(ty) {
370+
for ty in r.unsafe_tys {
362371
if ty_can_unsafely_include(cx, ty, var_t, assign) {
363372
r.ok = val_taken(ex.span, p);
364373
}
365-
}
366-
_ { }
367374
}
368375
} else if vec::member(my_defnum, r.bindings) {
369376
test_scope(cx, sc, r, p);
@@ -546,6 +553,49 @@ fn copy_is_expensive(tcx: ty::ctxt, ty: ty::t) -> bool {
546553
ret score_ty(tcx, ty) > 8u;
547554
}
548555

556+
type pattern_root = {id: node_id, name: ident, ds: @[deref], span: span};
557+
558+
fn pattern_roots(tcx: ty::ctxt, base: @[deref], pat: @ast::pat)
559+
-> @[pattern_root] {
560+
fn walk(tcx: ty::ctxt, base: [deref], pat: @ast::pat,
561+
&set: [pattern_root]) {
562+
alt pat.node {
563+
ast::pat_wild. | ast::pat_lit(_) {}
564+
ast::pat_bind(nm) {
565+
set += [{id: pat.id, name: nm, ds: @base, span: pat.span}];
566+
}
567+
ast::pat_tag(_, ps) | ast::pat_tup(ps) {
568+
let base = base + [@{mut: false, kind: field,
569+
outer_t: ty::node_id_to_type(tcx, pat.id)}];
570+
for p in ps { walk(tcx, base, p, set); }
571+
}
572+
ast::pat_rec(fs, _) {
573+
let ty = ty::node_id_to_type(tcx, pat.id);
574+
for f in fs {
575+
let mut = ty::get_field(tcx, ty, f.ident).mt.mut != ast::imm;
576+
let base = base + [@{mut: mut, kind: field, outer_t: ty}];
577+
walk(tcx, base, f.pat, set);
578+
}
579+
}
580+
ast::pat_box(p) {
581+
let ty = ty::node_id_to_type(tcx, pat.id);
582+
let mut = alt ty::struct(tcx, ty) {
583+
ty::ty_box(mt) { mt.mut != ast::imm }
584+
};
585+
walk(tcx, base + [@{mut: mut, kind: unbox, outer_t: ty}], p, set);
586+
}
587+
}
588+
}
589+
let set = [];
590+
walk(tcx, *base, pat, set);
591+
ret @set;
592+
}
593+
594+
fn inner_mut(ds: @[deref]) -> [ty::t] {
595+
for d: deref in *ds { if d.mut { ret [d.outer_t]; } }
596+
ret [];
597+
}
598+
549599
// Local Variables:
550600
// mode: rust
551601
// fill-column: 78;

src/comp/middle/mut.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,6 @@ fn mut_field(ds: @[deref]) -> bool {
110110
ret false;
111111
}
112112

113-
fn inner_mut(ds: @[deref]) -> option::t<ty::t> {
114-
for d: deref in *ds { if d.mut { ret some(d.outer_t); } }
115-
ret none;
116-
}
117-
118113
// Actual mut-checking pass
119114

120115
type mut_map = std::map::hashmap<node_id, ()>;

src/comp/middle/trans_alt.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -471,22 +471,37 @@ fn make_phi_bindings(bcx: @block_ctxt, map: [exit_node],
471471
ids: ast_util::pat_id_map) -> bool {
472472
let our_block = bcx.llbb as uint;
473473
let success = true;
474-
for each item: @{key: ast::ident, val: ast::node_id} in ids.items() {
474+
for each @{key: name, val: node_id} in ids.items() {
475475
let llbbs = [];
476476
let vals = [];
477477
for ex: exit_node in map {
478478
if ex.to as uint == our_block {
479-
alt assoc(item.key, ex.bound) {
479+
alt assoc(name, ex.bound) {
480480
some(val) { llbbs += [ex.from]; vals += [val]; }
481481
none. { }
482482
}
483483
}
484484
}
485485
if vec::len(vals) > 0u {
486-
let phi = Phi(bcx, val_ty(vals[0]), vals, llbbs);
487-
bcx.fcx.lllocals.insert(item.val, phi);
486+
let local = Phi(bcx, val_ty(vals[0]), vals, llbbs);
487+
bcx.fcx.lllocals.insert(node_id, local);
488488
} else { success = false; }
489489
}
490+
if success {
491+
// Copy references that the alias analysis considered unsafe
492+
for each @{val: node_id, _} in ids.items() {
493+
if bcx_ccx(bcx).copy_map.contains_key(node_id) {
494+
let local = bcx.fcx.lllocals.get(node_id);
495+
let e_ty = ty::node_id_to_type(bcx_tcx(bcx), node_id);
496+
let {bcx: abcx, val: alloc} = trans::alloc_ty(bcx, e_ty);
497+
bcx = trans::copy_val(abcx, trans::INIT, alloc,
498+
load_if_immediate(abcx, local, e_ty),
499+
e_ty);
500+
add_clean(bcx, alloc, e_ty);
501+
bcx.fcx.lllocals.insert(node_id, alloc);
502+
}
503+
}
504+
}
490505
ret success;
491506
}
492507

src/comp/middle/ty.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export expr_ty_params_and_ty;
4545
export fold_ty;
4646
export field;
4747
export field_idx;
48+
export get_field;
4849
export fm_general;
4950
export get_element_type;
5051
export hash_ty;
@@ -1680,6 +1681,16 @@ fn field_idx(sess: session::session, sp: span, id: ast::ident,
16801681
sess.span_fatal(sp, "unknown field '" + id + "' of record");
16811682
}
16821683

1684+
fn get_field(tcx: ctxt, rec_ty: t, id: ast::ident) -> field {
1685+
alt struct(tcx, rec_ty) {
1686+
ty_rec(fields) {
1687+
alt vec::find({|f| str::eq(f.ident, id) }, fields) {
1688+
some(f) { ret f; }
1689+
}
1690+
}
1691+
}
1692+
}
1693+
16831694
fn method_idx(sess: session::session, sp: span, id: ast::ident,
16841695
meths: [method]) -> uint {
16851696
let i: uint = 0u;
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// error-pattern:invalidate reference x
22

3-
fn whoknows(x: @mutable int) { *x = 10; }
3+
fn whoknows(x: @mutable {mutable x: int}) { x.x = 10; }
44

55
fn main() {
6-
let box = @mutable 1;
6+
let box = @mutable {mutable x: 1};
77
alt *box { x { whoknows(box); log_err x; } }
88
}

src/test/compile-fail/unsafe-alt.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// error-pattern:invalidate reference i
22

3-
tag foo { left(int); right(bool); }
3+
tag foo { left({mutable x: int}); right(bool); }
44

55
fn main() {
6-
let x = left(10);
6+
let x = left({mutable x: 10});
77
alt x { left(i) { x = right(false); log i; } _ { } }
88
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn main() {
2+
let x = @{mutable a: @10, b: @20};
3+
alt x {
4+
@{a, b} { assert *a == 10; (*x).a = @30; assert *a == 10; }
5+
}
6+
}

0 commit comments

Comments
 (0)