Skip to content

Commit b2cac5a

Browse files
committed
Implement typestate checking for move-mode args. Un-XFAIL compile-fail/move-arg.rs.
1 parent f7749b1 commit b2cac5a

File tree

4 files changed

+110
-32
lines changed

4 files changed

+110
-32
lines changed

src/comp/middle/tstate/auxiliary.rs

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,14 +1093,45 @@ fn locals_to_bindings(locals : &(@local)[]) -> binding[] {
10931093
ivec::map(local_to_bindings, locals)
10941094
}
10951095

1096-
fn anon_bindings(es : &(@expr)[]) -> binding[] {
1097-
fn expr_to_initializer(e : &@expr) -> initializer {
1098-
{op: init_assign, expr: e}
1096+
fn callee_modes(fcx: &fn_ctxt, callee: node_id) -> ty::mode[] {
1097+
let ty = ty::type_autoderef(fcx.ccx.tcx,
1098+
ty::node_id_to_type(fcx.ccx.tcx, callee));
1099+
alt ty::struct(fcx.ccx.tcx, ty) {
1100+
ty::ty_fn(_, args, _, _, _)
1101+
| ty::ty_native_fn(_, args, _) {
1102+
let modes = ~[];
1103+
for arg: ty::arg in args {
1104+
modes += ~[arg.mode];
1105+
}
1106+
ret modes;
1107+
}
1108+
_ {
1109+
// Shouldn't happen; callee should be ty_fn.
1110+
fcx.ccx.tcx.sess.bug("non-fn callee type in callee_modes: "
1111+
+ util::ppaux::ty_to_str(fcx.ccx.tcx, ty));
1112+
}
1113+
}
1114+
}
1115+
1116+
fn callee_arg_init_ops(fcx: &fn_ctxt, callee: node_id) -> init_op[] {
1117+
fn mode_to_op(m: &ty::mode) -> init_op {
1118+
alt m {
1119+
ty::mo_move. { init_move }
1120+
_ { init_assign }
1121+
}
1122+
}
1123+
ivec::map(mode_to_op, callee_modes(fcx, callee))
1124+
}
1125+
1126+
fn anon_bindings(ops: &init_op[], es : &(@expr)[]) -> binding[] {
1127+
let bindings: binding[] = ~[];
1128+
let i = 0;
1129+
for op: init_op in ops {
1130+
bindings += ~[{lhs: ~[],
1131+
rhs: some({op:op, expr: es.(i)})}];
1132+
i += 1;
10991133
}
1100-
ret ivec::map(fn (e : &@expr) -> binding {
1101-
{lhs: ~[],
1102-
rhs: some(expr_to_initializer(e)) } },
1103-
es);
1134+
ret bindings;
11041135
}
11051136

11061137
//

src/comp/middle/tstate/pre_post_conditions.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,18 @@ fn handle_var(fcx: &fn_ctxt, rslt: &pre_and_post, id: node_id, name: ident) {
316316
}
317317
}
318318
319+
fn forget_args_moved_in(fcx: &fn_ctxt, parent: &@expr,
320+
modes: &ty::mode[],
321+
operands: &(@expr)[]) {
322+
let i = 0;
323+
for mode: ty::mode in modes {
324+
if mode == ty::mo_move {
325+
forget_in_postcond(fcx, parent.id, operands.(i).id);
326+
}
327+
i += 1;
328+
}
329+
}
330+
319331
/* Fills in annotations as a side effect. Does not rebuild the expr */
320332
fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
321333
let enclosing = fcx.enclosing;
@@ -336,6 +348,8 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
336348
require(i, expr_pp(fcx.ccx, e));
337349
}
338350
351+
forget_args_moved_in(fcx, e, callee_modes(fcx, operator.id),
352+
operands);
339353
340354
/* if this is a failing call, its postcondition sets everything */
341355
alt controlflow_expr(fcx.ccx, operator) {
@@ -347,6 +361,8 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
347361
let /* copy */args = operands;
348362
args += ~[operator];
349363
find_pre_post_exprs(fcx, args, e.id);
364+
forget_args_moved_in(fcx, e, callee_modes(fcx, operator.id),
365+
operands);
350366
}
351367
expr_vec(args, _, _) { find_pre_post_exprs(fcx, args, e.id); }
352368
expr_path(p) {
@@ -544,14 +560,21 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
544560

545561
expr_bind(operator, maybe_args) {
546562
let args = ~[];
547-
for expr_opt: option::t[@expr] in maybe_args {
563+
let cmodes = callee_modes(fcx, operator.id);
564+
let modes = ~[];
565+
let i = 0;
566+
for expr_opt: option::t[@expr] in maybe_args {
548567
alt expr_opt {
549568
none. {/* no-op */ }
550-
some(expr) { args += ~[expr]; }
569+
some(expr) {
570+
modes += ~[cmodes.(i)];
571+
args += ~[expr];
572+
}
551573
}
574+
i += 1;
552575
}
553576
args += ~[operator]; /* ??? order of eval? */
554-
577+
forget_args_moved_in(fcx, e, modes, args);
555578
find_pre_post_exprs(fcx, args, e.id);
556579
}
557580
expr_break. { clear_pp(expr_pp(fcx.ccx, e)); }

src/comp/middle/tstate/states.rs

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ fn handle_move_or_copy(fcx: &fn_ctxt, post: &poststate, rhs_path: &path,
4545
// not a local -- do nothing
4646
}
4747
}
48-
if (init_op == init_move) {
49-
forget_in_poststate(fcx, post, rhs_id);
50-
}
5148
}
5249

5350
fn seq_states(fcx: &fn_ctxt, pres: &prestate, bindings: &binding[])
@@ -67,10 +64,14 @@ fn seq_states(fcx: &fn_ctxt, pres: &prestate, bindings: &binding[])
6764
handle_move_or_copy(fcx, post, p, an_init.expr.id, i,
6865
an_init.op);
6966
}
70-
_ {}
67+
_ { }
7168
}
7269
set_in_poststate_ident(fcx, i.node, i.ident, post);
7370
}
71+
// Forget the RHS if we just moved it.
72+
if an_init.op == init_move {
73+
forget_in_poststate(fcx, post, an_init.expr.id);
74+
}
7475
}
7576
none {
7677
for i: inst in b.lhs {
@@ -162,16 +163,24 @@ fn find_pre_post_state_two(fcx: &fn_ctxt, pres: &prestate, lhs: &@expr,
162163
}
163164

164165
fn find_pre_post_state_call(fcx: &fn_ctxt, pres: &prestate, a: &@expr,
165-
id: node_id, bs: &(@expr)[], cf: controlflow) ->
166-
bool {
166+
id: node_id, ops: &init_op[], bs: &(@expr)[],
167+
cf: controlflow) -> bool {
167168
let changed = find_pre_post_state_expr(fcx, pres, a);
168-
ret find_pre_post_state_exprs(fcx, expr_poststate(fcx.ccx, a), id, bs, cf)
169+
if ivec::len(bs) != ivec::len(ops) {
170+
fcx.ccx.tcx.sess.span_bug(a.span,
171+
#fmt("mismatched arg lengths: \
172+
%u exprs vs. %u ops",
173+
ivec::len(bs), ivec::len(ops)));
174+
}
175+
ret find_pre_post_state_exprs(fcx, expr_poststate(fcx.ccx, a), id,
176+
ops, bs, cf)
169177
|| changed;
170178
}
171179

172180
fn find_pre_post_state_exprs(fcx: &fn_ctxt, pres: &prestate, id: node_id,
173-
es: &(@expr)[], cf: controlflow) -> bool {
174-
let rs = seq_states(fcx, pres, anon_bindings(es));
181+
ops: &init_op[], es: &(@expr)[],
182+
cf: controlflow) -> bool {
183+
let rs = seq_states(fcx, pres, anon_bindings(ops, es));
175184
let changed = rs.changed | set_prestate_ann(fcx.ccx, id, pres);
176185
/* if this is a failing call, it sets everything as initialized */
177186
alt cf {
@@ -304,23 +313,39 @@ fn find_pre_post_state_expr(fcx: &fn_ctxt, pres: &prestate, e: @expr) ->
304313

305314
alt e.node {
306315
expr_vec(elts, _, _) {
307-
ret find_pre_post_state_exprs(fcx, pres, e.id, elts, return);
316+
ret find_pre_post_state_exprs(fcx, pres, e.id,
317+
ivec::init_elt(init_assign,
318+
ivec::len(elts)),
319+
elts, return);
308320
}
309321
expr_call(operator, operands) {
310-
ret find_pre_post_state_call(fcx, pres, operator, e.id, operands,
322+
ret find_pre_post_state_call(fcx, pres, operator, e.id,
323+
callee_arg_init_ops(fcx, operator.id),
324+
operands,
311325
controlflow_expr(fcx.ccx, operator));
312326
}
313327
expr_spawn(_, _, operator, operands) {
314-
ret find_pre_post_state_call(fcx, pres, operator, e.id, operands,
315-
return);
328+
ret find_pre_post_state_call(fcx, pres, operator, e.id,
329+
callee_arg_init_ops(fcx, operator.id),
330+
operands, return);
316331
}
317332
expr_bind(operator, maybe_args) {
318333
let args = ~[];
319-
for a_opt: option::t[@expr] in maybe_args {
320-
alt a_opt { none. {/* no-op */ } some(a) { args += ~[a]; } }
334+
let callee_ops = callee_arg_init_ops(fcx, operator.id);
335+
let ops = ~[];
336+
let i = 0;
337+
for a_opt: option::t[@expr] in maybe_args {
338+
alt a_opt {
339+
none. {/* no-op */ }
340+
some(a) {
341+
ops += ~[callee_ops.(i)];
342+
args += ~[a];
343+
}
344+
}
345+
i += 1;
321346
}
322-
323-
ret find_pre_post_state_call(fcx, pres, operator, e.id, args, return);
347+
ret find_pre_post_state_call(fcx, pres, operator, e.id, ops, args,
348+
return);
324349
}
325350
expr_path(_) { ret pure_exp(fcx.ccx, e.id, pres); }
326351
expr_log(_, ex) {
@@ -347,7 +372,10 @@ fn find_pre_post_state_expr(fcx: &fn_ctxt, pres: &prestate, e: @expr) ->
347372
}
348373
expr_rec(fields, maybe_base) {
349374
let changed =
350-
find_pre_post_state_exprs(fcx, pres, e.id, field_exprs(fields),
375+
find_pre_post_state_exprs(fcx, pres, e.id,
376+
ivec::init_elt(init_assign,
377+
ivec::len(fields)),
378+
field_exprs(fields),
351379
return);
352380
alt maybe_base {
353381
none. {/* do nothing */ }

src/test/compile-fail/move-arg.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
// xfail-stage0
2-
// xfail-stage1
3-
// xfail-stage2
4-
// xfail-stage3
51
// error-pattern: Unsatisfied precondition constraint
62
fn test(foo: -int) {
73
assert (foo == 10);

0 commit comments

Comments
 (0)